Repository: JosefNemec/Playnite Branch: master Commit: ab2eed4d09ea Files: 1302 Total size: 11.5 MB Directory structure: gitextract__t5v_oi7/ ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── custom.md │ │ └── feature_request.yml │ └── pull_request_template.md ├── .gitignore ├── LICENSE.md ├── README.md ├── appveyor.yml ├── build/ │ ├── ExtensionsRefIgnoreList.txt │ ├── PlayniteSDK.nuspec │ ├── VerifyLanguageFiles.ps1 │ ├── applyEnglishProofing.ps1 │ ├── build.ps1 │ ├── build.xml │ ├── buildLocConstants.ps1 │ ├── buildSdkNuget.ps1 │ ├── common.ps1 │ ├── generateRetroArchProfile.ps1 │ └── updateLocalizations.ps1 ├── crowdin.yml ├── media/ │ └── SplashScreen.xcf ├── references/ │ └── Windows.winmd ├── source/ │ ├── .editorconfig │ ├── Playnite/ │ │ ├── API/ │ │ │ ├── AddonsAPI.cs │ │ │ ├── DatabaseAPI.cs │ │ │ ├── DesignData/ │ │ │ │ ├── DesignNotificationsAPI.cs │ │ │ │ └── DesignPlayniteAPI.cs │ │ │ ├── NotificationsAPI.cs │ │ │ ├── PlayniteAPI.cs │ │ │ ├── PlayniteInfoAPI.cs │ │ │ ├── PlaynitePathsAPI.cs │ │ │ └── PlayniteSettingsAPI.cs │ │ ├── Addons/ │ │ │ └── Addons.cs │ │ ├── App/ │ │ │ ├── CmdLineOptions.cs │ │ │ ├── IPlayniteApplication.cs │ │ │ ├── PlayniteApplication.cs │ │ │ ├── UpdateManifest.cs │ │ │ └── Updater.cs │ │ ├── App.config │ │ ├── Archive.cs │ │ ├── Audio.cs │ │ ├── Backup.cs │ │ ├── Behaviors/ │ │ │ ├── AnimatedVisibility.cs │ │ │ ├── ExpanderBehaviors.cs │ │ │ ├── FocusBahaviors.cs │ │ │ ├── LeftClickContextMenuBehavior.cs │ │ │ ├── MediaElementBehaviors.cs │ │ │ ├── ScrollToSelectedBehavior.cs │ │ │ ├── ScrollViewerBehaviours.cs │ │ │ └── SelectorBehaviors.cs │ │ ├── BindingProxy.cs │ │ ├── CefTools.cs │ │ ├── CmdlineCommands.cs │ │ ├── Commands/ │ │ │ ├── GenericCommands.cs │ │ │ └── GlobalCommands.cs │ │ ├── Common/ │ │ │ ├── BindingTools.cs │ │ │ ├── Computer.cs │ │ │ ├── Constants.cs │ │ │ ├── CueSheet.cs │ │ │ ├── DesignerTools.cs │ │ │ ├── Exceptions.cs │ │ │ ├── Explorer.cs │ │ │ ├── Extensions/ │ │ │ │ ├── BitmapExtensions.cs │ │ │ │ ├── BitmapIconExtensions.cs │ │ │ │ ├── CloneObject.cs │ │ │ │ ├── Dictionary.cs │ │ │ │ ├── Enums.cs │ │ │ │ ├── IconExtension.cs │ │ │ │ ├── ItemsControlExtensions.cs │ │ │ │ ├── KeyExtensions.cs │ │ │ │ ├── LongExtensions.cs │ │ │ │ ├── NetExtensions.cs │ │ │ │ ├── ObjectExtensions.cs │ │ │ │ ├── ProcessExtensions.cs │ │ │ │ ├── StringExtensions.cs │ │ │ │ └── WindowExtensions.cs │ │ │ ├── FileSystem.cs │ │ │ ├── FileSystem_Checksum.cs │ │ │ ├── GdiFile.cs │ │ │ ├── GlobalRandom.cs │ │ │ ├── Images.cs │ │ │ ├── IniParser.cs │ │ │ ├── ItemsSource.cs │ │ │ ├── M3U.cs │ │ │ ├── MarkupConverter.cs │ │ │ ├── Media/ │ │ │ │ └── Icons/ │ │ │ │ ├── IconExtractor.cs │ │ │ │ └── IconUtil.cs │ │ │ ├── MemoryCache.cs │ │ │ ├── NLogLogProvider.cs │ │ │ ├── Network.cs │ │ │ ├── Paths.cs │ │ │ ├── ProcessMonitor.cs │ │ │ ├── ProcessStarter.cs │ │ │ ├── Programs.cs │ │ │ ├── Programs2.cs │ │ │ ├── Resources.cs │ │ │ ├── Roman.cs │ │ │ ├── SafeFileEnumerator.cs │ │ │ ├── Serialization.cs │ │ │ ├── SigningTools.cs │ │ │ ├── Sizes.cs │ │ │ ├── Sqlite.cs │ │ │ ├── SystemDialogs.cs │ │ │ ├── TGASharpLib.cs │ │ │ ├── TempDirectory.cs │ │ │ ├── Timer.cs │ │ │ ├── Units.cs │ │ │ ├── Web/ │ │ │ │ ├── Downloader.cs │ │ │ │ └── HttpDownloader.cs │ │ │ ├── Xaml.cs │ │ │ └── Xml.cs │ │ ├── Common.config │ │ ├── ControlTemplateTools.cs │ │ ├── Controllers/ │ │ │ ├── GameControllerFactory.cs │ │ │ └── GenericGameController.cs │ │ ├── Controls/ │ │ │ ├── ExtendedDataGrid.cs │ │ │ ├── ExtendedListBox.cs │ │ │ ├── ExtendedListView.cs │ │ │ ├── FadeImage.xaml │ │ │ ├── FadeImage.xaml.cs │ │ │ ├── GridEx.cs │ │ │ ├── HotKeyBox.cs │ │ │ ├── HtmlTextView.cs │ │ │ └── WindowBase.cs │ │ ├── Converters/ │ │ │ ├── BidirectionalEnumAndNumberConverter.cs │ │ │ ├── BoolToAutoWidthConverter.cs │ │ │ ├── BoolToYesNoConverter.cs │ │ │ ├── BooleanToHiddenConverter.cs │ │ │ ├── BooleanToVisibilityConverter.cs │ │ │ ├── CoversZoomToPercentageConverter.cs │ │ │ ├── DateTimeToLastPlayedConverter.cs │ │ │ ├── DockToStringConverter.cs │ │ │ ├── EnumToBooleanConverter.cs │ │ │ ├── EnumToVisibilityConverter.cs │ │ │ ├── GenericTypeConverter.cs │ │ │ ├── ICollectionNullOrEmptyToVisibilityConverter.cs │ │ │ ├── IconToImageSourceConverter.cs │ │ │ ├── ImageStringToImageConverter.cs │ │ │ ├── IntToVisibilityConverter.cs │ │ │ ├── InvertableBooleanToVisibilityConverter.cs │ │ │ ├── InvertedBoolenConverter.cs │ │ │ ├── ListToStringConverter.cs │ │ │ ├── NegateConverter.cs │ │ │ ├── NotificationIconConverter.cs │ │ │ ├── NullToBoolConverter.cs │ │ │ ├── NullToDependencyPropertyUnsetConverter.cs │ │ │ ├── NullToVisibilityConverter.cs │ │ │ ├── NullableDateToStringConverter.cs │ │ │ ├── NullableUlongBytesSizeToStringConverter.cs │ │ │ ├── NumericConverters.cs │ │ │ ├── ObjectEqualityToBoolConverter.cs │ │ │ ├── ObjectToStringConverter.cs │ │ │ ├── OpacityBoolConverter.cs │ │ │ ├── PlayTimeToStringConverter.cs │ │ │ ├── SortingOrderToStringConverter.cs │ │ │ ├── StrechToStringConverter.cs │ │ │ ├── StringNullOrEmptyToBoolConverter.cs │ │ │ ├── StringNullOrEmptyToVisibilityConverter.cs │ │ │ ├── StringToUpperCaseConverter.cs │ │ │ ├── TicksToTimeSpanConverter.cs │ │ │ ├── ValueConverterGroup.cs │ │ │ └── WidthToFontSizeConverter.cs │ │ ├── Database/ │ │ │ ├── Collections/ │ │ │ │ ├── AgeRatingsCollection.cs │ │ │ │ ├── AppSoftwareCollection.cs │ │ │ │ ├── CategoriesCollection.cs │ │ │ │ ├── CompaniesCollection.cs │ │ │ │ ├── CompletionStatusesCollection.cs │ │ │ │ ├── EmulatorsCollection.cs │ │ │ │ ├── FeaturesCollection.cs │ │ │ │ ├── FilterPresetsCollection.cs │ │ │ │ ├── GameScannersCollection.cs │ │ │ │ ├── GamesCollection.cs │ │ │ │ ├── GamesSourcesCollection.cs │ │ │ │ ├── GenresCollection.cs │ │ │ │ ├── ImportExclusionsCollection.cs │ │ │ │ ├── ItemCollection.cs │ │ │ │ ├── LiteDBFileReaderV7.cs │ │ │ │ ├── PlatformsCollection.cs │ │ │ │ ├── RegionsCollection.cs │ │ │ │ ├── SeriesCollection.cs │ │ │ │ └── TagsCollection.cs │ │ │ ├── DatabaseExplorer.cs │ │ │ ├── DatabaseFileEvent.cs │ │ │ ├── DatabaseFilter.cs │ │ │ ├── DatabaseSettings.cs │ │ │ ├── DatabaseStats.cs │ │ │ ├── EventBufferHandler.cs │ │ │ ├── GameDatabase.cs │ │ │ ├── GameDatabaseMigration.cs │ │ │ ├── GameDatabase_Filters.cs │ │ │ ├── GameFieldComparer.cs │ │ │ ├── InMemoryGameDatabase.cs │ │ │ └── OldModels/ │ │ │ ├── NewVer1/ │ │ │ │ ├── OldCompletionStatus.cs │ │ │ │ ├── OldDatabaseObject.cs │ │ │ │ ├── OldGame.cs │ │ │ │ ├── OldGameAction.cs │ │ │ │ └── OldLink.cs │ │ │ └── Ver2.cs │ │ ├── DateTimes.cs │ │ ├── Diagnostic.cs │ │ ├── DiagnosticPackageInfo.cs │ │ ├── DialogsFactory.cs │ │ ├── DiscordManager.cs │ │ ├── EasyAntiCheat.cs │ │ ├── ElementTreeHelper.cs │ │ ├── Emulation/ │ │ │ ├── Emulators/ │ │ │ │ ├── 4DO/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Altirra/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Atari800/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── BGB/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── BigPEmu/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── BizHawk/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── BlastEm/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Cemu/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Citra/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Cxbx-Reloaded/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── DOSBox/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── DeSmuME/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Dolphin/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── DuckStation/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── FCEUX/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── FS-UAE/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── FlashPlayerProjector/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Flycast/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Fuse/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── GBE+/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Gambatte/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── KegaFusion/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Lime3DS/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── LocaleEmulator/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── M64Py/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Mednafen/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Mesen/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Mesen-S/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── NanoboyAdvance/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Nestopia/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── NullDC/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── PCSX2/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── PCSXR-PGXP/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── PCem/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── PPSSPP/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Project64/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── RPCS3/ │ │ │ │ │ ├── emulator.yaml │ │ │ │ │ └── importGames.ps1 │ │ │ │ ├── Reicast/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── RetroArch/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── RosaliesMupenGui/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Ruffle/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Ryujinx/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── SameBoy/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── ScummVM/ │ │ │ │ │ ├── emulator.yaml │ │ │ │ │ └── importGames.ps1 │ │ │ │ ├── Snes9X/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Stella/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── VirtualJaguar/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── VisualBoyAdvance/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── VisualBoyAdvance-M/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Vita3K/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── WinUAE/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── WinVice/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Xemu/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Xenia/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Yabuse/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── Ymir/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── ZSNES/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── ares/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── blueMSX/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── bsnes/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── bsnes-hd/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── bsnes-mt/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── decaf-emu/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── ePSXe/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── gopher64/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── higan/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── jgenesis/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── m64p/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── mGBA/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── melonDS/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── puNES/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── redream/ │ │ │ │ │ └── emulator.yaml │ │ │ │ ├── shadPS4/ │ │ │ │ │ ├── emulator.yaml │ │ │ │ │ └── importGames.ps1 │ │ │ │ ├── simple64/ │ │ │ │ │ └── emulator.yaml │ │ │ │ └── yuzu/ │ │ │ │ └── emulator.yaml │ │ │ ├── Platforms.yaml │ │ │ └── Regions.yaml │ │ ├── Emulators/ │ │ │ ├── DatModels.cs │ │ │ ├── Emulation.cs │ │ │ ├── EmulationDatabase.cs │ │ │ └── Scanner.cs │ │ ├── Exceptions.cs │ │ ├── Extensions/ │ │ │ ├── ControlExtensions.cs │ │ │ ├── GameExtensions.cs │ │ │ ├── IProvideValueTarget.cs │ │ │ ├── Markup/ │ │ │ │ ├── Api.cs │ │ │ │ ├── BindingExtension.cs │ │ │ │ ├── MainViewModel.cs │ │ │ │ ├── PluginConverter.cs │ │ │ │ ├── PluginSettings.cs │ │ │ │ ├── PluginStatus.cs │ │ │ │ ├── Settings.cs │ │ │ │ ├── ThemeFile.cs │ │ │ │ └── ThemeFileBinding.cs │ │ │ └── ServiceProvider.cs │ │ ├── FakePlayniteLibraryPlugin.cs │ │ ├── GameTools.cs │ │ ├── GamesCollectionView.cs │ │ ├── GamesCollectionViewEntry.cs │ │ ├── GamesEditor.cs │ │ ├── GlobalTaskHandler.cs │ │ ├── GoogleImageDownloader.cs │ │ ├── HdrUtilities.cs │ │ ├── HotKey.cs │ │ ├── HttpFileCache.cs │ │ ├── ImageSourceManager.cs │ │ ├── Input/ │ │ │ ├── GameController.cs │ │ │ └── MouseWheelGesture.cs │ │ ├── ItemSelector.cs │ │ ├── Localization/ │ │ │ ├── LocSource.xaml │ │ │ ├── LocalizationKeys.cs │ │ │ ├── af_ZA.xaml │ │ │ ├── ar_SA.xaml │ │ │ ├── bg_BG.xaml │ │ │ ├── ca_ES.xaml │ │ │ ├── cs_CZ.xaml │ │ │ ├── cy_GB.xaml │ │ │ ├── da_DK.xaml │ │ │ ├── de_DE.xaml │ │ │ ├── el_GR.xaml │ │ │ ├── en_US.xaml │ │ │ ├── eo_UY.xaml │ │ │ ├── es_ES.xaml │ │ │ ├── et_EE.xaml │ │ │ ├── fa_IR.xaml │ │ │ ├── fi_FI.xaml │ │ │ ├── fr_FR.xaml │ │ │ ├── ga_IE.xaml │ │ │ ├── gl_ES.xaml │ │ │ ├── he_IL.xaml │ │ │ ├── hr_HR.xaml │ │ │ ├── hu_HU.xaml │ │ │ ├── id_ID.xaml │ │ │ ├── it_IT.xaml │ │ │ ├── ja_JP.xaml │ │ │ ├── ko_KR.xaml │ │ │ ├── locstatus.json │ │ │ ├── lt_LT.xaml │ │ │ ├── mr_IN.xaml │ │ │ ├── nl_NL.xaml │ │ │ ├── no_NO.xaml │ │ │ ├── pl_PL.xaml │ │ │ ├── pt_BR.xaml │ │ │ ├── pt_PT.xaml │ │ │ ├── ro_RO.xaml │ │ │ ├── ru_RU.xaml │ │ │ ├── si_LK.xaml │ │ │ ├── sk_SK.xaml │ │ │ ├── sl_SI.xaml │ │ │ ├── sr_SP.xaml │ │ │ ├── sv_SE.xaml │ │ │ ├── tr_TR.xaml │ │ │ ├── uk_UA.xaml │ │ │ ├── vi_VN.xaml │ │ │ ├── zh_CN.xaml │ │ │ └── zh_TW.xaml │ │ ├── Localization.cs │ │ ├── Manifests/ │ │ │ ├── AddonManifestBase.cs │ │ │ ├── AddonManifests.cs │ │ │ ├── ExtensionManifest.cs │ │ │ └── ThemeManifest.cs │ │ ├── MenuHelpers.cs │ │ ├── MenuItems.cs │ │ ├── Metadata/ │ │ │ ├── MetadataDownloader.cs │ │ │ ├── MetadataDownloaderSettings.cs │ │ │ └── MetadataFileExtensions.cs │ │ ├── Native/ │ │ │ ├── Fileapi.cs │ │ │ ├── Gdi32.cs │ │ │ ├── Kernel32.cs │ │ │ ├── Ntdll.cs │ │ │ ├── Powrprof.cs │ │ │ ├── Processthreadsapi.cs │ │ │ ├── Psapi.cs │ │ │ ├── Shell32.cs │ │ │ ├── Shlwapi.cs │ │ │ ├── User32.cs │ │ │ ├── WinError.cs │ │ │ ├── Winbase.cs │ │ │ ├── Windef.cs │ │ │ ├── Wingdi.cs │ │ │ ├── Winnt.cs │ │ │ ├── Wintrust.cs │ │ │ └── winuser.cs │ │ ├── ObservablePowerStatus.cs │ │ ├── ObservableTime.cs │ │ ├── PipeServer.cs │ │ ├── Playnite.csproj │ │ ├── PlayniteEnvironment.cs │ │ ├── PlayniteProcess.cs │ │ ├── PlayniteUriHandler.cs │ │ ├── Plugins/ │ │ │ ├── ExtensionFactory.cs │ │ │ └── ExtensionInstaller.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── SDL2.cs │ │ ├── SDL2_mixer.cs │ │ ├── Safe Mode.bat │ │ ├── Scripting/ │ │ │ ├── PlayniteScript.cs │ │ │ └── PowerShell/ │ │ │ ├── PowerShell.cs │ │ │ └── PowerShellScript.cs │ │ ├── SdkHelpers.cs │ │ ├── SelectableItem.cs │ │ ├── Services/ │ │ │ ├── BaseServicesClient.cs │ │ │ ├── GenericResponse.cs │ │ │ └── ServicesClient.cs │ │ ├── Settings/ │ │ │ ├── AutoClientShutdownSettings.cs │ │ │ ├── DetailsVisibilitySettings.cs │ │ │ ├── FilterSettings.cs │ │ │ ├── FullscreenSettings.cs │ │ │ ├── OldSettings/ │ │ │ │ └── Plugins.cs │ │ │ ├── PlaynitePaths.cs │ │ │ ├── PlayniteSettings.cs │ │ │ ├── SearchWindowVisibilitySettings.cs │ │ │ ├── SettingsAttributes.cs │ │ │ ├── ViewProperties.cs │ │ │ └── WindowPositions.cs │ │ ├── SortableNameConverter.cs │ │ ├── SystemIntegration.cs │ │ ├── Themes.cs │ │ ├── ThirdPartyClients/ │ │ │ └── ThirdPartyToolsList.cs │ │ ├── UrlConstants.cs │ │ ├── ViewModels/ │ │ │ ├── AddonsViewModelBase_Online.cs │ │ │ ├── CrashHandlerViewModel.cs │ │ │ ├── ItemSelectionViewModel.cs │ │ │ ├── LicenseAgreementViewModel.cs │ │ │ ├── MainViewModelBase.cs │ │ │ ├── ProgressViewViewModel.cs │ │ │ ├── RandomGameSelectViewModel.cs │ │ │ ├── SearchViewModel.cs │ │ │ └── UpdateViewModel.cs │ │ ├── WebView/ │ │ │ ├── CookieDestroyer.cs │ │ │ ├── OffscreenWebView.cs │ │ │ ├── WebView.cs │ │ │ ├── WebViewBase.cs │ │ │ └── WebViewFactory.cs │ │ ├── Windows/ │ │ │ ├── CrashHandlerWindowFactory.cs │ │ │ ├── ExtensionCrashHandlerWindowFactory.cs │ │ │ ├── ItemSelectorWindowFactory.cs │ │ │ ├── LicenseAgreementWindowFactory.cs │ │ │ ├── ProgressWindowFactory.cs │ │ │ ├── UpdateWindowFactory.cs │ │ │ ├── WebViewWindow.xaml │ │ │ ├── WebViewWindow.xaml.cs │ │ │ ├── WindowFactory.cs │ │ │ ├── WindowManager.cs │ │ │ └── WindowPositionHandler.cs │ │ ├── WindowsNotifyIconManager.cs │ │ ├── crash_reporter.cfg │ │ ├── gamecontrollerdb.txt │ │ ├── license.txt │ │ └── packages.config │ ├── Playnite.DesktopApp/ │ │ ├── Api/ │ │ │ └── MainViewAPI.cs │ │ ├── App.config │ │ ├── App.xaml │ │ ├── App.xaml.cs │ │ ├── ControlGalleryView.xaml │ │ ├── Controls/ │ │ │ ├── AddonsSections/ │ │ │ │ ├── AddonUpdates.xaml │ │ │ │ ├── AddonUpdates.xaml.cs │ │ │ │ ├── BrowseAddons.xaml │ │ │ │ ├── BrowseAddons.xaml.cs │ │ │ │ ├── InstalledExtensions.xaml │ │ │ │ ├── InstalledExtensions.xaml.cs │ │ │ │ ├── InstalledThemes.xaml │ │ │ │ └── InstalledThemes.xaml.cs │ │ │ ├── ComboBoxList.cs │ │ │ ├── ComboBoxListBase.cs │ │ │ ├── DdItemListSelectionBox.cs │ │ │ ├── ExpanderEx.cs │ │ │ ├── FilterEnumSelectionBox.cs │ │ │ ├── FilterSelectionBox.cs │ │ │ ├── FilterStringSelectionBox.cs │ │ │ ├── GameListItem.cs │ │ │ ├── GameTaskView.xaml │ │ │ ├── GameTaskView.xaml.cs │ │ │ ├── GamesGridView.xaml │ │ │ ├── GamesGridView.xaml.cs │ │ │ ├── GridViewPanel.cs │ │ │ ├── LibraryStatistics.xaml │ │ │ ├── LibraryStatistics.xaml.cs │ │ │ ├── LongNumericBox.cs │ │ │ ├── Menus/ │ │ │ │ ├── FilterPresetsMenu.cs │ │ │ │ ├── GameGroupMenu.cs │ │ │ │ ├── GameMenu.cs │ │ │ │ ├── GroupSettingsMenu.cs │ │ │ │ ├── MainMenu.cs │ │ │ │ ├── SortSettingsMenu.cs │ │ │ │ ├── TrayContextMenu.cs │ │ │ │ ├── ViewSelectionMenu.cs │ │ │ │ └── ViewSettingsMenu.cs │ │ │ ├── MetadataDownloadSettings.xaml │ │ │ ├── MetadataDownloadSettings.xaml.cs │ │ │ ├── NullableIntBox.cs │ │ │ ├── NumericDoubleBox.cs │ │ │ ├── PathSelectionBox.cs │ │ │ ├── SearchBox.cs │ │ │ ├── SettingsSections/ │ │ │ │ ├── AppearanceAdvanced.xaml │ │ │ │ ├── AppearanceAdvanced.xaml.cs │ │ │ │ ├── AppearanceDetailsView.xaml │ │ │ │ ├── AppearanceDetailsView.xaml.cs │ │ │ │ ├── AppearanceGeneral.xaml │ │ │ │ ├── AppearanceGeneral.xaml.cs │ │ │ │ ├── AppearanceGridView.xaml │ │ │ │ ├── AppearanceGridView.xaml.cs │ │ │ │ ├── AppearanceLayout.xaml │ │ │ │ ├── AppearanceLayout.xaml.cs │ │ │ │ ├── AppearanceListView.xaml │ │ │ │ ├── AppearanceListView.xaml.cs │ │ │ │ ├── AppearanceTopPanel.xaml │ │ │ │ ├── AppearanceTopPanel.xaml.cs │ │ │ │ ├── Backup.xaml │ │ │ │ ├── Backup.xaml.cs │ │ │ │ ├── ClientShutdown.xaml │ │ │ │ ├── ClientShutdown.xaml.cs │ │ │ │ ├── Development.xaml │ │ │ │ ├── Development.xaml.cs │ │ │ │ ├── EmptyParent.xaml │ │ │ │ ├── EmptyParent.xaml.cs │ │ │ │ ├── ErrorLoading.xaml │ │ │ │ ├── ErrorLoading.xaml.cs │ │ │ │ ├── General.xaml │ │ │ │ ├── General.xaml.cs │ │ │ │ ├── GeneralAdvanced.xaml │ │ │ │ ├── GeneralAdvanced.xaml.cs │ │ │ │ ├── ImportExlusionList.xaml │ │ │ │ ├── ImportExlusionList.xaml.cs │ │ │ │ ├── Input.xaml │ │ │ │ ├── Input.xaml.cs │ │ │ │ ├── LibrariesConfigWindowInfo.xaml │ │ │ │ ├── LibrariesConfigWindowInfo.xaml.cs │ │ │ │ ├── Metadata.xaml │ │ │ │ ├── Metadata.xaml.cs │ │ │ │ ├── NoSettingsAvailable.xaml │ │ │ │ ├── NoSettingsAvailable.xaml.cs │ │ │ │ ├── Performance.xaml │ │ │ │ ├── Performance.xaml.cs │ │ │ │ ├── Scripting.xaml │ │ │ │ ├── Scripting.xaml.cs │ │ │ │ ├── Search.xaml │ │ │ │ ├── Search.xaml.cs │ │ │ │ ├── Sorting.xaml │ │ │ │ ├── Sorting.xaml.cs │ │ │ │ ├── Updates.xaml │ │ │ │ └── Updates.xaml.cs │ │ │ ├── SidebarItem.cs │ │ │ ├── SliderEx.cs │ │ │ ├── SliderWithPopup.xaml │ │ │ ├── SliderWithPopup.xaml.cs │ │ │ ├── TopPanelItem.cs │ │ │ └── Views/ │ │ │ ├── BaseGamesView.cs │ │ │ ├── DetailsViewGameOverview.cs │ │ │ ├── ExplorerPanel.cs │ │ │ ├── FilterPanel.cs │ │ │ ├── GameOverview.cs │ │ │ ├── GridViewGameOverview.cs │ │ │ ├── Library.cs │ │ │ ├── LibraryDetailsView.cs │ │ │ ├── LibraryGridView.cs │ │ │ ├── LibraryListView.cs │ │ │ ├── MainWindow.cs │ │ │ ├── NotificationPanel.cs │ │ │ ├── SearchWindow.cs │ │ │ ├── Sidebar.cs │ │ │ └── TopPanel.cs │ │ ├── DesktopActionSelector.cs │ │ ├── DesktopApplication.cs │ │ ├── DesktopCollectionView.cs │ │ ├── DesktopDialogs.cs │ │ ├── DesktopGamesEditor.cs │ │ ├── GlobalResources.xaml │ │ ├── Markups.cs │ │ ├── Playnite.DesktopApp.csproj │ │ ├── PluginSettingsHelper.cs │ │ ├── ProgramEntry.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ ├── Resources.resx │ │ │ ├── Settings.Designer.cs │ │ │ └── Settings.settings │ │ ├── Resources/ │ │ │ └── contributors.txt │ │ ├── Themes/ │ │ │ ├── Desktop/ │ │ │ │ └── Default/ │ │ │ │ ├── Common.xaml │ │ │ │ ├── Constants.xaml │ │ │ │ ├── CustomControls/ │ │ │ │ │ ├── ComboBoxList.xaml │ │ │ │ │ ├── ExpanderEx.xaml │ │ │ │ │ ├── ExtendedDataGrid.xaml │ │ │ │ │ ├── ExtendedListBox.xaml │ │ │ │ │ ├── ExtendedListView.xaml │ │ │ │ │ ├── FilterSelectionBox.xaml │ │ │ │ │ ├── GameGroupMenu.xaml │ │ │ │ │ ├── GameMenu.xaml │ │ │ │ │ ├── HotKeyBox.xaml │ │ │ │ │ ├── HtmlTextView.xaml │ │ │ │ │ ├── NumericBoxes.xaml │ │ │ │ │ ├── PathSelectionBox.xaml │ │ │ │ │ ├── SearchBox.xaml │ │ │ │ │ ├── SidebarItem.xaml │ │ │ │ │ ├── SliderEx.xaml │ │ │ │ │ ├── TopPanelItem.xaml │ │ │ │ │ ├── TrayContextMenu.xaml │ │ │ │ │ └── WindowBase.xaml │ │ │ │ ├── DefaultControls/ │ │ │ │ │ ├── Border.xaml │ │ │ │ │ ├── Button.xaml │ │ │ │ │ ├── CheckBox.xaml │ │ │ │ │ ├── ComboBox.xaml │ │ │ │ │ ├── ContextMenu.xaml │ │ │ │ │ ├── DataGrid.xaml │ │ │ │ │ ├── DatePicker.xaml │ │ │ │ │ ├── Expander.xaml │ │ │ │ │ ├── GridSplitter.xaml │ │ │ │ │ ├── GroupBox.xaml │ │ │ │ │ ├── Hyperlink.xaml │ │ │ │ │ ├── Label.xaml │ │ │ │ │ ├── ListBox.xaml │ │ │ │ │ ├── ListView.xaml │ │ │ │ │ ├── Menu.xaml │ │ │ │ │ ├── PasswordBox.xaml │ │ │ │ │ ├── Popup.xaml │ │ │ │ │ ├── ProgressBar.xaml │ │ │ │ │ ├── RadioButton.xaml │ │ │ │ │ ├── RepeatButton.xaml │ │ │ │ │ ├── RichTextBox.xaml │ │ │ │ │ ├── ScrollViewer.xaml │ │ │ │ │ ├── Slider.xaml │ │ │ │ │ ├── TabControl.xaml │ │ │ │ │ ├── TextBlock.xaml │ │ │ │ │ ├── TextBox.xaml │ │ │ │ │ ├── Thumb.xaml │ │ │ │ │ ├── ToggleButton.xaml │ │ │ │ │ ├── ToolTip.xaml │ │ │ │ │ └── TreeView.xaml │ │ │ │ ├── DerivedStyles/ │ │ │ │ │ ├── BottomButton.xaml │ │ │ │ │ ├── DetailsHyperlink.xaml │ │ │ │ │ ├── DetailsScrollViewer.xaml │ │ │ │ │ ├── DetailsViewGroupStyle.xaml │ │ │ │ │ ├── DetailsViewItemStyle.xaml │ │ │ │ │ ├── DetailsViewItemTemplate.xaml │ │ │ │ │ ├── GridViewGroupStyle.xaml │ │ │ │ │ ├── GridViewItemStyle.xaml │ │ │ │ │ ├── GridViewItemTemplate.xaml │ │ │ │ │ ├── HighlightBorder.xaml │ │ │ │ │ ├── ImageHighlightButton.xaml │ │ │ │ │ ├── ListViewGroupStyle.xaml │ │ │ │ │ ├── MainWindowStyle.xaml │ │ │ │ │ ├── NotificationMessage.xaml │ │ │ │ │ ├── PlayButton.xaml │ │ │ │ │ ├── PropertyItemButton.xaml │ │ │ │ │ ├── SimpleButton.xaml │ │ │ │ │ ├── StandardWindowStyle.xaml │ │ │ │ │ ├── TextBlockGameScore.xaml │ │ │ │ │ └── WindowBarButton.xaml │ │ │ │ ├── DescriptionView.html │ │ │ │ ├── Media.xaml │ │ │ │ ├── Views/ │ │ │ │ │ ├── DetailsViewGameOverview.xaml │ │ │ │ │ ├── ExplorerPanel.xaml │ │ │ │ │ ├── FilterPanelView.xaml │ │ │ │ │ ├── GridViewGameOverview.xaml │ │ │ │ │ ├── Library.xaml │ │ │ │ │ ├── LibraryDetailsView.xaml │ │ │ │ │ ├── LibraryGridView.xaml │ │ │ │ │ ├── LibraryListView.xaml │ │ │ │ │ ├── MainWindow.xaml │ │ │ │ │ ├── NotificationPanel.xaml │ │ │ │ │ ├── SearchView.xaml │ │ │ │ │ ├── Sidebar.xaml │ │ │ │ │ └── TopPanel.xaml │ │ │ │ └── theme.yaml │ │ │ └── Generic.xaml │ │ ├── ViewModels/ │ │ │ ├── AboutViewModel.cs │ │ │ ├── ActionSelectionViewModel.cs │ │ │ ├── AddonsViewModel.cs │ │ │ ├── AddonsViewModel_Installed.cs │ │ │ ├── AddonsViewModel_Online.cs │ │ │ ├── CategoryConfigViewModel.cs │ │ │ ├── DatabaseFieldsManagerViewModel.cs │ │ │ ├── DesignData/ │ │ │ │ └── DesignMainViewModel.cs │ │ │ ├── DesktopAppViewModel.cs │ │ │ ├── DesktopAppViewModel_Commands.cs │ │ │ ├── DesktopAppViewModel_Sidebar.cs │ │ │ ├── DesktopAppViewModel_TopPanel.cs │ │ │ ├── EmulatedGamesImportViewModel.cs │ │ │ ├── EmulatorImportViewModel.cs │ │ │ ├── EmulatorsViewModel.cs │ │ │ ├── FirstTimeStartupViewModel.cs │ │ │ ├── GameDetailsViewModel.cs │ │ │ ├── GameEditViewModel.cs │ │ │ ├── GameEditViewModelCommands.cs │ │ │ ├── GameEditViewModelFieldChecks.cs │ │ │ ├── GameEditViewModelMetadata.cs │ │ │ ├── GoogleImageDownloadViewModel.cs │ │ │ ├── ImageSelectionViewModel.cs │ │ │ ├── InstalledGamesViewModel.cs │ │ │ ├── ItemSelectionWithSearchViewModel.cs │ │ │ ├── LibraryIntegrationsViewModel.cs │ │ │ ├── MetadataComparisonViewModel.cs │ │ │ ├── MetadataDownloadViewModel.cs │ │ │ ├── PluginSettingsViewModel.cs │ │ │ ├── SettingsViewModel.cs │ │ │ ├── StatisticsViewModel.cs │ │ │ └── ToolsConfigViewModel.cs │ │ ├── Windows/ │ │ │ ├── AboutWindow.xaml │ │ │ ├── AboutWindow.xaml.cs │ │ │ ├── ActionSelectionWindow.xaml │ │ │ ├── ActionSelectionWindow.xaml.cs │ │ │ ├── AddonsWindow.xaml │ │ │ ├── AddonsWindow.xaml.cs │ │ │ ├── CategoryConfigWindow.xaml │ │ │ ├── CategoryConfigWindow.xaml.cs │ │ │ ├── ControlGallery.xaml │ │ │ ├── ControlGallery.xaml.cs │ │ │ ├── CrashHandlerWindow.xaml │ │ │ ├── CrashHandlerWindow.xaml.cs │ │ │ ├── DatabaseFieldsManagerWindow.xaml │ │ │ ├── DatabaseFieldsManagerWindow.xaml.cs │ │ │ ├── EmulatedGameImportWindow.xaml │ │ │ ├── EmulatedGameImportWindow.xaml.cs │ │ │ ├── EmulatorDownloadWindow.xaml │ │ │ ├── EmulatorDownloadWindow.xaml.cs │ │ │ ├── EmulatorImportWindow.xaml │ │ │ ├── EmulatorImportWindow.xaml.cs │ │ │ ├── EmulatorsWindow.xaml │ │ │ ├── EmulatorsWindow.xaml.cs │ │ │ ├── ExtensionCrashHandlerWindow.xaml │ │ │ ├── ExtensionCrashHandlerWindow.xaml.cs │ │ │ ├── FirstTimeStartupWindow.xaml │ │ │ ├── FirstTimeStartupWindow.xaml.cs │ │ │ ├── GameEditWindow.xaml │ │ │ ├── GameEditWindow.xaml.cs │ │ │ ├── GoogleImageDownloadWindow.xaml │ │ │ ├── GoogleImageDownloadWindow.xaml.cs │ │ │ ├── ImageSelectionWindow.xaml │ │ │ ├── ImageSelectionWindow.xaml.cs │ │ │ ├── InstalledGamesWindow.xaml │ │ │ ├── InstalledGamesWindow.xaml.cs │ │ │ ├── ItemSelectionWithSearchWindow.xaml │ │ │ ├── ItemSelectionWithSearchWindow.xaml.cs │ │ │ ├── LibraryIntegrationsWindow.xaml │ │ │ ├── LibraryIntegrationsWindow.xaml.cs │ │ │ ├── LicenseAgreementWindow.xaml │ │ │ ├── LicenseAgreementWindow.xaml.cs │ │ │ ├── MainWindow.xaml │ │ │ ├── MainWindow.xaml.cs │ │ │ ├── MessageBoxWindow.xaml │ │ │ ├── MessageBoxWindow.xaml.cs │ │ │ ├── MetadataComparisonWindow.xaml │ │ │ ├── MetadataComparisonWindow.xaml.cs │ │ │ ├── MetadataDownloadWindow.xaml │ │ │ ├── MetadataDownloadWindow.xaml.cs │ │ │ ├── MultiItemSelectionWindow.xaml │ │ │ ├── MultiItemSelectionWindow.xaml.cs │ │ │ ├── PluginSettingsWindow.xaml │ │ │ ├── PluginSettingsWindow.xaml.cs │ │ │ ├── ProgressWindow.xaml │ │ │ ├── ProgressWindow.xaml.cs │ │ │ ├── RandomGameSelectWindow.xaml │ │ │ ├── RandomGameSelectWindow.xaml.cs │ │ │ ├── SearchWindow.xaml │ │ │ ├── SearchWindow.xaml.cs │ │ │ ├── SettingsWindow.xaml │ │ │ ├── SettingsWindow.xaml.cs │ │ │ ├── SingleItemSelectionWindow.xaml │ │ │ ├── SingleItemSelectionWindow.xaml.cs │ │ │ ├── ToolsConfigWindow.xaml │ │ │ ├── ToolsConfigWindow.xaml.cs │ │ │ ├── UpdateWindow.xaml │ │ │ └── UpdateWindow.xaml.cs │ │ ├── app.manifest │ │ └── packages.config │ ├── Playnite.FullscreenApp/ │ │ ├── Api/ │ │ │ └── MainViewAPI.cs │ │ ├── App.config │ │ ├── App.xaml │ │ ├── App.xaml.cs │ │ ├── Commands/ │ │ │ └── GlobalCommands.cs │ │ ├── ControlGalleryView.xaml │ │ ├── Controls/ │ │ │ ├── ButtonEx.cs │ │ │ ├── CheckBoxEx.cs │ │ │ ├── ComboBoxEx.cs │ │ │ ├── FilterDbItemtSelection.cs │ │ │ ├── FilterEnumListSelection.cs │ │ │ ├── FilterPresetSelector.cs │ │ │ ├── FilterStringListSelection.cs │ │ │ ├── FullscreenTilePanel.cs │ │ │ ├── GameListItem.cs │ │ │ ├── ItemsControlEx.cs │ │ │ ├── ListBoxEx.cs │ │ │ ├── ScrollViewerEx.cs │ │ │ ├── SettingsSections/ │ │ │ │ ├── Audio.xaml │ │ │ │ ├── Audio.xaml.cs │ │ │ │ ├── General.xaml │ │ │ │ ├── General.xaml.cs │ │ │ │ ├── Input.xaml │ │ │ │ ├── Input.xaml.cs │ │ │ │ ├── Layout.xaml │ │ │ │ ├── Layout.xaml.cs │ │ │ │ ├── Menus.xaml │ │ │ │ ├── Menus.xaml.cs │ │ │ │ ├── SettingsSectionControl.cs │ │ │ │ ├── Visuals.xaml │ │ │ │ └── Visuals.xaml.cs │ │ │ ├── SliderEx.cs │ │ │ ├── ToggleButtonEx.cs │ │ │ └── Views/ │ │ │ ├── Filters.cs │ │ │ ├── FiltersAdditional.cs │ │ │ ├── GameDetails.cs │ │ │ ├── GameStatus.cs │ │ │ └── Main.cs │ │ ├── FullscreenActionSelector.cs │ │ ├── FullscreenApplication.cs │ │ ├── FullscreenCollectionView.cs │ │ ├── FullscreenDialogs.cs │ │ ├── GlobalResources.xaml │ │ ├── HiddenStyles.xaml │ │ ├── Markup/ │ │ │ └── Markups.cs │ │ ├── Playnite.FullscreenApp.csproj │ │ ├── ProgramEntry.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ ├── Resources.resx │ │ │ ├── Settings.Designer.cs │ │ │ └── Settings.settings │ │ ├── Themes/ │ │ │ ├── Fullscreen/ │ │ │ │ └── Default/ │ │ │ │ ├── Constants.xaml │ │ │ │ ├── CustomControls/ │ │ │ │ │ ├── FilterDbItemtSelection.xaml │ │ │ │ │ ├── FilterEnumListSelection.xaml │ │ │ │ │ ├── FilterPresetSelector.xaml │ │ │ │ │ └── FilterStringListSelection.xaml │ │ │ │ ├── DefaultControls/ │ │ │ │ │ ├── Button.xaml │ │ │ │ │ ├── CheckBox.xaml │ │ │ │ │ ├── ComboBox.xaml │ │ │ │ │ ├── ProgressBar.xaml │ │ │ │ │ ├── ScrollViewer.xaml │ │ │ │ │ ├── Slider.xaml │ │ │ │ │ ├── TextBox.xaml │ │ │ │ │ ├── ToggleButton.xaml │ │ │ │ │ └── ToolTip.xaml │ │ │ │ ├── DerivedStyles/ │ │ │ │ │ ├── ButtonBottomMenu.xaml │ │ │ │ │ ├── ButtonTopMenu.xaml │ │ │ │ │ ├── ListGameItemStyle.xaml │ │ │ │ │ └── ListGameItemTemplate.xaml │ │ │ │ ├── DescriptionView.html │ │ │ │ ├── Images/ │ │ │ │ │ └── ButtonPrompts/ │ │ │ │ │ ├── PlayStation/ │ │ │ │ │ │ └── PlayStation.xaml │ │ │ │ │ └── Xbox/ │ │ │ │ │ └── Xbox.xaml │ │ │ │ ├── Media.xaml │ │ │ │ ├── Views/ │ │ │ │ │ ├── CustomMenus.xaml │ │ │ │ │ ├── FiltersAdditional.xaml │ │ │ │ │ ├── FiltersView.xaml │ │ │ │ │ ├── GameDetails.xaml │ │ │ │ │ ├── GameMenu.xaml │ │ │ │ │ ├── GameStatus.xaml │ │ │ │ │ ├── HelpMenu.xaml │ │ │ │ │ ├── ItemSelection.xaml │ │ │ │ │ ├── Main.xaml │ │ │ │ │ ├── MainMenu.xaml │ │ │ │ │ ├── MessageBox.xaml │ │ │ │ │ ├── NotificationsMenu.xaml │ │ │ │ │ ├── SettingsMenus.xaml │ │ │ │ │ └── TextInput.xaml │ │ │ │ └── theme.yaml │ │ │ └── Generic.xaml │ │ ├── ViewModels/ │ │ │ ├── AddonsViewModel.cs │ │ │ ├── DesignData/ │ │ │ │ └── DesignMainViewModel.cs │ │ │ ├── ExtensionsMenuViewModels.cs │ │ │ ├── FullscreenAppViewModel.cs │ │ │ ├── FullscreenAppViewModel_Commands.cs │ │ │ ├── GameClientsMenuViewModel.cs │ │ │ ├── GameDetailsViewModel.cs │ │ │ ├── GameMenuViewModel.cs │ │ │ ├── GameStatusViewModel.cs │ │ │ ├── HelpMenuViewModel.cs │ │ │ ├── MainMenuViewModel.cs │ │ │ ├── NotificationsViewModel.cs │ │ │ ├── SettingsViewModel.cs │ │ │ └── SoftwareToolsMenuViewModel.cs │ │ ├── Windows/ │ │ │ ├── AddonsUpdateWindow.xaml │ │ │ ├── AddonsUpdateWindow.xaml.cs │ │ │ ├── CrashWindow.xaml │ │ │ ├── CrashWindow.xaml.cs │ │ │ ├── ExtensionCrashWindow.xaml │ │ │ ├── ExtensionCrashWindow.xaml.cs │ │ │ ├── ExtensionsMenuWindow.xaml │ │ │ ├── ExtensionsMenuWindow.xaml.cs │ │ │ ├── GameClientsMenuWindow.xaml │ │ │ ├── GameClientsMenuWindow.xaml.cs │ │ │ ├── GameMenuWindow.xaml │ │ │ ├── GameMenuWindow.xaml.cs │ │ │ ├── HelpMenuWindow.xaml │ │ │ ├── HelpMenuWindow.xaml.cs │ │ │ ├── LicenseAgreementWindow.xaml │ │ │ ├── LicenseAgreementWindow.xaml.cs │ │ │ ├── MainMenuWindow.xaml │ │ │ ├── MainMenuWindow.xaml.cs │ │ │ ├── MainWindow.xaml │ │ │ ├── MainWindow.xaml.cs │ │ │ ├── MessageBoxWindow.xaml │ │ │ ├── MessageBoxWindow.xaml.cs │ │ │ ├── MultiItemSelectionWindow.xaml │ │ │ ├── MultiItemSelectionWindow.xaml.cs │ │ │ ├── NotificationsWindow.xaml │ │ │ ├── NotificationsWindow.xaml.cs │ │ │ ├── ProgressWindow.xaml │ │ │ ├── ProgressWindow.xaml.cs │ │ │ ├── RandomGameSelectWindow.xaml │ │ │ ├── RandomGameSelectWindow.xaml.cs │ │ │ ├── SettingsWindow.xaml │ │ │ ├── SettingsWindow.xaml.cs │ │ │ ├── SingleItemSelectionWindow.xaml │ │ │ ├── SingleItemSelectionWindow.xaml.cs │ │ │ ├── SoftwareToolsMenuWindow.xaml │ │ │ ├── SoftwareToolsMenuWindow.xaml.cs │ │ │ ├── TextInputWindow.xaml │ │ │ ├── TextInputWindow.xaml.cs │ │ │ ├── UpdateWindow.xaml │ │ │ ├── UpdateWindow.xaml.cs │ │ │ └── WindowTools.cs │ │ ├── app.manifest │ │ └── packages.config │ ├── Playnite.sln │ ├── PlayniteSDK/ │ │ ├── ApplicationMode.cs │ │ ├── BuiltInExtensions.cs │ │ ├── Collections/ │ │ │ ├── ComparableList.cs │ │ │ ├── ObservableConcurrentDictionary.cs │ │ │ ├── ObservableObject.cs │ │ │ └── RangeObservableCollection.cs │ │ ├── Controls/ │ │ │ └── PluginUserControl.cs │ │ ├── Data/ │ │ │ ├── DataSerialization.cs │ │ │ ├── MarkupConverter.cs │ │ │ └── SQLite.cs │ │ ├── Database/ │ │ │ ├── IGameDatabase.cs │ │ │ ├── IGameDatabaseAPI.cs │ │ │ └── IItemCollection.cs │ │ ├── Events/ │ │ │ ├── ApplicationEvents.cs │ │ │ ├── PlayniteUriEventArgs.cs │ │ │ └── WebViewEvents.cs │ │ ├── Exceptions/ │ │ │ ├── LocalizedException.cs │ │ │ ├── ReferenceException.cs │ │ │ ├── ScriptRuntimeException.cs │ │ │ └── TypeMismatchException.cs │ │ ├── ExpandableVariables.cs │ │ ├── ExtensionFunction.cs │ │ ├── Extensions/ │ │ │ ├── ListExtensions.cs │ │ │ └── StringExtensions.cs │ │ ├── IAddons.cs │ │ ├── IDialogsFactory.cs │ │ ├── IEmulationAPI.cs │ │ ├── ILogger.cs │ │ ├── IMainViewAPI.cs │ │ ├── INotificationsAPI.cs │ │ ├── IPlayniteAPI.cs │ │ ├── IPlayniteInfoAPI.cs │ │ ├── IPlaynitePathsAPI.cs │ │ ├── IPlayniteSettingsAPI.cs │ │ ├── ISettings.cs │ │ ├── IUriHandlerAPI.cs │ │ ├── IWebView.cs │ │ ├── LibraryClient.cs │ │ ├── LogManager.cs │ │ ├── MetadataProvider.cs │ │ ├── Models/ │ │ │ ├── AgeRating.cs │ │ │ ├── AgeRatingOrg.cs │ │ │ ├── AppSoftware.cs │ │ │ ├── Category.cs │ │ │ ├── Company.cs │ │ │ ├── CompletionStatus.cs │ │ │ ├── DatabaseObject.cs │ │ │ ├── Emulator.cs │ │ │ ├── FilterPreset.cs │ │ │ ├── Game.cs │ │ │ ├── GameAction.cs │ │ │ ├── GameFeature.cs │ │ │ ├── GameMetadata.cs │ │ │ ├── GameRom.cs │ │ │ ├── GameScannerConfig.cs │ │ │ ├── GameSource.cs │ │ │ ├── Genre.cs │ │ │ ├── IIdentifiable.cs │ │ │ ├── ImportExclusionList.cs │ │ │ ├── InstallSizeGroup.cs │ │ │ ├── InstallationStatus.cs │ │ │ ├── Link.cs │ │ │ ├── PastTimeSegment.cs │ │ │ ├── Platform.cs │ │ │ ├── PlaytimeCategory.cs │ │ │ ├── Region.cs │ │ │ ├── ReleaseDate.cs │ │ │ ├── ScoreGroup.cs │ │ │ ├── Series.cs │ │ │ └── Tag.cs │ │ ├── Playnite.SDK.csproj │ │ ├── Plugins/ │ │ │ ├── Actions.cs │ │ │ ├── LibraryPlugin.cs │ │ │ ├── MenuEntry.cs │ │ │ ├── MetadataPlugin.cs │ │ │ ├── Plugin.cs │ │ │ ├── Search.cs │ │ │ ├── SidebarItem.cs │ │ │ └── TopPanelItem.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── RelayCommand.cs │ │ ├── ResourceProvider.cs │ │ ├── SdkVersions.cs │ │ ├── WebViewModels.cs │ │ └── readme.md │ ├── Tests/ │ │ ├── Playnite.DesktopApp.Tests/ │ │ │ ├── GamesEditorTests.cs │ │ │ ├── MainViewAPITests.cs │ │ │ ├── Playnite.DesktopApp.Tests.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── SkinsTests.cs │ │ │ ├── TestsSetupClass.cs │ │ │ ├── ViewModels/ │ │ │ │ ├── DatabaseFieldsManagerViewModelTests.cs │ │ │ │ └── GameEditViewModelTests.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Playnite.FullscreenApp.Tests/ │ │ │ ├── Controls/ │ │ │ │ └── FullscreenTilePanelTests.cs │ │ │ ├── Playnite.FullscreenApp.Tests.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Playnite.Tests/ │ │ │ ├── Api/ │ │ │ │ └── PluginDescriptionTests.cs │ │ │ ├── App/ │ │ │ │ └── UpdateTests.cs │ │ │ ├── App.config │ │ │ ├── ArchiveTests.cs │ │ │ ├── BackupTests.cs │ │ │ ├── CloneObjectTests.cs │ │ │ ├── Converters/ │ │ │ │ └── PlayTimeToStringConverterTests.cs │ │ │ ├── CueSheetTests.cs │ │ │ ├── Database/ │ │ │ │ ├── EmulatorsCollectionTests.cs │ │ │ │ ├── FiltersDatabaseTests.cs │ │ │ │ ├── GameDatabaseFileTests.cs │ │ │ │ ├── GameDatabaseMigrationTests.cs │ │ │ │ ├── GameDatabasePlatformsTests.cs │ │ │ │ ├── GameDatabaseTests.cs │ │ │ │ ├── GameLibraryFilterTests.cs │ │ │ │ ├── GameLibraryTests.cs │ │ │ │ └── ItemCollectionTests.cs │ │ │ ├── DictionaryTests.cs │ │ │ ├── Emulators/ │ │ │ │ ├── EmulationDatabaseTests.cs │ │ │ │ ├── EmulationTests.cs │ │ │ │ ├── EmulatorDefinitionTests.cs │ │ │ │ ├── EmulatorScannerTests.cs │ │ │ │ ├── ScannedGameTests.cs │ │ │ │ └── ScannerTests.cs │ │ │ ├── ExtensionFactoryTests.cs │ │ │ ├── Extensions/ │ │ │ │ ├── BitmapExtensionsTests.cs │ │ │ │ ├── DateTimesTests.cs │ │ │ │ ├── EnumsTests.cs │ │ │ │ ├── NetExtensionsTests.cs │ │ │ │ ├── ObjectExtensionsTests.cs │ │ │ │ └── StringExtensionsTests.cs │ │ │ ├── FileSystemTests.cs │ │ │ ├── GameFieldComparerTests.cs │ │ │ ├── GamesEditorTests.cs │ │ │ ├── GamesStatsTests.cs │ │ │ ├── ImageSourceManagerTests.cs │ │ │ ├── ImagesTests.cs │ │ │ ├── IniParserTests.cs │ │ │ ├── InstallSizeScanTests.cs │ │ │ ├── ListExtensionsTests.cs │ │ │ ├── LocalizationTests.cs │ │ │ ├── M3UTests.cs │ │ │ ├── Manifests/ │ │ │ │ └── AddonManifestTests.cs │ │ │ ├── Metadata/ │ │ │ │ └── MetadataDownloaderDownloadTests.cs │ │ │ ├── Models/ │ │ │ │ ├── CopyDiffToTest.cs │ │ │ │ ├── DatabaseObjectTests.cs │ │ │ │ ├── GameActionTests.cs │ │ │ │ ├── GameTests.cs │ │ │ │ └── MetadataPropertyTests.cs │ │ │ ├── PathsTests.cs │ │ │ ├── Playnite.Tests.csproj │ │ │ ├── PlayniteUriHandlerTests.cs │ │ │ ├── Plugins/ │ │ │ │ └── ExtensionManifestTests.cs │ │ │ ├── ProcessStarterTests.cs │ │ │ ├── ProgramsTests.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── ReleaseDateTests.cs │ │ │ ├── Resources/ │ │ │ │ ├── GOG/ │ │ │ │ │ └── library_unreleased.json │ │ │ │ ├── Serialization/ │ │ │ │ │ ├── invalid.json │ │ │ │ │ ├── invalid.toml │ │ │ │ │ ├── invalid.yaml │ │ │ │ │ ├── valid.json │ │ │ │ │ ├── valid.toml │ │ │ │ │ └── valid.yaml │ │ │ │ ├── SizeScan/ │ │ │ │ │ ├── CueNonExistingFiles.cue │ │ │ │ │ └── CueTestFiles.cue │ │ │ │ ├── TestIni.ini │ │ │ │ ├── TestIni.md5 │ │ │ │ ├── TestIni.sfv │ │ │ │ ├── TestUpdateManifest.json │ │ │ │ ├── XmlTest/ │ │ │ │ │ ├── Xml1.xaml │ │ │ │ │ ├── Xml2.xaml │ │ │ │ │ ├── Xml3.xaml │ │ │ │ │ └── Xml4.xaml │ │ │ │ └── test.m3u │ │ │ ├── SafeFileEnumeratorTests.cs │ │ │ ├── Scripting/ │ │ │ │ └── PowerShell/ │ │ │ │ └── PowerShellTests.cs │ │ │ ├── SearchViewModelTests.cs │ │ │ ├── SelectableDbItemListTests.cs │ │ │ ├── SerializationTests.cs │ │ │ ├── Settings/ │ │ │ │ └── FilterSettingsTests.cs │ │ │ ├── SettingsTests.cs │ │ │ ├── SigningToolsTests.cs │ │ │ ├── SizesTests.cs │ │ │ ├── SortableNameConverterTests.cs │ │ │ ├── StringExtensionsTests.cs │ │ │ ├── System/ │ │ │ │ ├── ComputerTests.cs │ │ │ │ └── ProcessExtensionsTests.cs │ │ │ ├── TimerTests.cs │ │ │ ├── Web/ │ │ │ │ └── HttpDownloaderTests.cs │ │ │ ├── XmlTests.cs │ │ │ ├── _TestTools/ │ │ │ │ ├── GameDbTestWrapper.cs │ │ │ │ ├── MockDialogsFactory.cs │ │ │ │ ├── MockWindowFactory.cs │ │ │ │ ├── PlayniteTests.cs │ │ │ │ ├── TestAppTools.cs │ │ │ │ ├── TestDateTimes.cs │ │ │ │ ├── TestEmulationDatabase.cs │ │ │ │ ├── TestPlayniteApplication.cs │ │ │ │ ├── TestResourceProvider.cs │ │ │ │ └── TestsSetupClass.cs │ │ │ └── packages.config │ │ ├── Playnite.Toolbox.Tests/ │ │ │ ├── ExtensionsTests.cs │ │ │ ├── PathsTests.cs │ │ │ ├── Playnite.Toolbox.Tests.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Resources/ │ │ │ │ └── Changelog/ │ │ │ │ ├── 1.1.0-1.2.0.txt │ │ │ │ ├── 1.2.0-1.3.0.txt │ │ │ │ └── 1.3.0-1.4.0.txt │ │ │ ├── ThemesTests.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── TestApp/ │ │ │ ├── App.config │ │ │ ├── Properties/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ ├── Resources.Designer.cs │ │ │ │ ├── Resources.resx │ │ │ │ ├── Settings.Designer.cs │ │ │ │ └── Settings.settings │ │ │ ├── TestApp.cs │ │ │ ├── TestApp.csproj │ │ │ ├── TestAppProcInfo.cs │ │ │ ├── app.manifest │ │ │ └── packages.config │ │ ├── TestGameLibrary/ │ │ │ ├── BuildCopyExclude.txt │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Resources/ │ │ │ │ └── icon.tga │ │ │ ├── TestGameLibrary.cs │ │ │ ├── TestGameLibrary.csproj │ │ │ └── extension.yaml │ │ ├── TestMetadataPlugin/ │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── TestMetadataPlugin.cs │ │ │ ├── TestMetadataPlugin.csproj │ │ │ └── extension.yaml │ │ └── TestPlugin/ │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── TestPlugin.cs │ │ ├── TestPlugin.csproj │ │ ├── TestPluginSettings.cs │ │ ├── TestPluginSettingsView.xaml │ │ ├── TestPluginSettingsView.xaml.cs │ │ ├── TestPluginUserControl.xaml │ │ ├── TestPluginUserControl.xaml.cs │ │ └── extension.yaml │ └── Tools/ │ ├── Playnite.Toolbox/ │ │ ├── App.config │ │ ├── CmdLineOptions.cs │ │ ├── Extensions.cs │ │ ├── NLog.config │ │ ├── Paths.cs │ │ ├── Playnite.Toolbox.csproj │ │ ├── Program.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Strings.cs │ │ ├── Templates/ │ │ │ ├── Extensions/ │ │ │ │ ├── CustomLibraryPlugin/ │ │ │ │ │ ├── App.xaml │ │ │ │ │ ├── BuildInclude.txt │ │ │ │ │ ├── CustomLibraryPlugin.csproj │ │ │ │ │ ├── CustomLibraryPlugin.sln │ │ │ │ │ ├── Localization/ │ │ │ │ │ │ └── en_US.xaml │ │ │ │ │ ├── Properties/ │ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ │ ├── _name_.cs │ │ │ │ │ ├── _name_Client.cs │ │ │ │ │ ├── _name_Settings.cs │ │ │ │ │ ├── _name_SettingsView.xaml │ │ │ │ │ ├── _name_SettingsView.xaml.cs │ │ │ │ │ ├── extension.yaml │ │ │ │ │ └── packages.config │ │ │ │ ├── CustomMetadataPlugin/ │ │ │ │ │ ├── App.xaml │ │ │ │ │ ├── BuildInclude.txt │ │ │ │ │ ├── CustomMetadataPlugin.csproj │ │ │ │ │ ├── CustomMetadataPlugin.sln │ │ │ │ │ ├── Localization/ │ │ │ │ │ │ └── en_US.xaml │ │ │ │ │ ├── Properties/ │ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ │ ├── _name_.cs │ │ │ │ │ ├── _name_Provider.cs │ │ │ │ │ ├── _name_Settings.cs │ │ │ │ │ ├── _name_SettingsView.xaml │ │ │ │ │ ├── _name_SettingsView.xaml.cs │ │ │ │ │ ├── extension.yaml │ │ │ │ │ └── packages.config │ │ │ │ ├── GenericPlugin/ │ │ │ │ │ ├── App.xaml │ │ │ │ │ ├── BuildInclude.txt │ │ │ │ │ ├── GenericPlugin.csproj │ │ │ │ │ ├── GenericPlugin.sln │ │ │ │ │ ├── Localization/ │ │ │ │ │ │ └── en_US.xaml │ │ │ │ │ ├── Properties/ │ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ │ ├── _name_.cs │ │ │ │ │ ├── _name_Settings.cs │ │ │ │ │ ├── _name_SettingsView.xaml │ │ │ │ │ ├── _name_SettingsView.xaml.cs │ │ │ │ │ ├── extension.yaml │ │ │ │ │ └── packages.config │ │ │ │ └── PowerShellScript/ │ │ │ │ ├── BuildInclude.txt │ │ │ │ ├── PowerShellScript.psm1 │ │ │ │ └── extension.yaml │ │ │ └── Themes/ │ │ │ ├── Changelog/ │ │ │ │ ├── 1.0.0-1.1.0.txt │ │ │ │ ├── 1.1.0-1.2.0.txt │ │ │ │ ├── 1.2.0-1.3.0.txt │ │ │ │ ├── 1.3.0-1.4.0.txt │ │ │ │ ├── 1.4.0-1.4.1.txt │ │ │ │ ├── 1.4.1-1.5.0.txt │ │ │ │ ├── 1.5.0-1.6.0.txt │ │ │ │ ├── 1.6.0-1.7.0.txt │ │ │ │ ├── 1.7.0-1.8.0.txt │ │ │ │ ├── 1.8.0-1.9.0.txt │ │ │ │ ├── 1.9.0-2.0.0.txt │ │ │ │ ├── 2.0.0-2.1.0.txt │ │ │ │ ├── 2.1.0-2.2.0.txt │ │ │ │ ├── 2.2.0-2.3.0.txt │ │ │ │ ├── 2.3.0-2.4.0.txt │ │ │ │ ├── 2.4.0-2.5.0.txt │ │ │ │ ├── 2.5.0-2.6.0.txt │ │ │ │ ├── 2.7.0-2.8.0.txt │ │ │ │ └── 2.8.0-2.9.0.txt │ │ │ ├── Desktop/ │ │ │ │ ├── Theme.csproj │ │ │ │ └── Theme.sln │ │ │ └── Fullscreen/ │ │ │ ├── Theme.csproj │ │ │ └── Theme.sln │ │ ├── Themes.cs │ │ ├── Verify.cs │ │ └── packages.config │ ├── Playnite.Utilities/ │ │ ├── App.config │ │ ├── DatParser.cs │ │ ├── Playnite.Utilities.csproj │ │ ├── Program.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ └── packages.config │ └── PlayniteInstaller/ │ ├── App.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── Classic.xaml │ ├── Converters.cs │ ├── Logger.cs │ ├── MainViewModel.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── PlayniteInstaller.csproj │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── app.manifest │ └── installer_mirrors.txt └── tests/ ├── Extensions/ │ ├── Plugins/ │ │ ├── FullTestPluginDev/ │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── TestPluginDev.cs │ │ │ ├── TestPluginDev.csproj │ │ │ ├── TestPluginDev.sln │ │ │ └── packages.config │ │ ├── FullTestPluginNuget/ │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── TestPluginNuget.cs │ │ │ ├── TestPluginNuget.csproj │ │ │ ├── TestPluginNuget.sln │ │ │ └── packages.config │ │ └── LibraryExporter/ │ │ ├── LibraryExporter.psm1 │ │ └── plugin.info │ └── Scripts/ │ ├── FullPowerShellScript.ps1 │ └── FullPythonScript.py ├── Mapping/ │ ├── AboutWindow.ps1 │ ├── CategoryConfigWindow.ps1 │ ├── CrashHandlerWindow.ps1 │ ├── FirstTimeWizardWindow.ps1 │ ├── GameEditWindow.ps1 │ ├── InstalledGamesWindow.ps1 │ ├── MainWindow.ps1 │ ├── MetadataLookupWindow.ps1 │ ├── NotificationsWindow.ps1 │ ├── OpenFileWindow.ps1 │ ├── SettingsWindow.ps1 │ ├── SetupUninstallWindow.ps1 │ ├── SetupWindow.ps1 │ └── SystemDialog.ps1 ├── PlayniteCommon.ps1 ├── RunTests.ps1 ├── Setup/ │ └── Setup.Tests.ps1 ├── TestConfig.Template.yaml ├── TestExtensions.ps1 └── UI/ ├── CustomGames.Tests.ps1 ├── FirstTimeWizard.Tests.ps1 ├── GameEditWindow.Tests.ps1 ├── InstalledGamesImport.Tests.ps1 └── Startup.Tests.ps1 ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms patreon: playnite ko_fi: playnite ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Bug report description: Create a report to help us improve labels: ['bug'] body: - type: markdown attributes: value: | **Before creating bug report:** - Please post in English language only! - Please use issue search in the repository first. If the bug is something really obvious there's a high change it was already reported. - Make sure that the issue is not caused by custom theme or extension. You can restart Playnite in "safe mode" from help menu to quickly test it. - Check list of known issues https://github.com/JosefNemec/Playnite/wiki/Known-Issues **Integration issues** If an issue is related to library integrations, use separate [extensions repository](https://github.com/JosefNemec/PlayniteExtensions) to file a report. - type: textarea attributes: label: Bug Description description: A clear and concise description of what the bug is. validations: required: true - type: textarea attributes: label: To Reproduce description: If it''s not clear from the description how to reproduce the issue please add clear steps to reproduce the behavior. validations: required: false - type: input attributes: label: Diagnostics ID description: 'Please generate diagnostics package from "About Playnite" menu and attach identifier you received. If the issue is a crash bug, you can generate diag. package directly from crash dialog.' validations: required: true - type: textarea attributes: label: Screenshots description: If applicable, add screenshots to help explain your problem. validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Playnite Discord server url: https://playnite.link/discord about: Official Discord server for general chat and support with Playnite. - name: Playnite SDK documentation url: https://playnite.link/docs/ about: Useful documentation when creating plugins and themes. ================================================ FILE: .github/ISSUE_TEMPLATE/custom.md ================================================ ## Use [issue templates](https://github.com/JosefNemec/Playnite/issues/new/choose) instead! ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: Feature request description: Suggest an idea for this project labels: ['enhancement'] body: - type: markdown attributes: value: | Please post in English language only! **Integration features** If a feature request is related to library integrations, use separate [extensions repository](https://github.com/JosefNemec/PlayniteExtensions) to file a request. **Check for existing issue** Please use issue search in the repository if your feature request is something really obvious there's a high change it was already requested. - type: textarea attributes: label: Feature description description: A clear and concise description of feature you want to be added. validations: required: true - type: textarea attributes: label: Screenshots description: If applicable, add screenshots to help explain requested changes. validations: required: false ================================================ FILE: .github/pull_request_template.md ================================================ Pull requests are generally on pause because majority of code base is being rewritten for Playnite 11. Smaller "safe" changes for P10 might get accepted based on what they and if they come with test coverage. If you plan to work on bigger changes, please discuss it first in related issue or on Discord, thank you. ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. [Originals]/ # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json project.fragment.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.pfx *.publishsettings node_modules/ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Visual Studio Code stuff .vscode/ build/nuget.exe build/PlayniteSetup.exe build/PlaynitePortable.zip build/TestPackage/* build/PlayniteServices/* build/ReleaseSDK/* build/DebugSDK/* build/Playnite.zip tests/TestConfig.yaml # Don't share API keys with anyone :) source/PlayniteServices/customSettings.json source/PlayniteServices/patreonTokens.json source/PlayniteServices/twitchTokens.json # Not redistributable XBOXONE.ttf PlayStation4.ttf source/Playnite.FullscreenApp/Themes/Fullscreen/Default/audio/* ================================================ FILE: LICENSE.md ================================================ MIT License Copyright (c) 2020 Josef Nemec Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Playnite [![Crowdin](https://badges.crowdin.net/playnite/localized.svg)](https://crowdin.com/project/playnite) An open source video game library manager and launcher with support for 3rd party libraries like Steam, Epic, GOG, EA App, Battle.net and [others](https://playnite.link/addons.html). Includes game emulation support, providing one unified interface for your games. Screenshots are available at the [Homepage](http://playnite.link/) *If you find Playnite useful please consider supporting the lead developer [Josef Nemec](https://github.com/JosefNemec) on [Patreon](https://www.patreon.com/playnite).* Features --------- See the [Homepage](http://playnite.link/) for the list of features. Download --------- Grab the latest installer or portable package from the [download](https://playnite.link/download.html) page. Playnite will automatically notify you about a new version upon release. Requirements: Windows 10 or 11 FAQ, Known Issues, user manual --------- Can be found [here](https://api.playnite.link/docs/) Questions, issues etc. --------- If you find a bug please file an [issue](https://github.com/JosefNemec/Playnite/issues) and if relevant (crashes, broken features) please attach a diagnostics package, which can be created from inside the "About Playnite..." submenu. Biggest community around Playnite currently gathers on our [Discord server](https://playnite.link/discord) and [Reddit](https://www.reddit.com/r/playnite/). Privacy Statement --------- Playnite itself doesn't store any user information and you generally don't need to provide any information to import installed games. All game library data is stored locally on your PC. Account connection process depends on how a library plugin is implemented, but is usually done via official login web forms and only the web session cookies or tokens are stored, the same way when you login to those services via the web browser. Add-ons --------- Playnite can be extended with plugins (written in .NET languages), PowerShell scripts and user interface themes. See the [extensions portal](https://api.playnite.link/docs/tutorials/index.html) for more information about how to make these addons. Translations --------- We use Crowdin to manage localization, please join our project if you want to submit translations: https://crowdin.com/project/playnite Proofreading changes to original English strings can be submitted by creating pull request for [LocSource.xaml](https://github.com/JosefNemec/Playnite/blob/devel/source/Playnite/Localization/LocSource.xaml) file. Code Contributions --------- Pull requests are generally on pause because majority of code base is being rewritten for Playnite 11. Smaller "safe" changes for P10 might get accepted based on what they and if they come with test coverage. If you plan to work on bigger changes, please discuss it first in related issue or on Discord, thank you. Please ask in the related issue first before starting implementing something to make sure that nobody else is already working on it. If an issue doesn't exist for your feature/bug fix, create one first. Regarding code styling, there are only a few major rules: - private fields and properties should use camelCase (without underscore) - all methods (private and public) should use PascalCase - use spaces instead of tabs with 4 spaces width - add empty line between code block end `}` and additional expression - always encapsulate the code body after *if, for, foreach, while* etc. with curly braces: ```csharp if (true) { DoSomething(); } DoSomethingElse(); ``` instead of ```csharp if (true) DoSomething(); DoSomethingElse(); ``` Branches --------- * `master` - default branch representing state of currently released build. * `devel` - development branch containing latest changes. All pull requests should be made against `devel` branch. * `devel*` - development branches for specific features/versions. Roadmap --------- Playnite is currently being rewritten from scratch for next major version release 11. The work is being done in private repository until beta release, after which the code will be release in this repository under the same license as current version 10 release. There is no list of planned changes and new features for version 11. Development --------- See the [wiki](https://github.com/JosefNemec/Playnite/wiki/Building) for info about building and setting up the development environment. Others --------- .NET development tools courtesy of [JetBrains](https://www.jetbrains.com/?from=Playnite) [![jetbrains](https://user-images.githubusercontent.com/3874087/128503701-884cdae4-3283-4d67-8ad1-6103e777a660.png)](https://www.jetbrains.com/?from=Playnite) This program uses free code signing provided by [SignPath.io](https://signpath.io?utm_source=foundation&utm_medium=github&utm_campaign=playnite), and a free code signing certificate by the [SignPath Foundation](https://signpath.org?utm_source=foundation&utm_medium=github&utm_campaign=playnite) [![Capture](https://user-images.githubusercontent.com/3874087/128503363-9c39f8cd-9900-4a8b-83f2-81359d4fc731.PNG)](https://about.signpath.io?utm_source=foundation&utm_medium=github&utm_campaign=playnite) ================================================ FILE: appveyor.yml ================================================ version: '{branch}_{build}' pull_requests: do_not_increment_build_number: true branches: only: - master skip_non_tags: true image: Visual Studio 2019 shallow_clone: true environment: LicensedDependenciesUrl: secure: ZTSg1DK6QAtwZoCBUDQ94cIaMCKWgSeJMI0dCpVJBPE40+UqG9MNu+UJjjGWjvVa OnlineInstallerConfig: https://playnite.link/build/installer_mirrors.txt build_script: - pwsh: .\build\build.ps1 -Package -SdkNuget -LicensedDependenciesUrl $env:LicensedDependenciesUrl -OnlineInstallerConfig $env:OnlineInstallerConfig test: off artifacts: - path: 'build\Playnite.zip' deploy: - provider: Webhook url: https://app.signpath.io/API/v1/4028e7e6-f183-4bbf-9640-c4b4d9a6992d/Integrations/AppVeyor?ProjectSlug=Playnite&SigningPolicySlug=release-signing authorization: secure: ZXw8Ro7eQ0opsdjLpNH7ZgTgS8ycnud7ag2qNOMRvqdxBmvegj+7QHrsh4pSgTSINhB5tbTZwgiMKFDjJV9QHg== ================================================ FILE: build/ExtensionsRefIgnoreList.txt ================================================ AngleSharp.dll CefSharp.dll CefSharp.BrowserSubprocess.Core.dll CefSharp.Core.dll CefSharp.Core.Runtime.dll CefSharp.OffScreen.dll CefSharp.Wpf.dll CommandLine.dll concrt140.dll Crc32.NET.dll d3dcompiler_47.dll DiscordRPC.dll Flurl.dll Hardcodet.Wpf.TaskbarNotification.dll HtmlRenderer.dll HtmlRenderer.WPF.dll chrome_elf.dll libcef.dll libEGL.dll libGLESv2.dll LiteDB.dll Magick.Native-Q8-x86.dll Magick.NET.Core.dll Magick.NET.SystemWindowsMedia.dll Magick.NET-Q8-x86.dll Markdig.dll Microsoft.Dynamic.dll Microsoft.Practices.ServiceLocation.dll Microsoft.Scripting.dll Microsoft.Scripting.Metadata.dll Microsoft.VisualStudio.CodeCoverage.Shim.dll Microsoft.Win32.Primitives.dll Microsoft.WindowsAPICodePack.dll Microsoft.WindowsAPICodePack.ExtendedLinguisticServices.dll Microsoft.WindowsAPICodePack.Sensors.dll Microsoft.WindowsAPICodePack.Shell.dll Microsoft.WindowsAPICodePack.ShellExtensions.dll Microsoft.Xaml.Behaviors.dll msvcp140.dll msvcp140_1.dll msvcp140_2.dll msvcp140_atomic_wait.dll msvcp140_codecvt_ids.dll netstandard.dll Nett.dll Newtonsoft.Json.dll NLog.dll PhotoSauce.MagicScaler.dll Playnite.dll Playnite.SDK.dll Polly.dll Prism.dll Prism.Wpf.dll protobuf-net.dll SDL2.dll SDL2_mixer.dll SharpCompress.dll sqlite3.x86.dll SQLNado.dll System.AppContext.dll System.Buffers.dll System.Collections.dll System.Collections.Concurrent.dll System.Collections.NonGeneric.dll System.Collections.Specialized.dll System.ComponentModel.dll System.ComponentModel.EventBasedAsync.dll System.ComponentModel.Primitives.dll System.ComponentModel.TypeConverter.dll System.Console.dll System.Data.Common.dll System.Diagnostics.Contracts.dll System.Diagnostics.Debug.dll System.Diagnostics.FileVersionInfo.dll System.Diagnostics.Process.dll System.Diagnostics.StackTrace.dll System.Diagnostics.TextWriterTraceListener.dll System.Diagnostics.Tools.dll System.Diagnostics.TraceSource.dll System.Diagnostics.Tracing.dll System.Drawing.Primitives.dll System.Dynamic.Runtime.dll System.Globalization.dll System.Globalization.Calendars.dll System.Globalization.Extensions.dll System.IO.dll System.IO.Abstractions.dll System.IO.Compression.dll System.IO.Compression.ZipFile.dll System.IO.FileSystem.dll System.IO.FileSystem.DriveInfo.dll System.IO.FileSystem.Primitives.dll System.IO.FileSystem.Watcher.dll System.IO.IsolatedStorage.dll System.IO.MemoryMappedFiles.dll System.IO.Pipes.dll System.IO.UnmanagedMemoryStream.dll System.Linq.dll System.Linq.Expressions.dll System.Linq.Parallel.dll System.Linq.Queryable.dll System.Memory.dll System.Net.Http.dll System.Net.NameResolution.dll System.Net.NetworkInformation.dll System.Net.Ping.dll System.Net.Primitives.dll System.Net.Requests.dll System.Net.Security.dll System.Net.Sockets.dll System.Net.WebHeaderCollection.dll System.Net.WebSockets.dll System.Net.WebSockets.Client.dll System.Numerics.Vectors.dll System.ObjectModel.dll System.Reflection.dll System.Reflection.Extensions.dll System.Reflection.Primitives.dll System.Resources.Reader.dll System.Resources.ResourceManager.dll System.Resources.Writer.dll System.Runtime.dll System.Runtime.CompilerServices.Unsafe.dll System.Runtime.CompilerServices.VisualC.dll System.Runtime.Extensions.dll System.Runtime.Handles.dll System.Runtime.InteropServices.dll System.Runtime.InteropServices.RuntimeInformation.dll System.Runtime.Numerics.dll System.Runtime.Serialization.Formatters.dll System.Runtime.Serialization.Json.dll System.Runtime.Serialization.Primitives.dll System.Runtime.Serialization.Xml.dll System.Security.Claims.dll System.Security.Cryptography.Algorithms.dll System.Security.Cryptography.Csp.dll System.Security.Cryptography.Encoding.dll System.Security.Cryptography.Primitives.dll System.Security.Cryptography.X509Certificates.dll System.Security.Principal.dll System.Security.SecureString.dll System.Text.Encoding.dll System.Text.Encoding.Extensions.dll System.Text.RegularExpressions.dll System.Threading.dll System.Threading.Overlapped.dll System.Threading.Tasks.dll System.Threading.Tasks.Parallel.dll System.Threading.Thread.dll System.Threading.ThreadPool.dll System.Threading.Timer.dll System.ValueTuple.dll System.Windows.Interactivity.dll System.Xml.ReaderWriter.dll System.Xml.XDocument.dll System.Xml.XmlDocument.dll System.Xml.XmlSerializer.dll System.Xml.XPath.dll System.Xml.XPath.XDocument.dll vccorlib140.dll vcruntime140.dll vcruntime140_threads.dll vk_swiftshader.dll vulkan-1.dll YamlDotNet.dll AngleSharp.xml CefSharp.xml CefSharp.BrowserSubprocess.Core.xml CefSharp.Core.xml CefSharp.Core.Runtime.xml CefSharp.OffScreen.xml CefSharp.Wpf.xml CommandLine.xml concrt140.xml Crc32.NET.xml d3dcompiler_47.xml DiscordRPC.xml Flurl.xml Hardcodet.Wpf.TaskbarNotification.xml HtmlRenderer.xml HtmlRenderer.WPF.xml chrome_elf.xml libcef.xml libEGL.xml libGLESv2.xml LiteDB.xml Magick.Native-Q8-x86.xml Magick.NET.Core.xml Magick.NET.SystemWindowsMedia.xml Magick.NET-Q8-x86.xml Markdig.xml Microsoft.Dynamic.xml Microsoft.Practices.ServiceLocation.xml Microsoft.Scripting.xml Microsoft.Scripting.Metadata.xml Microsoft.VisualStudio.CodeCoverage.Shim.xml Microsoft.Win32.Primitives.xml Microsoft.WindowsAPICodePack.xml Microsoft.WindowsAPICodePack.ExtendedLinguisticServices.xml Microsoft.WindowsAPICodePack.Sensors.xml Microsoft.WindowsAPICodePack.Shell.xml Microsoft.WindowsAPICodePack.ShellExtensions.xml Microsoft.Xaml.Behaviors.xml msvcp140.xml msvcp140_1.xml msvcp140_2.xml msvcp140_atomic_wait.xml msvcp140_codecvt_ids.xml netstandard.xml Nett.xml Newtonsoft.Json.xml NLog.xml PhotoSauce.MagicScaler.xml Playnite.xml Playnite.SDK.xml Polly.xml Prism.xml Prism.Wpf.xml protobuf-net.xml SDL2.xml SDL2_mixer.xml SharpCompress.xml sqlite3.x86.xml SQLNado.xml System.AppContext.xml System.Buffers.xml System.Collections.xml System.Collections.Concurrent.xml System.Collections.NonGeneric.xml System.Collections.Specialized.xml System.ComponentModel.xml System.ComponentModel.EventBasedAsync.xml System.ComponentModel.Primitives.xml System.ComponentModel.TypeConverter.xml System.Console.xml System.Data.Common.xml System.Diagnostics.Contracts.xml System.Diagnostics.Debug.xml System.Diagnostics.FileVersionInfo.xml System.Diagnostics.Process.xml System.Diagnostics.StackTrace.xml System.Diagnostics.TextWriterTraceListener.xml System.Diagnostics.Tools.xml System.Diagnostics.TraceSource.xml System.Diagnostics.Tracing.xml System.Drawing.Primitives.xml System.Dynamic.Runtime.xml System.Globalization.xml System.Globalization.Calendars.xml System.Globalization.Extensions.xml System.IO.xml System.IO.Abstractions.xml System.IO.Compression.xml System.IO.Compression.ZipFile.xml System.IO.FileSystem.xml System.IO.FileSystem.DriveInfo.xml System.IO.FileSystem.Primitives.xml System.IO.FileSystem.Watcher.xml System.IO.IsolatedStorage.xml System.IO.MemoryMappedFiles.xml System.IO.Pipes.xml System.IO.UnmanagedMemoryStream.xml System.Linq.xml System.Linq.Expressions.xml System.Linq.Parallel.xml System.Linq.Queryable.xml System.Memory.xml System.Net.Http.xml System.Net.NameResolution.xml System.Net.NetworkInformation.xml System.Net.Ping.xml System.Net.Primitives.xml System.Net.Requests.xml System.Net.Security.xml System.Net.Sockets.xml System.Net.WebHeaderCollection.xml System.Net.WebSockets.xml System.Net.WebSockets.Client.xml System.Numerics.Vectors.xml System.ObjectModel.xml System.Reflection.xml System.Reflection.Extensions.xml System.Reflection.Primitives.xml System.Resources.Reader.xml System.Resources.ResourceManager.xml System.Resources.Writer.xml System.Runtime.xml System.Runtime.CompilerServices.Unsafe.xml System.Runtime.CompilerServices.VisualC.xml System.Runtime.Extensions.xml System.Runtime.Handles.xml System.Runtime.InteropServices.xml System.Runtime.InteropServices.RuntimeInformation.xml System.Runtime.Numerics.xml System.Runtime.Serialization.Formatters.xml System.Runtime.Serialization.Json.xml System.Runtime.Serialization.Primitives.xml System.Runtime.Serialization.Xml.xml System.Security.Claims.xml System.Security.Cryptography.Algorithms.xml System.Security.Cryptography.Csp.xml System.Security.Cryptography.Encoding.xml System.Security.Cryptography.Primitives.xml System.Security.Cryptography.X509Certificates.xml System.Security.Principal.xml System.Security.SecureString.xml System.Text.Encoding.xml System.Text.Encoding.Extensions.xml System.Text.RegularExpressions.xml System.Threading.xml System.Threading.Overlapped.xml System.Threading.Tasks.xml System.Threading.Tasks.Parallel.xml System.Threading.Thread.xml System.Threading.ThreadPool.xml System.Threading.Timer.xml System.ValueTuple.xml System.Windows.Interactivity.xml System.Xml.ReaderWriter.xml System.Xml.XDocument.xml System.Xml.XmlDocument.xml System.Xml.XmlSerializer.xml System.Xml.XPath.xml System.Xml.XPath.XDocument.xml vccorlib140.xml vcruntime140.xml vcruntime140_threads.xml vk_swiftshader.xml vulkan-1.xml YamlDotNet.xml ================================================ FILE: build/PlayniteSDK.nuspec ================================================  PlayniteSDK {Version} Playnite SDK Josef Nemec JosefNemec MIT https://github.com/JosefNemec/Playnite images\applogo.png false SDK plugin library for Playnite video game library manager. https://playnite.link/docs/changelog.html Copyright © Josef Nemec 2018 sdk gaming docs\readme.md ================================================ FILE: build/VerifyLanguageFiles.ps1 ================================================ #Requires -Version 7 $ErrorActionPreference = "Stop" Add-Type -AssemblyName "PresentationFramework" $locDir = "..\source\Playnite\Localization\" $allOk = $true foreach ($locFile in (Get-ChildItem $locDir -Filter "*.xaml")) { $stream = New-Object "System.IO.StreamReader" $locFile.FullName try { $xaml = [System.Windows.Markup.XamlReader]::Load($stream.BaseStream) Write-Host "$($locFile.Name)...OK" -ForegroundColor Green } catch { $allOk = $false Write-Host "$($locFile.Name)...FAIL" -ForegroundColor Red Write-Host $_.Exception.InnerException.Message -ForegroundColor Red } finally { $stream.Dispose() } } if (-not $allOk) { throw "Some localization files failed verification." } ================================================ FILE: build/applyEnglishProofing.ps1 ================================================ #Requires -Version 7 $ErrorActionPreference = "Stop" Add-Type -AssemblyName "PresentationFramework" $locDir = Join-Path $pwd "..\source\Playnite\Localization\" $proofFile = Join-Path $locDir "en_US.xaml" $lngSourceFile = Join-Path $locDir "LocSource.xaml" [xml]$proofXaml = Get-Content $proofFile [xml]$lngSourceXaml = Get-Content $lngSourceFile foreach ($node in $proofXaml.ResourceDictionary.ChildNodes) { if (!$node.Key) { continue } if (![string]::IsNullOrEmpty($node.InnerXml)) { $lngSourceNode = $lngSourceXaml.ResourceDictionary.ChildNodes | Where-Object { $_.Key -eq $node.Key } if ($lngSourceNode) { $importNode = $lngSourceXaml.ImportNode($node, $true) $lngSourceXaml.ResourceDictionary.ReplaceChild($importNode, $lngSourceNode) | Out-Null } } } $settings = new-object System.Xml.XmlWriterSettings $settings.Indent = $true; $settings.IndentChars = " "; $settings.Encoding = [System.Text.Encoding]::UTF8; $settings.OmitXmlDeclaration = $true; $writer = [System.Xml.XmlWriter]::Create($lngSourceFile, $settings); [System.Xml.Linq.XDocument]::Parse($lngSourceXaml.OuterXml).WriteTo($writer); $writer.Flush(); $writer.Dispose(); ================================================ FILE: build/build.ps1 ================================================ #Requires -Version 7 param( # Build configuration [ValidateSet("Release", "Debug")] [string]$Configuration = "Release", # Target platform [ValidateSet("x86", "x64")] [string]$Platform = "x86", # Target directory for build files [string]$OutputDir, # Target directory for installer files [string]$InstallerDir, # Package build output into zip file [switch]$Package = $false, # Skip build process [switch]$SkipBuild = $false, # Temp directory for build process [string]$TempDir = (Join-Path $env:TEMP "PlayniteBuild"), [string]$LicensedDependenciesUrl, [switch]$SdkNuget, [string]$OnlineInstallerConfig ) $ErrorActionPreference = "Stop" if (!(Get-InstalledModule "powershell-yaml" -EA 0)) { Install-Module powershell-yaml } Set-Location $PSScriptRoot & .\common.ps1 if (!$OutputDir) { $OutputDir = Join-Path $PWD $Configuration } if (!$InstallerDir) { $InstallerDir = $PWD } function PackExtensionTemplate() { param( [Parameter(Mandatory = $true)] [string]$TemplateRootName, [Parameter(Mandatory = $true)] [string]$OutputDir ) $templatesDir = Join-Path $OutputDir "Templates\Extensions\" $templateOutDir = Join-Path $templatesDir $TemplateRootName $tempFiles = Get-Content "..\source\Tools\Playnite.Toolbox\Templates\Extensions\$TemplateRootName\BuildInclude.txt" | Where { ![string]::IsNullOrEmpty($_) } $targetZip = Join-Path $templatesDir "$TemplateRootName.zip" foreach ($file in $tempFiles) { $target = Join-Path $templateOutDir $file New-FolderFromFilePath $target Copy-Item (Join-Path "..\source\Tools\Playnite.Toolbox\Templates\Extensions\$TemplateRootName" $file) $target } New-ZipFromDirectory $templateOutDir $targetZip Remove-Item $templateOutDir -Recurse -Force } # ------------------------------------------- # Verify various non-build files # ------------------------------------------- .\VerifyLanguageFiles.ps1 $platforms = Get-Content "..\source\Playnite\Emulation\Platforms.yaml" -Raw | ConvertFrom-Yaml if (!($platforms.Count -gt 0)) { throw "Platforms definition file is not valid." } Write-OperationLog "Platforms definitions are OK" $regions = Get-Content "..\source\Playnite\Emulation\Regions.yaml" -Raw | ConvertFrom-Yaml if (!($regions.Count -gt 0)) { throw "Regions definition file is not valid." } Write-OperationLog "Regions definitions are OK" Get-ChildItem "..\source\Playnite\Emulation\" -Filter "*.yaml" -Recurse | ForEach { $emuDef = Get-Content $_.FullName -Raw | ConvertFrom-Yaml if (!$emuDef.Id) { throw "$($_.FullName) is not valid emulator definition." } foreach ($profile in $emuDef.Profiles) { foreach ($platId in $profile.Platforms) { if (!($platforms | Where { $_.Id -eq $platId } )) { throw "Platform $platId not found, $($_.FullName)." } } } } Write-OperationLog "Emulator definitions are OK" # ------------------------------------------- # Compile application # ------------------------------------------- if (!$SkipBuild) { if (Test-Path $OutputDir) { Remove-Item "$OutputDir\*" -Recurse -Force } if ($LicensedDependenciesUrl) { $depArchive = Join-Path $env:TEMP "deps.zip" Invoke-WebRequest $LicensedDependenciesUrl -OutFile $depArchive Expand-ZipToDirectory $depArchive (Resolve-Path "..").Path } if ($OnlineInstallerConfig) { Write-OperationLog "Updating online installer config..." $locaConfigPath = "..\source\Tools\PlayniteInstaller\installer_mirrors.txt" if ($OnlineInstallerConfig.StartsWith("http")) { $configFile = Join-Path $env:TEMP "installer_mirrors.txt" Invoke-WebRequest $OnlineInstallerConfig -OutFile $locaConfigPath } else { Copy-Item $OnlineInstallerConfig $locaConfigPath -Force } } $solutionDir = Join-Path $pwd "..\source" Invoke-Nuget "restore ..\source\Playnite.sln" $msbuildpath = Get-MsBuildPath $arguments = "build.xml /p:SolutionDir=`"$solutionDir\\`" /p:OutputPath=`"$OutputDir`";Configuration=$configuration /property:Platform=$Platform /t:Build" $compilerResult = StartAndWait $msbuildPath $arguments if ($compilerResult -ne 0) { throw "Build failed." } # Copy extension templates PackExtensionTemplate "CustomLibraryPlugin" $OutputDir PackExtensionTemplate "CustomMetadataPlugin" $OutputDir PackExtensionTemplate "GenericPlugin" $OutputDir PackExtensionTemplate "PowerShellScript" $OutputDir } New-Folder $InstallerDir # ------------------------------------------- # SDK nuget # ------------------------------------------- if ($SdkNuget) { & .\buildSdkNuget.ps1 -SkipBuild -OutputPath $OutputDir | Out-Null } # ------------------------------------------- # Build zip package # ------------------------------------------- if ($Package) { Write-OperationLog "Building zip package..." $packageName = Join-Path $InstallerDir "Playnite.zip" New-ZipFromDirectory $OutputDir $packageName } (Get-ChildItem (Join-Path $OutputDir "Playnite.dll")).VersionInfo.FileVersion | Write-Host -ForegroundColor Green return $true ================================================ FILE: build/build.xml ================================================ ================================================ FILE: build/buildLocConstants.ps1 ================================================ #Requires -Version 7 $ErrorActionPreference = "Stop" & .\common.ps1 $locFile = Join-Path $pwd "..\source\Playnite\Localization\LocSource.xaml" $locKeysSrc = Join-Path $pwd "..\source\Playnite\Localization\LocalizationKeys.cs" [xml]$locFileXaml = Get-Content $locFile @" /// /// DO NOT MODIFY! Automatically generated via buildLocConstants.ps1 script. /// namespace Playnite { public static class LOC { "@ | Out-File $locKeysSrc -Encoding utf8 foreach ($node in $locFileXaml.ResourceDictionary.ChildNodes) { if (!$node.Key) { continue } if (![string]::IsNullOrEmpty($node.InnerXml)) { @" /// {0} /// "@ -f (($node.InnerXml -split "\n") -replace "^", "/// ") | Out-File $locKeysSrc -Encoding utf8 -Append " public const string $($node.Key -replace `"^LOC`", `"`") = `"$($node.Key)`";" ` | Out-File $locKeysSrc -Encoding utf8 -Append } } @" } } "@ | Out-File $locKeysSrc -Encoding utf8 -Append ================================================ FILE: build/buildSdkNuget.ps1 ================================================ #Requires -Version 7 param( [ValidateSet("Release", "Debug")] [string]$Configuration = "Release", [string]$OutputPath = (Join-Path $PWD "$($Configuration)SDK"), [switch]$SkipBuild = $false, [string]$LocalPublish ) $ErrorActionPreference = "Stop" & .\common.ps1 # ------------------------------------------- # Compile SDK # ------------------------------------------- if (!$SkipBuild) { New-EmptyFolder $OutputPath $project = Join-Path $pwd "..\source\PlayniteSDK\Playnite.SDK.csproj" $msbuildPath = Get-MsBuildPath $arguments = "`"$project`" /p:OutputPath=`"$outputPath`";Configuration=$configuration /t:Build" $compilerResult = StartAndWait $msbuildPath $arguments if ($compilerResult -ne 0) { throw "Build failed." } } # ------------------------------------------- # Create NUGET # ------------------------------------------- $version = (Get-ChildItem (Join-Path $OutputPath "Playnite.SDK.dll")).VersionInfo.ProductVersion $version = $version -replace "\.0$", "" $spec = Get-Content "PlayniteSDK.nuspec" $spec = $spec -replace "{Version}", $version $spec = $spec -replace "{OutDir}", $OutputPath $specFile = "nuget.nuspec" try { $spec | Out-File $specFile $packageRes = Invoke-Nuget "pack $specFile -OutputDirectory $OutputPath" if ($packageRes -ne 0) { throw "Nuget packing failed." } } finally { Remove-Item $specFile -EA 0 } if ($LocalPublish) { Invoke-Nuget "init `"$pwd`" `"$LocalPublish`"" } return $true ================================================ FILE: build/common.ps1 ================================================ #Requires -Version 7 $global:NugetUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" function global:StartAndWait() { param( [string]$Path, [string]$Arguments, [string]$WorkingDir ) if ($WorkingDir) { $proc = Start-Process $Path $Arguments -PassThru -NoNewWindow -WorkingDirectory $WorkingDir } else { $proc = Start-Process $Path $Arguments -PassThru -NoNewWindow } $handle = $proc.Handle # cache proc.Handle http://stackoverflow.com/a/23797762/1479211 $proc.WaitForExit() return $proc.ExitCode } function global:Invoke-Nuget() { param( [string]$NugetArgs ) $nugetCommand = Get-Command -Name "nuget" -Type Application -ErrorAction Ignore if (-not $nugetCommand) { if (-not (Test-Path "nuget.exe")) { Invoke-WebRequest -Uri $NugetUrl -OutFile "nuget.exe" } } if ($nugetCommand) { return StartAndWait "nuget" $NugetArgs } else { return StartAndWait ".\nuget.exe" $NugetArgs } } function global:Get-MsBuildPath() { $VSWHERE_CMD = "vswhere" if (-not (Get-Command -Name $VSWHERE_CMD -Type Application -ErrorAction Ignore)) { $VSWHERE_CMD = "..\source\packages\vswhere.*\tools\vswhere.exe" if (-not (Get-Command -Name $VSWHERE_CMD -Type Application -ErrorAction Ignore)) { Invoke-Nuget "install vswhere -SolutionDirectory `"$solutionDir`"" | Out-Null } } $path = & $VSWHERE_CMD -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe" -latest | Select-Object -First 1 if ($path -and (Test-Path $path)) { return $path } throw "MS Build not found." } function global:New-Folder() { param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string]$Path ) if (Test-Path $Path) { return } mkdir $Path | Out-Null } function global:New-FolderFromFilePath() { param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string]$FilePath ) $dirPath = Split-Path $FilePath if (Test-Path $dirPath) { return } mkdir $dirPath | Out-Null } function global:New-EmptyFolder() { param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string]$Path ) if (Test-Path $Path) { Remove-Item $Path -Recurse -Force } mkdir $Path | Out-Null } function global:New-ZipFromDirectory() { param( [string]$directory, [string]$resultZipPath, [bool]$includeBaseDirectory = $false ) if (Test-path $resultZipPath) { Remove-Item $resultZipPath } Add-Type -assembly "System.IO.Compression.Filesystem" | Out-Null [IO.Compression.ZipFile]::CreateFromDirectory($directory, $resultZipPath, "Optimal", $includeBaseDirectory) } function global:Expand-ZipToDirectory() { param( [string]$zipPath, [string]$directory ) Add-Type -assembly "System.IO.Compression.Filesystem" | Out-Null [IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $directory, $true) } function global:Write-OperationLog() { param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string]$Message ) Write-Host $Message -ForegroundColor Green } function global:Write-ErrorLog() { param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string]$Message ) Write-Host $Message -ForegroundColor Red -BackgroundColor Black } function global:Write-WarningLog() { param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string]$Message ) Write-Host $Message -ForegroundColor Yellow } function global:Write-InfoLog() { param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string]$Message ) Write-Host $Message -ForegroundColor White } function global:Write-DebugLog() { param( [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)] [string]$Message ) Write-Host $Message -ForegroundColor DarkGray } ================================================ FILE: build/generateRetroArchProfile.ps1 ================================================ param( [Parameter(Mandatory=$true)] [string]$RetroArchDir, [Parameter(Mandatory=$true)] [string]$PlayniteSourceDirectory ) $ErrorActionPreference = "Stop" if (!(Get-InstalledModule "powershell-yaml" -EA 0)) { Install-Module powershell-yaml } function Get-IsYamlValid { param ( [Parameter(Mandatory=$true)] [string] $yamlContent ) try { $yamlContent | ConvertFrom-Yaml | Out-Null return $true } catch { return $false } } function ParseInfoFile() { param( [Parameter(Mandatory=$true)] [string]$Path ) $properties = @{} foreach ($line in (Get-Content $Path)) { if ($line.StartsWith("#")) { continue } if ($line -match "^(.*)\s*=\s*`"(.*)`"$") { $property = $Matches[1].Trim() if (!$properties.ContainsKey($property)) { $properties.Add($property, $Matches[2].Trim()) } } } return $properties } $emulationDirPath = [System.IO.Path]::Combine($PlayniteSourceDirectory, "Playnite", "Emulation") $platformsDefinitionPath = [System.IO.Path]::Combine($emulationDirPath, "Platforms.yaml") $retroArchEmuDefinitionPath = [System.IO.Path]::Combine($emulationDirPath, "Emulators", "RetroArch", "emulator.yaml") if (!(Test-Path $platformsDefinitionPath -Type Leaf)) { Write-Host "Playnite platforms definition file not found in $platformsDefinitionPath" -ForegroundColor Yellow return } $ignoreList = @( "00_example_libretro.info", "2048_libretro.info", "3dengine_libretro.info", "advanced_tests_libretro.info", "bk_libretro.info", "boom3_libretro.info", "boom3_xp_libretro.info", "cannonball_libretro.info", "chaigame_libretro.info", "chailove_libretro.info", "craft_libretro.info", "cruzes_libretro.info", "dhewm3_libretro.info", "dinothawr_libretro.info", "ecwolf_libretro.info", "ffmpeg_libretro.info", "freechaf_libretro.info", "freej2me_libretro.info", "gme_libretro.info", "hbmame_libretro.info", "imageviewer_libretro.info", "lutro_libretro.info", "mojozork_libretro.info", "mpv_libretro.info", "mrboom_libretro.info", "mu_libretro.info", "nxengine_libretro.info", "oberon_libretro.info", "openlara_libretro.info", "opentyrian_libretro.info", "pascal_pong_libretro.info", "pocketcdg_libretro.info", "pokemini_libretro.info", "prboom_libretro.info", "quasi88_libretro.info", "redbook_libretro.info", "reminiscence_libretro.info", "remotejoy_libretro.info", "rvvm_libretro.info", "scummvm_libretro.info", "simcp_libretro.info", "squirreljme_libretro.info", "stonesoup_libretro.info", "test_libretro.info", "test_netplay_libretro.info", "testaudio_callback_libretro.info", "testaudio_no_callback_libretro.info", "testaudio_playback_wav_libretro.info", "testgl_compute_shaders_libretro.info", "testgl_ff_libretro.info", "testgl_libretro.info", "testinput_buttontest_libretro.info", "testretroluxury_libretro.info", "testsw_libretro.info", "testsw_vram_libretro.info", "testvulkan_async_compute_libretro.info", "testvulkan_libretro.info", "thepowdertoy_libretro.info", "tic80_libretro.info", "tyrquake_libretro.info", "ume2015_libretro.info", "uw8_libretro.info", "vaporspec_libretro.info", "vitaquake2-rogue_libretro.info", "vitaquake2-xatrix_libretro.info", "vitaquake2-zaero_libretro.info", "vitaquake2_libretro.info", "vitaquake3_libretro.info", "vitavoyager_libretro.info", "wasm4_libretro.info", "x1_libretro.info", "xrick_libretro.info" ) $retroarchArcadeSystems = @( "16-bit (Various)", "16-bit + 32X (Various)", "Arcade (various)", "CP System I", "CP System II", "CP System III", "Multi (various)", "Neo Geo" ) $retroarchMiscSystems = @( "2003 Game Engine", "4", "CHIP-8", "Handheld Electronic", #Handheld Electronic (GW) "LowRes NX", #LowRes NX "Magnavox Odyssey2", #133 Philips Videopac G7000 "Mega Duck", # Mega Duck "Moonlight", # Moonlight, "PICO8", # PICO-8 "Pong Game Clone", # Gong, "RPG Maker 2000", "SEGA Visual Memory Unit", #VeMUlator / Sega VMU "Uzebox" # Uzebox (Uzem) ) $raCoreNameToPlatformIdsTranslate = @{ "Gearsystem" = @("sega_mastersystem", "sega_gamegear", "sega_sg1000", "coleco_vision"); "Genesis Plus GX" = @("sega_mastersystem", "sega_gamegear", "sega_genesis", "sega_cd"); "Genesis Plus GX Wide" = @("sega_mastersystem", "sega_gamegear", "sega_genesis", "sega_cd"); "nSide (Super Famicom Accuracy)" = @("nintendo_super_nes", "nintendo_gameboy", "nintendo_gameboycolor"); "PicoDrive" = @("sega_mastersystem", "sega_genesis", "sega_cd", "sega_32x"); "SMS Plus GX" = @("sega_mastersystem", "sega_gamegear", "sega_sg1000", "coleco_vision"); } $raSystemIdToPlatformIdsTranslate = @{ "msx" = @("microsoft_msx", "microsoft_msx2"); "neo_geo_pocket" = @("snk_neogeopocket", "snk_neogeopocket_color"); } $raSystemNameToPlatformIdTranslate = @{ "3DO" = "3do"; "3DS" = "nintendo_3ds"; "Amiga" = "commodore_amiga"; "Atari 2600" = "atari_2600"; "Atari 5200" = "atari_5200"; "Atari 7800" = "atari_7800"; "Atari ST" = "atari_st"; "C128" = "commodore_64"; "C64" = "commodore_64"; "C64 SuperCPU" = "commodore_64"; "CBM-5x0" = "commodore_cbm5x0"; "CBM-II" = "commodore_cbm2"; "CD" = "nec_turbografx_cd"; "ColecoVision" = "coleco_vision"; "Color" = "bandai_wonderswan_color"; "Commodore Amiga" = "commodore_amiga"; "CPC" = "amstrad_cpc"; "DOS" = "pc_dos"; "DS" = "nintendo_ds"; "Falcon" = "atari_falcon030"; "Game Boy" = "nintendo_gameboy"; "Game Boy Advance" = "nintendo_gameboyadvance"; "Game Boy Color" = "nintendo_gameboycolor"; "GameCube" = "nintendo_gamecube"; "GG" = "sega_gamegear"; "Intellivision" = "mattel_intellivision"; "Jaguar" = "atari_jaguar"; "Lynx" = "atari_lynx"; "Nintendo 64" = "nintendo_64"; "Nintendo DS" = "nintendo_ds"; "Nintendo Entertainment System" = "nintendo_nes"; "PC" = "pc_dos"; "PC Engine" = "nec_turbografx_16"; "PC Engine SuperGrafx" = "nec_supergrafx"; "PC-98" = "nec_pc98"; "PC-FX" = "nec_pcfx"; "PCE-CD" = "nec_turbografx_cd"; "PET" = "commodore_pet"; "PlayStation" = "sony_playstation"; "PLUS" = "commodore_plus4"; "PSP" = "sony_psp"; "Saturn" = "sega_saturn"; "Sega Dreamcast" = "sega_dreamcast"; "Sega Genesis" = "sega_genesis"; "Sega Master System" = "sega_mastersystem"; "Sharp X68000" = "sharp_x68000"; "SNK Neo Geo CD" = "snk_neogeo_cd"; "Sony PlayStation 2" = "sony_playstation2"; "STE" = "atari_st"; "Super Nintendo Entertainment System" = "nintendo_super_nes"; "SuperGrafx" = "nec_supergrafx"; "Supervision" = "watara_supervision"; "SVI" = "microsoft_msx"; "Thomson MO" = "thomson_mo5"; "TO" = "thomson_to7"; "TT" = "atari_st"; # The Atari TT030 is a member of the Atari ST family "Vectrex" = "vectrex"; "VIC-20" = "commodore_vci20"; "Virtual Boy" = "nintendo_virtualboy"; "Wii" = "nintendo_wii"; "WonderSwan" = "bandai_wonderswan"; "Xbox" = "xbox"; "ZX Spectrum (various)" = "sinclair_zxspectrum"; "ZX81" = "sinclair_zx81"; } $emuDefinitionTemplate = "Id: retroarch Name: RetroArch Website: 'http://www.retroarch.com/' Profiles: {0}" $profileTemplate = ' - Name: {0} StartupArguments: ''-L ".\cores\{1}.dll" "{{ImagePath}}"'' Platforms: [{2}] ImageExtensions: [{3}] ProfileFiles: [''cores\{4}.dll''] StartupExecutable: ^retroarch\.exe$' $existingProfileReaddTemplate = ' - Name: {0} StartupArguments: ''{1}'' Platforms: [{2}] ImageExtensions: [{3}] ProfileFiles: [''{4}''] StartupExecutable: ^retroarch\.exe$' $existingEmuDefinition = Get-Content $retroArchEmuDefinitionPath -Raw | ConvertFrom-Yaml $usedProfileNames = @() $existingProfiles = @{} foreach ($existingProfile in $existingEmuDefinition.Profiles) { $existingProfiles[$existingProfile.ProfileFiles[0]] = $existingProfile $usedProfileNames += $existingProfile.Name } $foundCoreNames = @() $foundPlatformIds = @() $profiles = @() $infoPath = Join-Path $RetroArchDir "info" $infoFiles = Get-ChildItem $infoPath -Filter "*.info" foreach ($infoFile in $infoFiles) { if ($ignoreList.Contains($infoFile.Name)) { continue } $coreInfo = ParseInfoFile $infoFile.FullName $coreFile = "cores\{0}.dll" -f $infoFile.BaseName $platformIds = @() if ($existingProfiles.ContainsKey($coreFile)) { $coreName = $existingProfiles[$coreFile].Name $platformIds = $existingProfiles[$coreFile].Platforms } else { if ($usedProfileNames.Contains($coreInfo.corename)) { $coreName = "{0} - {1}" -f $coreInfo.corename, $coreInfo.display_name } else { $coreName = $coreInfo.corename } } if (!($coreInfo.ContainsKey("corename"))) { Write-Host "$($infoFile.Name) does not contain the core name" -ForegroundColor Yellow # Some cores like anarch_libretro.info don't contain a core name continue } $coreInfoCoreName = $coreInfo.corename if ($raCoreNameToPlatformIdsTranslate.ContainsKey($coreInfoCoreName)) { $platformIds = $raCoreNameToPlatformIdsTranslate[$coreInfoCoreName] } elseif ($null -ne $coreInfo.systemid -and $raSystemIdToPlatformIdsTranslate.ContainsKey($coreInfo.systemid.Trim())) { $platformIds = $raSystemIdToPlatformIdsTranslate[$coreInfo.systemid.Trim()] } else { if (!($coreInfo.ContainsKey("systemname"))) { Write-Host "$($infoFile.Name) does not contain systemname" -ForegroundColor Yellow # galaksija_libretro.info continue } $coreInfo.systemname.Split("/", [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object { $system = $_.Trim() if (($retroarchArcadeSystems -contains $system) -or ($retroarchMiscSystems -contains $system)) { Continue } if ($raSystemNameToPlatformIdTranslate.ContainsKey($system)) { $platformId = $raSystemNameToPlatformIdTranslate[$system] if ($platformIds -notcontains $platformId) { $platformIds += $platformId } } else { Write-Host "System to platform translate not found. System: $system, CoreName: $($coreInfo.corename), DisplayName: $($coreInfo.display_name)" -ForegroundColor Yellow } } } if ($null -eq $platformIds -or $platformIds.Count -eq 0) { Write-Host "PlatformIds not found. System: $system, CoreName: $($coreInfo.corename), DisplayName: $($coreInfo.display_name)" -ForegroundColor Yellow continue } if ($coreInfo.supported_extensions) { $extensions = @() $coreInfo.supported_extensions.Split("|", [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object { $extensions += $_ } if ($extensions -notcontains "zip") { $extensions += "zip" } if ($extensions -notcontains "7z") { $extensions += "7z" } $extensions = $extensions | Sort-Object $extensionsString = [System.String]::Join(", ", $extensions) } else { Write-Host "Profile did not have extensions. System: $system, CoreName: $($coreInfo.corename), DisplayName: $($coreInfo.display_name)" -ForegroundColor Yellow continue } foreach ($platformId in $platformIds) { if ($foundPlatformIds -notcontains $platformId) { $foundPlatformIds += $platformId } } $platformIds = $platformIds | Sort-Object $extensions = $extensions | Sort-Object $platformIdsString = [System.String]::Join(", ", $platformIds) $profileString = $profileTemplate -f $coreName, $infoFile.BaseName, $platformIdsString, $extensionsString, $infoFile.BaseName $profiles += $profileString $foundCoreNames += $coreName $usedProfileNames += $coreName } # Previous profiles need to be kept or existing emulator configuration will break # Here emulators not generated will be added back foreach ($existingProfile in $existingEmuDefinition.Profiles) { if ($foundCoreNames -notcontains $existingProfile.Name) { foreach ($platformId in $existingProfile.Platforms) { if ($foundPlatformIds -notcontains $platformId) { $foundPlatformIds += $platformId } } $platformIds = [System.String]::Join(", ", ($existingProfile.Platforms | Sort-Object)) $extensionsString = [System.String]::Join(", ", ($existingProfile.ImageExtensions | Sort-Object)) $profileFilesString = [System.String]::Join(", ", ($existingProfile.ProfileFiles | Sort-Object)) $profileString = $existingProfileReaddTemplate -f $existingProfile.Name, $existingProfile.StartupArguments, $platformIds, $extensionsString, $profileFilesString $profiles += $profileString Write-Host "Existing profile $($existingProfile.Name) not found in newly generated definition and was added back from previous definition" -ForegroundColor Yellow } } # Join generated profiles and validated created profile yaml content $profiles = $profiles | Sort-Object $profilesString = [System.String]::Join("`n`n", $profiles) $emuDefinitionContent = $emuDefinitionTemplate -f $profilesString if ((Get-IsYamlValid $emuDefinitionContent) -eq $false) { Write-Host "Newly generated profile definition file is not valid!" -ForegroundColor Red Set-Clipboard $emuDefinitionContent return } # Add RetroArch to emulators list in Playnite platforms definitions $platformDefinitionTemplate = '- Name: {0} {1}' $newPlatformsDefinitions = @() $existingPlatformsDefinition = Get-Content $platformsDefinitionPath -Raw | ConvertFrom-Yaml foreach ($platform in $existingPlatformsDefinition) { $platformData = @() $platformData += " Id: {0}" -f $platform.Id if ($platform.IgdbId) { $platformData += "IgdbId: {0}" -f $platform.IgdbId } if ($platform.Databases) { $platformData += "Databases: [{0}]" -f [System.String]::Join(", ", ($platform.Databases | Sort-Object)) } $isSupportedByRetroArch = $foundPlatformIds -contains $platform.Id if ($platform.Emulators) { if ($isSupportedByRetroArch -and ($platform.Emulators -notcontains "retroarch")) { $platform.Emulators += "retroarch" $platform.Emulators = $platform.Emulators | Sort-Object Write-Host "Added retroarch emulator to `"$($platform.Name)`" platform" -ForegroundColor Blue } $platformData += "Emulators: [{0}]" -f [System.String]::Join(", ", ($platform.Emulators | Sort-Object)) } elseif ($isSupportedByRetroArch) { $platformData += "Emulators: [{0}]" -f "retroarch" } $platformDataString = $platformDefinitionTemplate -f $platform.Name, [System.String]::Join("`n ", $platformData) $newPlatformsDefinitions += $platformDataString } $newPlatformsDefinitionsContent = [System.String]::Join("`n `n", $newPlatformsDefinitions) # Validate created platforms definition yaml content if ((Get-IsYamlValid $newPlatformsDefinitionsContent) -eq $false) { Write-Host "Newly generated platforms definition file is not valid!" -ForegroundColor Red Set-Clipboard $newPlatformsDefinitionsContent return } # Save generated yaml files. New lines are replaced to maintain CRLF endings [System.IO.File]::WriteAllLines($retroArchEmuDefinitionPath, $emuDefinitionContent.Replace("`n","`r`n"), [System.Text.Encoding]::UTF8) [System.IO.File]::WriteAllLines($platformsDefinitionPath, $newPlatformsDefinitionsContent.Replace("`n","`r`n"), [System.Text.Encoding]::UTF8) Write-Host "RetroArch emulator and Playnite platforms definitions updated and saved successfully!" -ForegroundColor Green ================================================ FILE: build/updateLocalizations.ps1 ================================================ #Requires -Version 7 param( [string]$AccessToken ) $ErrorActionPreference = "Stop" $locDir = Join-Path $pwd "..\source\Playnite\Localization" $urlRoot = "https://crowdin.com/api/v2" $playnitePrjId = 345875 $requestHeaders = @{ "Authorization" = "Bearer $AccessToken" } $locProgressData = New-Object "System.Collections.Specialized.OrderedDictionary" $locProgress = Invoke-RestMethod -Method Get -Headers $requestHeaders -Uri "$urlRoot/projects/$playnitePrjId/files/30/languages/progress?limit=100" ` -ContentType "application/json" foreach ($lng in $locProgress.data) { $locProgressData.Add($lng.data.languageId, $lng.data.translationProgress); $locDownloadData = Invoke-RestMethod -Method Post -Headers $requestHeaders -Uri "$urlRoot/projects/$playnitePrjId/translations/builds/files/30" ` -Body "{`"targetLanguageId`":`"$($lng.data.languageId)`"}" -ContentType "application/json" $tempFile = Join-Path $locDir "temp.xaml" Remove-Item $tempFile -EA 0 $locDownload = Invoke-WebRequest -Uri $locDownloadData.data.url -OutFile $tempFile -PassThru $locDownload.Headers["Content-Disposition"][0] -match '"(.+)"' | Out-Null $fileName = $Matches[1] Move-Item $tempFile (Join-Path $locDir $fileName) -Force } $locProgressData | ConvertTo-Json | Out-File (Join-Path $locDir "locstatus.json") .\VerifyLanguageFiles.ps1 ================================================ FILE: crowdin.yml ================================================ files: - source: LocSource.xaml translation: /%original_path%/%locale_with_underscore%.xaml ================================================ FILE: source/.editorconfig ================================================ [*.cs] # IDE0058: Expression value is never used dotnet_diagnostic.IDE0058.severity = none # IDE0036: Order modifiers dotnet_diagnostic.IDE0036.severity = none # IDE0025: Use expression body for properties dotnet_diagnostic.IDE0025.severity = none ================================================ FILE: source/Playnite/API/AddonsAPI.cs ================================================ using Playnite.Plugins; using Playnite.SDK; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.API { public class AddonsAPI : IAddons { private readonly ExtensionFactory extensions; private readonly PlayniteSettings settings; public List DisabledAddons => settings.DisabledPlugins.ToList(); public List Addons => ExtensionFactory.GetInstalledManifests().Select(a => a.Id).ToList(); public List Plugins => extensions.Plugins.Select(a => a.Value.Plugin).ToList(); public AddonsAPI(ExtensionFactory extensions, PlayniteSettings settings) { this.extensions = extensions; this.settings = settings; } } } ================================================ FILE: source/Playnite/API/DatabaseAPI.cs ================================================ using LiteDB; using Playnite.Database; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite.API { public class DatabaseAPI : IGameDatabaseAPI { private GameDatabase database; #pragma warning disable CS0067 public event EventHandler DatabaseOpened; #pragma warning restore CS0067 public IItemCollection Games => database.Games; public IItemCollection Platforms => database.Platforms; public IItemCollection Emulators => database.Emulators; public IItemCollection Genres => database.Genres; public IItemCollection Companies => database.Companies; public IItemCollection Tags => database.Tags; public IItemCollection Categories => database.Categories; public IItemCollection Series => database.Series; public IItemCollection AgeRatings => database.AgeRatings; public IItemCollection Regions => database.Regions; public IItemCollection Sources => database.Sources; public IItemCollection Features => database.Features; public IItemCollection GameScanners => database.GameScanners; public IItemCollection CompletionStatuses => database.CompletionStatuses; public IItemCollection ImportExclusions => database.ImportExclusions; public IItemCollection FilterPresets => database.FilterPresets; public string DatabasePath { get => database?.DatabasePath; } public bool IsOpen { get => database?.IsOpen == true; } public DatabaseAPI(GameDatabase database) { this.database = database; } public string AddFile(string path, Guid parentId) { if (!File.Exists(path)) { throw new FileNotFoundException("Cannot add file to database, file not found."); } return database.AddFile(path, parentId, false, CancellationToken.None); } public void SaveFile(string id, string path) { database.CopyFile(id, path); } public void RemoveFile(string id) { database.RemoveFile(id); } public IDisposable BufferedUpdate() { return database.BufferedUpdate(); } public string GetFileStoragePath(Guid parentId) { return database.GetFileStoragePath(parentId); } public string GetFullFilePath(string databasePath) { return database.GetFullFilePath(databasePath); } public Game ImportGame(GameMetadata game) { return database.ImportGame(game); } public Game ImportGame(GameMetadata game, LibraryPlugin sourcePlugin) { return database.ImportGame(game, sourcePlugin.Id); } public void BeginBufferUpdate() { database.BeginBufferUpdate(); } public void EndBufferUpdate() { database.EndBufferUpdate(); } public bool GetGameMatchesFilter(Game game, FilterPresetSettings filterSettings) { return database.GetGameMatchesFilter(game, filterSettings, false); } public IEnumerable GetFilteredGames(FilterPresetSettings filterSettings) { return database.GetFilteredGames(filterSettings, false); } public bool GetGameMatchesFilter(Game game, FilterPresetSettings filterSettings, bool useFuzzyNameMatch) { return database.GetGameMatchesFilter(game, filterSettings, useFuzzyNameMatch); } public IEnumerable GetFilteredGames(FilterPresetSettings filterSettings, bool useFuzzyNameMatch) { return database.GetFilteredGames(filterSettings, useFuzzyNameMatch); } } } ================================================ FILE: source/Playnite/API/DesignData/DesignNotificationsAPI.cs ================================================ using Playnite.Commands; using Playnite.SDK; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.API.DesignData { public class DesignNotificationsAPI : ObservableObject, INotificationsAPI { public RelayCommand RemoveAllCommands { get => new RelayCommand((a) => { RemoveAll(); }); } public ObservableCollection Messages { get; set; } public int Count { get; } public DesignNotificationsAPI() { Count = 3; Messages = new ObservableCollection { new NotificationMessage("design1", "Design notification message 1", NotificationType.Error), new NotificationMessage("design2", "Design message 2", NotificationType.Info), new NotificationMessage("design3", "Design notification message 3, long message that does to multiple lies. Long message that does to multiple lies.", NotificationType.Error) }; } public void Add(NotificationMessage message) { throw new NotImplementedException(); } public void Add(string id, string text, NotificationType type) { throw new NotImplementedException(); } public void Remove(string id) { throw new NotImplementedException(); } public void RemoveAll() { Messages.Clear(); } } } ================================================ FILE: source/Playnite/API/DesignData/DesignPlayniteAPI.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Playnite.SDK.Events; namespace Playnite.API.DesignData { public class DesignPlayniteAPI : IPlayniteAPI { public IMainViewAPI MainView => throw new NotImplementedException(); public IGameDatabaseAPI Database => throw new NotImplementedException(); public IDialogsFactory Dialogs => throw new NotImplementedException(); public IPlaynitePathsAPI Paths => throw new NotImplementedException(); public INotificationsAPI Notifications { get; } = new DesignNotificationsAPI(); public IPlayniteInfoAPI ApplicationInfo => throw new NotImplementedException(); public IWebViewFactory WebViews => throw new NotImplementedException(); public IResourceProvider Resources => throw new NotImplementedException(); public IUriHandlerAPI UriHandler => throw new NotImplementedException(); public IPlayniteSettingsAPI ApplicationSettings => throw new NotImplementedException(); public IAddons Addons => throw new NotImplementedException(); public IEmulationAPI Emulation => throw new NotImplementedException(); public void AddCustomElementSupport(Plugin source, AddCustomElementSupportArgs args) { throw new NotImplementedException(); } public ILogger CreateLogger(string name) { throw new NotImplementedException(); } public ILogger CreateLogger() { throw new NotImplementedException(); } public string ExpandGameVariables(Game game, string inputString) { throw new NotImplementedException(); } public GameAction ExpandGameVariables(Game game, GameAction action) { throw new NotImplementedException(); } public void StartGame(Guid gameId) { throw new NotImplementedException(); } public void InstallGame(Guid gameId) { throw new NotImplementedException(); } public void UninstallGame(Guid gameId) { throw new NotImplementedException(); } public void AddSettingsSupport(Plugin source, AddSettingsSupportArgs args) { throw new NotImplementedException(); } public void AddConvertersSupport(Plugin source, AddConvertersSupportArgs args) { throw new NotImplementedException(); } public List GetConnectedControllers() { throw new NotImplementedException(); } public string ExpandGameVariables(Game game, string inputString, string emulatorDir) { throw new NotImplementedException(); } } } ================================================ FILE: source/Playnite/API/NotificationsAPI.cs ================================================ using Playnite.Commands; using Playnite.SDK; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite.API { public class NotificationsAPI : ObservableObject, INotificationsAPI { public class MessageEventArgs : EventArgs { public NotificationMessage Message { get; } public MessageEventArgs(NotificationMessage message) { Message = message; } } private readonly SynchronizationContext context; public event EventHandler ActivationRequested; public event EventHandler CloseRequested; public ObservableCollection Messages { get; } public int Count { get => Messages.Count; } public NotificationsAPI() { context = SynchronizationContext.Current; Messages = new ObservableCollection(); } private void Message_Activated(object sender, EventArgs e) { ActivationRequested(this, new MessageEventArgs(sender as NotificationMessage)); } private void Message_Closed(object sender, EventArgs e) { CloseRequested(this, new MessageEventArgs(sender as NotificationMessage)); } public void Add(NotificationMessage message) { context.Send((c => { if (!Messages.Any(a => a.Id == message.Id)) { message.Activated += Message_Activated; message.Closed += Message_Closed; Messages.Add(message); OnPropertyChanged(nameof(Count)); } }), null); } public void Add(string id, string text, NotificationType type) { context.Send((c => { if (!Messages.Any(a => a.Id == id)) { Add(new NotificationMessage(id, text, type)); } }), null); } public void Remove(string id) { context.Send((c => { var message = Messages.FirstOrDefault(a => a.Id == id); if (message != null) { message.Activated -= Message_Activated; message.Closed -= Message_Closed; Messages.Remove(message); OnPropertyChanged(nameof(Count)); } }), null); } public void RemoveAll() { context.Send((c => { foreach (var message in Messages) { message.Activated -= Message_Activated; message.Closed -= Message_Closed; } Messages.Clear(); OnPropertyChanged(nameof(Count)); }), null); } } } ================================================ FILE: source/Playnite/API/PlayniteAPI.cs ================================================ using Newtonsoft.Json; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using Playnite.Settings; using Playnite.Common; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using Playnite.Plugins; using System.Threading; using Playnite.Database; using Playnite.Input; using Playnite.SDK.Events; using static Microsoft.Scripting.Hosting.Shell.ConsoleHostOptions; namespace Playnite.API { public interface IPlayniteAPIRoot { string ExpandGameVariables(Game game, string inputString); GameAction ExpandGameVariables(Game game, GameAction action); string ExpandGameVariables(Game game, string inputString, string emulatorDir); void StartGame(Guid gameId); void InstallGame(Guid gameId); void UninstallGame(Guid gameId); void AddCustomElementSupport(Plugin source, AddCustomElementSupportArgs args); void AddSettingsSupport(Plugin source, AddSettingsSupportArgs args); void AddConvertersSupport(Plugin source, AddConvertersSupportArgs args); List GetConnectedControllers(); } public class PlayniteApiRoot : IPlayniteAPIRoot { private static readonly ILogger logger = LogManager.GetLogger(); private readonly GamesEditor gameEditor; private readonly ExtensionFactory extensions; private readonly GameDatabase database; private readonly ViewModels.MainViewModelBase mainModel; private readonly SynchronizationContext execContext; public PlayniteApiRoot( GamesEditor gameEditor, ExtensionFactory extensions, GameDatabase database, ViewModels.MainViewModelBase mainModel) { this.gameEditor = gameEditor; this.extensions = extensions; this.database = database; this.mainModel = mainModel; execContext = SynchronizationContext.Current; } public string ExpandGameVariables(Game game, string inputString) { return game?.ExpandVariables(inputString); } public string ExpandGameVariables(Game game, string inputString, string emulatorDir) { return game?.ExpandVariables(inputString, emulatorDir: emulatorDir); } public GameAction ExpandGameVariables(Game game, GameAction action) { return action?.ExpandVariables(game); } public void StartGame(Guid gameId) { var game = database.Games.Get(gameId); if (game == null) { logger.Error($"Can't start game, game ID {gameId} not found."); } else { // Run on main thread for edge cases like this: // https://www.reddit.com/r/playnite/comments/slyg99/boot_another_game_as_a_play_action/ execContext.Send((_) => gameEditor.PlayGame(game, false), null); } } public void InstallGame(Guid gameId) { var game = database.Games.Get(gameId); if (game == null) { logger.Error($"Can't install game, game ID {gameId} not found."); } else { gameEditor.InstallGame(game); } } public void UninstallGame(Guid gameId) { var game = database.Games.Get(gameId); if (game == null) { logger.Error($"Can't uninstall game, game ID {gameId} not found."); } else { gameEditor.UnInstallGame(game); } } public void AddCustomElementSupport(Plugin source, AddCustomElementSupportArgs args) { extensions.AddCustomElementSupport(source, args); } public void AddSettingsSupport(Plugin source, AddSettingsSupportArgs args) { extensions.AddSettingsSupport(source, args); } public void AddConvertersSupport(Plugin source, AddConvertersSupportArgs args) { extensions.AddConvertersSupport(source, args); } public List GetConnectedControllers() { return mainModel.App.GameController.Controllers.Cast().ToList(); } } public class PlayniteAPI : IPlayniteAPI { public IPlayniteAPIRoot RootApi { get; set; } public IDialogsFactory Dialogs { get; set; } public IGameDatabaseAPI Database { get; set; } public IMainViewAPI MainView { get; set; } public IPlaynitePathsAPI Paths { get; set; } public IPlayniteInfoAPI ApplicationInfo { get; set; } public IWebViewFactory WebViews { get; set; } public IResourceProvider Resources { get; set; } public INotificationsAPI Notifications { get; set; } public IUriHandlerAPI UriHandler { get; set; } public IPlayniteSettingsAPI ApplicationSettings { get; set; } public IAddons Addons { get; set; } public IEmulationAPI Emulation { get; set; } public PlayniteAPI() { } public string ExpandGameVariables(Game game, string inputString) { return RootApi.ExpandGameVariables(game, inputString); } public string ExpandGameVariables(Game game, string inputString, string emulatorDir) { return RootApi.ExpandGameVariables(game, inputString, emulatorDir); } public GameAction ExpandGameVariables(Game game, GameAction action) { return RootApi.ExpandGameVariables(game, action); } public void StartGame(Guid gameId) { RootApi.StartGame(gameId); } public void InstallGame(Guid gameId) { RootApi.InstallGame(gameId); } public void UninstallGame(Guid gameId) { RootApi.UninstallGame(gameId); } public void AddCustomElementSupport(Plugin source, AddCustomElementSupportArgs args) { RootApi.AddCustomElementSupport(source, args); } public void AddSettingsSupport(Plugin source, AddSettingsSupportArgs args) { RootApi.AddSettingsSupport(source, args); } public void AddConvertersSupport(Plugin source, AddConvertersSupportArgs args) { RootApi.AddConvertersSupport(source, args); } public List GetConnectedControllers() { return RootApi.GetConnectedControllers(); } } } ================================================ FILE: source/Playnite/API/PlayniteInfoAPI.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.API { public class PlayniteInfoAPI : IPlayniteInfoAPI { public System.Version ApplicationVersion { get => Updater.CurrentVersion; } public ApplicationMode Mode => PlayniteApplication.Current.Mode; public bool IsPortable => PlayniteSettings.IsPortable; public bool InOfflineMode => PlayniteEnvironment.InOfflineMode; public bool IsDebugBuild => PlayniteEnvironment.IsDebugBuild; public bool ThrowAllErrors => PlayniteEnvironment.ThrowAllErrors; } } ================================================ FILE: source/Playnite/API/PlaynitePathsAPI.cs ================================================ using Playnite.SDK; using Playnite.Settings; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.API { public class PlaynitePathsAPI : IPlaynitePathsAPI { public bool IsPortable { get => PlayniteSettings.IsPortable; } public string ApplicationPath { get => PlaynitePaths.ProgramPath; } public string ConfigurationPath { get => PlaynitePaths.ConfigRootPath; } public string ExtensionsDataPath { get => PlaynitePaths.ExtensionsDataPath; } } } ================================================ FILE: source/Playnite/API/PlayniteSettingsAPI.cs ================================================ using Playnite.Database; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; namespace Playnite.API { public class FullscreenSettingsAPI : IFullscreenSettingsAPI { private readonly FullscreenSettings settings; public bool IsMusicMuted { get => settings.IsMusicMuted; set => settings.IsMusicMuted = value; } public bool SwapConfirmCancelButtons => settings.SwapConfirmCancelButtons; public bool SwapStartDetailsAction => settings.SwapStartDetailsAction; public bool GuideButtonFocus => settings.GuideButtonFocus; public FullscreenSettingsAPI(FullscreenSettings settings) { this.settings = settings; } } public class CompletionStatusSettignsApi : ICompletionStatusSettignsApi { private readonly GameDatabase db; public CompletionStatusSettignsApi(GameDatabase database) { db = database; } public Guid DefaultStatus => db.GetCompletionStatusSettings().DefaultStatus; public Guid PlayedStatus => db.GetCompletionStatusSettings().DefaultStatus; } public class PlayniteSettingsAPI : IPlayniteSettingsAPI { private readonly PlayniteSettings settings; private readonly GameDatabase db; public int Version => settings.Version; public int GridItemWidthRatio => settings.GridItemWidthRatio; public int GridItemHeightRatio => settings.GridItemHeightRatio; public bool FirstTimeWizardComplete => settings.FirstTimeWizardComplete; public bool DisableHwAcceleration => settings.DisableHwAcceleration; public bool AsyncImageLoading => settings.AsyncImageLoading; public bool DownloadMetadataOnImport => settings.DownloadMetadataOnImport; public bool InstallSizeScanUseSizeOnDisk => settings.InstallSizeScanUseSizeOnDisk; public bool ScanLibInstallSizeOnLibUpdate => settings.ScanLibInstallSizeOnLibUpdate; public bool StartInFullscreen => settings.StartInFullscreen; public string DatabasePath => settings.DatabasePath; public bool MinimizeToTray => settings.MinimizeToTray; public bool CloseToTray => settings.CloseToTray; public bool EnableTray => settings.EnableTray; public string Language => settings.Language == "english" ? "en_US" : settings.Language; public bool UpdateLibStartup => settings.CheckForLibraryUpdates == LibraryUpdateCheckFrequency.OnEveryStartup; public string DesktopTheme => settings.Theme; public string FullscreenTheme => settings.Fullscreen.Theme; public bool StartMinimized => settings.StartMinimized; public bool StartOnBoot => settings.StartOnBoot; public string FontFamilyName => settings.FontFamilyName; public bool DiscordPresenceEnabled => settings.DiscordPresenceEnabled; public AgeRatingOrg AgeRatingOrgPriority => settings.AgeRatingOrgPriority; public bool SidebarVisible => settings.ShowSidebar; public Dock SidebarPosition => settings.SidebarPosition; public IFullscreenSettingsAPI Fullscreen { get; } public ICompletionStatusSettignsApi CompletionStatus { get; } public bool ForcePlayTimeSync => false; public PlaytimeImportMode PlaytimeImportMode => settings.PlaytimeImportMode; public PlayniteSettingsAPI(PlayniteSettings settings, GameDatabase db) { this.settings = settings; this.db = db; Fullscreen = new FullscreenSettingsAPI(settings.Fullscreen); CompletionStatus = new CompletionStatusSettignsApi(db); } public bool GetGameExcludedFromImport(string gameId, Guid libraryId) { if (gameId.IsNullOrEmpty() || libraryId == Guid.Empty) { throw new ArgumentNullException("gameId and libraryId must be specified."); } return db.ImportExclusions.Get(ImportExclusionItem.GetId(gameId, libraryId)) != null; } } } ================================================ FILE: source/Playnite/Addons/Addons.cs ================================================ using Playnite.Plugins; using Playnite.SDK; using Playnite.Services; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite { public enum AddonUpdateStatus { [Description("")] None, [Description(LOC.AddonUpdateStatusDownloaded)] Downloaded, [Description(LOC.AddonUpdateStatusFailed)] Failed, [Description(LOC.AddonUpdateStatusLicenseRejected)] LicenseRejected, } public class AddonUpdate : SelectableItem { public string UpdateInfo { get; set; } public string Changelog { get; set; } public AddonInstallerPackage Package { get; set; } private AddonUpdateStatus status = AddonUpdateStatus.None; public AddonUpdateStatus Status { get => status; set { status = value; OnPropertyChanged(); } } private string statusMessage; public string StatusMessage { get => statusMessage; set { statusMessage = value; OnPropertyChanged(); } } public AddonUpdate(AddonManifest item) : base(item) { } } public static class Addons { private static ILogger logger = LogManager.GetLogger(); private static List CheckAddonsForUpdate(IEnumerable manifests, ServicesClient serviceClient) { var random = new Random(); var updateList = new List(); foreach (var manifest in manifests) { try { var addonManifest = serviceClient.GetAddon(manifest.Id); if (addonManifest == null) { continue; } var installer = addonManifest.InstallerManifest; var package = installer.GetLatestCompatiblePackage(); var currentVersion = Version.Parse(manifest.Version); var changeLog = string.Empty; if (package != null && package.Version > currentVersion) { if (installer.Packages.HasItems()) { var changes = installer.Packages.Where(a => a.Version > currentVersion && a.Version <= package.Version).ToList(); if (changes.HasItems()) { changes.ForEach(a => { changeLog += a.Version.ToString(); a.Changelog?.ForEach(b => changeLog += Environment.NewLine + $" • {b}"); changeLog += Environment.NewLine; }); } } updateList.Add(new AddonUpdate(addonManifest) { Selected = true, UpdateInfo = $"{currentVersion} -> {package.Version}", Changelog = changeLog, Package = package }); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to check for addon for update. {manifest.Id}"); } } return updateList; } public static List CheckAddonUpdates(ServicesClient serviceClient) { var updateList = new List(); if (PlayniteEnvironment.InOfflineMode) { return updateList; } var descriptions = ExtensionFactory.GetInstalledManifests(); updateList.AddRange(CheckAddonsForUpdate(descriptions.Where(a => a.Type == ExtensionType.MetadataProvider), serviceClient)); updateList.AddRange(CheckAddonsForUpdate(descriptions.Where(a => a.Type == ExtensionType.GameLibrary), serviceClient)); updateList.AddRange(CheckAddonsForUpdate(descriptions.Where(a => a.Type == ExtensionType.GenericPlugin), serviceClient)); updateList.AddRange(CheckAddonsForUpdate(descriptions.Where(a => a.Type == ExtensionType.Script), serviceClient)); updateList.AddRange(CheckAddonsForUpdate(ThemeManager.GetAvailableThemes(ApplicationMode.Desktop).Where(a => !a.IsBuiltInTheme), serviceClient)); updateList.AddRange(CheckAddonsForUpdate(ThemeManager.GetAvailableThemes(ApplicationMode.Fullscreen).Where(a => !a.IsBuiltInTheme), serviceClient)); var blackList = serviceClient.GetAddonBlacklist(); updateList.Where(a => blackList.Contains(a.Item.AddonId)).ToList().ForEach(a => updateList.Remove(a)); return updateList; } } } ================================================ FILE: source/Playnite/App/CmdLineOptions.cs ================================================ using CommandLine; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class CmdLineOptions { [Option("start")] public string Start { get; set; } [Option("uridata")] public string UriData { get; set; } [Option("nolibupdate")] public bool SkipLibUpdate { get; set; } [Option("startdesktop")] public bool StartInDesktop { get; set; } [Option("startfullscreen")] public bool StartInFullscreen { get; set; } [Option("forcesoftrender")] public bool ForceSoftwareRender { get; set; } [Option("forcedefaulttheme")] public bool ForceDefaultTheme { get; set; } [Option("hidesplashscreen")] public bool HideSplashScreen { get; set; } [Option("installext")] public string InstallExtension { get; set; } [Option("clearwebcache")] public bool ClearWebCache { get; set; } [Option("shutdown")] public bool Shutdown { get; set; } [Option("safestartup")] public bool SafeStartup { get; set; } [Option("resetsettings")] public bool ResetSettings { get; set; } [Option("masterinstance")] public bool MasterInstance { get; set; } [Option("backup")] public string Backup { get; set; } [Option("restorebackup")] public string RestoreBackup { get; set; } [Option("startclosedtotray")] public bool StartClosedToTray { get; set; } [Option("userdatadir")] public string UserDataDir { get; set; } [Option("fullscreenwidth")] public int FullscreenWidth { get; set; } [Option("fullscreenheight")] public int FullscreenHeight { get; set; } public override string ToString() { return Parser.Default.FormatCommandLine(this); } } } ================================================ FILE: source/Playnite/App/IPlayniteApplication.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public interface IPlayniteApplication { void Quit(bool saveSettings); void Restart(bool saveSettings); void QuitAndStart(string path, string arguments, bool asAdmin = false, bool saveSettings = true); } } ================================================ FILE: source/Playnite/App/PlayniteApplication.cs ================================================ using Playnite.Controllers; using Playnite.Input; using Playnite.SDK; using Playnite.Plugins; using Playnite.ViewModels; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Diagnostics; using Playnite.Database; using Playnite.API; using TheArtOfDev.HtmlRenderer; using Playnite.Services; using System.Windows.Input; using System.Windows.Interop; using System.Reflection; using System.IO; using Playnite.Common; using System.ComponentModel; using Playnite.Windows; using Polly; using System.Windows.Media; using Playnite.SDK.Events; using System.Windows.Threading; using System.Net; using Playnite.Common.Web; using System.ServiceProcess; using System.Drawing.Imaging; namespace Playnite { public abstract class PlayniteApplication : ObservableObject, IPlayniteApplication { private ILogger logger = LogManager.GetLogger(); private const string instanceMuxet = "PlayniteInstaceMutex"; private Mutex appMutex; public bool ResourcesReleased { get; private set; } = false; private PipeService pipeService; private PipeServer pipeServer; private System.Threading.Timer updateCheckTimer; private bool installingAddon = false; private AddonLoadError themeLoadError = AddonLoadError.None; private ThemeManifest customTheme; private bool isActive; public bool IsActive { get => isActive; set { isActive = value; OnPropertyChanged(); } } public static Version CurrentVersion => Updater.CurrentVersion; public event EventHandler ExtensionsLoaded; public ApplicationMode Mode { get; } public IDialogsFactory Dialogs { get; set; } public PlayniteSettings AppSettings { get; set; } public GamesEditor GamesEditor { get; set; } public ExtensionFactory Extensions { get; set; } public GameDatabase Database { get; set; } public GameControllerFactory Controllers { get; set; } public CmdLineOptions CmdLine { get; set; } public DpiScale DpiScale { get; set; } = new DpiScale(1, 1); public ComputerScreen CurrentScreen { get; set; } = Computer.GetPrimaryScreen(); public DiscordManager Discord { get; set; } public SynchronizationContext SyncContext { get; private set; } public Action AppUriHandler { get; set; } public static Application CurrentNative { get; private set; } public static PlayniteApplication Current { get; private set; } public ServicesClient ServicesClient { get; private set; } public MainViewModelBase MainModelBase { get; set; } public List ExtensionsInstallResult { get; set; } public NotificationsAPI Notifications { get; } public PlayniteUriHandler UriHandler { get; } public PlayniteAPI PlayniteApiGlobal { get; set; } public GameControllerManager GameController { get; set; } private ExtensionsStatusBinder extensionsStatusBinder = new ExtensionsStatusBinder(); public ExtensionsStatusBinder ExtensionsStatusBinder { get => extensionsStatusBinder; set => SetValue(ref extensionsStatusBinder, value); } public PlayniteApplication() { } public PlayniteApplication( Func appInitializer, ApplicationMode mode, CmdLineOptions cmdLine) { if (Current != null) { throw new Exception("Only one application instance is allowed."); } // TODO: remove after switch to .NET 5 // Fixes various network issues on 2004+ Win10 if TLS 1.3 is forced via registry. if (Computer.IsTLS13SystemWideEnabled()) { logger.Warn("System wide TLS 1.3 is enabled, forcing 1.2."); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; } CmdLine = cmdLine; Mode = mode; Current = this; if (!Debugger.IsAttached) { AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; } if (!CmdLine.MasterInstance) { if (CheckOtherInstances() || CmdLine.Shutdown) { ResourcesReleased = true; Environment.Exit(0); return; } } #if !DEBUG if (FileSystem.FileExists(PlaynitePaths.SafeStartupFlagFile)) { if (MessageBox.Show( "Playnite closed unexpectedly while starting. This is usually caused by 3rd party theme or extension. Do you want to start in safe mode with all 3rd party add-ons disabled?", "Startup Error", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes) { cmdLine.SafeStartup = true; } } else { FileSystem.CreateFile(PlaynitePaths.SafeStartupFlagFile); } #endif // All code above has to be called before we create instance of WPF app, // because MessageBox forces WPF to initialize and fire startup app events. CurrentNative = appInitializer(); CurrentNative.ShutdownMode = ShutdownMode.OnExplicitShutdown; SyncContext = new DispatcherSynchronizationContext(CurrentNative.Dispatcher); SynchronizationContext.SetSynchronizationContext(SyncContext); appMutex = new Mutex(true, instanceMuxet); try { // This can fail in rare cases when switching application modes // if an old instance fails to clean after itself or if it gets stuck on exit. Policy.Handle() .WaitAndRetry(3, a => TimeSpan.FromSeconds(3)) .Execute(() => pipeService = new PipeService()); pipeService.CommandExecuted += PipeService_CommandExecuted; pipeServer = new PipeServer(PlayniteSettings.GetAppConfigValue("PipeEndpoint")); pipeServer.StartServer(pipeService); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to start pipe service."); } PlayniteSettings.MigrateSettingsConfig(); AppSettings = PlayniteSettings.LoadSettings(); Commands.GlobalCommands.AppSettings = AppSettings; NLogLogger.IsTraceEnabled = AppSettings.TraceLogEnabled; if (AppSettings.ShouldDataBackupOnStartup()) { var backOptions = Backup.GetAutoBackupOptions(AppSettings, PlaynitePaths.ConfigRootPath, GameDatabase.GetFullDbPath(AppSettings.DatabasePath)); FileSystem.WriteStringToFile(PlaynitePaths.BackupActionFile, Serialization.ToJson(backOptions)); CmdLine.Backup = PlaynitePaths.BackupActionFile; AppSettings.LastAutoBackup = DateTime.Now; AppSettings.SaveSettings(); } if (!CmdLine.Backup.IsNullOrEmpty() || !CmdLine.RestoreBackup.IsNullOrEmpty()) { ServicesClient = new ServicesClient(); CurrentNative.SessionEnding += Application_SessionEnding; CurrentNative.Exit += Application_Exit; CurrentNative.Startup += Application_Startup; CurrentNative.Activated += Application_Activated; CurrentNative.Deactivated += Application_Deactivated; var defaultTheme = new ThemeManifest() { DirectoryName = ThemeManager.DefaultThemeDirName, DirectoryPath = Path.Combine(PlaynitePaths.ThemesProgramPath, ThemeManager.GetThemeRootDir(Mode), ThemeManager.DefaultThemeDirName), Name = ThemeManager.DefaultThemeDirName, Id = mode == ApplicationMode.Desktop ? ThemeManager.DefaultDesktopThemeId : ThemeManager.DefaultFullscreenThemeId }; ThemeManager.SetDefaultTheme(defaultTheme); InitializeNative(); try { Localization.SetLanguage(AppSettings.Language); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, $"Failed to set {AppSettings.Language} langauge."); } } else { if (CmdLine.ResetSettings) { var settings = PlayniteSettings.GetDefaultSettings(); settings.FirstTimeWizardComplete = true; settings.DatabasePath = AppSettings.DatabasePath; settings.SaveSettings(); AppSettings = settings; } var relaunchPath = string.Empty; if (AppSettings.StartInFullscreen && mode == ApplicationMode.Desktop && !CmdLine.StartInDesktop) { relaunchPath = PlaynitePaths.FullscreenExecutablePath; } if (CmdLine.StartInDesktop && mode != ApplicationMode.Desktop) { relaunchPath = PlaynitePaths.DesktopExecutablePath; } else if (CmdLine.StartInFullscreen && mode != ApplicationMode.Fullscreen) { relaunchPath = PlaynitePaths.FullscreenExecutablePath; } if (!relaunchPath.IsNullOrEmpty()) { FileSystem.DeleteFile(PlaynitePaths.SafeStartupFlagFile); ProcessStarter.StartProcess(relaunchPath, CmdLine.ToString()); CurrentNative.Shutdown(0); return; } ServicesClient = new ServicesClient(); CurrentNative.SessionEnding += Application_SessionEnding; CurrentNative.Exit += Application_Exit; CurrentNative.Startup += Application_Startup; CurrentNative.Activated += Application_Activated; CurrentNative.Deactivated += Application_Deactivated; OnPropertyChanged(nameof(AppSettings)); var defaultTheme = new ThemeManifest() { DirectoryName = ThemeManager.DefaultThemeDirName, DirectoryPath = Path.Combine(PlaynitePaths.ThemesProgramPath, ThemeManager.GetThemeRootDir(Mode), ThemeManager.DefaultThemeDirName), Name = ThemeManager.DefaultThemeDirName, Id = mode == ApplicationMode.Desktop ? ThemeManager.DefaultDesktopThemeId : ThemeManager.DefaultFullscreenThemeId }; try { WaitForOtherInstacesToExit(false); ExtensionsInstallResult = ExtensionInstaller.InstallExtensionQueue(); var installedTheme = ExtensionsInstallResult.FirstOrDefault(a => a.InstalledManifest is ThemeManifest && !a.Updated); if (installedTheme?.InstalledManifest != null) { var theme = installedTheme.InstalledManifest as ThemeManifest; if (theme.Mode == Mode) { if (theme.Mode == ApplicationMode.Desktop) { AppSettings.Theme = theme.Id; } else { AppSettings.Fullscreen.Theme = theme.Id; } } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to finish installing extenions."); } ThemeManager.SetDefaultTheme(defaultTheme); // Theme must be set BEFORE default app resources are initialized for ThemeFile markup to apply custom theme's paths. customTheme = null; if (CmdLine.ForceDefaultTheme || CmdLine.SafeStartup) { logger.Warn("Default theme forced by cmdline."); } else { var theme = mode == ApplicationMode.Desktop ? AppSettings.Theme : AppSettings.Fullscreen.Theme; if (theme != ThemeManager.DefaultTheme.Id) { customTheme = ThemeManager.GetAvailableThemes(mode).Where(a => a.Id == theme).OrderByDescending(a => a.Version).FirstOrDefault(); if (customTheme == null) { logger.Error($"Failed to apply theme {theme}, theme not found."); if (mode == ApplicationMode.Desktop) { AppSettings.Theme = ThemeManager.DefaultDesktopThemeId; } else { AppSettings.Fullscreen.Theme = ThemeManager.DefaultFullscreenThemeId; } ThemeManager.SetCurrentTheme(defaultTheme); } else { ThemeManager.SetCurrentTheme(customTheme); } } } InitializeNative(); try { if (!AppSettings.FirstTimeWizardComplete) { var cultName = System.Globalization.CultureInfo.CurrentUICulture.Name.Replace('-', '_'); var validLang = Localization.AvailableLanguages.FirstOrDefault(a => a.Id == cultName && a.TranslatedPercentage > 75); if (validLang != null) { AppSettings.Language = validLang.Id; } } Localization.SetLanguage(AppSettings.Language); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, $"Failed to set {AppSettings.Language} langauge."); } // Must be applied AFTER default app resources are initialized, otherwise custom resource dictionaries won't be properly added to application scope. if (customTheme != null) { themeLoadError = ThemeManager.ApplyTheme(CurrentNative, customTheme, Mode); if (themeLoadError != AddonLoadError.None) { ThemeManager.SetCurrentTheme(null); logger.Error($"Failed to load theme {customTheme.Name}, {themeLoadError}."); } } if (mode == ApplicationMode.Desktop) { try { if (System.Drawing.FontFamily.Families.Any(a => a.Name == AppSettings.FontFamilyName)) { CurrentNative.Resources.Add( "FontFamily", new FontFamily(AppSettings.FontFamilyName)); } else { logger.Error($"Cannot set font {AppSettings.FontFamilyName}, font not found."); } if (System.Drawing.FontFamily.Families.Any(a => a.Name == AppSettings.MonospaceFontFamilyName)) { CurrentNative.Resources.Add( "MonospaceFontFamily", new FontFamily(AppSettings.MonospaceFontFamilyName)); } else { logger.Error($"Cannot set monospace font {AppSettings.MonospaceFontFamilyName}, font not found."); } if (AppSettings.FontSize > 0) { CurrentNative.Resources.Add( "FontSize", AppSettings.FontSize); } if (AppSettings.FontSizeSmall > 0) { CurrentNative.Resources.Add( "FontSizeSmall", AppSettings.FontSizeSmall); } if (AppSettings.FontSizeLarge > 0) { CurrentNative.Resources.Add( "FontSizeLarge", AppSettings.FontSizeLarge); } if (AppSettings.FontSizeLarger > 0) { CurrentNative.Resources.Add( "FontSizeLarger", AppSettings.FontSizeLarger); } if (AppSettings.FontSizeLargest > 0) { CurrentNative.Resources.Add( "FontSizeLargest", AppSettings.FontSizeLargest); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to set font {AppSettings.FontFamilyName}"); } } else { if (AppSettings.Fullscreen.FontSize > 0) { CurrentNative.Resources.Add( "FontSize", AppSettings.Fullscreen.FontSize); } if (AppSettings.Fullscreen.FontSizeSmall > 0) { CurrentNative.Resources.Add( "FontSizeSmall", AppSettings.Fullscreen.FontSizeSmall); } } // Only use this for Desktop mode. Non-default options look terrible in Fullscreen because of viewport scaling. if (mode == ApplicationMode.Desktop) { Controls.WindowBase.SetTextRenderingOptions(AppSettings.TextFormattingMode, AppSettings.TextRenderingMode); } Notifications = new NotificationsAPI(); UriHandler = new PlayniteUriHandler(); } } public abstract void InstantiateApp(); public abstract void InitializeNative(); public abstract void Restore(); public abstract void Minimize(); public abstract void ShowWindowsNotification(string title, string body, Action action); public abstract void SwitchAppMode(ApplicationMode mode); public abstract void ConfigureViews(); private void Application_SessionEnding(object sender, SessionEndingCancelEventArgs e) { logger.Info("Shutting down application because of session ending."); // Don't dispose CefSharp here because of bug in CefSharp during system shutdown // https://github.com/JosefNemec/Playnite/issues/866 AppSettings?.SaveSettings(); ReleaseResources(false); CurrentNative.Shutdown(0); } private void Application_Exit(object sender, ExitEventArgs e) { ReleaseResources(); } private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { // Running under Wine is not supported if (PlayniteProcess.WorkingSetMemory == 0 && Programs.GetUnistallProgramsList().Any(a => a.DisplayName.StartsWith("Wine Mono", StringComparison.OrdinalIgnoreCase))) { Process.GetCurrentProcess().Kill(); return; } var exception = (Exception)e.ExceptionObject; var crashInfo = Exceptions.GetExceptionInfo(exception, Extensions); logger.Error(exception, $"Unhandled exception occured."); logger.Error($"HResult: 0x{exception.HResult:X8}"); if (exception is Win32Exception win32exc) logger.Error($"Win32 NativeErrorCode: 0x{win32exc.NativeErrorCode:X8}"); CrashHandlerViewModel crashModel = null; // Delete safe startup flag if we are able to handle the crash, // safe startup option should show for crashes we are not handling. FileSystem.DeleteFile(PlaynitePaths.SafeStartupFlagFile); if (crashInfo.IsLiteDbCorruptionCrash) { Dialogs.ShowErrorMessage(LOC.DBCorruptionCrashMessage.GetLocalized()); Process.GetCurrentProcess().Kill(); return; } if (crashInfo.IsExtensionCrash) { crashModel = new CrashHandlerViewModel( new ExtensionCrashHandlerWindowFactory(), Dialogs, new ResourceProvider(), Mode, crashInfo, AppSettings); } else { // unchecked use reason: https://stackoverflow.com/a/10043486/1107424 // This started happening after the infamous 2026 January Win 11 update, Smart App Control is agersively blocking unsigned files from loading. // Based on crash reports, this usually happens to SDL's and CefSharp's dlls, also random plugins. if ((exception is FileLoadException || exception is DllNotFoundException) && exception.Message.Contains("0x800711C7")) // This is actually not set in HResult, it's in exception message and replaced by .NET to generic 0x80131524 { Dialogs.ShowErrorMessage("Failed to load dependencies needed for Playnite to continue operating properly.\n\nThis is usually caused by Windows Smart App Control blocking dlls in Playnite's install folder."); Process.GetCurrentProcess().Kill(); return; } // Have nonsense crashes with this about normal .NET runtime methods and Playnite class methods missing. if (exception is MissingMethodException || exception is BadImageFormatException || exception is InvalidProgramException || // Looks like there are some nested TargetInvocationException with MissingMethodException actual extension, // which seems to look like corrupted installed where binaries from different version got mixed up. exception.StackTrace?.Contains("System.MissingMethodException") == true || // Usually COM execution error from WindowsAPICodePack when opening folder selection dialog. As far as I can tell, this happens on "debloated" Windows edition only. (exception is System.Runtime.InteropServices.COMException && (exception.HResult == unchecked((int)0x80004005) || exception.HResult == unchecked((int)0x80040111))) || // DWM_E_COMPOSITIONDISABLED, looks like this can happen when GPU driver crashes and doesn't reboot properly exception.HResult == unchecked((int)0x80263001)) { Dialogs.ShowErrorMessage("System issue or corrupted Playnite install detected."); Process.GetCurrentProcess().Kill(); return; } // This seems to start happening after January Windows 11 update for libraries synced via cloud // storage (OneDrive and DropBox reported by users). Looks like something fucked in that Windows update // but we generally do not recommend/support storing files on cloud folders. if (Diagnostic.IsHResultCloudError(exception.HResult)) { Dialogs.ShowErrorMessage("Cloud related file system operation failed. Might be issue with Windows or cloud sync app you are using.\n\nWe do not recommend storing Playnite files on cloud synced folders since it's been known to cause data corruption in the past."); Process.GetCurrentProcess().Kill(); return; } // ERROR_DISK_FULL if (exception.HResult == unchecked((int)0x80070070) || // "device not ready" error. Happens when people run Playnite from attached storage as far as I can tell. exception.HResult == unchecked((int)0x80070015) || // self-explanatory exception is OutOfMemoryException) { Dialogs.ShowErrorMessage(exception.Message, LOC.CrashWindowTitle.GetLocalized()); Process.GetCurrentProcess().Kill(); return; } crashModel = new CrashHandlerViewModel( new CrashHandlerWindowFactory(), Dialogs, new ResourceProvider(), Mode); } crashModel.OpenView(); Process.GetCurrentProcess().Kill(); } private void Application_Startup(object sender, StartupEventArgs e) { logger.Info($"Application started from '{PlaynitePaths.ProgramPath}'"); SDK.Data.Markup.Init(new MarkupConverter()); SDK.Data.Serialization.Init(new DataSerializer()); SDK.Data.SQLite.Init((a, b) => new Sqlite(a, b)); EventManager.RegisterClassHandler(typeof(Controls.WindowBase), Controls.WindowBase.ClosedRoutedEvent, new RoutedEventHandler(WindowBaseCloseHandler)); EventManager.RegisterClassHandler(typeof(Controls.WindowBase), Controls.WindowBase.LoadedRoutedEvent, new RoutedEventHandler(WindowBaseLoadedHandler)); ConfigureViews(); if (!CmdLine.Backup.IsNullOrEmpty()) { BackupOptions backupOptions = null; try { backupOptions = Serialization.FromJsonFile(CmdLine.Backup); var progRes = Dialogs.ActivateGlobalProgress( (progArgs) => { WaitForOtherInstacesToExit(true); Backup.BackupData(backupOptions, progArgs.CancelToken); }, new GlobalProgressOptions(LOC.BackupProgress, true) { IsIndeterminate = true }); if (progRes.Error != null) { logger.Error(progRes.Error, "Failed to backup data."); throw progRes.Error; } if (progRes.Canceled) { Dialogs.ShowErrorMessage(LOC.BackupCancelled, LOC.BackupErrorTitle); } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { Dialogs.ShowErrorMessage(LOC.BackupFailed.GetLocalized() + Environment.NewLine + Environment.NewLine + exc.Message, LOC.BackupErrorTitle); logger.Error(exc, "Failed to backup data."); } finally { if (backupOptions == null || !backupOptions.ClosedWhenDone) { Restart(new CmdLineOptions { StartClosedToTray = CmdLine.StartClosedToTray, HideSplashScreen = CmdLine.HideSplashScreen, StartInFullscreen = CmdLine.StartInFullscreen }, false); } } FileSystem.DeleteFile(PlaynitePaths.SafeStartupFlagFile); Quit(false); return; } else if (!CmdLine.RestoreBackup.IsNullOrEmpty()) { BackupRestoreOptions restoreOptions = null; try { restoreOptions = Serialization.FromJsonFile(CmdLine.RestoreBackup); var progRes = Dialogs.ActivateGlobalProgress( (progArgs) => { WaitForOtherInstacesToExit(true); Backup.RestoreBackup(restoreOptions); }, new GlobalProgressOptions(LOC.BackupRestoreProgress, false) { IsIndeterminate = true }); if (progRes.Error != null) { logger.Error(progRes.Error, "Failed to restore data from backup."); throw progRes.Error; } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { Dialogs.ShowErrorMessage(LOC.BackupRestoreFailed.GetLocalized() + Environment.NewLine + Environment.NewLine + exc.Message, LOC.BackupErrorTitle); logger.Error(exc, "Failed to restore data from backup."); } finally { if (restoreOptions == null || !restoreOptions.ClosedWhenDone) { Restart(new CmdLineOptions(), false); } } FileSystem.DeleteFile(PlaynitePaths.SafeStartupFlagFile); Quit(false); return; } if (!Startup()) { Quit(); return; } logger.Info($"Application {CurrentVersion} started"); ExtensionsInstallResult?.Where(a => a.InstallError != null).ForEach(ext => Notifications.Add(new NotificationMessage( "inst_err" + ext.PackagePath, ResourceProvider.GetString(LOC.AddonInstallFaild).Format(Path.GetFileNameWithoutExtension(ext.PackagePath)) + "\n" + ext.InstallError.Message, NotificationType.Error))); foreach (var fail in Extensions.FailedExtensions) { Notifications.Add(new NotificationMessage( fail.manifest.DirectoryPath, fail.error == AddonLoadError.SDKVersion ? ResourceProvider.GetString(LOC.SpecificExtensionLoadSDKError).Format(fail.manifest.Name) : ResourceProvider.GetString(LOC.SpecificExtensionLoadError).Format(fail.manifest.Name), NotificationType.Error)); } if (themeLoadError != AddonLoadError.None && customTheme != null) { Notifications.Add(new NotificationMessage( customTheme.DirectoryPath, themeLoadError == AddonLoadError.SDKVersion ? ResourceProvider.GetString(LOC.SpecificThemeLoadSDKError).Format(customTheme.Name) : ResourceProvider.GetString(LOC.SpecificThemeLoadError).Format(customTheme.Name), NotificationType.Error)); } try { if (AppSettings.ShowNahimicServiceWarning) { if (ServiceController.GetServices().FirstOrDefault(a => (a.ServiceName?.Contains("nahimic", StringComparison.OrdinalIgnoreCase) == true || a.DisplayName?.Contains("nahimic", StringComparison.OrdinalIgnoreCase) == true) && a.Status != ServiceControllerStatus.Stopped) != null) { var okResponse = new MessageBoxOption(LOC.OKLabel, true, true); var dontShowResponse = new MessageBoxOption(LOC.DontShowAgainTitle); var res = Dialogs.ShowMessage( LOC.NahimicServiceWarning, "", MessageBoxImage.Warning, new List { okResponse, dontShowResponse }); if (res == dontShowResponse) { AppSettings.ShowNahimicServiceWarning = false; } } } } catch (Exception nahExc) { // ServiceController.GetServices() can apparently blow up on Win32Exception sometimes logger.Error(nahExc, "Failed to check for Nahimic service."); } if (PlayniteEnvironment.IsElevated && AppSettings.ShowElevatedRightsWarning) { var okResponse = new MessageBoxOption(LOC.OKLabel, true, true); var dontShowResponse = new MessageBoxOption(LOC.DontShowAgainTitle); var res = Dialogs.ShowMessage( LOC.ElevatedProcessWarning, "", MessageBoxImage.Warning, new List { okResponse, dontShowResponse }); if (res == dontShowResponse) { AppSettings.ShowElevatedRightsWarning = false; } } } private void WindowBaseCloseHandler(object sender, RoutedEventArgs e) { WindowManager.NotifyChildOwnershipChanges(); } private void WindowBaseLoadedHandler(object sender, RoutedEventArgs e) { WindowManager.NotifyChildOwnershipChanges(); } private void PipeService_CommandExecuted(object sender, CommandExecutedEventArgs args) { logger.Info($"Executing command \"{args.Command}\" from pipe with arguments \"{args.Args}\""); switch (args.Command) { case CmdlineCommand.Focus: Restore(); break; case CmdlineCommand.Start: if (Guid.TryParse(args.Args, out var gameId)) { var game = Database.Games[gameId]; if (game == null) { logger.Error($"Cannot start game, game {args.Args} not found."); } else { GamesEditor.PlayGame(game, false); } } else { logger.Error($"Can't start game, failed to parse game id: {args.Args}"); } break; case CmdlineCommand.UriRequest: UriHandler.ProcessUri(args.Args); break; case CmdlineCommand.ExtensionInstall: if (installingAddon) { return; } var extPath = args.Args; if (!File.Exists(extPath)) { logger.Error($"Cannot install extension, file doesn't exists: {extPath}"); return; } installingAddon = true; var ext = Path.GetExtension(extPath).ToLower(); if (ext.Equals(PlaynitePaths.PackedThemeFileExtention, StringComparison.OrdinalIgnoreCase)) { MainModelBase.Window.RestoreWindow(); InstallThemeFile(extPath); } else if (ext.Equals(PlaynitePaths.PackedExtensionFileExtention, StringComparison.OrdinalIgnoreCase)) { MainModelBase.Window.RestoreWindow(); InstallExtensionFile(extPath); } installingAddon = false; break; case CmdlineCommand.SwitchMode: if (args.Args == "desktop") { SyncContext.Post(_ => SwitchAppMode(ApplicationMode.Desktop), null); } else if (args.Args == "fullscreen") { SyncContext.Post(_ => SwitchAppMode(ApplicationMode.Fullscreen), null); } else { logger.Error($"Can't switch to uknwon application mode: {args.Args}"); } break; case CmdlineCommand.Shutdown: Quit(); break; case CmdlineCommand.BackupData: if (!File.Exists(args.Args)) { return; } var backupOptions = Serialization.FromJsonFile(args.Args); if (backupOptions.CancelIfGameRunning && GamesEditor.RunningGames.HasItems()) { return; } else { Restart(new CmdLineOptions { Backup = args.Args }); } break; case CmdlineCommand.RestoreBackup: if (!File.Exists(args.Args)) { return; } var restoreOptions = Serialization.FromJsonFile(args.Args); if (restoreOptions.CancelIfGameRunning && GamesEditor.RunningGames.HasItems()) { return; } else { Restart(new CmdLineOptions { RestoreBackup = args.Args }); } break; default: logger.Warn("Unknown command received"); break; } } private void Application_Activated(object sender, EventArgs e) { IsActive = true; } private void Application_Deactivated(object sender, EventArgs e) { IsActive = false; } public void Run() { CurrentNative.Run(); } public abstract bool Startup(); public bool CheckOtherInstances() { var curProcess = Process.GetCurrentProcess(); if (Mutex.TryOpenExisting(instanceMuxet, out var mutex)) { try { Policy.Handle() .WaitAndRetry(3, a => TimeSpan.FromSeconds(3)) .Execute(() => { var client = new PipeClient(PlayniteSettings.GetAppConfigValue("PipeEndpoint")); if (!CmdLine.Start.IsNullOrEmpty()) { client.InvokeCommand(CmdlineCommand.Start, CmdLine.Start); } else if (!CmdLine.UriData.IsNullOrEmpty()) { client.InvokeCommand(CmdlineCommand.UriRequest, CmdLine.UriData); } else if (!CmdLine.InstallExtension.IsNullOrEmpty()) { client.InvokeCommand(CmdlineCommand.ExtensionInstall, CmdLine.InstallExtension); } else if (CmdLine.StartInDesktop) { client.InvokeCommand(CmdlineCommand.SwitchMode, "desktop"); } else if (CmdLine.StartInFullscreen) { client.InvokeCommand(CmdlineCommand.SwitchMode, "fullscreen"); } else if (CmdLine.Shutdown) { client.InvokeCommand(CmdlineCommand.Shutdown, null); } else if (!CmdLine.Backup.IsNullOrEmpty()) { client.InvokeCommand(CmdlineCommand.BackupData, CmdLine.Backup); } else if (!CmdLine.RestoreBackup.IsNullOrEmpty()) { client.InvokeCommand(CmdlineCommand.RestoreBackup, CmdLine.RestoreBackup); } else { var existingProcess = Process.GetProcesses(). First(a => IsProcessPlayniteProcess(a) && a.Id != curProcess.Id); if (existingProcess.ProcessName == curProcess.ProcessName) { client.InvokeCommand(CmdlineCommand.Focus, string.Empty); } else { client.InvokeCommand(CmdlineCommand.SwitchMode, Mode == ApplicationMode.Desktop ? "desktop" : "fullscreen"); } } }); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { MessageBox.Show( "Playnite failed to start. Please close all other instances and try again.", "Startup Error"); logger.Error(exc, "Can't process communication with other instances."); } logger.Info("Application already running, shutting down."); return true; } else { var processes = Process.GetProcesses().Where(a => IsProcessPlayniteProcess(a)).ToList(); // In case multiple processes end up in this branch, // the process with highest process id gets to live. if (processes.Count > 1 && processes.Max(a => a.Id) != curProcess.Id) { logger.Info("Another process instance(s) is already running, shutting down."); return true; } } return false; } public bool ConfigureApplication() { HtmlRendererSettings.ImageCachePath = PlaynitePaths.ImagesCachePath; HtmlRendererSettings.ImageLoader = BitmapExtensions.HtmlComponentImageLoader; if (AppSettings.DisableHwAcceleration || CmdLine.ForceSoftwareRender) { logger.Info("Enabling software rendering."); System.Windows.Media.RenderOptions.ProcessRenderMode = System.Windows.Interop.RenderMode.SoftwareOnly; } if (CmdLine.ClearWebCache) { try { FileSystem.DeleteDirectory(PlaynitePaths.BrowserCachePath); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to clear CEF cache."); } } try { CefTools.ConfigureCef(AppSettings.TraceLogEnabled); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to initialize CefSharp."); } if (!CefTools.IsInitialized) { Dialogs.ShowErrorMessage( ResourceProvider.GetString("LOCCefSharpInitError"), ResourceProvider.GetString("LOCStartupError")); Quit(); return false; } try { ExtensionFactory.CreatePluginFolders(); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to script and plugin directories."); } try { SystemIntegration.SetBootupStateRegistration(AppSettings.StartOnBoot, AppSettings.StartOnBootClosedToTray); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to register Playnite to start on boot."); } try { SystemIntegration.RegisterPlayniteUriProtocol(); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to register playnite URI scheme."); } try { SystemIntegration.RegisterFileExtensions(); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to register playnite extensions."); } return true; } public void ProcessArguments() { UriHandler.Handlers.Add("playnite", ProcessUriRequest); if (!CmdLine.Start.IsNullOrEmpty()) { PipeService_CommandExecuted(this, new CommandExecutedEventArgs(CmdlineCommand.Start, CmdLine.Start)); } else if (!CmdLine.UriData.IsNullOrEmpty()) { PipeService_CommandExecuted(this, new CommandExecutedEventArgs(CmdlineCommand.UriRequest, CmdLine.UriData)); } else if (!CmdLine.InstallExtension.IsNullOrEmpty()) { PipeService_CommandExecuted(this, new CommandExecutedEventArgs(CmdlineCommand.ExtensionInstall, CmdLine.InstallExtension)); } else if (CmdLine.StartInDesktop) { PipeService_CommandExecuted(this, new CommandExecutedEventArgs(CmdlineCommand.SwitchMode, "desktop")); } else if (CmdLine.StartInFullscreen) { PipeService_CommandExecuted(this, new CommandExecutedEventArgs(CmdlineCommand.SwitchMode, "fullscreen")); } else if (CmdLine.Shutdown) { PipeService_CommandExecuted(this, new CommandExecutedEventArgs(CmdlineCommand.Shutdown, null)); } } internal void ProcessUriRequest(PlayniteUriEventArgs args) { var arguments = args.Arguments; if (args.Arguments.Count() == 0) { return; } var command = arguments[0]; switch (command) { case UriCommands.CreateDiag: CrashHandlerViewModel.CreateDiagPackage(Dialogs); break; case UriCommands.StartGame: if (arguments.Count() != 2) { return; } if (Guid.TryParse(arguments[1], out var gameId)) { var game = Database.Games[gameId]; if (game == null) { logger.Error($"Cannot start game, game {arguments[1]} not found."); } else { GamesEditor.PlayGame(game, false); } } else { logger.Error($"Can't start game, failed to parse game id: {arguments[1]}"); } break; case UriCommands.InstallAddon: if (arguments.Count() != 2) { return; } InstallOnlineAddon(arguments[1]); break; case UriCommands.Search: if (Mode == ApplicationMode.Desktop) { PlayniteApiGlobal.MainView.OpenSearch(arguments.Length >= 2 ? arguments[1] : string.Empty); } break; default: AppUriHandler(args); break; } } public void Quit(bool saveSettings = true) { logger.Info("Shutting down Playnite"); if (saveSettings) { AppSettings?.SaveSettings(); } ReleaseResources(); CurrentNative.Shutdown(0); } public void QuitAndStart(string path, string arguments, bool asAdmin = false, bool saveSettings = true) { logger.Info("Shutting down Playnite and starting an app."); if (saveSettings) { AppSettings?.SaveSettings(); } ReleaseResources(); try { ProcessStarter.StartProcess(path, arguments, asAdmin); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { // Not sure how this can happen, but there are some "operation cancelled by user" crashes here. // People probably running Playnite as admin and cancelling UAC for new process, or something... logger.Error(e, "Failed to start process on app shutdown."); } CurrentNative.Shutdown(0); } public void QuitAndExecute(Action action, bool saveSettings = true) { logger.Info("Shutting down Playnite and executing an action."); if (saveSettings) { AppSettings?.SaveSettings(); } ReleaseResources(); try { action(); } catch(Exception e) { logger.Error(e, "Failed to execute app quit action."); } CurrentNative.Shutdown(0); } public abstract void Restart(bool saveSettings = true); public abstract void Restart(CmdLineOptions options, bool saveSettings = true); public virtual void ReleaseResources(bool releaseCefSharp = true) { if (ResourcesReleased) { return; } logger.Debug("Releasing Playnite resources..."); CurrentNative.Dispatcher.Invoke(() => { try { appMutex?.ReleaseMutex(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { // Only happens when trying to release mutext created by a different process. // This shouldn't normally happen since the mutex is released here before starting another instance. logger.Error(e, "Failed to release app mutext."); } }); try { pipeServer?.StopServer(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { // I have no idea why this fails for some people. logger.Error(e, "Failed to stop pipe server."); } // Rare crash report of DiscordRPC not being loaded properly and then crashing on this try { Discord?.Dispose(); } catch (Exception e) { logger.Error(e, "Failed to dispose Discord RPC."); } updateCheckTimer?.Dispose(); MainModelBase?.RunShutdowScript(); Extensions?.NotifiyOnApplicationStopped(); Dialogs.ActivateGlobalProgress(_ => { try { if (GlobalTaskHandler.CancelAndWait(Common.Timer.SecondsToMilliseconds(5)) == false) { logger.Warn("Global task cancelation failed in time."); } GamesEditor?.Dispose(); Controllers?.Dispose(); Extensions?.Dispose(); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to dispose Playnite objects."); } }, new GlobalProgressOptions("LOCClosingPlaynite")); // This must run on main thread if (releaseCefSharp) { CurrentNative.Dispatcher.Invoke(() => { if (CefTools.IsInitialized) { CefTools.Shutdown(); } }); } Database?.Dispose(); ResourcesReleased = true; } private void CheckAddonBlacklist() { try { var manifests = ExtensionFactory.GetInstalledManifests(); var blackList = ServicesClient.GetAddonBlacklist(); var installedList = manifests.Where(a => blackList.Contains(a.Id)).ToList(); if (installedList.HasItems()) { Dialogs.ShowMessage(ResourceProvider.GetString(LOC.WarningBlacklistedExtensions).Format( string.Join(Environment.NewLine, installedList.Select(a => a.Name))), "", MessageBoxButton.OK, MessageBoxImage.Warning); } } catch (Exception exc) { logger.Warn(exc, "Failed to process addon blacklist check."); } } private void CheckForUpdates(bool checkProgram, bool checkAddons) { if (checkProgram) { try { var showNotification = false; var updater = new Updater(this); if (updater.IsUpdateAvailable) { if (AppSettings.UpdateNotificationOnPatchesOnly) { showNotification = Updater.CurrentVersion.Major == updater.GetLatestVersion().Major; } else { showNotification = true; } } if (showNotification) { var updateTitle = ResourceProvider.GetString("LOCUpdaterWindowTitle"); var updateBody = ResourceProvider.GetString("LOCUpdateIsAvailableNotificationBody"); if (!Current.IsActive) { ShowWindowsNotification(updateTitle, updateBody, () => { Restore(); new UpdateViewModel( updater, new UpdateWindowFactory(), new ResourceProvider(), Dialogs, Mode).OpenView(); }); } MainModelBase.UpdatesAvailable = true; } } catch (Exception exc) { logger.Warn(exc, "Failed to process program update."); } AppSettings.LastProgramUpdateCheck = DateTimes.Now; } if (checkAddons) { try { var updates = Addons.CheckAddonUpdates(ServicesClient); if (updates.HasItems()) { Notifications.Add(MainModelBase.GetAddonUpdatesFoundMessage(updates)); } } catch (Exception exc) { logger.Warn(exc, "Failed to process addon update check."); } AppSettings.LastAddonUpdateCheck = DateTimes.Now; } } private void UpdateCheckerCallback(object state) { CheckForUpdates(AppSettings.ShouldCheckProgramUpdatePeriodic(), AppSettings.ShouldCheckAddonUpdatePeriodic()); CheckAddonBlacklist(); } public async Task StartUpdateCheckerAsync() { if (PlayniteEnvironment.InOfflineMode) { return; } await Task.Delay(Common.Timer.SecondsToMilliseconds(5)); if (GlobalTaskHandler.IsActive) { await GlobalTaskHandler.ProgressTask; } await Task.Run(() => { CheckForUpdates(AppSettings.ShouldCheckProgramUpdateStartup(), AppSettings.ShouldCheckAddonUpdateStartup()); CheckAddonBlacklist(); }); updateCheckTimer = new System.Threading.Timer( UpdateCheckerCallback, null, Common.Timer.HoursToMilliseconds(4), Common.Timer.HoursToMilliseconds(4)); } public bool MigrateDatabase() { if (GameDatabase.GetMigrationRequired(AppSettings.DatabasePath)) { var migrationProgress = new ProgressViewViewModel( new ProgressWindowFactory(), new GlobalProgressOptions(LOC.DBUpgradeProgress)); if (migrationProgress.ActivateProgress( _ => GameDatabase.MigrateNewDatabaseFormat(GameDatabase.GetFullDbPath(AppSettings.DatabasePath))).Result != true) { logger.Error(migrationProgress.FailException, "Failed to migrate database to new version."); var message = ResourceProvider.GetString("LOCDBUpgradeFail"); if (migrationProgress.FailException is NoDiskSpaceException exc) { message = string.Format(ResourceProvider.GetString("LOCDBUpgradeEmptySpaceFail"), Units.BytesToMegaBytes(exc.RequiredSpace)); } Dialogs.ShowErrorMessage(message, ""); return false; } } return true; } public void UpdateScreenInformation(Controls.WindowBase window) { try { DpiScale = VisualTreeHelper.GetDpi(window); CurrentScreen = window.GetScreen(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { DpiScale = new DpiScale(1, 1); CurrentScreen = Computer.GetPrimaryScreen(); logger.Error(e, $"Failed to get window information for main {Mode} window."); } } public void ShowAddonPerfNotice() { if (AppSettings.AddonsPerfNoticeShown) { return; } Dialogs.ShowMessage(LOC.AddonPerfNotice, "", MessageBoxButton.OK, MessageBoxImage.Warning); AppSettings.AddonsPerfNoticeShown = true; AppSettings.SaveSettings(); } public void InstallOnlineAddon(string addonId) { try { var addon = ServicesClient.GetAddon(addonId); var package = addon.InstallerManifest.GetLatestCompatiblePackage(); if (package == null) { Dialogs.ShowErrorMessage(LOC.AddonErrorNotCompatible, ""); return; } var message = string.Format( ResourceProvider.GetString(addon.IsTheme ? LOC.ThemeInstallPrompt : LOC.ExtensionInstallPrompt), addon.Name, addon.Author, package.Version); BaseExtensionManifest existing = null; if (addon.IsTheme) { existing = ThemeManager.GetAvailableThemes().FirstOrDefault(a => a.Id == addon.AddonId); } else { existing = ExtensionFactory.GetInstalledManifests().FirstOrDefault(a => a.Id == addon.AddonId); } if (existing != null) { message = string.Format( ResourceProvider.GetString(addon.IsTheme ? LOC.ThemeUpdatePrompt : LOC.ExtensionUpdatePrompt), addon.Name, existing.Version, package.Version); } if (Dialogs.ShowMessage(message, LOC.GeneralExtensionInstallTitle, MessageBoxButton.YesNo) != MessageBoxResult.Yes) { return; } var licenseRes = addon.CheckAddonLicense(); if (licenseRes == null) { Dialogs.ShowErrorMessage(LOC.AddonErrorDownloadFailed, string.Empty); return; } if (licenseRes == false) { return; } ShowAddonPerfNotice(); var locaPath = addon.GetTargetDownloadPath(); FileSystem.DeleteFile(locaPath); var res = Dialogs.ActivateGlobalProgress((_) => { if (package.PackageUrl.IsHttpUrl()) { FileSystem.PrepareSaveFile(locaPath); HttpDownloader.DownloadFile(package.PackageUrl, locaPath); } else { File.Copy(package.PackageUrl, locaPath); } }, new GlobalProgressOptions(LOC.DownloadingLabel, false)); if (res.Error != null) { logger.Error(res.Error, $"Failed to download addon {package.PackageUrl}"); Dialogs.ShowErrorMessage(LOC.AddonErrorDownloadFailed, string.Empty); return; } ExtensionInstaller.QueuePackageInstall(locaPath); if (Dialogs.ShowMessage(LOC.ExtInstallationRestartNotif, LOC.SettingsRestartTitle, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { Restart(new CmdLineOptions { SkipLibUpdate = true }); }; } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to install addon from uri {addonId}"); } } public void InstallThemeFile(string themeFile) { try { ExtensionInstaller.VerifyThemePackage(themeFile); var desc = ExtensionInstaller.GetPackedThemeManifest(themeFile); desc.VerifyManifest(); if (new Version(desc.ThemeApiVersion).Major != ThemeManager.GetApiVersion(desc.Mode).Major) { throw new Exception(ResourceProvider.GetString("LOCGeneralExtensionInstallApiVersionFails")); } var message = string.Format(ResourceProvider.GetString("LOCThemeInstallPrompt"), desc.Name, desc.Author, desc.Version); var existing = ThemeManager.GetAvailableThemes(desc.Mode).FirstOrDefault(a => a.Id == desc.Id); if (existing != null) { message = string.Format(ResourceProvider.GetString("LOCThemeUpdatePrompt"), desc.Name, existing.Version, desc.Version); } if (Dialogs.ShowMessage( message, ResourceProvider.GetString("LOCGeneralExtensionInstallTitle"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { ShowAddonPerfNotice(); ExtensionInstaller.QueuePackageInstall(themeFile); if (Dialogs.ShowMessage( ResourceProvider.GetString("LOCExtInstallationRestartNotif"), ResourceProvider.GetString("LOCSettingsRestartTitle"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { Restart(new CmdLineOptions() { SkipLibUpdate = true, }); }; } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to install theme."); Dialogs.ShowErrorMessage( string.Format(ResourceProvider.GetString("LOCThemeInstallFail"), e.Message), ""); } } public void InstallExtensionFile(string extensionFile) { try { ExtensionInstaller.VerifyExtensionPackage(extensionFile); var desc = ExtensionInstaller.GetPackedExtensionManifest(extensionFile); desc.VerifyManifest(); var message = string.Format(ResourceProvider.GetString("LOCExtensionInstallPrompt"), desc.Name, desc.Author, desc.Version); var existing = ExtensionFactory.GetInstalledManifests().FirstOrDefault(a => a.Id == desc.Id); if (existing != null) { message = string.Format(ResourceProvider.GetString("LOCExtensionUpdatePrompt"), desc.Name, existing.Version, desc.Version); } if (Dialogs.ShowMessage( message, ResourceProvider.GetString("LOCGeneralExtensionInstallTitle"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { ShowAddonPerfNotice(); ExtensionInstaller.QueuePackageInstall(extensionFile); if (Dialogs.ShowMessage( ResourceProvider.GetString("LOCExtInstallationRestartNotif"), ResourceProvider.GetString("LOCSettingsRestartTitle"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { Restart(new CmdLineOptions() { SkipLibUpdate = true, }); }; } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to install extension."); Dialogs.ShowErrorMessage( string.Format(ResourceProvider.GetString("LOCExtensionInstallFail"), e.Message), ""); } } public void OnExtensionsLoaded() { ExtensionsLoaded?.Invoke(this, EventArgs.Empty); OnPropertyChanged(nameof(this.ExtensionsStatusBinder)); } private void WaitForOtherInstacesToExit(bool throwOnTimetout) { if (Process.GetProcesses().Where(a => IsProcessPlayniteProcess(a)).Count() > 1) { logger.Info("Multiple Playnite instances detected, waiting for them to close."); for (int i = 0; i < 10; i++) { Thread.Sleep(500); if (Process.GetProcesses().Where(a => IsProcessPlayniteProcess(a)).Count() == 1) { break; } else if (i == 9) { if (throwOnTimetout) { throw new Exception("Another Playnite instance didn't shutdown in time."); } else { logger.Warn("Another Playnite instance didn't shutdown in time."); } } } } } public static bool IsProcessPlayniteProcess(Process process) { return process.ProcessName.StartsWith("Playnite.DesktopApp") || process.ProcessName.StartsWith("Playnite.FullscreenApp"); } public abstract PlayniteAPI GetApiInstance(ExtensionManifest pluginOwner); public abstract PlayniteAPI GetApiInstance(); } } ================================================ FILE: source/Playnite/App/UpdateManifest.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class UpdateManifest : ObservableObject { public const string ServerManifestFileName = "update.json"; private Version version; private Version sdkVersion; private Version fullscreenThemeVersion; private Version desktopThemeVersion; private string checksum; private List packageUrls; private List versionHistory; public Version Version { get => version; set => SetValue(ref version, value); } public Version SdkVersion { get => sdkVersion; set => SetValue(ref sdkVersion, value); } public Version FullscreenThemeVersion { get => fullscreenThemeVersion; set => SetValue(ref fullscreenThemeVersion, value); } public Version DesktopThemeVersion { get => desktopThemeVersion; set => SetValue(ref desktopThemeVersion, value); } public string Checksum { get => checksum; set => SetValue(ref checksum, value); } public List PackageUrls { get => packageUrls; set => SetValue(ref packageUrls, value); } public List VersionHistory { get => versionHistory; set => SetValue(ref versionHistory, value); } } public class ReleaseNoteData { public Version Version { get; set; } public string Note { get; set; } } } ================================================ FILE: source/Playnite/App/Updater.cs ================================================ using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using System.Windows; using Flurl; using System.Net; using Playnite.Common; using Playnite.Common.Web; using Playnite.Settings; using Playnite.SDK; namespace Playnite { public class Updater { private static string updateBranch { get { return ConfigurationManager.AppSettings["UpdateBranch"]; } } private static ILogger logger = LogManager.GetLogger(); private UpdateManifest updateManifest; private IPlayniteApplication playniteApp; private IDownloader downloader; private string updaterPath { get { return Path.Combine(PlaynitePaths.TempPath, "update.exe"); } } public bool IsUpdateAvailable { get { var latest = GetLatestVersion(); var current = CurrentVersion; if (latest > current) { // Windows 7 and 8 and 32bit systems should no longer update, except for patches if (Computer.WindowsVersion == WindowsVersion.Win7 || Computer.WindowsVersion == WindowsVersion.Win8 || !Environment.Is64BitOperatingSystem) { return latest.Major == current.Major; } else { return true; } } return false; } } private static Version currentVersion; public static Version CurrentVersion { get { if (currentVersion == null) { currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; } return currentVersion; } } public Updater(IPlayniteApplication app) : this(app, new Downloader()) { } public Updater(IPlayniteApplication app, IDownloader webDownloader) { playniteApp = app; downloader = webDownloader; } private string GetUpdateDataRootUrl(string configKey) { return Url.Combine(ConfigurationManager.AppSettings[configKey], updateBranch, $"{CurrentVersion.Major}.{CurrentVersion.Minor}"); } public List GetReleaseNotes() { var notes = new List(); if (updateManifest == null) { DownloadManifest(); } foreach (var version in updateManifest.VersionHistory) { if (version.CompareTo(CurrentVersion) > 0) { var noteUrls = new List { Url.Combine(ConfigurationManager.AppSettings["UpdateUrl"], updateBranch, $"{version.Major}.{version.Minor}.html"), Url.Combine(ConfigurationManager.AppSettings["UpdateUrl2"], updateBranch, $"{version.Major}.{version.Minor}.html") }; var note = downloader.DownloadString(noteUrls); notes.Add(new ReleaseNoteData() { Version = version, Note = note }); } } return notes; } private bool VerifyUpdateFile(string checksum, string path) { var newMD5 = FileSystem.GetMD5(path); if (newMD5 != checksum) { logger.Error($"Checksum of downloaded file doesn't match: {newMD5} vs {checksum}"); return false; } return true; } public async Task DownloadUpdate(Action progressHandler) { if (updateManifest == null) { DownloadManifest(); } if (File.Exists(updaterPath)) { if (VerifyUpdateFile(updateManifest.Checksum, updaterPath)) { logger.Info("Update already downloaded skipping download."); return; } } try { await downloader.DownloadFileAsync(updateManifest.PackageUrls, updaterPath, progressHandler); } catch (Exception e) { logger.Error(e, "Failed to download update file."); throw new Exception("Failed to download update file."); } if (!VerifyUpdateFile(updateManifest.Checksum, updaterPath)) { throw new Exception($"Update file integrity check failed."); } } public void InstallUpdate(ApplicationMode mode) { var portable = PlayniteSettings.IsPortable ? "/PORTABLE" : ""; var fullscreen = mode == ApplicationMode.Fullscreen ? "/FULLSCREEN" : ""; logger.Info("Installing new update to {0}, in {1} mode".Format(PlaynitePaths.ProgramPath, portable)); playniteApp.QuitAndStart( updaterPath, @"/SILENT /NOCANCEL /DIR=""{0}"" /UPDATE {1} {2}".Format(PlaynitePaths.ProgramPath, portable, fullscreen), !FileSystem.CanWriteToFolder(PlaynitePaths.ProgramPath)); } public UpdateManifest DownloadManifest() { var dataString = string.Empty; try { dataString = GetUpdateManifestData(GetUpdateDataRootUrl("UpdateUrl")); } catch (Exception e) { logger.Warn(e, "Failed to download update manifest from main URL"); } try { if (string.IsNullOrEmpty(dataString)) { dataString = GetUpdateManifestData(GetUpdateDataRootUrl("UpdateUrl2")); } } catch (Exception e) { logger.Warn(e, "Failed to download update manifest from secondary URL"); } if (string.IsNullOrEmpty(dataString)) { throw new Exception("Failed to download update manifest."); } updateManifest = JsonConvert.DeserializeObject(dataString); return updateManifest; } public Version GetLatestVersion() { if (updateManifest == null) { DownloadManifest(); } return updateManifest.Version; } private string GetUpdateManifestData(string url) { return downloader.DownloadString(Url.Combine(url, UpdateManifest.ServerManifestFileName)); } } } ================================================ FILE: source/Playnite/App.config ================================================ 
================================================ FILE: source/Playnite/Archive.cs ================================================ using Playnite.Common; using SharpCompress.Archives; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite { public static class Archive { public static List GetArchiveFiles(string archivePath) { using (var archive = ArchiveFactory.Open(archivePath)) { return archive.Entries.Where(a => !a.IsDirectory).Select(a => a.Key).ToList(); } } public static Tuple GetEntryStream(string archivePath, string entryName) { var archive = ArchiveFactory.Open(archivePath); var entry = archive.Entries.FirstOrDefault(a => a.Key == entryName); if (entry == null) { archive.Dispose(); return null; } return new Tuple(entry.OpenEntryStream(), archive); } public static void CreateEntryFromDirectory(this ZipArchive archive, string directory, string entryName, CancellationToken cancelToken) { if (cancelToken.IsCancellationRequested) { return; } foreach (var file in Directory.GetFiles(directory)) { if (cancelToken.IsCancellationRequested) { return; } archive.CreateEntryFromFile(Paths.FixPathLength(file), Path.Combine(entryName, Path.GetFileName(file))); } if (cancelToken.IsCancellationRequested) { return; } foreach (var dir in Directory.GetDirectories(directory)) { if (cancelToken.IsCancellationRequested) { return; } CreateEntryFromDirectory(archive, dir, Path.Combine(entryName, Path.GetFileName(dir)), cancelToken); } } } } ================================================ FILE: source/Playnite/Audio.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using static SDL2.SDL; using static SDL2.SDL_mixer; namespace Playnite.Audio { public class AudioEngine : IDisposable { private static readonly ILogger logger = LogManager.GetLogger(); public static readonly string[] SupportedFileTypes = new string[] { "wav", "ogg", "mp3", "flac" }; public static readonly string SupportedFileTypesRegex = string.Join("|", SupportedFileTypes); public bool AudioClosed { get; private set; } = false; public bool AudioInitialized { get; private set; } = false; public DateTime LastAudioEvent { get; private set; } = DateTime.Now; public AudioEngine() { OpenAudio(); } private void OpenAudio() { if (Mix_OpenAudio(44_100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { logger.Error("Failed to open SDL2 audio device:"); logger.Error(Mix_GetError()); AudioInitialized = false; AudioClosed = true; return; } AudioInitialized = true; AudioClosed = false; } public static int GetVolume(float floatVolume) { floatVolume *= floatVolume; // lerp settings float 0 -> 1 range to SDL's 0 -> 128 range var res = (int)Math.Ceiling((0f * (1f - floatVolume)) + (128f * floatVolume)); return res; } public void PlaySound(IntPtr sound) { if (sound == IntPtr.Zero) return; if (!AudioInitialized) return; if (AudioClosed) OpenAudio(); LastAudioEvent = DateTime.Now; try { Mix_PlayChannel(-1, sound, 0); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to play SDL sound."); } } public void SetSoundVolume(IntPtr sounds, float volume) { if (!AudioInitialized) return; Mix_VolumeChunk(sounds, GetVolume(volume)); } public IntPtr LoadSound(string path) { if (!AudioInitialized) return IntPtr.Zero; return Mix_LoadWAV(path); } public void SetMusicVolume(float volume) { if (!AudioInitialized) return; Mix_VolumeMusic(GetVolume(volume)); } public IntPtr LoadMusic(string path) { if (!AudioInitialized) return IntPtr.Zero; return Mix_LoadMUS(path); } public void PlayMusic(IntPtr music) { if (music == IntPtr.Zero) return; if (!AudioInitialized) return; if (AudioClosed) OpenAudio(); LastAudioEvent = DateTime.Now; try { Mix_PlayMusic(music, -1); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to play SDL music."); } } public void PauseMusic() { if (!AudioInitialized) return; LastAudioEvent = DateTime.Now; Mix_PauseMusic(); } public void StopMusic() { if (!AudioInitialized) return; LastAudioEvent = DateTime.Now; Mix_HaltMusic(); } public void ResumeMusic() { if (!AudioInitialized) return; if (AudioClosed) OpenAudio(); LastAudioEvent = DateTime.Now; Mix_ResumeMusic(); } public bool GetIsMusicPlaying() { if (!AudioInitialized) return false; return Mix_PlayingMusic() == 1; } public bool GetIsMusicPaused() { if (!AudioInitialized) return false; return Mix_PausedMusic() == 1; } public void CloseAudio() { if (AudioClosed || !AudioInitialized) return; Mix_CloseAudio(); AudioClosed = true; } public void DisposeSound(IntPtr sound) { Mix_FreeChunk(sound); } public void DisposeMusic(IntPtr music) { Mix_FreeMusic(music); } public void Dispose() { AudioInitialized = false; AudioClosed = true; Mix_Quit(); } } } ================================================ FILE: source/Playnite/Backup.cs ================================================ using Playnite.Common; using Playnite.Database; using Playnite.SDK; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace Playnite { public enum BackupDataItem { [Description(LOC.BackupOptionSettings)] Settings = 0, [Description(LOC.BackupOptionLibrary)] Library = 1, [Description(LOC.BackupOptionGameMedia)] LibraryFiles = 2, [Description(LOC.BackupOptionExtensions)] Extensions = 3, [Description(LOC.BackupOptionThemes)] Themes = 4, [Description(LOC.BackupOptionExtensionsData)] ExtensionsData = 5 } public class BackupOptions { public string DataDir { get; set; } public string LibraryDir { get; set; } public string OutputFile { get; set; } public string OutputDir { get; set; } public List BackupItems { get; set; } public bool ClosedWhenDone { get; set; } public bool CancelIfGameRunning { get; set; } public int RotatingBackups { get; set; } = 0; } public class BackupRestoreOptions { public string BackupFile { get; set; } public string DataDir { get; set; } public string LibraryDir { get; set; } public List RestoreItems { get; set; } public bool ClosedWhenDone { get; set; } public bool CancelIfGameRunning { get; set; } public string RestoreLibrarySettingsPath { get; set; } } public class Backup { private const string backupDateFormat = "yyyy-MM-dd-HH-mm-ss"; private const string autoBackupFilePattern = autoBackupFileName + @"\-\d{4}\-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}"; private const string autoBackupFileName = "PlayniteBackup"; private const string libraryEntryRoot = "library"; private const string libraryFilesEntryRoot = "libraryfiles"; private const string extensionsDataEntryRoot = "extensiondata"; private const string extensionsEntryRoot = "extension"; private const string themesEntryRoot = "themes"; private static readonly string[] configFilesNames = new string[] { PlaynitePaths.ConfigFileName, PlaynitePaths.FullscreenConfigFileName }; public static void BackupData(string optionsFile, CancellationToken cancelToken) { BackupData(Serialization.FromJsonFile(optionsFile), cancelToken); } public static void BackupData(BackupOptions options, CancellationToken cancelToken) { if (options.OutputDir.IsNullOrEmpty() && options.OutputFile.IsNullOrEmpty()) { throw new Exception("Backup output path not specified!"); } if (!options.OutputDir.IsNullOrEmpty()) { options.OutputFile = Path.Combine(options.OutputDir, GetAutoBackupFileName()); } if (options.BackupItems == null) { options.BackupItems = new List(); } FileSystem.DeleteFile(options.OutputFile); using (var zipFile = new FileStream(options.OutputFile, FileMode.Create)) using (var archive = new ZipArchive(zipFile, ZipArchiveMode.Create)) { // Settings foreach (var config in configFilesNames) { var fullPath = Path.Combine(options.DataDir, config); if (File.Exists(fullPath)) { archive.CreateEntryFromFile(fullPath, config); } }; if (Directory.Exists(options.LibraryDir)) { // Library foreach (var libFile in Directory.GetFiles(Path.Combine(options.LibraryDir))) { if (cancelToken.IsCancellationRequested) { goto archiveDone; } archive.CreateEntryFromFile(libFile, Path.Combine(libraryEntryRoot, Path.GetFileName(libFile))); } if (cancelToken.IsCancellationRequested) { goto archiveDone; } // Library files var libFilesDir = Path.Combine(options.LibraryDir, GameDatabase.filesDirName); if (options.BackupItems.Contains(BackupDataItem.LibraryFiles) && Directory.Exists(libFilesDir)) { archive.CreateEntryFromDirectory(libFilesDir, libraryFilesEntryRoot, cancelToken); } if (cancelToken.IsCancellationRequested) { goto archiveDone; } } // Extensions var addonsDir = Path.Combine(options.DataDir, PlaynitePaths.ExtensionsDirName); if (options.BackupItems.Contains(BackupDataItem.Extensions) && Directory.Exists(addonsDir)) { archive.CreateEntryFromDirectory(addonsDir, extensionsEntryRoot, cancelToken); } if (cancelToken.IsCancellationRequested) { goto archiveDone; } // Extensions data var extDataDir = Path.Combine(options.DataDir, PlaynitePaths.ExtensionsDataDirName); if (options.BackupItems.Contains(BackupDataItem.ExtensionsData) && Directory.Exists(extDataDir)) { archive.CreateEntryFromDirectory(extDataDir, extensionsDataEntryRoot, cancelToken); } if (cancelToken.IsCancellationRequested) { goto archiveDone; } // Themes var themesDir = Path.Combine(options.DataDir, PlaynitePaths.ThemesDirName); if (options.BackupItems.Contains(BackupDataItem.Themes) && Directory.Exists(themesDir)) { void packThemes(ApplicationMode mode) { var themeRootDir = ThemeManager.GetThemeRootDir(mode); var modeThemesDir = Path.Combine(themesDir, themeRootDir); if (Directory.Exists(modeThemesDir)) { foreach (var themeDir in Directory.GetDirectories(modeThemesDir)) { var themeDirName = Path.GetFileName(themeDir); // Never backup default themes if (themeDirName == ThemeManager.DefaultThemeDirName) { continue; } archive.CreateEntryFromDirectory(themeDir, Path.Combine(themesEntryRoot, themeRootDir, themeDirName), cancelToken); } } } packThemes(ApplicationMode.Desktop); packThemes(ApplicationMode.Fullscreen); } } archiveDone: if (cancelToken.IsCancellationRequested) { FileSystem.DeleteFile(options.OutputFile); } else { var backupDir = Path.GetDirectoryName(options.OutputFile); var files = Directory.GetFiles(backupDir, $"{autoBackupFileName}*.zip").Where(a => Regex.IsMatch(a, autoBackupFilePattern)).OrderBy(a => a).ToArray(); if (files.Length > options.RotatingBackups + 1) { for (int i = 0; i < files.Length - (options.RotatingBackups + 1); i++) { File.Delete(files[i]); } } } } public static void RestoreBackup(string optionsFile) { RestoreBackup(Serialization.FromJsonFile(optionsFile)); } public static void RestoreBackup(BackupRestoreOptions options) { if (options.RestoreItems == null) { return; } FileSystem.CreateDirectory(options.DataDir); FileSystem.CreateDirectory(options.LibraryDir); using (var zipFile = new FileStream(options.BackupFile, FileMode.Open)) using (var archive = new ZipArchive(zipFile, ZipArchiveMode.Read)) { // Settings if (options.RestoreItems.Contains(BackupDataItem.Settings)) { foreach (var config in configFilesNames) { var configEntry = archive.GetEntry(config); if (configEntry != null) { var origFile = Path.Combine(options.DataDir, config); FileSystem.DeleteFile(origFile); configEntry.ExtractToFile(Paths.FixPathLength(origFile)); } } if (!options.RestoreLibrarySettingsPath.IsNullOrEmpty()) { // We are doing direct string replacement because proper serialization might not be safe here. // We don't know what settings model version is the original file and potential conversion will // be left to setting load on next startup. var mainConfigFile = Paths.FixPathLength(Path.Combine(options.DataDir, PlaynitePaths.ConfigFileName)); var resultLine = $"\"{nameof(PlayniteSettings.DatabasePath)}\": {Newtonsoft.Json.JsonConvert.ToString(options.RestoreLibrarySettingsPath)},"; var configContent = File.ReadAllLines(mainConfigFile); for (int i = 0; i < configContent.Length; i++) { if (configContent[i].Contains($"\"{nameof(PlayniteSettings.DatabasePath)}\"")) { configContent[i] = resultLine; } } File.WriteAllLines(mainConfigFile, configContent); } } // Library var libPrefix = libraryEntryRoot + Path.DirectorySeparatorChar; if (options.RestoreItems.Contains(BackupDataItem.Library) && archive.Entries.Any(a => a.FullName.StartsWith(libPrefix, StringComparison.OrdinalIgnoreCase))) { foreach (var file in Directory.GetFiles(options.LibraryDir)) { File.Delete(file); } foreach (var entry in archive.Entries) { if (entry.FullName.StartsWith(libPrefix, StringComparison.OrdinalIgnoreCase) && entry.FullName.Count(a => a == Path.DirectorySeparatorChar) == 1) { entry.ExtractToFile(Paths.FixPathLength(Path.Combine(options.LibraryDir, entry.Name))); } } } void unpackBackupDir(bool restore, string outputDir, string dirPrefix) { dirPrefix = dirPrefix + Path.DirectorySeparatorChar; if (restore && archive.Entries.Any(a => a.FullName.StartsWith(dirPrefix, StringComparison.OrdinalIgnoreCase))) { FileSystem.CreateDirectory(outputDir, true); foreach (var entry in archive.Entries) { if (entry.FullName.StartsWith(dirPrefix, StringComparison.OrdinalIgnoreCase)) { var outFile = Path.Combine(outputDir, entry.FullName.Replace(dirPrefix, "")); FileSystem.PrepareSaveFile(outFile); entry.ExtractToFile(Paths.FixPathLength(outFile)); } } } } void unpackThemeBackupDir(bool restore, string outputDir, string dirPrefix) { dirPrefix = dirPrefix + Path.DirectorySeparatorChar; if (restore && archive.Entries.Any(a => a.FullName.StartsWith(dirPrefix, StringComparison.OrdinalIgnoreCase))) { void cleanThemeModeDir(ApplicationMode mode) { var modeDir = Path.Combine(outputDir, ThemeManager.GetThemeRootDir(mode)); FileSystem.CreateDirectory(modeDir, false); foreach (var dir in Directory.GetDirectories(modeDir)) { // Default themes must not be deleted since they are never included in the backup if (new DirectoryInfo(dir).Name.Equals(ThemeManager.DefaultThemeDirName, StringComparison.OrdinalIgnoreCase)) { continue; } FileSystem.DeleteDirectory(dir); } foreach (var file in Directory.GetFiles(modeDir)) { FileSystem.DeleteFile(file); } } FileSystem.CreateDirectory(outputDir, false); cleanThemeModeDir(ApplicationMode.Desktop); cleanThemeModeDir(ApplicationMode.Fullscreen); foreach (var entry in archive.Entries) { if (entry.FullName.StartsWith(dirPrefix, StringComparison.OrdinalIgnoreCase)) { var outFile = Path.Combine(outputDir, entry.FullName.Replace(dirPrefix, "")); FileSystem.PrepareSaveFile(outFile); entry.ExtractToFile(Paths.FixPathLength(outFile)); } } } } // Library files unpackBackupDir(options.RestoreItems.Contains(BackupDataItem.LibraryFiles), Path.Combine(options.LibraryDir, GameDatabase.filesDirName), libraryFilesEntryRoot); // Extensions unpackBackupDir(options.RestoreItems.Contains(BackupDataItem.Extensions), Path.Combine(options.DataDir, PlaynitePaths.ExtensionsDirName), extensionsEntryRoot); // Extensions data unpackBackupDir(options.RestoreItems.Contains(BackupDataItem.ExtensionsData), Path.Combine(options.DataDir, PlaynitePaths.ExtensionsDataDirName), extensionsDataEntryRoot); // Themes unpackThemeBackupDir(options.RestoreItems.Contains(BackupDataItem.Themes), Path.Combine(options.DataDir, PlaynitePaths.ThemesDirName), themesEntryRoot); } } public static List GetRestoreSelections(string backupFile) { var selections = new List { BackupDataItem.Settings, BackupDataItem.Library }; using (var zipFile = new FileStream(backupFile, FileMode.Open)) using (var archive = new ZipArchive(zipFile, ZipArchiveMode.Read)) { var entires = archive.Entries; if (entires.Any(a => a.FullName.StartsWith(libraryFilesEntryRoot, StringComparison.OrdinalIgnoreCase))) { selections.Add(BackupDataItem.LibraryFiles); } if (entires.Any(a => a.FullName.StartsWith(extensionsEntryRoot, StringComparison.OrdinalIgnoreCase))) { selections.Add(BackupDataItem.Extensions); } if (entires.Any(a => a.FullName.StartsWith(extensionsDataEntryRoot, StringComparison.OrdinalIgnoreCase))) { selections.Add(BackupDataItem.ExtensionsData); } if (entires.Any(a => a.FullName.StartsWith(themesEntryRoot, StringComparison.OrdinalIgnoreCase))) { selections.Add(BackupDataItem.Themes); } } return selections; } public static BackupOptions GetAutoBackupOptions(PlayniteSettings settings, string dataDir, string libraryDir) { var options = new BackupOptions { BackupItems = new List { BackupDataItem.Settings, BackupDataItem.Library }, DataDir = dataDir, LibraryDir = libraryDir, ClosedWhenDone = false, RotatingBackups = settings.RotatingBackups }; if (settings.AutoBackupIncludeExtensions) { options.BackupItems.Add(BackupDataItem.Extensions); } if (settings.AutoBackupIncludeExtensionsData) { options.BackupItems.Add(BackupDataItem.ExtensionsData); } if (settings.AutoBackupIncludeThemes) { options.BackupItems.Add(BackupDataItem.Themes); } if (settings.AutoBackupIncludeLibFiles) { options.BackupItems.Add(BackupDataItem.LibraryFiles); } options.OutputDir = settings.AutoBackupDir; return options; } private static string GetAutoBackupFileName() { return $"{autoBackupFileName}-{DateTime.Now.ToString(backupDateFormat)}.zip"; } } } ================================================ FILE: source/Playnite/Behaviors/AnimatedVisibility.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media.Animation; namespace Playnite.Behaviors { public class AnimationControl { private static readonly DependencyProperty AnimationOnVisibleProperty = DependencyProperty.RegisterAttached( "AnimationOnVisible", typeof(Storyboard), typeof(AnimatedVisibility), new PropertyMetadata(new PropertyChangedCallback(HandleAnimationOnVisibleChanged))); public static Storyboard GetAnimationOnVisible(DependencyObject obj) { return (Storyboard)obj.GetValue(AnimationOnVisibleProperty); } public static void SetAnimationOnVisible(DependencyObject obj, Storyboard value) { obj.SetValue(AnimationOnVisibleProperty, value); } private static void HandleAnimationOnVisibleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { if (DesignerProperties.GetIsInDesignMode(obj)) { return; } var control = (FrameworkElement)obj; void handler(object s, DependencyPropertyChangedEventArgs e) { if (control.Visibility == Visibility.Visible) { GetAnimationOnVisible(control)?.Begin(); } else { GetAnimationOnVisible(control)?.Stop(); } } control.IsVisibleChanged -= handler; if (args.NewValue != null) { var sb = (Storyboard)args.NewValue; control.IsVisibleChanged += handler; } } } public class AnimatedVisibility { #region Visibility private static readonly DependencyProperty VisibilityProperty = DependencyProperty.RegisterAttached( "Visibility", typeof(Visibility), typeof(AnimatedVisibility), new PropertyMetadata(new PropertyChangedCallback(HandleVisibilityChanged))); public static Visibility GetVisibility(DependencyObject obj) { return (Visibility)obj.GetValue(VisibilityProperty); } public static void SetVisibility(DependencyObject obj, Visibility value) { obj.SetValue(VisibilityProperty, value); } private static void HandleVisibilityChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { var control = (FrameworkElement)obj; if (DesignerProperties.GetIsInDesignMode(control)) { return; } var visibility = (Visibility)args.NewValue; if (visibility == Visibility.Visible) { control.Visibility = visibility; var anim = GetVisible(control); anim?.Begin(); } else if (visibility == Visibility.Collapsed) { var anim = GetCollapsed(control); if (anim == null) { control.Visibility = Visibility.Collapsed; } else { anim.Begin(); } } } #endregion Visibility #region Visible private static readonly DependencyProperty VisibleProperty = DependencyProperty.RegisterAttached( "Visible", typeof(Storyboard), typeof(AnimatedVisibility)); public static Storyboard GetVisible(DependencyObject obj) { return (Storyboard)obj.GetValue(VisibleProperty); } public static void SetVisible(DependencyObject obj, Storyboard value) { obj.SetValue(VisibleProperty, value); } #endregion Visible #region Collapsed private static readonly DependencyProperty CollapsedProperty = DependencyProperty.RegisterAttached( "Collapsed", typeof(Storyboard), typeof(AnimatedVisibility), new PropertyMetadata(new PropertyChangedCallback(HandleCollapsedChanged))); public static Storyboard GetCollapsed(DependencyObject obj) { return (Storyboard)obj.GetValue(CollapsedProperty); } public static void SetCollapsed(DependencyObject obj, Storyboard value) { obj.SetValue(CollapsedProperty, value); } private static void HandleCollapsedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { var control = (FrameworkElement)obj; if (DesignerProperties.GetIsInDesignMode(control)) { return; } void handler(object s, EventArgs e) { control.Visibility = Visibility.Collapsed; } if (args.NewValue != args.OldValue) { if (args.OldValue != null) { var sb = (Storyboard)args.OldValue; sb.Completed -= handler; } if (args.NewValue != null) { var sb = (Storyboard)args.NewValue; sb.Completed += handler; } } } #endregion Collapsed } } ================================================ FILE: source/Playnite/Behaviors/ExpanderBehaviors.cs ================================================ using Playnite; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; namespace Playnite.Behaviors { public class ExpanderBehaviors { private static readonly DependencyProperty SaveStateProperty = DependencyProperty.RegisterAttached("SaveState", typeof(bool), typeof(ExpanderBehaviors)); public static bool GetSaveState(DependencyObject obj) { return (bool)obj.GetValue(SaveStateProperty); } public static void SetSaveState(DependencyObject obj, bool value) { obj.SetValue(SaveStateProperty, value); } private static readonly DependencyProperty SaveStateIdProperty = DependencyProperty.RegisterAttached("SaveStateId", typeof(string), typeof(ExpanderBehaviors)); public static string GetSaveStateId(DependencyObject obj) { return (string)obj.GetValue(SaveStateIdProperty); } public static void SetSaveStateId(DependencyObject obj, string value) { obj.SetValue(SaveStateIdProperty, value); } } } ================================================ FILE: source/Playnite/Behaviors/FocusBahaviors.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; namespace Playnite.Behaviors { public class FocusBahaviors { public static readonly DependencyProperty FocusBindingProperty = DependencyProperty.RegisterAttached( "FocusBinding", typeof(bool), typeof(FocusBahaviors), new PropertyMetadata(new PropertyChangedCallback(FocusBindingPropertyChanged))); public static bool GetFocusBinding(DependencyObject obj) { return (bool)obj.GetValue(FocusBindingProperty); } public static void SetFocusBinding(DependencyObject obj, bool value) { obj.SetValue(FocusBindingProperty, value); } private static void FocusBindingPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { if (DesignerProperties.GetIsInDesignMode(obj)) { return; } var control = (FrameworkElement)obj; if ((bool)args.NewValue) { if (control.Focusable) { control.Focus(); } else { if (!control.IsLoaded) { control.Loaded += Control_Loaded; } else { foreach (var child in ElementTreeHelper.FindVisualChildren(control)) { if (child.Focusable && child.IsVisible) { child.Focus(); return; } } } } } } private static void Control_Loaded(object sender, RoutedEventArgs e) { var elem = (FrameworkElement)sender; elem.Loaded -= Control_Loaded; foreach (var child in ElementTreeHelper.FindVisualChildren(elem)) { if (child.Focusable) { child.Focus(); return; } } } private static readonly DependencyProperty OnVisibilityFocusProperty = DependencyProperty.RegisterAttached( "OnVisibilityFocus", typeof(bool), typeof(FocusBahaviors), new PropertyMetadata(new PropertyChangedCallback(OnVisibilityFocusPropertyChanged))); public static bool GetOnVisibilityFocus(DependencyObject obj) { return (bool)obj.GetValue(OnVisibilityFocusProperty); } public static void SetOnVisibilityFocus(DependencyObject obj, bool value) { obj.SetValue(OnVisibilityFocusProperty, value); } private static void OnVisibilityFocusPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { if (DesignerProperties.GetIsInDesignMode(obj)) { return; } var control = (FrameworkElement)obj; if ((bool)args.NewValue) { control.IsVisibleChanged += Control_IsVisibleChanged; } else { control.IsVisibleChanged -= Control_IsVisibleChanged; } } private static void Control_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { var control = (FrameworkElement)sender; if (control.IsVisible) { if (control.Focusable) { control.Focus(); } else { foreach (FrameworkElement child in LogicalTreeHelper.GetChildren(control)) { if (child.Focusable && child.IsVisible) { child.Focus(); return; } } } } } } } ================================================ FILE: source/Playnite/Behaviors/LeftClickContextMenuBehavior.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; namespace Playnite.Behaviors { public class LeftClickContextMenuBehavior { private static readonly DependencyProperty LeftClickContextMenuProperty = DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(LeftClickContextMenuBehavior), new PropertyMetadata(new PropertyChangedCallback(HandlePropertyChanged))); public static bool GetEnabled(DependencyObject obj) { return (bool)obj.GetValue(LeftClickContextMenuProperty); } public static void SetEnabled(DependencyObject obj, bool value) { obj.SetValue(LeftClickContextMenuProperty, value); } private static void HandlePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { var control = (FrameworkElement)obj; if (DesignerProperties.GetIsInDesignMode(control)) { return; } if ((bool)args.NewValue) { control.PreviewMouseLeftButtonUp += Control_PreviewMouseLeftButtonUp; } else { control.PreviewMouseLeftButtonUp -= Control_PreviewMouseLeftButtonUp; } } private static void Control_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var control = (FrameworkElement)sender; if (control.ContextMenu != null) { control.ContextMenu.PlacementTarget = control; control.ContextMenu.IsOpen = true; } } } } ================================================ FILE: source/Playnite/Behaviors/MediaElementBehaviors.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace Playnite.Behaviors { public class MediaElementBehaviors { private static readonly DependencyProperty RepeatProperty = DependencyProperty.RegisterAttached( "Repeat", typeof(bool), typeof(MediaElementBehaviors), new PropertyMetadata(new PropertyChangedCallback(RepeatPropertyChanged))); public static bool GetRepeat(DependencyObject obj) { return (bool)obj.GetValue(RepeatProperty); } public static void SetRepeat(DependencyObject obj, bool value) { obj.SetValue(RepeatProperty, value); } private static void RepeatPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { if (DesignerProperties.GetIsInDesignMode(obj)) { return; } var media = (MediaElement)obj; if ((bool)args.NewValue) { media.UnloadedBehavior = MediaState.Manual; media.MediaEnded += Control_MediaEnded; } else { media.MediaEnded -= Control_MediaEnded; } } private static void Control_MediaEnded(object sender, RoutedEventArgs e) { var media = (MediaElement)sender; media.Position = TimeSpan.Zero; media.Play(); } } } ================================================ FILE: source/Playnite/Behaviors/ScrollToSelectedBehavior.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Input; namespace Playnite.Behaviors { public class ScrollToSelectedBehavior { private static readonly DependencyProperty ScrollToSelectedProperty = DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(ScrollToSelectedBehavior), new PropertyMetadata(new PropertyChangedCallback(HandlePropertyChanged))); public static bool GetEnabled(DependencyObject obj) { return (bool)obj.GetValue(ScrollToSelectedProperty); } public static void SetEnabled(DependencyObject obj, bool value) { obj.SetValue(ScrollToSelectedProperty, value); } private static void HandlePropertyChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args) { var control = (Selector)obj; if (DesignerProperties.GetIsInDesignMode(control)) { return; } if ((bool)args.NewValue) { control.SelectionChanged += Control_SelectionChanged; } else { control.SelectionChanged -= Control_SelectionChanged; } } private static void Control_SelectionChanged(object sender, SelectionChangedEventArgs e) { if ((sender as Selector).SelectedItem == null) { return; } if (sender is ListView listView) { if (listView.SelectedItems?.Count == 1) { listView.ScrollIntoView(listView.SelectedItem); return; } } if (sender is ListBox listBox) { if (listBox.SelectedItems?.Count == 1) { listBox.ScrollIntoView(listBox.SelectedItem); return; } } } } } ================================================ FILE: source/Playnite/Behaviors/ScrollViewerBehaviours.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; namespace Playnite.Behaviors { public class ScrollAnimationData { private readonly ScrollViewer scroller; public bool IsAnimating { get; private set; } public FrameworkElement ScrollOwner { get; set; } public double TargetOffset { get; set; } public Storyboard Storyboard { get; private set; } public DoubleAnimation Animation { get; private set; } public ScrollAnimationData(ScrollViewer scroller, FrameworkElement owner) { this.scroller = scroller; ScrollOwner = owner; Animation = new DoubleAnimation(); Animation.Completed += Storyboard_Completed; } private void Storyboard_Completed(object sender, EventArgs e) { IsAnimating = false; TargetOffset = scroller.VerticalOffset; } public void BeginAnimation(double to, TimeSpan speed) { TargetOffset = to; Animation.From = scroller.VerticalOffset; Animation.To = TargetOffset; Animation.Duration = new Duration(speed); scroller.BeginAnimation(ScrollViewerBehaviours.VerticalOffsetProperty, Animation); IsAnimating = true; } } public class ScrollViewerBehaviours { // ---------------- VerticalOffset // This is "internal" property only, should NOT be used in any other place. public static DependencyProperty ScrollDataProperty = DependencyProperty.RegisterAttached( "ScrollData", typeof(ScrollAnimationData), typeof(ScrollViewerBehaviours), new PropertyMetadata(null)); public static void SetScrollData(FrameworkElement target, ScrollAnimationData value) { target.SetValue(ScrollDataProperty, value); } public static ScrollAnimationData GetScrollData(FrameworkElement target) { return (ScrollAnimationData)target.GetValue(ScrollDataProperty); } // ---------------- VerticalOffset // This is "internal" property only, should NOT be used in any other place. // It's done this way so vertical offset can be driven by an animation. public static DependencyProperty VerticalOffsetProperty = DependencyProperty.RegisterAttached( "VerticalOffset", typeof(double), typeof(ScrollViewerBehaviours), new PropertyMetadata(0.0, OnVerticalOffsetChanged)); public static void SetVerticalOffset(FrameworkElement target, double value) { target.SetValue(VerticalOffsetProperty, value); } public static double GetVerticalOffset(FrameworkElement target) { return (double)target.GetValue(VerticalOffsetProperty); } private static void OnVerticalOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) { if (target is ScrollViewer scrollViewer) { scrollViewer.ScrollToVerticalOffset((double)e.NewValue); } } // ---------------- ScrollAmount public static readonly DependencyProperty SensitivityProperty = DependencyProperty.RegisterAttached( "Sensitivity", typeof(double), typeof(ScrollViewerBehaviours), new PropertyMetadata(0.0)); public static void SetSensitivity(DependencyObject obj, double value) { obj.SetValue(SensitivityProperty, value); } public static double GetSensitivity(DependencyObject obj) { return (double)obj.GetValue(SensitivityProperty); } // ---------------- TimeDuration public static DependencyProperty SpeedProperty = DependencyProperty.RegisterAttached( "Speed", typeof(TimeSpan), typeof(ScrollViewerBehaviours), new PropertyMetadata(new TimeSpan(0, 0, 0, 0, 250))); public static void SetSpeed(FrameworkElement target, TimeSpan value) { target.SetValue(SpeedProperty, value); } public static TimeSpan GetSpeed(FrameworkElement target) { return (TimeSpan)target.GetValue(SpeedProperty); } // ---------------- SmoothScrollEnabled public static DependencyProperty SmoothScrollEnabledProperty = DependencyProperty.RegisterAttached( "SmoothScrollEnabled", typeof(bool), typeof(ScrollViewerBehaviours), new PropertyMetadata(false)); public static void SetSmoothScrollEnabled(FrameworkElement target, bool value) { target.SetValue(SmoothScrollEnabledProperty, value); } public static bool GetSmoothScrollEnabled(FrameworkElement target) { return (bool)target.GetValue(SmoothScrollEnabledProperty); } // ---------------- CustomScrollEnabled public static DependencyProperty CustomScrollEnabledProperty = DependencyProperty.RegisterAttached( "CustomScrollEnabled", typeof(bool), typeof(ScrollViewerBehaviours), new PropertyMetadata(false, OnCustomScrollEnabledChanged)); public static void SetCustomScrollEnabled(FrameworkElement target, bool value) { target.SetValue(CustomScrollEnabledProperty, value); } public static bool GetCustomScrollEnabled(FrameworkElement target) { return (bool)target.GetValue(CustomScrollEnabledProperty); } private static void OnCustomScrollEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { if (DesignerProperties.GetIsInDesignMode(obj)) { return; } var control = (FrameworkElement)obj; if (obj is ScrollViewer scr) { SetScrollerEvents(control, scr, (bool)args.NewValue); } else { if (control.IsLoaded) { var scroll = ElementTreeHelper.FindVisualChildren(obj).FirstOrDefault(); if (scroll != null) { SetScrollerEvents(control, scroll, (bool)args.NewValue); } } else { void controlLoaded(object a, RoutedEventArgs e) { var ctrl = (FrameworkElement)a; ctrl.Loaded -= controlLoaded; var scroll = ElementTreeHelper.FindVisualChildren(ctrl).FirstOrDefault(); if (scroll != null) { SetScrollerEvents(control, scroll, (bool)args.NewValue); } } control.Loaded += controlLoaded; } } } private static void SetScrollerEvents(FrameworkElement owner, ScrollViewer scroller, bool hook) { if (hook) { SetScrollData(scroller, new ScrollAnimationData(scroller, owner)); scroller.PreviewMouseWheel += PreviewMouseWheel; } else { SetScrollData(scroller, null); scroller.PreviewMouseWheel -= PreviewMouseWheel; } } private static void PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e) { if (e.Handled) { return; } var mouseDelta = (double)e.Delta; var scrollData = GetScrollData(sender as FrameworkElement); var sensitivity = GetSensitivity(scrollData.ScrollOwner); var scroll = sender as ScrollViewer; if (scroll.ComputedVerticalScrollBarVisibility != Visibility.Visible) { return; } if (GetSmoothScrollEnabled(scrollData.ScrollOwner)) { var targetOffset = scrollData.TargetOffset; if (!scrollData.IsAnimating) { targetOffset = scroll.VerticalOffset; } var newOffset = targetOffset - (mouseDelta * sensitivity); if (newOffset < 0) { newOffset = 0; } if (newOffset > scroll.ScrollableHeight) { newOffset = scroll.ScrollableHeight; } scrollData.BeginAnimation(newOffset, GetSpeed(scrollData.ScrollOwner)); } else { scroll.ScrollToVerticalOffset(scroll.VerticalOffset - e.Delta * sensitivity); } e.Handled = true; } } } ================================================ FILE: source/Playnite/Behaviors/SelectorBehaviors.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace Playnite.Behaviors { public class SelectorBehaviors { public class EnumSelector { public Enum Value { get; set; } public string DisplayText { get; set; } } private static readonly DependencyProperty EnumSourceProperty = DependencyProperty.RegisterAttached( "EnumSource", typeof(Type), typeof(SelectorBehaviors), new PropertyMetadata(new PropertyChangedCallback(EnumSourcePropertyChanged))); public static Type GetEnumSource(DependencyObject obj) { return (Type)obj.GetValue(EnumSourceProperty); } public static void SetEnumSource(DependencyObject obj, Type value) { obj.SetValue(EnumSourceProperty, value); } private static void EnumSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { if (DesignerProperties.GetIsInDesignMode(obj)) { return; } var select = (System.Windows.Controls.Primitives.Selector)obj; if (args.NewValue == null) { return; } select.DisplayMemberPath = nameof(EnumSelector.DisplayText); select.SelectedValuePath = nameof(EnumSelector.Value); select.Items.Clear(); var vals = Enum.GetValues((Type)args.NewValue); foreach (Enum val in vals) { select.Items.Add(new EnumSelector { Value = val, DisplayText = val.GetDescription() }); } } } } ================================================ FILE: source/Playnite/BindingProxy.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; namespace Playnite { public class BindingProxy : Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } public object Data { get { return (object)GetValue(DataProperty); } set { SetValue(DataProperty, value); } } public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); } } ================================================ FILE: source/Playnite/CefTools.cs ================================================ using CefSharp; using CefSharp.Wpf; using Playnite.Common; using Playnite.SDK; using Playnite.Settings; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class CefTools { private static ILogger logger = LogManager.GetLogger(); public static bool IsInitialized { get; private set; } public static void ConfigureCef(bool traceLogsEnabled) { FileSystem.CreateDirectory(PlaynitePaths.BrowserCachePath); var settings = new CefSettings(); settings.WindowlessRenderingEnabled = true; if (settings.CefCommandLineArgs.ContainsKey("disable-gpu")) { settings.CefCommandLineArgs.Remove("disable-gpu"); } if (settings.CefCommandLineArgs.ContainsKey("disable-gpu-compositing")) { settings.CefCommandLineArgs.Remove("disable-gpu-compositing"); } settings.CefCommandLineArgs.Add("disable-gpu", "1"); settings.CefCommandLineArgs.Add("disable-gpu-compositing", "1"); // This is needed since Chromium 138 and up automatically de-elevates elevated instances. // This however breaks webviews in case Playnite is started as admin. // This in unsafe, but we already warn users to not run Playnite as admin with explicit dialog on startup... settings.CefCommandLineArgs.Add("do-not-de-elevate"); settings.CachePath = PlaynitePaths.BrowserCachePath; settings.PersistSessionCookies = true; settings.LogFile = Path.Combine(PlaynitePaths.ConfigRootPath, "cef.log"); settings.LogSeverity = traceLogsEnabled ? LogSeverity.Verbose : LogSeverity.Info; IsInitialized = Cef.Initialize(settings); if (!IsInitialized) logger.Error($"CEF failed to initialize: {Cef.GetExitCode()}"); } public static void Shutdown() { Cef.Shutdown(); IsInitialized = false; } } } ================================================ FILE: source/Playnite/CmdlineCommands.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public enum CmdlineCommand : int { Start = 0, Focus = 1, UriRequest = 2, ExtensionInstall = 3, SwitchMode = 4, Shutdown = 5, BackupData = 6, RestoreBackup = 7 } } ================================================ FILE: source/Playnite/Commands/GenericCommands.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace Playnite.Commands { public static class GenericCommands { public static RoutedUICommand ZoomInCmd = new RoutedUICommand("Zoom in view command", "ZoomInCmd", typeof(GenericCommands)); public static RoutedUICommand ZoomOutCmd = new RoutedUICommand("zoom out view command", "ZoomOutCmd", typeof(GenericCommands)); public static RoutedUICommand PlayGameCmd = new RoutedUICommand("play game command", "PlayGameCmd", typeof(GenericCommands)); public static RoutedUICommand ShowDetailsCmd = new RoutedUICommand("show game details command", "ShowDetailsCmd", typeof(GenericCommands)); } } ================================================ FILE: source/Playnite/Commands/GlobalCommands.cs ================================================ using Flurl; using Playnite.Common; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Playnite.Commands { public static class GlobalCommands { private static ILogger logger = LogManager.GetLogger(); internal static PlayniteSettings AppSettings { get; set; } public static RelayCommand NavigateUrlCommand { get; } = new RelayCommand((url) => { try { NavigateUrl(url); } catch (Exception e) when (!Debugger.IsAttached) { logger.Error(e, "Failed to open url."); } }); public static RelayCommand NavigateDirectoryCommand { get; } = new RelayCommand((path) => { try { if (AppSettings?.DirectoryOpenCommand?.IsNullOrWhiteSpace() == false) { try { ProcessStarter.ShellExecute(AppSettings.DirectoryOpenCommand.Replace("{Dir}", path)); } catch (Exception e) { logger.Error(e, "Failed to open directory using custom command."); Process.Start(path)?.Dispose(); } } else { Explorer.OpenDirectory(path); } } catch (Exception e) when (!Debugger.IsAttached) { logger.Error(e, "Failed to open directory."); } }); public static void NavigateUrl(object url) { if (url is string stringUrl) { NavigateUrl(stringUrl); } else if (url is Link linkUrl) { NavigateUrl(linkUrl.Url); } else if (url is Uri uriUrl) { NavigateUrl(uriUrl.OriginalString); } else { throw new Exception("Unsupported URL format."); } } public static void NavigateUrl(string url) { if (url.IsNullOrEmpty()) { throw new Exception("No URL was given."); } if (url.StartsWith("{DocsRootUrl}", StringComparison.OrdinalIgnoreCase)) { url = Url.Combine(PlayniteEnvironment.DocsRootUrl, url.Replace("{DocsRootUrl}", "")); } url = url.Replace("{AppBranch}", PlayniteEnvironment.AppBranch); if (!Regex.IsMatch(url, @"^.*:\/\/")) { if (Paths.IsFullPath(url)) { // Do nothing, some people put local file paths to link fields: #2562 } else { url = "http://" + url; } } ProcessStarter.StartUrl(url); } } } ================================================ FILE: source/Playnite/Common/BindingTools.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; namespace Playnite.Common { public class BindingTools { public static BindingExpressionBase SetBinding(DependencyObject target, DependencyProperty dp, BindingBase binding) { return BindingOperations.SetBinding(target, dp, binding); } public static BindingExpressionBase SetBinding( DependencyObject target, DependencyProperty dp, object source, object path, BindingMode mode = BindingMode.OneWay, UpdateSourceTrigger trigger = UpdateSourceTrigger.Default, IValueConverter converter = null, object converterParameter = null, string stringFormat = null, object fallBackValue = null, int delay = 0, bool isAsync = false, object targetNullValue = null) { var binding = new Binding { Mode = mode, UpdateSourceTrigger = trigger }; if (path is string stringPath) { binding.Path = new PropertyPath(stringPath); } else if (path is PropertyPath propPath) { binding.Path = propPath; } else { binding.Path = new PropertyPath(path); } if (converter != null) { binding.Converter = converter; } if (converterParameter != null) { binding.ConverterParameter = converterParameter; } if (source != null) { binding.Source = source; } if (fallBackValue != null) { binding.FallbackValue = fallBackValue; } if (targetNullValue != null) { binding.TargetNullValue = targetNullValue; } if (delay > 0) { binding.Delay = delay; } if (!stringFormat.IsNullOrEmpty()) { binding.StringFormat = stringFormat; } if (isAsync) { binding.IsAsync = true; } return BindingOperations.SetBinding(target, dp, binding); } public static BindingExpressionBase SetBinding( DependencyObject target, DependencyProperty dp, string path, BindingMode mode = BindingMode.OneWay, UpdateSourceTrigger trigger = UpdateSourceTrigger.Default, IValueConverter converter = null, object converterParameter = null, string stringFormat = null, object fallBackValue = null, int delay = 0, bool isAsync = false) { return SetBinding( target, dp, null, path, mode, trigger, converter, converterParameter, stringFormat, fallBackValue, delay, isAsync); } public static void ClearBinding(DependencyObject target, DependencyProperty dp) { BindingOperations.ClearBinding(target, dp); } } } ================================================ FILE: source/Playnite/Common/Computer.cs ================================================ using Microsoft.Win32; using Playnite.SDK; using Playnite.Native; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Management; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Diagnostics; namespace Playnite.Common { public class SystemInfo { public bool Is64Bit { get; set; } public string WindowsVersion { get; set; } public string ActualWindowsVersion { get; set; } public string WindowsEdition { get; set; } public int WindowsBuildVersion { get; set; } public string Cpu { get; set; } public int Ram { get; set; } public List Gpus { get; set; } public List Screens { get; set; } } public class ComputerScreen { public System.Drawing.Rectangle WorkingArea { get; private set; } public bool Primary { get; private set; } public string DeviceName { get; private set; } public System.Drawing.Rectangle Bounds { get; private set; } public int BitsPerPixel { get; private set; } public ComputerScreen() { } public ComputerScreen(Screen screen) { WorkingArea = screen.WorkingArea; Primary = screen.Primary; DeviceName = screen.DeviceFriendlyName(); Bounds = screen.Bounds; BitsPerPixel = screen.BitsPerPixel; } } public enum WindowsVersion { Unknown, Win7, Win8, Win10, Win11 } public enum HwCompany { Intel, AMD, Nvidia, VMware, Uknown } public static class Computer { private static readonly ILogger logger = LogManager.GetLogger(); public static readonly (string path, string args) ShutdownCmd = ("shutdown.exe", "-s -hybrid -t 0"); public static readonly (string path, string args) RestartCmd = ("shutdown.exe", "-r -t 0"); public static WindowsVersion WindowsVersion { get { var version = Environment.OSVersion.Version; if (version.Major == 6 && version.Minor == 1) { return WindowsVersion.Win7; } else if (version.Major == 6 && (version.Minor == 2 || version.Minor == 3)) { return WindowsVersion.Win8; } else if (version.Major == 10) { // Apparently some people are spoofing Windows 10 build versions but whatherer they are using // is not updating instaled product name, so we need to check that as well. var windowsProd = Computer.GetWindowsProductName(); if (windowsProd?.Contains("Windows 7") == true) return WindowsVersion.Win7; if (windowsProd?.Contains("Windows 8") == true) return WindowsVersion.Win8; return version.Build >= 22000 ? WindowsVersion.Win11 : WindowsVersion.Win10; } else { return WindowsVersion.Unknown; } } } public static bool IsTLS13SystemWideEnabled() { try { using (var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Client")) { if (key != null) { var isEnabled = key.GetValue("Enabled"); if (isEnabled != null) { return Convert.ToBoolean(isEnabled); } } } } catch (Exception e) { logger.Error(e, "Failed to test TLS 1.3 state."); } return false; } public static int GetWindowsReleaseId() { var relVal = Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ReleaseId", ""); if (relVal?.ToString().IsNullOrEmpty() == true) { return 0; } else { return Convert.ToInt32(relVal); } } public static string GetWindowsProductName() { return Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductName", "")?.ToString(); } public static Guid GetMachineGuid() { RegistryKey root = null; if (Environment.Is64BitOperatingSystem) { root = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); } else { root = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); } try { using (var cryptography = root.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography")) { return Guid.Parse((string)cryptography.GetValue("MachineGuid")); } } finally { root.Dispose(); } } public static SystemInfo GetSystemInfo() { var info = new SystemInfo { Is64Bit = Environment.Is64BitOperatingSystem, WindowsVersion = Environment.OSVersion.VersionString, ActualWindowsVersion = Computer.WindowsVersion.ToString(), WindowsBuildVersion = GetWindowsReleaseId(), WindowsEdition = GetWindowsProductName() }; using (var win32Proc = new ManagementObjectSearcher("SELECT * FROM Win32_Processor")) { foreach (var obj in win32Proc.Get()) { info.Cpu = obj["Name"].ToString().Trim(); break; } } using (var memory = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem")) { double totalCapacity = 0; foreach (var obj in memory.Get()) { totalCapacity += Convert.ToDouble(obj["TotalPhysicalMemory"]); } info.Ram = Convert.ToInt32(totalCapacity / 1048576); } using (var video = new ManagementObjectSearcher("SELECT * FROM Win32_VideoController")) { info.Gpus = new List(); foreach (var obj in video.Get()) { info.Gpus.Add(obj["Name"].ToString()); } } info.Screens = GetScreens(); return info; } public static List GetScreens() { return Screen.AllScreens.Select(a => a.ToComputerScreen()).ToList(); } public static ComputerScreen GetPrimaryScreen() { return Screen.PrimaryScreen?.ToComputerScreen(); } public static int GetGetPrimaryScreenIndex() { var allScreens = Screen.AllScreens; for (int i = 0; i < allScreens.Length; i++) { if (allScreens[i].Primary) { return i; } } return 0; } public static void SetMouseCursorVisibility(bool show) { if (show) { while (User32.ShowCursor(true) < 0) { } } else { while (User32.ShowCursor(false) >= 0) { } } } public static void Shutdown() { // Used instead of shutdown.exe because it doesn't replicate the same shutdown behavior // as user initiated "from start menu" shutdown: https://github.com/JosefNemec/Playnite/issues/3947 // EnablePrivilege needed https://stackoverflow.com/a/24726453/1107424 if (!User32.EnablePrivilege("SeShutdownPrivilege", true) || !User32.ExitWindowsEx( User32.ExitWindowsFlags.EWX_SHUTDOWN | User32.ExitWindowsFlags.EWX_HYBRID_SHUTDOWN | User32.ExitWindowsFlags.EWX_ARSO, 0)) { logger.Error("ExitWindowsEx shutdown failed, using fallback via shutdown.exe"); logger.Error(Marshal.GetLastWin32Error().ToString()); ProcessStarter.StartProcess(ShutdownCmd.path, ShutdownCmd.args); } } public static void Restart() { ProcessStarter.StartProcess(RestartCmd.path, RestartCmd.args); } public static bool Sleep() { return Powrprof.SetSuspendState(false, true, false); } public static bool Hibernate() { return Powrprof.SetSuspendState(true, true, false); } public static bool Lock() { return User32.LockWorkStation(); } public static bool Logout() { return User32.ExitWindowsEx(User32.ExitWindowsFlags.EWX_LOGOFF, 0); } public static ComputerScreen ToComputerScreen(this Screen screen) { if (screen == null) { return null; } else { return new ComputerScreen(screen); } } public static List GetGpuVendors() { var gpus = new List(); var vendors = new List(); try { using (var video = new ManagementObjectSearcher("SELECT * FROM Win32_VideoController")) { foreach (var obj in video.Get()) { gpus.Add(obj["Name"].ToString()); } } foreach (var gpu in gpus) { if (gpu.Contains("intel", StringComparison.OrdinalIgnoreCase)) { vendors.AddMissing(HwCompany.Intel); } else if (gpu.Contains("nvidia", StringComparison.OrdinalIgnoreCase)) { vendors.AddMissing(HwCompany.Nvidia); } else if (gpu.Contains("amd", StringComparison.OrdinalIgnoreCase)) { vendors.AddMissing(HwCompany.AMD); } else if (gpu.Contains("vmware", StringComparison.OrdinalIgnoreCase)) { vendors.AddMissing(HwCompany.VMware); } else { return new List { HwCompany.Uknown }; } } if (vendors.Count > 0) { return vendors; } } catch (Exception e) { logger.Error(e, "Failed to get GPU vendor."); } return new List { HwCompany.Uknown }; } private static string GetMonitorFriendlyName(LUID adapterId, uint targetId) { var deviceName = new DISPLAYCONFIG_TARGET_DEVICE_NAME { header = { size = (uint)Marshal.SizeOf(typeof (DISPLAYCONFIG_TARGET_DEVICE_NAME)), adapterId = adapterId, id = targetId, type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME } }; var error = User32.DisplayConfigGetDeviceInfo(ref deviceName); if (error != WinError.ERROR_SUCCESS) { throw new Win32Exception(error); } return deviceName.monitorFriendlyDeviceName; } private static IEnumerable GetAllMonitorsFriendlyNames() { var error = User32.GetDisplayConfigBufferSizes( QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, out uint pathCount, out uint modeCount); if (error != WinError.ERROR_SUCCESS) { throw new Win32Exception(error); } var displayPaths = new DISPLAYCONFIG_PATH_INFO[pathCount]; var displayModes = new DISPLAYCONFIG_MODE_INFO[modeCount]; error = User32.QueryDisplayConfig( QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, ref pathCount, displayPaths, ref modeCount, displayModes, IntPtr.Zero); if (error != WinError.ERROR_SUCCESS) { throw new Win32Exception(error); } for (var i = 0; i < modeCount; i++) { if (displayModes[i].infoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET) { yield return GetMonitorFriendlyName(displayModes[i].adapterId, displayModes[i].id); } } } public static string DeviceFriendlyName(this Screen screen) { try { var allFriendlyNames = GetAllMonitorsFriendlyNames(); for (var index = 0; index < Screen.AllScreens.Length; index++) { if (Equals(screen, Screen.AllScreens[index])) { return allFriendlyNames.ToArray()[index]; } } } catch (Exception e) { logger.Error(e, "Failed to get display name."); } return screen.DeviceName; } public static bool GetScreenReaderActive() { // In theory this method should be using SystemParametersInfo API with SPI_GETSCREENREADER // but according to my testing that returns screen reader presence even if no screen reader is running. // No idea why, so for now we will just check for Narrator, NVDA and JAWS readers. if (Process.GetProcessesByName("narrator").HasItems()) { return true; } if (Process.GetProcessesByName("nvda").HasItems()) { return true; } if (Process.GetProcessesByName("jfw").HasItems()) { return true; } return false; } } } ================================================ FILE: source/Playnite/Common/Constants.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public static class Constants { public const string DefaultDateTimeFormat = "d"; public const string DefaultPartialReleaseDateTimeFormat = "y"; public static string DateUiFormat { get; } = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; public static string TimeUiFormat { get; } = CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern; public static char[] ListSeparators { get; } = new char[] { ListSeparator }; public const char ListSeparator = ','; public static readonly Guid MaxGuidVal = new Guid("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"); } } ================================================ FILE: source/Playnite/Common/CueSheet.cs ================================================ using Playnite.Common; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Playnite { public class CueSheet { public struct CueFileEntry { public string Path { get; } public string Type { get; } public CueFileEntry(string path, string type) { Path = path; Type = type; } } public static List GetFileEntries(string cuePath) { var files = new List(); foreach (var line in File.ReadAllLines(Paths.FixPathLength(cuePath))) { if (!line.IsNullOrWhiteSpace()) { var match = Regex.Match(line, @"FILE\s+""(.+)""\s+(.+)$"); if (match.Success) { files.Add(new CueFileEntry(match.Groups[1].Value, match.Groups[2].Value)); } } } return files; } } } ================================================ FILE: source/Playnite/Common/DesignerTools.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace System.Windows { public class DesignerTools { private static bool? inDesignMode = null; public static bool IsInDesignMode { get { if (inDesignMode == null) { inDesignMode = DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject()); } return inDesignMode.Value; } } } } ================================================ FILE: source/Playnite/Common/Exceptions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class NoDiskSpaceException : Exception { public long RequiredSpace { get; } public NoDiskSpaceException(long requiredSpace) : base() { RequiredSpace = requiredSpace; } public NoDiskSpaceException(string message) : base(message) { } public NoDiskSpaceException(string message, long requiredSpace) : base(message) { RequiredSpace = requiredSpace; } } } ================================================ FILE: source/Playnite/Common/Explorer.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class Explorer { [DllImport("shell32.dll", SetLastError = true)] private static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, uint dwFlags); [DllImport("shell32.dll", SetLastError = true)] private static extern void SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, [Out] out IntPtr pidl, uint sfgaoIn, [Out] out uint psfgaoOut); public static void NavigateToFileSystemEntry(string path) { var parentFolder = Path.GetDirectoryName(path); if (string.IsNullOrEmpty(parentFolder)) return; SHParseDisplayName(parentFolder, IntPtr.Zero, out var nativeFolder, 0, out _); if (nativeFolder == IntPtr.Zero) return; var itemToSelect = Path.GetFileName(path); SHParseDisplayName(Path.Combine(parentFolder, itemToSelect), IntPtr.Zero, out var nativeFile, 0, out _); var fileArray = new[] { nativeFile == IntPtr.Zero ? nativeFolder : nativeFile }; SHOpenFolderAndSelectItems(nativeFolder, (uint)fileArray.Length, fileArray, 0); Marshal.FreeCoTaskMem(nativeFolder); if (nativeFile != IntPtr.Zero) Marshal.FreeCoTaskMem(nativeFile); } public static void OpenDirectory(string path) { // Adding trailing backslash ensures the path is treated as a directory // and using UseShellExecute avoids the process hanging issue if (!path.EndsWith("\\")) path += "\\"; Process.Start(new ProcessStartInfo(path) { UseShellExecute = true }); } } } ================================================ FILE: source/Playnite/Common/Extensions/BitmapExtensions.cs ================================================ using PhotoSauce.MagicScaler; using Playnite; using Playnite.Common; using Playnite.SDK; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using ImageMagick; using TGASharpLib; namespace System.Drawing.Imaging { public class BitmapLoadProperties: IEquatable { public ImageLoadScaling Scaling { get; set; } = ImageLoadScaling.BitmapDotNet; public DpiScale? DpiScale { get; set; } public int MaxDecodePixelWidth { get; set; } = 0; public int MaxDecodePixelHeight { get; set; } = 0; public int DpiAwareMaxDecodePixelWidth => DpiScale == null ? MaxDecodePixelWidth : (int)Math.Round(MaxDecodePixelWidth * DpiScale.Value.DpiScaleX); public int DpiAwareMaxDecodePixelHeight => DpiScale == null ? MaxDecodePixelHeight : (int)Math.Round(MaxDecodePixelHeight * DpiScale.Value.DpiScaleY); public string Source { get; set; } public BitmapLoadProperties(int decodePixelWidth, int decodePixelHeight) { MaxDecodePixelWidth = decodePixelWidth; MaxDecodePixelHeight = decodePixelHeight; } public BitmapLoadProperties(int decodePixelWidth, int decodePixelHeight, DpiScale? dpiScale) : this(decodePixelWidth, decodePixelHeight) { DpiScale = dpiScale; } public BitmapLoadProperties(int decodePixelWidth, int decodePixelHeight, DpiScale? dpiScale, ImageLoadScaling scaling) : this(decodePixelWidth, decodePixelHeight, dpiScale) { Scaling = scaling; } public bool Equals(BitmapLoadProperties other) { if (other is null) { return false; } if (DpiScale?.Equals(other.DpiScale) == false) { return false; } if (MaxDecodePixelWidth != other.MaxDecodePixelWidth) { return false; } if (MaxDecodePixelHeight != other.MaxDecodePixelHeight) { return false; } if (!string.Equals(Source, other.Source, StringComparison.Ordinal)) { return false; } if (Scaling != other.Scaling) { return false; } return true; } public override bool Equals(object obj) => Equals(obj as BitmapLoadProperties); public override int GetHashCode() { return base.GetHashCode(); } public static bool operator ==(BitmapLoadProperties obj1, BitmapLoadProperties obj2) { if (obj1 is null && obj2 is null) { return true; } else { return obj1?.Equals(obj2) == true; } } public static bool operator !=(BitmapLoadProperties obj1, BitmapLoadProperties obj2) { return obj1?.Equals(obj2) == false; } public override string ToString() { return $"{MaxDecodePixelWidth}x{MaxDecodePixelHeight};{DpiScale?.DpiScaleX}x{DpiScale?.DpiScaleY};{Source};{Scaling}"; } } public static partial class BitmapExtensions { private static ILogger logger = LogManager.GetLogger(); private static readonly byte[] webpSig = new byte[] { 0x57, 0x45, 0x42, 0x50 }; // Apparently both ftypavif and ftypmif1 can appear in avif encoded images private static readonly byte[] avif1 = new byte[] { 0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66 }; private static readonly byte[] avif2 = new byte[] { 0x66, 0x74, 0x79, 0x70, 0x6d, 0x69, 0x66, 0x31 }; public static BitmapSource CreateSourceFromURI(Uri imageUri) { return CreateSourceFromURI(imageUri.LocalPath); } public static BitmapSource CreateSourceFromURI(string imageUri) { return BitmapFromFile(imageUri); } public static byte[] ToPngArray(this BitmapImage image) { return ToPngArray((BitmapSource)image); } public static byte[] ToPngArray(this BitmapSource image) { var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(image)); using (var stream = new MemoryStream()) { encoder.Save(stream); return stream.ToArray(); } } public static BitmapSource GetClone(this BitmapSource image, BitmapLoadProperties loadProperties = null) { var encoder = new BmpBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(image)); using (var stream = new MemoryStream()) { encoder.Save(stream); return BitmapExtensions.BitmapFromStream(stream, loadProperties); } } public static Windows.Controls.Image ToImage(this BitmapSource bitmap, BitmapScalingMode scaling = BitmapScalingMode.Fant) { var image = new Windows.Controls.Image() { Source = bitmap }; RenderOptions.SetBitmapScalingMode(image, scaling); return image; } public static BitmapSource BitmapFromFile(string imagePath, BitmapLoadProperties loadProperties = null) { try { using (var fStream = FileSystem.OpenReadFileStreamSafe(imagePath)) { return BitmapFromStream(fStream, loadProperties); } } catch (Exception e) { logger.Error(e, $"Failed to create bitmap from file {imagePath}."); return null; } } public static bool IsFormatForImageMagickDecode(Stream stream) { if (stream.Length < 12) return false; // WEBP stream.Seek(8, SeekOrigin.Begin); var buffer = new byte[4]; stream.Read(buffer, 0, 4); if (buffer.SequenceEqual(webpSig)) return true; // AVIF stream.Seek(4, SeekOrigin.Begin); buffer = new byte[8]; stream.Read(buffer, 0, 8); if (buffer.SequenceEqual(avif1)) return true; if (buffer.SequenceEqual(avif2)) return true; return false; } public static BitmapSource HtmlComponentImageLoader(Stream stream) { if (IsFormatForImageMagickDecode(stream)) return BitmapFromStreamImageMagick(stream); // This is exactly how HTML renderer loads them by default, for compatbility reasons. // With an exception of IgnoreColorProfile, which speeds up image decode and doesn't really matter for our use case. stream.Seek(0, SeekOrigin.Begin); var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.StreamSource = stream; bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.CreateOptions = BitmapCreateOptions.IgnoreColorProfile; bitmap.EndInit(); bitmap.Freeze(); return bitmap; } public static BitmapSource BitmapFromStreamImageMagick(Stream stream, BitmapLoadProperties loadProperties = null) { // It doesn't look look we can decode images from ImageMagick at specific size for speed and memory gains. // So loadProperties are ignored for now. // https://github.com/dlemstra/Magick.NET/discussions/1824#discussioncomment-12810888 //var info = new MagickImageInfo(stream); stream.Seek(0, SeekOrigin.Begin); using (var mImage = new MagickImage(stream)) { var image = mImage.ToBitmapSource(); image.Freeze(); return image; } } // TODO: Modify scaling to scale by both axies. // This will currently work properly only if load properties force only width or height, not both. public static BitmapSource BitmapFromStream(Stream stream, BitmapLoadProperties loadProperties = null) { // Have to call .Freeze() on sources created here otherwise images decoded from non-UI // thread won't load on UI thread. try { // For webp and avif primarily, since Windows' decoder doesn't work properly with some files. // Primary example being images in Steam store's game descriptions. if (IsFormatForImageMagickDecode(stream)) return BitmapFromStreamImageMagick(stream, loadProperties); stream.Seek(0, SeekOrigin.Begin); var properties = Images.GetImageProperties(stream); var aspect = new AspectRatio(properties.Width, properties.Height); stream.Seek(0, SeekOrigin.Begin); var bitmap = new BitmapImage(); bitmap.BeginInit(); MemoryStream tempStream = null; var shouldRescale = false; if ((loadProperties?.MaxDecodePixelWidth > 0 && properties?.Width > loadProperties?.MaxDecodePixelWidth) || (loadProperties?.MaxDecodePixelHeight > 0 && properties?.Height > loadProperties?.MaxDecodePixelHeight)) { shouldRescale = true; } if (loadProperties?.Scaling == ImageLoadScaling.None) { shouldRescale = false; } if (shouldRescale) { if (loadProperties?.Scaling == ImageLoadScaling.BitmapDotNet) { if (loadProperties?.MaxDecodePixelWidth > 0 && properties?.Width > loadProperties?.MaxDecodePixelWidth) { bitmap.DecodePixelWidth = loadProperties.DpiAwareMaxDecodePixelWidth; } if (loadProperties?.MaxDecodePixelHeight > 0 && properties?.Height > loadProperties?.MaxDecodePixelHeight) { bitmap.DecodePixelHeight = loadProperties.DpiAwareMaxDecodePixelHeight; } if (bitmap.DecodePixelHeight != 0 && bitmap.DecodePixelWidth == 0) { bitmap.DecodePixelWidth = (int)Math.Round(aspect.GetWidth(bitmap.DecodePixelHeight)); } else if (bitmap.DecodePixelWidth != 0 && bitmap.DecodePixelHeight == 0) { bitmap.DecodePixelHeight = (int)Math.Round(aspect.GetHeight(bitmap.DecodePixelWidth)); } } else if (loadProperties?.Scaling == ImageLoadScaling.Custom) { var settings = new ProcessImageSettings { SaveFormat = FileFormat.Bmp, Sharpen = false }; if (loadProperties.MaxDecodePixelWidth > 0 && properties?.Width > loadProperties?.MaxDecodePixelWidth) { settings.Width = loadProperties.DpiAwareMaxDecodePixelWidth; } if (loadProperties.MaxDecodePixelHeight > 0 && properties?.Height > loadProperties?.MaxDecodePixelHeight) { settings.Height = loadProperties.DpiAwareMaxDecodePixelHeight; } tempStream = new MemoryStream(); // MagicImage can't run on UI thread. if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) { Task.Run(() => MagicImageProcessor.ProcessImage(stream, tempStream, settings)).Wait(); } else { MagicImageProcessor.ProcessImage(stream, tempStream, settings); } tempStream.Seek(0, SeekOrigin.Begin); } } bitmap.StreamSource = tempStream ?? stream; bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.CreateOptions = BitmapCreateOptions.IgnoreColorProfile; bitmap.EndInit(); bitmap.Freeze(); tempStream?.Dispose(); return bitmap; } catch (Exception e) { logger.Error(e, "Failed to create bitmap from stream."); return null; } } public static long GetSizeInMemory(this BitmapSource image) { try { return Convert.ToInt64(image.PixelHeight * image.PixelWidth * 4); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to get image size from bitmap."); return 0; } } public static BitmapImage TgaToBitmap(TGA tga) { try { using (var tgaBitmap = tga.ToBitmap()) { using (var memory = new MemoryStream()) { tgaBitmap.Save(memory, ImageFormat.Png); memory.Position = 0; var bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); bitmapImage.Freeze(); return bitmapImage; } } } catch (Exception e) { logger.Error(e, $"Failed to create bitmap from TGA image."); return null; } } public static Bitmap ToBitmap(this BitmapImage bitmapImage) { using (MemoryStream outStream = new MemoryStream()) { var enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(bitmapImage)); enc.Save(outStream); Bitmap bitmap = new Bitmap(outStream); return new Bitmap(bitmap); } } public static BitmapImage TgaToBitmap(string tgaPath) { return TgaToBitmap(new TGA(tgaPath)); } public static BitmapImage TgaToBitmap(byte[] tgaContent) { return TgaToBitmap(new TGA(tgaContent)); } } } ================================================ FILE: source/Playnite/Common/Extensions/BitmapIconExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace System.Drawing.Imaging { /// /// Adapted from this gist: https://gist.github.com/darkfall/1656050 /// Provides helper methods for imaging /// public static partial class BitmapExtensions { /// /// Converts a PNG image to a icon (ico) with all the sizes windows likes /// /// The input bitmap /// The output stream /// Wether or not the icon was succesfully generated public static bool ConvertToIcon(Bitmap inputBitmap, Stream output) { if (inputBitmap == null) return false; int[] sizes = new int[] { 256, 48, 32, 16 }; // Generate bitmaps for all the sizes and toss them in streams List imageStreams = new List(); foreach (int size in sizes) { Bitmap newBitmap = ResizeImage(inputBitmap, size, size); if (newBitmap == null) return false; MemoryStream memoryStream = new MemoryStream(); newBitmap.Save(memoryStream, ImageFormat.Png); imageStreams.Add(memoryStream); } BinaryWriter iconWriter = new BinaryWriter(output); if (output == null || iconWriter == null) return false; int offset = 0; // 0-1 reserved, 0 iconWriter.Write((byte)0); iconWriter.Write((byte)0); // 2-3 image type, 1 = icon, 2 = cursor iconWriter.Write((short)1); // 4-5 number of images iconWriter.Write((short)sizes.Length); offset += 6 + (16 * sizes.Length); for (int i = 0; i < sizes.Length; i++) { // image entry 1 // 0 image width iconWriter.Write((byte)sizes[i]); // 1 image height iconWriter.Write((byte)sizes[i]); // 2 number of colors iconWriter.Write((byte)0); // 3 reserved iconWriter.Write((byte)0); // 4-5 color planes iconWriter.Write((short)0); // 6-7 bits per pixel iconWriter.Write((short)32); // 8-11 size of image data iconWriter.Write((int)imageStreams[i].Length); // 12-15 offset of image data iconWriter.Write((int)offset); offset += (int)imageStreams[i].Length; } for (int i = 0; i < sizes.Length; i++) { // write image data // png data must contain the whole png data file iconWriter.Write(imageStreams[i].ToArray()); imageStreams[i].Close(); } iconWriter.Flush(); return true; } /// /// Converts a PNG image to a icon (ico) /// /// The input stream /// The output streamWether or not the icon was succesfully generated public static bool ConvertToIcon(Stream input, Stream output) { Bitmap inputBitmap = (Bitmap)Bitmap.FromStream(input); return ConvertToIcon(inputBitmap, output); } /// /// Converts a PNG image to a icon (ico) /// /// The input path /// The output path /// Wether or not the icon was succesfully generated public static bool ConvertToIcon(string inputPath, string outputPath) { using (FileStream inputStream = new FileStream(inputPath, FileMode.Open)) using (FileStream outputStream = new FileStream(outputPath, FileMode.Create)) { return ConvertToIcon(inputStream, outputStream); } } /// /// Converts an image to a icon (ico) /// /// The input image /// The output path /// Wether or not the icon was succesfully generated public static bool ConvertToIcon(Image inputImage, string outputPath) { using (FileStream outputStream = new FileStream(outputPath, FileMode.Create)) { return ConvertToIcon(new Bitmap(inputImage), outputStream); } } /// /// Resize the image to the specified width and height. /// Found on stackoverflow: https://stackoverflow.com/questions/1922040/resize-an-image-c-sharp /// /// The image to resize. /// The width to resize to. /// The height to resize to. /// The resized image. public static Bitmap ResizeImage(Image image, int width, int height) { var destRect = new Rectangle(0, 0, width, height); var destImage = new Bitmap(width, height); destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); using (var graphics = Graphics.FromImage(destImage)) { graphics.CompositingMode = CompositingMode.SourceCopy; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; using (var wrapMode = new ImageAttributes()) { wrapMode.SetWrapMode(WrapMode.TileFlipXY); graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode); } } return destImage; } } } ================================================ FILE: source/Playnite/Common/Extensions/CloneObject.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using System.Reflection; using Playnite.SDK.Data; using Playnite.Common; using Playnite.SDK.Models; namespace System { public static class CloneObject { private static readonly JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings() { Formatting = Formatting.None, ContractResolver = JsonResolver.Global, MaxDepth = 128 }; /// /// Perform a deep copy of the object, using Json as a serialisation method. /// /// The type of object being copied. /// The object instance to copy. /// The copied object. public static T GetClone(this T source) { if (Object.ReferenceEquals(source, null)) { return default(T); } return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source, jsonSerializerSettings)); } public static Game GetClone(this Game source) { if (source == null) { return null; } return source.GetCopy(); } public static GameAction GetClone(this GameAction source) { if (source == null) { return null; } return source.GetCopy(); } public static CustomEmulatorProfile GetClone(this CustomEmulatorProfile source) { if (source == null) { return null; } return source.GetCopy(); } public static BuiltInEmulatorProfile GetClone(this BuiltInEmulatorProfile source) { if (source == null) { return null; } return source.GetCopy(); } public static Emulator GetClone(this Emulator source) { if (source == null) { return null; } return source.GetCopy(); } public static U GetClone(this T source) { if (Object.ReferenceEquals(source, null)) { return default(U); } return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source, jsonSerializerSettings)); } public static bool IsEqualJson(this object source, object targer) { var first = JsonConvert.SerializeObject(source, jsonSerializerSettings); var second = JsonConvert.SerializeObject(targer, jsonSerializerSettings); return first == second; } /// /// Extension for 'Object' that copies the properties to a destination object. /// Courtesy of http://stackoverflow.com/questions/930433/apply-properties-values-from-one-object-to-another-of-the-same-type-automaticall /// /// The source. /// The destination. public static void CopyProperties(this object source, object destination, bool diffOnly, List ignoreNames = null, bool acceptJsonIgnore = false) { // If any this null throw an exception if (source == null || destination == null) throw new Exception("Source or/and Destination Objects are null"); // Getting the Types of the objects Type typeDest = destination.GetType(); Type typeSrc = source.GetType(); // Iterate the Properties of the source instance and // populate them from their desination counterparts PropertyInfo[] srcProps = typeSrc.GetProperties(); foreach (PropertyInfo srcProp in srcProps) { if (ignoreNames?.Any() == true && ignoreNames.Contains(srcProp.Name)) { continue; } if (!srcProp.CanRead) { continue; } PropertyInfo targetProperty = typeDest.GetProperty(srcProp.Name); if (targetProperty == null) { continue; } if (!targetProperty.CanWrite) { continue; } if (targetProperty.GetSetMethod(true) != null && targetProperty.GetSetMethod(true).IsPrivate) { continue; } if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0) { continue; } if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)) { continue; } if (acceptJsonIgnore && (Attribute.IsDefined(targetProperty, typeof(DontSerializeAttribute)) || Attribute.IsDefined(targetProperty, typeof(JsonIgnoreAttribute)))) { continue; } var sourceValue = srcProp.GetValue(source); var targetValue = targetProperty.GetValue(destination); if (sourceValue == null && targetValue == null) { continue; } // TODO Add support for lists if (sourceValue is IComparable && diffOnly) { var equal = ((IComparable)sourceValue).CompareTo(targetValue) == 0; if (!equal) { targetProperty.SetValue(destination, sourceValue); } } else { if (sourceValue != null) { var genericComparable = sourceValue.GetType().GetInterface("IComparable`1"); if (genericComparable != null && genericComparable.GenericTypeArguments.Any(a => a == sourceValue.GetType()) && diffOnly) { int res = (int)genericComparable.GetMethod("CompareTo").Invoke(sourceValue, new object[] { targetValue }); if (res != 0) { targetProperty.SetValue(destination, sourceValue); } continue; } } targetProperty.SetValue(destination, sourceValue); } } } } } ================================================ FILE: source/Playnite/Common/Extensions/Dictionary.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace System { public static class DictionaryExtensions { public static void AddOrUpdate(this Dictionary source, TKey key, TVal value) { if (source.ContainsKey(key)) { source[key] = value; } else { source.Add(key, value); } } public static void AddMissing(this Dictionary source, TKey key, TVal value) { if (!source.ContainsKey(key)) { source.Add(key, value); } } } } ================================================ FILE: source/Playnite/Common/Extensions/Enums.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace System { public static class EnumExtensions { public static int GetMax(this Enum source) { return Enum.GetValues(source.GetType()).Cast().Max(); } public static int GetMin(this Enum source) { return Enum.GetValues(source.GetType()).Cast().Min(); } public static string GetDescription(this Enum source) { FieldInfo field = source.GetType().GetField(source.ToString()); if (field == null) { return string.Empty; } var attributes = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false); if (attributes != null && attributes.Length > 0) { var desc = attributes[0].Description; if (desc.StartsWith("LOC", StringComparison.Ordinal)) { return ResourceProvider.GetString(desc); } else { return attributes[0].Description; } } else { return source.ToString(); } } } } ================================================ FILE: source/Playnite/Common/Extensions/IconExtension.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using Playnite.Native; namespace System.Drawing { public static class IconExtension { public static byte[] ToByteArray(this Icon icon, System.Drawing.Imaging.ImageFormat format) { using (var stream = new MemoryStream()) { using (var bitmap = icon.ToBitmap()) { bitmap.Save(stream, format); return stream.ToArray(); } } } public static BitmapSource ToImageSource(this Icon icon) { using (Bitmap bitmap = icon.ToBitmap()) { IntPtr hBitmap = bitmap.GetHbitmap(); BitmapSource wpfBitmap = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); if (!Gdi32.DeleteObject(hBitmap)) { throw new Win32Exception(); } return wpfBitmap; } } } } ================================================ FILE: source/Playnite/Common/Extensions/ItemsControlExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace System.Windows.Controls { public static class ItemsControlExtensions { public static void ScrollIntoView(this ItemsControl control, object item) { var framework = control.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; if (framework == null) { return; } framework.BringIntoView(); } public static void ScrollIntoView(this ItemsControl control) { int count = control.Items.Count; if (count == 0) { return; } object item = control.Items[count - 1]; control.ScrollIntoView(item); } public static string ToHtml(this Color color) { return $"#{color.R:X2}{color.G:X2}{color.B:X2}"; } } } ================================================ FILE: source/Playnite/Common/Extensions/KeyExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace Playnite.Common { public static class KeyExtensions { public static bool IsNumericKey(this Key key) { return ( key == Key.D0 || key == Key.D1 || key == Key.D2 || key == Key.D3 || key == Key.D4 || key == Key.D5 || key == Key.D6 || key == Key.D7 || key == Key.D8 || key == Key.D9 || key == Key.NumPad0 || key == Key.NumPad1 || key == Key.NumPad2 || key == Key.NumPad3 || key == Key.NumPad4 || key == Key.NumPad5 || key == Key.NumPad6 || key == Key.NumPad7 || key == Key.NumPad8 || key == Key.NumPad9); } } } ================================================ FILE: source/Playnite/Common/Extensions/LongExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace System { public static class LongExtensions { public static DateTime ToDateFromUnixMs(this long value) { return DateTimeOffset.FromUnixTimeMilliseconds(value).DateTime; } public static DateTime ToDateFromUnixSeconds(this long value) { return DateTimeOffset.FromUnixTimeSeconds(value).DateTime; } } } ================================================ FILE: source/Playnite/Common/Extensions/NetExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace System.Net { public static class NetExtensions { public static bool IsSuccess(this HttpStatusCode statusCode) { return statusCode >= HttpStatusCode.OK && statusCode < HttpStatusCode.MultipleChoices; } } } ================================================ FILE: source/Playnite/Common/Extensions/ObjectExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace System { public static class ObjectExtensions { public static bool HasMethod(this object obj, string methodName) { if (obj == null) { return false; } try { return obj.GetType().GetMethod(methodName) != null; } catch (AmbiguousMatchException) { // Ambiguous means there is more than one result return true; } } public static object CrateInstance(this Type type) { return Activator.CreateInstance(type); } public static T CrateInstance(this Type type) { return (T)Activator.CreateInstance(type); } public static T CrateInstance(this Type type, params object[] parameters) { return (T)Activator.CreateInstance(type, parameters); } public static object CreateGenericInstance(Type genericTypeDefinition, Type genericType) { Type resultType = genericTypeDefinition.MakeGenericType(genericType); return Activator.CreateInstance(resultType); } public static object CreateGenericInstance(Type genericTypeDefinition, Type genericType, params object[] parameters) { Type resultType = genericTypeDefinition.MakeGenericType(genericType); return Activator.CreateInstance(resultType, parameters); } public static bool HasPropertyAttribute(this Type type, string propertyName) where TAttribute : Attribute { var prop = type.GetProperty(propertyName); if (prop == null) { return false; } else { return prop.GetCustomAttribute(typeof(TAttribute)) != null; } } public static bool IsGenericList(this Type type, out Type itemType) { var isGeneric = type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(List<>)); if (isGeneric) { itemType = type.GenericTypeArguments.First(); return true; } else { itemType = null; return false; } } } } ================================================ FILE: source/Playnite/Common/Extensions/ProcessExtensions.cs ================================================ using Playnite.Native; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Management; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace System.Diagnostics { public static class ProcessExtensions { public static bool TryGetMainModuleFileName(this Process process, out string fileName, int buffer = 1024) { fileName = null; var handle = Kernel32.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, process.Id); if (handle == IntPtr.Zero) { return false; } try { var fileNameBuilder = new StringBuilder(buffer); uint bufferLength = (uint)fileNameBuilder.Capacity + 1; var result = Kernel32.QueryFullProcessImageName(handle, 0, fileNameBuilder, ref bufferLength); fileName = result ? fileNameBuilder.ToString() : null; return result; } finally { Kernel32.CloseHandle(handle); } } public static bool TryGetParentId(this Process process, out int processId) { processId = 0; var handle = Kernel32.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, process.Id); if (handle == IntPtr.Zero) { return false; } try { var info = new PROCESS_BASIC_INFORMATION(); int status = Ntdll.NtQueryInformationProcess(handle, 0, ref info, Marshal.SizeOf(info), out var returnLength); if (status != 0) { return false; } processId = info.InheritedFromUniqueProcessId.ToInt32(); return true; } finally { Kernel32.CloseHandle(handle); } } public static bool IsRunning(string processPattern) { return Process.GetProcesses().FirstOrDefault(a => Regex.IsMatch(a.ProcessName, processPattern, RegexOptions.IgnoreCase)) != null; } public static string GetCommandLine(this Process process) { using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id)) using (ManagementObjectCollection objects = searcher.Get()) { return objects.Cast().SingleOrDefault()?["CommandLine"]?.ToString(); } } } } ================================================ FILE: source/Playnite/Common/Extensions/StringExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; namespace System { class CaseInsensitiveCharComparer : EqualityComparer { public override bool Equals(char x, char y) { return char.ToUpperInvariant(x) == char.ToUpperInvariant(y); } public override int GetHashCode(char obj) { return char.ToUpperInvariant(obj).GetHashCode(); } } public static class StringExtensions { private static readonly CultureInfo enUSCultInfo = new CultureInfo("en-US", false); private const double defaultWinklerWeightThreshold = 0.7; //Winkler's paper used a default value of 0.7 private const int winklerNumChars = 4; //Size of the prefix to be considered by the Winkler modification. private static readonly EqualityComparer charCaseInsensitiveComparer = new CaseInsensitiveCharComparer(); public static string MD5(this string s) { var builder = new StringBuilder(); foreach (byte b in MD5Bytes(s)) { builder.Append(b.ToString("x2").ToLower()); } return builder.ToString(); } public static byte[] MD5Bytes(this string s) { using (var provider = System.Security.Cryptography.MD5.Create()) { return provider.ComputeHash(Encoding.UTF8.GetBytes(s)); } } public static string RemoveTrademarks(this string str, string remplacement = "") { if (str.IsNullOrEmpty()) { return str; } return Regex.Replace(str, @"[™©®]", remplacement); } public static bool IsNullOrEmpty(this string source) { return string.IsNullOrEmpty(source); } public static bool IsNullOrWhiteSpace(this string source) { return string.IsNullOrWhiteSpace(source); } public static string Format(this string source, params object[] args) { return string.Format(source, args); } public static string TrimEndString(this string source, string value, StringComparison comp = StringComparison.Ordinal) { if (!source.EndsWith(value, comp)) { return source; } return source.Remove(source.LastIndexOf(value, comp)); } public static string ToTileCase(this string source, CultureInfo culture = null) { if (source.IsNullOrEmpty()) { return source; } if (culture != null) { return culture.TextInfo.ToTitleCase(source); } else { return enUSCultInfo.TextInfo.ToTitleCase(source); } } public static bool IsStartOfStringAcronym(this string acronymStart, string input) { if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(acronymStart) || acronymStart.Length < 2 || acronymStart.Length > input.Length) { return false; } for (int i = 0; i < acronymStart.Length; i++) { if (!char.IsLetterOrDigit(acronymStart[i])) { return false; } } var acronymIndex = 0; for (int i = 0; i < input.Length; i++) { if (char.IsLetterOrDigit(input[i]) && (i == 0 || input[i - 1] == ' ')) { if (char.ToUpperInvariant(input[i]) != char.ToUpperInvariant(acronymStart[acronymIndex])) { return false; } else { acronymIndex++; // If the acronym index and acronym start length is the same // it means all the characters have been matched if (acronymIndex == acronymStart.Length) { return true; } } } } return false; } private static string RemoveUnlessThatEmptiesTheString(string input, string pattern) { string output = Regex.Replace(input, pattern, string.Empty); if (string.IsNullOrWhiteSpace(output)) { return input; } return output; } public static string NormalizeGameName(this string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } var newName = name; newName = newName.RemoveTrademarks(); newName = newName.Replace("_", " "); newName = newName.Replace(".", " "); newName = newName.Replace('’', '\''); newName = RemoveUnlessThatEmptiesTheString(newName, @"\[.*?\]"); newName = RemoveUnlessThatEmptiesTheString(newName, @"\(.*?\)"); newName = Regex.Replace(newName, @"\s*:\s*", ": "); newName = Regex.Replace(newName, @"\s+", " "); if (Regex.IsMatch(newName, @",\s*The$")) { newName = "The " + Regex.Replace(newName, @",\s*The$", "", RegexOptions.IgnoreCase); } return newName.Trim(); } public static string GetSHA256Hash(this string input) { using (var sha = System.Security.Cryptography.SHA256.Create()) { var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(input)); return BitConverter.ToString(hash).Replace("-", ""); } } public static byte[] GetSHA256HashByte(this string input) { using (var sha = System.Security.Cryptography.SHA256.Create()) { return sha.ComputeHash(Encoding.UTF8.GetBytes(input)); } } public static string GetPathWithoutAllExtensions(string path) { if (string.IsNullOrEmpty(path)) { return string.Empty; } return Regex.Replace(path, @"(\.[A-Za-z0-9]+)+$", ""); } public static bool Contains(this string str, string value, StringComparison comparisonType) { return str?.IndexOf(value, 0, comparisonType) != -1; } public static bool ContainsAny(this string str, char[] chars) { return str?.IndexOfAny(chars) >= 0; } public static bool IsHttpUrl(this string str) { if (string.IsNullOrWhiteSpace(str)) { return false; } return Regex.IsMatch(str, @"^https?:\/\/", RegexOptions.IgnoreCase); } public static bool IsUri(this string str) { if (string.IsNullOrWhiteSpace(str)) { return false; } return Uri.IsWellFormedUriString(str, UriKind.Absolute); } public static string UrlEncode(this string str) { if (string.IsNullOrWhiteSpace(str)) { return str; } return HttpUtility.UrlPathEncode(str); } public static string UrlDecode(this string str) { if (string.IsNullOrWhiteSpace(str)) { return str; } return HttpUtility.UrlDecode(str); } public static int GetLineCount(this string str) { if (str == null) { return 0; } else { return Regex.Matches(str, "\n").Count + 1; } } // Courtesy of https://stackoverflow.com/questions/6275980/string-replace-ignoring-case public static string Replace(this string str, string oldValue, string @newValue, StringComparison comparisonType) { // Check inputs. if (str == null) { // Same as original .NET C# string.Replace behavior. throw new ArgumentNullException(nameof(str)); } if (str.Length == 0) { // Same as original .NET C# string.Replace behavior. return str; } if (oldValue == null) { // Same as original .NET C# string.Replace behavior. throw new ArgumentNullException(nameof(oldValue)); } if (oldValue.Length == 0) { // Same as original .NET C# string.Replace behavior. throw new ArgumentException("String cannot be of zero length."); } // Prepare string builder for storing the processed string. // Note: StringBuilder has a better performance than String by 30-40%. StringBuilder resultStringBuilder = new StringBuilder(str.Length); // Analyze the replacement: replace or remove. bool isReplacementNullOrEmpty = string.IsNullOrEmpty(@newValue); // Replace all values. const int valueNotFound = -1; int foundAt; int startSearchFromIndex = 0; while ((foundAt = str.IndexOf(oldValue, startSearchFromIndex, comparisonType)) != valueNotFound) { // Append all characters until the found replacement. int @charsUntilReplacment = foundAt - startSearchFromIndex; bool isNothingToAppend = @charsUntilReplacment == 0; if (!isNothingToAppend) { resultStringBuilder.Append(str, startSearchFromIndex, @charsUntilReplacment); } // Process the replacement. if (!isReplacementNullOrEmpty) { resultStringBuilder.Append(@newValue); } // Prepare start index for the next search. // This needed to prevent infinite loop, otherwise method always start search // from the start of the string. For example: if an oldValue == "EXAMPLE", newValue == "example" // and comparisonType == "any ignore case" will conquer to replacing: // "EXAMPLE" to "example" to "example" to "example" … infinite loop. startSearchFromIndex = foundAt + oldValue.Length; if (startSearchFromIndex == str.Length) { // It is end of the input string: no more space for the next search. // The input string ends with a value that has already been replaced. // Therefore, the string builder with the result is complete and no further action is required. return resultStringBuilder.ToString(); } } // Append the last part to the result. int @charsUntilStringEnd = str.Length - startSearchFromIndex; resultStringBuilder.Append(str, startSearchFromIndex, @charsUntilStringEnd); return resultStringBuilder.ToString(); } public static string EndWithDirSeparator(this string source) { if (source.IsNullOrEmpty()) { return source; } return source.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; } public static string PrefixWithDirSeparator(this string source) { if (source.IsNullOrWhiteSpace()) { return source; } if (source[0] == Path.DirectorySeparatorChar) { return source; } return Path.DirectorySeparatorChar + source; } public static bool ContainsInvariantCulture(this string source, string value, CompareOptions compareOptions) { return CultureInfo.InvariantCulture.CompareInfo.IndexOf(source, value, compareOptions) >= 0; } public static bool ContainsCurrentCulture(this string source, string value, CompareOptions compareOptions) { return CultureInfo.CurrentCulture.CompareInfo.IndexOf(source, value, compareOptions) >= 0; } public static int GetLevenshteinDistanceIgnoreCase(this string source, string value) { return GetLevenshteinDistance(source, value, charCaseInsensitiveComparer); } public static int GetLevenshteinDistance(this string source, string value) { return GetLevenshteinDistance(source, value, EqualityComparer.Default); } //From https://github.com/DanHarltey/Fastenshtein /// /// Compares the two values to find the minimum Levenshtein distance. /// Thread safe. /// /// Difference. 0 complete match. public static int GetLevenshteinDistance(this string value1, string value2, IEqualityComparer comparer) { if (value2.Length == 0) { return value1.Length; } int[] costs = new int[value2.Length]; // Add indexing for insertion to first row for (int i = 0; i < costs.Length;) { costs[i] = ++i; } for (int i = 0; i < value1.Length; i++) { // cost of the first index int cost = i; int previousCost = i; // cache value for inner loop to avoid index lookup and bonds checking, profiled this is quicker char value1Char = value1[i]; for (int j = 0; j < value2.Length; j++) { int currentCost = cost; cost = costs[j]; if (!comparer.Equals(value1Char, value2[j])) { if (previousCost < currentCost) { currentCost = previousCost; } if (cost < currentCost) { currentCost = cost; } ++currentCost; } costs[j] = currentCost; previousCost = currentCost; } } return costs[costs.Length - 1]; } //Based on https://gist.github.com/ronnieoverby/2aa19724199df4ec8af6 public static double GetJaroWinklerSimilarityIgnoreCase(this string str, string str2, double winklerWeightThreshold = defaultWinklerWeightThreshold) { return GetJaroWinklerSimilarity(str, str2, charCaseInsensitiveComparer, winklerWeightThreshold); } public static double GetJaroWinklerSimilarity(this string str, string str2, double winklerWeightThreshold = defaultWinklerWeightThreshold) { return GetJaroWinklerSimilarity(str, str2, EqualityComparer.Default, winklerWeightThreshold); } /// /// Returns the Jaro-Winkler similarity between the specified /// strings. The distance is symmetric and will fall in the /// range 0 (no match) to 1 (perfect match). /// /// First String /// Second String /// Comparer used to determine character equality. /// The weight threshold is used to determine whether the similarity score is high enough to consider two strings as a match. Winkler's paper used a default value of 0.7. /// Similarity between the specified strings. public static double GetJaroWinklerSimilarity(this string str, string str2, IEqualityComparer comparer, double winklerWeightThreshold = defaultWinklerWeightThreshold) { var lLen1 = str.Length; var lLen2 = str2.Length; if (lLen1 == 0) { return lLen2 == 0 ? 1.0 : 0.0; } var lSearchRange = Math.Max(0, Math.Max(lLen1, lLen2) / 2 - 1); var lMatched1 = new bool[lLen1]; var lMatched2 = new bool[lLen2]; var lNumCommon = 0; for (var i = 0; i < lLen1; ++i) { var lStart = Math.Max(0, i - lSearchRange); var lEnd = Math.Min(i + lSearchRange + 1, lLen2); for (var j = lStart; j < lEnd; ++j) { if (lMatched2[j]) { continue; } if (!comparer.Equals(str[i], str2[j])) { continue; } lMatched1[i] = true; lMatched2[j] = true; ++lNumCommon; break; } } if (lNumCommon == 0) { return 0.0; } var lNumHalfTransposed = 0; var k = 0; for (var i = 0; i < lLen1; ++i) { if (!lMatched1[i]) { continue; } while (!lMatched2[k]) { ++k; } if (!comparer.Equals(str[i], str2[k])) { ++lNumHalfTransposed; } ++k; } var lNumTransposed = lNumHalfTransposed / 2; double lNumCommonD = lNumCommon; var lWeight = (lNumCommonD / lLen1 + lNumCommonD / lLen2 + (lNumCommon - lNumTransposed) / lNumCommonD) / 3.0; if (lWeight <= winklerWeightThreshold) { return lWeight; } var lMax = Math.Min(winklerNumChars, Math.Min(str.Length, str2.Length)); var lPos = 0; while (lPos < lMax && comparer.Equals(str[lPos], str2[lPos])) { ++lPos; } if (lPos == 0) { return lWeight; } return lWeight + 0.1 * lPos * (1.0 - lWeight); } } } ================================================ FILE: source/Playnite/Common/Extensions/WindowExtensions.cs ================================================ using Playnite.Common; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; namespace System.Windows { public static class WindowExtensions { public static ComputerScreen GetScreen(this Window window) { return Screen.FromPoint(new System.Drawing.Point((int)window.Left, (int)window.Top)).ToComputerScreen(); } } } ================================================ FILE: source/Playnite/Common/FileSystem.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.Security.Cryptography; using Playnite.SDK; using System.Diagnostics; using Playnite.Native; using System.Runtime.InteropServices; namespace Playnite.Common { public enum FileSystemItem { File, Directory } public static partial class FileSystem { private static ILogger logger = LogManager.GetLogger(); public static void CreateDirectory(string path) { CreateDirectory(path, false); } public static void CreateDirectory(string path, bool clean) { var directory = Paths.FixPathLength(path); if (string.IsNullOrEmpty(directory)) { return; } if (Directory.Exists(directory)) { if (clean) { DeleteDirectory(directory, true); } else { return; } } Directory.CreateDirectory(directory); } public static void PrepareSaveFile(string path) { path = Paths.FixPathLength(path); CreateDirectory(Path.GetDirectoryName(path)); if (File.Exists(path)) { File.Delete(path); } } public static bool IsDirectoryEmpty(string path) { path = Paths.FixPathLength(path); if (Directory.Exists(path)) { return !Directory.EnumerateFileSystemEntries(path).Any(); } else { return true; } } public static void DeleteFile(string path) { path = Paths.FixPathLength(path); if (File.Exists(path)) { File.Delete(path); } } public static void CreateFile(string path) { path = Paths.FixPathLength(path); FileSystem.PrepareSaveFile(path); File.Create(path).Dispose(); } public static void CopyFile(string sourcePath, string targetPath, bool overwrite = true) { sourcePath = Paths.FixPathLength(sourcePath); targetPath = Paths.FixPathLength(targetPath); logger.Debug($"Copying file {sourcePath} to {targetPath}"); PrepareSaveFile(targetPath); File.Copy(sourcePath, targetPath, overwrite); } public static void DeleteDirectory(string path) { path = Paths.FixPathLength(path, true); // we need to force prefix because otherwise recursive delete will fail if some nested path is too long if (Directory.Exists(path)) { Directory.Delete(path, true); } } public static void DeleteDirectory(string path, bool includeReadonly) { path = Paths.FixPathLength(path); if (!Directory.Exists(path)) { return; } if (includeReadonly) { foreach (var s in Directory.GetDirectories(path)) { DeleteDirectory(s, true); } foreach (var f in Directory.GetFiles(path)) { var file = Paths.FixPathLength(f); var attr = File.GetAttributes(file); if ((attr & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) { File.SetAttributes(file, attr ^ FileAttributes.ReadOnly); } File.Delete(file); } var dirAttr = File.GetAttributes(path); if ((dirAttr & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) { File.SetAttributes(path, dirAttr ^ FileAttributes.ReadOnly); } Directory.Delete(path, false); } else { DeleteDirectory(path); } } public static bool CanWriteToFolder(string folder) { folder = Paths.FixPathLength(folder); try { if (!Directory.Exists(folder)) { Directory.CreateDirectory(folder); } using (var stream = File.Create(Path.Combine(folder, Path.GetRandomFileName()), 1, FileOptions.DeleteOnClose)) { } return true; } catch { return false; } } public static string ReadFileAsStringSafe(string path, int retryAttempts = 5) { path = Paths.FixPathLength(path); IOException ioException = null; for (int i = 0; i < retryAttempts; i++) { try { return File.ReadAllText(path); } catch (IOException exc) { logger.Debug($"Can't read from file, trying again. {path}"); ioException = exc; Task.Delay(500).Wait(); } } throw new IOException($"Failed to read {path}", ioException); } public static byte[] ReadFileAsBytesSafe(string path, int retryAttempts = 5) { path = Paths.FixPathLength(path); IOException ioException = null; for (int i = 0; i < retryAttempts; i++) { try { return File.ReadAllBytes(path); } catch (IOException exc) { logger.Debug($"Can't read from file, trying again. {path}"); ioException = exc; Task.Delay(500).Wait(); } } throw new IOException($"Failed to read {path}", ioException); } public static Stream CreateWriteFileStreamSafe(string path, int retryAttempts = 5) { path = Paths.FixPathLength(path); IOException ioException = null; for (int i = 0; i < retryAttempts; i++) { try { return new FileStream(path, FileMode.Create, FileAccess.ReadWrite); } catch (IOException exc) { logger.Debug($"Can't open write file stream, trying again. {path}"); ioException = exc; Task.Delay(500).Wait(); } } throw new IOException($"Failed to read {path}", ioException); } public static Stream OpenReadFileStreamSafe(string path, int retryAttempts = 5) { path = Paths.FixPathLength(path); IOException ioException = null; for (int i = 0; i < retryAttempts; i++) { try { return new FileStream(path, FileMode.Open, FileAccess.Read); } catch (IOException exc) { logger.Debug($"Can't open read file stream, trying again. {path}"); ioException = exc; Task.Delay(500).Wait(); } } throw new IOException($"Failed to read {path}", ioException); } public static void WriteStringToFile(string path, string content) { path = Paths.FixPathLength(path); PrepareSaveFile(path); File.WriteAllText(path, content); } public static string ReadStringFromFile(string path) { path = Paths.FixPathLength(path); return File.ReadAllText(path); } public static void WriteStringToFileSafe(string path, string content, int retryAttempts = 5) { path = Paths.FixPathLength(path); IOException ioException = null; for (int i = 0; i < retryAttempts; i++) { try { PrepareSaveFile(path); File.WriteAllText(path, content); return; } catch (IOException exc) { logger.Debug($"Can't write to a file, trying again. {path}"); ioException = exc; Task.Delay(500).Wait(); } } throw new IOException($"Failed to write to {path}", ioException); } public static void DeleteFileSafe(string path, int retryAttempts = 5) { if (!File.Exists(path)) { return; } IOException ioException = null; for (int i = 0; i < retryAttempts; i++) { try { File.Delete(path); return; } catch (IOException exc) { logger.Debug($"Can't detele file, trying again. {path}"); ioException = exc; Task.Delay(500).Wait(); } catch (UnauthorizedAccessException exc) { logger.Error(exc, $"Can't detele file, UnauthorizedAccessException. {path}"); return; } } throw new IOException($"Failed to delete {path}", ioException); } public static long GetFreeSpace(string drivePath) { var root = Path.GetPathRoot(drivePath); var drive = DriveInfo.GetDrives().FirstOrDefault(a => a.RootDirectory.FullName.Equals(root, StringComparison.OrdinalIgnoreCase)); ; if (drive != null) { return drive.AvailableFreeSpace; } else { return 0; } } public static long GetFileSize(string path) { path = Paths.FixPathLength(path); return GetFileSize(new FileInfo(path)); } public static long GetFileSize(FileInfo fi) { return fi.Length; } public static long GetDirectorySize(string path, bool getSizeOnDisk) { return GetDirectorySize(new DirectoryInfo(Paths.FixPathLength(path)), getSizeOnDisk); } private static long GetDirectorySize(DirectoryInfo dirInfo, bool getSizeOnDisk) { long size = 0; try { foreach (FileInfo fileInfo in dirInfo.GetFiles()) { size += getSizeOnDisk ? GetFileSizeOnDisk(fileInfo) : GetFileSize(fileInfo); } } catch (DirectoryNotFoundException) { // Directory not being found here means that directory is a symlink // with an invalid target path. // TODO Rework with proper symlinks handling with FileSystemInfo.ResolveLinkTarget // method after Net runtime upgrade return size; } foreach (DirectoryInfo subdirInfo in dirInfo.GetDirectories()) { if (!IsDirectorySubdirSafeToRecurse(subdirInfo)) { continue; } size += GetDirectorySize(subdirInfo.FullName, getSizeOnDisk); } return size; } public static long GetFileSizeOnDisk(string path) { return GetFileSizeOnDisk(new FileInfo(Paths.FixPathLength(path))); } public static long GetFileSizeOnDisk(FileInfo fileInfo) { // Method will fail if file is a symlink that has a target // that does not exist. To avoid, we can check its lenght before continuing if (fileInfo.Length == 0) { return 0; } // Method will fail when checking a file that's not valid on Windows, // for example files used by Proton containing a colon (:). // 'Directory' will be null when encountering such a file. if (fileInfo.Directory is null) { return 0; } // From https://stackoverflow.com/a/3751135 int result = Kernel32.GetDiskFreeSpaceW(fileInfo.Directory.Root.FullName, out uint sectorsPerCluster, out uint bytesPerSector, out _, out _); if (result == 0) { throw new System.ComponentModel.Win32Exception(); } uint clusterSize = sectorsPerCluster * bytesPerSector; uint losize = Kernel32.GetCompressedFileSizeW(Paths.FixPathLength(fileInfo.FullName), out uint hosize); int error = Marshal.GetLastWin32Error(); if (losize == 0xFFFFFFFF && error != 0) { throw new System.ComponentModel.Win32Exception(error); } var size = (long)hosize << 32 | losize; return ((size + clusterSize - 1) / clusterSize) * clusterSize; } private static bool IsDirectorySubdirSafeToRecurse(DirectoryInfo childDirectory) { // Whitespace characters can cause confusion in methods, causing them to process // the parent directory instead and causing an infinite loop if (childDirectory.Name.IsNullOrWhiteSpace()) { return false; } return true; } public static void CopyDirectory(string sourceDirName, string destDirName, bool copySubDirs = true, bool overwrite = true) { sourceDirName = Paths.FixPathLength(sourceDirName); destDirName = Paths.FixPathLength(destDirName); var dir = new DirectoryInfo(sourceDirName); if (!dir.Exists) { throw new DirectoryNotFoundException( "Source directory does not exist or could not be found: " + sourceDirName); } var dirs = dir.GetDirectories(); if (!Directory.Exists(destDirName)) { Directory.CreateDirectory(destDirName); } var files = dir.GetFiles(); foreach (FileInfo file in files) { string temppath = Path.Combine(destDirName, file.Name); file.CopyTo(temppath, overwrite); } if (copySubDirs) { foreach (DirectoryInfo subdir in dirs) { string temppath = Path.Combine(destDirName, subdir.Name); CopyDirectory(subdir.FullName, temppath, copySubDirs); } } } public static bool FileExistsOnAnyDrive(string filePath, out string existringPath) { return PathExistsOnAnyDrive(filePath, path => File.Exists(path), out existringPath); } public static bool DirectoryExistsOnAnyDrive(string directoryPath, out string existringPath) { return PathExistsOnAnyDrive(directoryPath, path => Directory.Exists(path), out existringPath); } private static bool PathExistsOnAnyDrive(string originalPath, Predicate predicate, out string existringPath) { originalPath = Paths.FixPathLength(originalPath); existringPath = null; try { if (predicate(originalPath)) { existringPath = originalPath; return true; } if (!Paths.IsFullPath(originalPath)) { return false; } var rootPath = Path.GetPathRoot(originalPath); var availableDrives = DriveInfo.GetDrives().Where(d => d.IsReady); foreach (var drive in availableDrives) { var pathWithoutDrive = originalPath.Substring(drive.Name.Length); var newPath = Path.Combine(drive.Name, pathWithoutDrive); if (predicate(newPath)) { existringPath = newPath; return true; } } } catch (Exception ex) when (!Debugger.IsAttached) { logger.Error(ex, $"Error checking if path exists on different drive \"{originalPath}\""); } return false; } public static bool DirectoryExists(string path) { return Directory.Exists(Paths.FixPathLength(path)); } public static bool FileExists(string path) { return File.Exists(Paths.FixPathLength(path)); } public static DateTime DirectoryGetLastWriteTime(string path) { return Directory.GetLastWriteTime(Paths.FixPathLength(path)); } public static DateTime FileGetLastWriteTime(string path) { return File.GetLastWriteTime(Paths.FixPathLength(path)); } public static void ReplaceStringInFile(string path, string oldValue, string newValue, Encoding encoding = null) { encoding = encoding ?? Encoding.UTF8; var fileContent = File.ReadAllText(path, encoding); File.WriteAllText(path, fileContent.Replace(oldValue, newValue), encoding); } } } ================================================ FILE: source/Playnite/Common/FileSystem_Checksum.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.Security.Cryptography; using Playnite.SDK; using System.Diagnostics; namespace Playnite.Common { public static partial class FileSystem { public static string GetCRC32(Stream stream) { uint crc = 0; var buffer = new byte[4096]; int bytesRead; while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) { crc = Force.Crc32.Crc32Algorithm.Append(crc, buffer, 0, bytesRead); } return string.Format("{0:X8}", crc); } public static string GetCRC32(string filePath) { using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { return GetCRC32(stream); } } public static string GetMD5(Stream stream) { using (var md5 = MD5.Create()) { return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", ""); } } public static string GetMD5(string filePath) { using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { return GetMD5(stream); } } public static bool AreFileContentsEqual(string path1, string path2) { var info1 = new FileInfo(path1); var info2 = new FileInfo(path2); if (info1.Length != info2.Length) { return false; } else { if (GetMD5(path1) == GetMD5(path2)) { return true; } else { return false; } } } } } ================================================ FILE: source/Playnite/Common/GdiFile.cs ================================================ using Playnite.Common; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Playnite { // gdi media descriptor file used mostly in Dreamcast dumps public class GdiFile { public class GdiEntry { public string Path { get; set; } public GdiEntry(string path) { Path = path; } public override string ToString() { return Path ?? base.ToString(); } } public static List GetEntries(string filePath) { var entries = new List(); filePath = Paths.FixPathLength(filePath); // Apparently there are some scuffed "big" gdi files, no idea what they are, // but to be sure lets ignore files larged than 10kB if (new FileInfo(filePath).Length > 10240) { return entries; } foreach (var line in File.ReadAllLines(filePath).Skip(1)) { var match = Regex.Match(line, @"^\d+\s+\d+\s+\d+\s+\d+\s+(.+)\s+\d+$"); if (!match.Success) { continue; } entries.Add(new GdiEntry(match.Groups[1].Value.Trim('"'))); } return entries; } } } ================================================ FILE: source/Playnite/Common/GlobalRandom.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class GlobalRandom { private static readonly Random generator = new Random(); public static int Next() { return generator.Next(); } public static int Next(int minValue, int maxValue) { return generator.Next(minValue, maxValue); } public static int Next(int maxValue) { return generator.Next(maxValue); } public static void NextBytes(byte[] buffer) { generator.NextBytes(buffer); } public static double NextDouble() { return generator.NextDouble(); } } } ================================================ FILE: source/Playnite/Common/Images.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Media.Imaging; using System.Windows.Controls; using System.Windows.Media; using System.IO; using Playnite.SDK; using Playnite.Common.Media.Icons; using System.Drawing.Imaging; namespace Playnite.Common { public class ImageProperties { public int Height { get; set; } public int Width { get; set; } } public class Images { private static readonly ILogger logger = LogManager.GetLogger(); public static Image GetImageFromResource(string path, string assemblyName, BitmapScalingMode scaling = BitmapScalingMode.HighQuality, double height = 16, double width = 16) { var image = new Image() { Source = new BitmapImage(new Uri($"pack://application:,,,/{assemblyName};component/" + path, UriKind.Absolute)), Height = height, Width = width }; RenderOptions.SetBitmapScalingMode(image, scaling); return image; } public static Image GetImageFromFile(string path, BitmapScalingMode scaling = BitmapScalingMode.HighQuality, double height = 16, double width = 16) { var image = new Image() { Source = System.Drawing.Imaging.BitmapExtensions.BitmapFromFile(path), Height = height, Width = width }; RenderOptions.SetBitmapScalingMode(image, scaling); return image; } public static Image GetEmptyImage(double height = 16, double width = 16) { return new Image() { Height = height, Width = width }; } public static ImageProperties GetImageProperties(string imagePath) { try { using (var stream = new FileStream(imagePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { return GetImageProperties(stream); } } catch (Exception e) { logger.Error(e, $"Failed to load image properties from file {imagePath}"); return new ImageProperties(); } } public static ImageProperties GetImageProperties(Stream imageStream) { try { var decoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default); if (decoder.Frames.HasItems()) { return new ImageProperties { Height = decoder.Frames.Max(a => a.PixelHeight), Width = decoder.Frames.Max(a => a.PixelWidth), }; } else { logger.Warn("Images stream has no frames."); return new ImageProperties(); } } catch (Exception e) { logger.Error(e, "Failed to load image properties from stream."); return new ImageProperties(); } } /// /// Converts file to an image file. /// /// /// Resulting file name without extension. /// File path to converted image if conversion was successful or original path if file is alreadny an image. public static string ConvertToCompatibleFormat(string imagePath, string outFileRoot) { if (imagePath.IsNullOrEmpty() || !File.Exists(imagePath)) { return null; } FileSystem.CreateDirectory(Path.GetDirectoryName(outFileRoot)); if (imagePath.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { var icoPath = outFileRoot + ".ico"; if (IconExtractor.ExtractMainIconFromFile(imagePath, icoPath)) { return icoPath; } else { return null; } } else if (imagePath.EndsWith(".tga", StringComparison.OrdinalIgnoreCase)) { var pngPath = outFileRoot + ".png"; try { File.WriteAllBytes(pngPath, BitmapExtensions.TgaToBitmap(imagePath).ToPngArray()); return pngPath; } catch (Exception e) { logger.Error(e, $"Failed to covert {imagePath} to png."); return null; } } return imagePath; } } } ================================================ FILE: source/Playnite/Common/IniParser.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Playnite.Common { public class IniSection { public string Name { get; } public List Items { get; } = new List(); public string this[string itemName] { get { return Items.FirstOrDefault(a => a.Name == itemName)?.Value; } set { new NotImplementedException(); } } public IniSection(string name) { if (string.IsNullOrEmpty(name)) new ArgumentNullException(nameof(name)); Name = name; } public override string ToString() { return Name; } } public class IniItem { public string Name { get; } public string Value { get; } public IniItem(string name, string value) { if (string.IsNullOrEmpty(name)) new ArgumentNullException(nameof(name)); Name = name; Value = value; } public override string ToString() { return Name; } } public class IniData { public List Sections { get; } = new List(); public IniSection this[string sectionName] { get { return Sections.FirstOrDefault(a => a.Name == sectionName); } set { new NotImplementedException(); } } } public class IniParser { public static IniData Parse(string[] iniString) { if (iniString?.Any() != true) { throw new ArgumentNullException(); } var data = new IniData(); IniSection curSection = null; foreach (var line in iniString) { if (string.IsNullOrWhiteSpace(line)) { continue; } // Comment if (line.TrimStart().StartsWith(";")) { continue; } // Section var sectionMatch = Regex.Match(line.Trim(), @"^\[(.+)\]$"); if (sectionMatch.Success) { curSection = new IniSection(sectionMatch.Groups[1].Value); data.Sections.Add(curSection); continue; } // Section item var valueMatch = Regex.Match(line.Trim(), @"^(.+)=(.*)$"); if (valueMatch.Success) { if (curSection != null) { curSection.Items.Add(new IniItem(valueMatch.Groups[1].Value, valueMatch.Groups[2].Value)); } } } return data; } } } ================================================ FILE: source/Playnite/Common/ItemsSource.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class ItemsSource { public class EnumItem { public string Name { get; set; } public Enum Value { get; set; } public EnumItem(string name, Enum value) { Name = name; Value = value; } public override string ToString() { return Name; } } public static IEnumerable GetEnumSources(Type enumType) { foreach (Enum type in Enum.GetValues(enumType)) { yield return new EnumItem(type.GetDescription(), type); } } } } ================================================ FILE: source/Playnite/Common/M3U.cs ================================================ using Playnite.Common; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class M3U { public class M3UEntry { public Dictionary Extensions { get; set; } = new Dictionary(); public string Path { get; set; } public override string ToString() { return Path ?? base.ToString(); } } public static List GetEntries(string filePath) { var entries = new List(); var currentEntry = new M3UEntry(); foreach (var line in File.ReadAllLines(Paths.FixPathLength(filePath))) { if (line.IsNullOrWhiteSpace() || line == "#EXTM3U") { continue; } if (line.StartsWith("#")) { var sep = line.IndexOf(':'); currentEntry.Extensions.AddOrUpdate(line.Substring(0, sep), line.Substring(sep + 1)); } else { currentEntry.Path = line; entries.Add(currentEntry); currentEntry = new M3UEntry(); } } return entries; } } } ================================================ FILE: source/Playnite/Common/MarkupConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class MarkupConverter : SDK.Data.IMarkupConverter { public string MarkdownToHtml(string markdown) { return Markdig.Markdown.ToHtml(markdown); } } } ================================================ FILE: source/Playnite/Common/Media/Icons/IconExtractor.cs ================================================ /* * IconExtractor/IconUtil for .NET * Copyright (C) 2014 Tsuda Kageyu. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using Playnite.SDK; using Playnite.Native; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace Playnite.Common.Media.Icons { public class IconExtractor { private static ILogger logger = LogManager.GetLogger(); public static bool ExtractMainIconFromFile(string exePath, string outPath) { try { var extractor = new IconExtractor(exePath); if (extractor.Count == 0) { return false; } var ico = extractor.GetIcon(0); try { FileSystem.PrepareSaveFile(outPath); using (var fs = File.OpenWrite(outPath)) { ico.Save(fs); } return true; } finally { ico.Dispose(); } } catch (Exception e)// when (false) { logger.Error(e, $"Failed to extract icon from {exePath}."); return false; } } public static bool ExtractMainIconFromFile(string exePath, Stream outStream) { try { var extractor = new IconExtractor(exePath); if (extractor.Count == 0) { return false; } var ico = extractor.GetIcon(0); try { ico.Save(outStream); return true; } finally { ico.Dispose(); } } catch (Exception e)// when (false) { logger.Error(e, $"Failed to extract icon from {exePath}."); return false; } } public static Icon ExtractMainIconFromFile(string exePath) { try { var extractor = new IconExtractor(exePath); if (extractor.Count == 0) { return null; } return extractor.GetIcon(0); } catch (Exception e)// when (false) { logger.Error(e, $"Failed to extract icon from {exePath}."); return null; } } //////////////////////////////////////////////////////////////////////// // Constants // Flags for LoadLibraryEx(). private const uint LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020; // Resource types for EnumResourceNames(). private readonly static IntPtr RT_ICON = (IntPtr)3; private readonly static IntPtr RT_GROUP_ICON = (IntPtr)14; private const int MAX_PATH = 260; //////////////////////////////////////////////////////////////////////// // Fields private byte[][] iconData = null; // Binary data of each icon. //////////////////////////////////////////////////////////////////////// // Public properties /// /// Gets the full path of the associated file. /// public string FileName { get; private set; } /// /// Gets the count of the icons in the associated file. /// public int Count { get { return iconData.Length; } } /// /// Initializes a new instance of the IconExtractor class from the specified file name. /// /// The file to extract icons from. public IconExtractor(string fileName) { Initialize(fileName); } /// /// Extracts an icon from the file. /// /// Zero based index of the icon to be extracted. /// A System.Drawing.Icon object. /// Always returns new copy of the Icon. It should be disposed by the user. public Icon GetIcon(int index) { if (index < 0 || Count <= index) throw new ArgumentOutOfRangeException("index"); // Create an Icon based on a .ico file in memory. using (var ms = new MemoryStream(iconData[index])) { return new Icon(ms); } } /// /// Extracts all the icons from the file. /// /// An array of System.Drawing.Icon objects. /// Always returns new copies of the Icons. They should be disposed by the user. public Icon[] GetAllIcons() { var icons = new List(); for (int i = 0; i < Count; ++i) icons.Add(GetIcon(i)); return icons.ToArray(); } private void Initialize(string fileName) { if (fileName == null) throw new ArgumentNullException("fileName"); IntPtr hModule = IntPtr.Zero; try { hModule = Kernel32.LoadLibraryEx(fileName, IntPtr.Zero, LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (hModule == IntPtr.Zero) throw new Win32Exception(); FileName = GetFileName(hModule); // Enumerate the icon resource and build .ico files in memory. var tmpData = new List(); ENUMRESNAMEPROC callback = (h, t, name, l) => { // Refer the following URL for the data structures used here: // http://msdn.microsoft.com/en-us/library/ms997538.aspx // RT_GROUP_ICON resource consists of a GRPICONDIR and GRPICONDIRENTRY's. var dir = GetDataFromResource(hModule, RT_GROUP_ICON, name); // Calculate the size of an entire .icon file. int count = BitConverter.ToUInt16(dir, 4); // GRPICONDIR.idCount int len = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count for (int i = 0; i < count; ++i) len += BitConverter.ToInt32(dir, 6 + 14 * i + 8); // GRPICONDIRENTRY.dwBytesInRes using (var dst = new BinaryWriter(new MemoryStream(len))) { // Copy GRPICONDIR to ICONDIR. dst.Write(dir, 0, 6); int picOffset = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count for (int i = 0; i < count; ++i) { // Load the picture. ushort id = BitConverter.ToUInt16(dir, 6 + 14 * i + 12); // GRPICONDIRENTRY.nID var pic = GetDataFromResource(hModule, RT_ICON, (IntPtr)id); // Copy GRPICONDIRENTRY to ICONDIRENTRY. dst.Seek(6 + 16 * i, SeekOrigin.Begin); dst.Write(dir, 6 + 14 * i, 8); // First 8bytes are identical. dst.Write(pic.Length); // ICONDIRENTRY.dwBytesInRes dst.Write(picOffset); // ICONDIRENTRY.dwImageOffset // Copy a picture. dst.Seek(picOffset, SeekOrigin.Begin); dst.Write(pic, 0, pic.Length); picOffset += pic.Length; } tmpData.Add(((MemoryStream)dst.BaseStream).ToArray()); } return true; }; Kernel32.EnumResourceNames(hModule, RT_GROUP_ICON, callback, IntPtr.Zero); iconData = tmpData.ToArray(); } finally { if (hModule != IntPtr.Zero) Kernel32.FreeLibrary(hModule); } } private byte[] GetDataFromResource(IntPtr hModule, IntPtr type, IntPtr name) { // Load the binary data from the specified resource. IntPtr hResInfo = Kernel32.FindResource(hModule, name, type); if (hResInfo == IntPtr.Zero) throw new Win32Exception(); IntPtr hResData = Kernel32.LoadResource(hModule, hResInfo); if (hResData == IntPtr.Zero) throw new Win32Exception(); IntPtr pResData = Kernel32.LockResource(hResData); if (pResData == IntPtr.Zero) throw new Win32Exception(); uint size = Kernel32.SizeofResource(hModule, hResInfo); if (size == 0) throw new Win32Exception(); byte[] buf = new byte[size]; Marshal.Copy(pResData, buf, 0, buf.Length); return buf; } private string GetFileName(IntPtr hModule) { // Alternative to GetModuleFileName() for the module loaded with // LOAD_LIBRARY_AS_DATAFILE option. // Get the file name in the format like: // "\\Device\\HarddiskVolume2\\Windows\\System32\\shell32.dll" string fileName; { var buf = new StringBuilder(MAX_PATH); int len = Psapi.GetMappedFileName( Kernel32.GetCurrentProcess(), hModule, buf, buf.Capacity); if (len == 0) throw new Win32Exception(); fileName = buf.ToString(); } // Convert the device name to drive name like: // "C:\\Windows\\System32\\shell32.dll" for (char c = 'A'; c <= 'Z'; ++c) { var drive = c + ":"; var buf = new StringBuilder(MAX_PATH); int len = Kernel32.QueryDosDevice(drive, buf, buf.Capacity); if (len == 0) continue; var devPath = buf.ToString(); if (fileName.StartsWith(devPath)) return (drive + fileName.Substring(devPath.Length)); } return fileName; } } } ================================================ FILE: source/Playnite/Common/Media/Icons/IconUtil.cs ================================================ /* * IconExtractor/IconUtil for .NET * Copyright (C) 2014 Tsuda Kageyu. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Reflection; using System.Reflection.Emit; namespace Playnite.Common.Media.Icons { public static class IconUtil { private delegate byte[] GetIconDataDelegate(Icon icon); static GetIconDataDelegate getIconData; static IconUtil() { // Create a dynamic method to access Icon.iconData private field. var dm = new DynamicMethod( "GetIconData", typeof(byte[]), new Type[] { typeof(Icon) }, typeof(Icon)); var fi = typeof(Icon).GetField( "iconData", BindingFlags.Instance | BindingFlags.NonPublic); var gen = dm.GetILGenerator(); gen.Emit(OpCodes.Ldarg_0); gen.Emit(OpCodes.Ldfld, fi); gen.Emit(OpCodes.Ret); getIconData = (GetIconDataDelegate)dm.CreateDelegate(typeof(GetIconDataDelegate)); } /// /// Split an Icon consists of multiple icons into an array of Icon each /// consists of single icons. /// /// A System.Drawing.Icon to be split. /// An array of System.Drawing.Icon. public static Icon[] Split(Icon icon) { if (icon == null) throw new ArgumentNullException("icon"); // Get an .ico file in memory, then split it into separate icons. var src = GetIconData(icon); var splitIcons = new List(); { int count = BitConverter.ToUInt16(src, 4); for (int i = 0; i < count; i++) { int length = BitConverter.ToInt32(src, 6 + 16 * i + 8); // ICONDIRENTRY.dwBytesInRes int offset = BitConverter.ToInt32(src, 6 + 16 * i + 12); // ICONDIRENTRY.dwImageOffset using (var dst = new BinaryWriter(new MemoryStream(6 + 16 + length))) { // Copy ICONDIR and set idCount to 1. dst.Write(src, 0, 4); dst.Write((short)1); // Copy ICONDIRENTRY and set dwImageOffset to 22. dst.Write(src, 6 + 16 * i, 12); // ICONDIRENTRY except dwImageOffset dst.Write(22); // ICONDIRENTRY.dwImageOffset // Copy a picture. dst.Write(src, offset, length); // Create an icon from the in-memory file. dst.BaseStream.Seek(0, SeekOrigin.Begin); splitIcons.Add(new Icon(dst.BaseStream)); } } } return splitIcons.ToArray(); } /// /// Converts an Icon to a GDI+ Bitmap preserving the transparent area. /// /// An System.Drawing.Icon to be converted. /// A System.Drawing.Bitmap Object. public static Bitmap ToBitmap(Icon icon) { if (icon == null) throw new ArgumentNullException("icon"); // Quick workaround: Create an .ico file in memory, then load it as a Bitmap. using (var ms = new MemoryStream()) { icon.Save(ms); using (var bmp = (Bitmap)Image.FromStream(ms)) { return new Bitmap(bmp); } } } /// /// Gets the bit depth of an Icon. /// /// An System.Drawing.Icon object. /// Bit depth of the icon. /// /// This method takes into account the PNG header. /// If the icon has multiple variations, this method returns the bit /// depth of the first variation. /// public static int GetBitCount(Icon icon) { if (icon == null) throw new ArgumentNullException("icon"); // Get an .ico file in memory, then read the header. var data = GetIconData(icon); if (data.Length >= 51 && data[22] == 0x89 && data[23] == 0x50 && data[24] == 0x4e && data[25] == 0x47 && data[26] == 0x0d && data[27] == 0x0a && data[28] == 0x1a && data[29] == 0x0a && data[30] == 0x00 && data[31] == 0x00 && data[32] == 0x00 && data[33] == 0x0d && data[34] == 0x49 && data[35] == 0x48 && data[36] == 0x44 && data[37] == 0x52) { // The picture is PNG. Read IHDR chunk. switch (data[47]) { case 0: return data[46]; case 2: return data[46] * 3; case 3: return data[46]; case 4: return data[46] * 2; case 6: return data[46] * 4; default: // NOP break; } } else if (data.Length >= 22) { // The picture is not PNG. Read ICONDIRENTRY structure. return BitConverter.ToUInt16(data, 12); } throw new ArgumentException("The icon is corrupt. Couldn't read the header.", "icon"); } private static byte[] GetIconData(Icon icon) { var data = getIconData(icon); if (data != null) { return data; } else { using (var ms = new MemoryStream()) { icon.Save(ms); return ms.ToArray(); } } } } } ================================================ FILE: source/Playnite/Common/MemoryCache.cs ================================================ using Playnite.SDK; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Media.Imaging; namespace Playnite.Common { public class CacheItem { public object CacheObject { get; } public DateTime LastAccess { get; internal set; } public DateTime CachedTime { get; } public long Size { get; } public Dictionary Metadata { get; } = new Dictionary(); public CacheItem(object item, long size) { CacheObject = item; CachedTime = DateTime.Now; LastAccess = CachedTime; Size = size; } public CacheItem(object item, long size, Dictionary metadata) : this (item, size) { Metadata = metadata; } } public class MemoryCache { private static readonly ILogger logger = LogManager.GetLogger(); private ConcurrentDictionary cache = new ConcurrentDictionary(); private long memorySizeLimit = 0; private long currentSize = 0; public MemoryCache(long memoryLimit) { memorySizeLimit = memoryLimit; } public void Clear() { cache.Clear(); currentSize = 0; GC.Collect(); } private void ReleaseOldestItems() { // ToArray reason: // https://stackoverflow.com/questions/11692389/getting-argument-exception-in-concurrent-dictionary-when-sorting-and-displaying foreach (var item in cache.ToArray().OrderBy(a => a.Value.LastAccess)) { if (currentSize > memorySizeLimit) { TryRemove(item.Key, out var removed); } else { break; } } } public bool TryAdd(string id, object item, long size, Dictionary metadata = null) { if (size >= memorySizeLimit) { logger.Warn($"Cannot add item to memory cache. Size: {size}, cache limit: {memorySizeLimit}"); return false; } Interlocked.Add(ref currentSize, size); if (currentSize > memorySizeLimit) { ReleaseOldestItems(); } if (metadata == null) { return cache.TryAdd(id, new CacheItem(item, size)); } else { return cache.TryAdd(id, new CacheItem(item, size, metadata)); } } public bool TryRemove(string id, out CacheItem item) { if (cache.TryRemove(id, out var cacheItem)) { item = cacheItem; Interlocked.Add(ref currentSize, -cacheItem.Size); return true; } else { item = null; return false; } } public bool TryRemove(string id) { if (cache.TryRemove(id, out var cacheItem)) { Interlocked.Add(ref currentSize, -cacheItem.Size); return true; } else { return false; } } public bool TryGet(string id, out CacheItem item) { if (cache.TryGetValue(id, out var cacheItem)) { cacheItem.LastAccess = DateTime.Now; item = cacheItem; return true; } else { item = null; return false; } } } } ================================================ FILE: source/Playnite/Common/NLogLogProvider.cs ================================================ using NLog.Config; using NLog.Targets; using Playnite.SDK; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class NLogLogger : ILogger { public static bool IsTraceEnabled { get; set; } = false; private NLog.Logger logger; public NLogLogger(string loggerName) { logger = NLog.LogManager.GetLogger(loggerName); } public void Debug(string message) { logger.Debug(message); } public void Debug(Exception exception, string message) { logger.Debug(exception, message); } public void Error(string message) { logger.Error(message); } public void Error(Exception exception, string message) { logger.Error(exception, message); } public void Info(string message) { logger.Info(message); } public void Info(Exception exception, string message) { logger.Info(exception, message); } public void Warn(string message) { logger.Warn(message); } public void Warn(Exception exception, string message) { logger.Warn(exception, message); } public void Trace(string message) { if (IsTraceEnabled) { logger.Trace(message); } } public void Trace(Exception exception, string message) { if (IsTraceEnabled) { logger.Trace(exception, message); } } } public class NLogLogProvider : ILogProvider { public NLogLogProvider() { if (NLog.LogManager.Configuration != null) { return; } var config = new LoggingConfiguration(); config.DefaultCultureInfo = new System.Globalization.CultureInfo("en-US"); #if DEBUG var consoleTarget = new ColoredConsoleTarget() { Layout = @"${level:uppercase=true}|${logger}:${message}${exception}" }; config.AddTarget("console", consoleTarget); var rule1 = new LoggingRule("*", NLog.LogLevel.Trace, consoleTarget); config.LoggingRules.Add(rule1); #endif var loggerDir = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location); var fileTarget = new FileTarget() { FileName = Path.Combine(loggerDir, "nlog.log"), Layout = "${longdate}|${level:uppercase=true}:${message}${exception:format=toString}", KeepFileOpen = false, ArchiveFileName = Path.Combine(loggerDir, "nlog.{#####}.log"), ArchiveAboveSize = 4096000, ArchiveNumbering = ArchiveNumberingMode.Sequence, MaxArchiveFiles = 2, Encoding = Encoding.UTF8 }; config.AddTarget("file", fileTarget); var rule2 = new LoggingRule("*", NLog.LogLevel.Trace, fileTarget); config.LoggingRules.Add(rule2); NLog.LogManager.Configuration = config; } public ILogger GetLogger(string loggerName) { return new NLogLogger(loggerName); } } } ================================================ FILE: source/Playnite/Common/Network.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class Network { public static async Task GetIsConnectedToInternet() { // NetworkListManager is too slow (5 or so seconds) even if connection is available. // InternetGetConnectedState is no longer recommended to be used by MS. // Plus both options are not portable to non-Windows systems. // 1.1.1.1 is Cloudflare's DNS server. using (var ping = new Ping()) { try { return (await ping.SendPingAsync(new System.Net.IPAddress(new byte[] { 1, 1, 1, 1 }), 5_000)).Status == IPStatus.Success; } catch { return false; } } } } } ================================================ FILE: source/Playnite/Common/Paths.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Playnite.Native; namespace Playnite.Common { public class Paths { private const string longPathPrefix = @"\\?\"; private const string longPathUncPrefix = @"\\?\UNC\"; public static readonly char[] DirectorySeparators = new char[] { '\\', '/' }; public static string GetFinalPathName(string path) { var h = Kernel32.CreateFile(path, 0, FileShare.ReadWrite | FileShare.Delete, IntPtr.Zero, FileMode.Open, Fileapi.FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero); if (path.StartsWith(@"\\")) { return path; } if (h == Winuser.INVALID_HANDLE_VALUE) { throw new Win32Exception(); } try { var sb = new StringBuilder(1024); var res = Kernel32.GetFinalPathNameByHandle(h, sb, 1024, 0); if (res == 0) { throw new Win32Exception(); } var targetPath = sb.ToString(); if (targetPath.StartsWith(longPathUncPrefix)) { return targetPath.Replace(longPathUncPrefix, @"\\"); } else { return targetPath.Replace(longPathPrefix, string.Empty); } } finally { Kernel32.CloseHandle(h); } } public static bool IsValidFilePath(string path) { try { if (string.IsNullOrEmpty(path)) { return false; } if (string.IsNullOrEmpty(Path.GetExtension(path))) { return false; } string drive = Path.GetPathRoot(path); if (!string.IsNullOrEmpty(drive) && !Directory.Exists(drive)) { return false; } return true; } catch { // Any of Path methods can throw exception in case that path is some weird string return false; } } public static string FixSeparators(string path) { if (path.IsNullOrWhiteSpace()) { return path; } char prev = default; var sb = new StringBuilder(path.Length); for (int i = 0; i < path.Length; i++) { var current = path[i]; if (current == Path.AltDirectorySeparatorChar) { current = Path.DirectorySeparatorChar; } if (prev != current || current != Path.DirectorySeparatorChar || (current == Path.DirectorySeparatorChar && prev != Path.DirectorySeparatorChar)) { prev = current; sb.Append(current); continue; } } if (path.StartsWith(@"\\")) { sb.Insert(0, @"\"); } return sb.ToString(); } private static string Normalize(string path) { var formatted = path; try { formatted = new Uri(path).LocalPath; } catch { // this shound't happen } return Path.GetFullPath(formatted).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).ToUpperInvariant(); } public static bool AreEqual(string path1, string path2) { if (string.IsNullOrEmpty(path1) && !string.IsNullOrEmpty(path2)) { return false; } if (!string.IsNullOrEmpty(path1) && string.IsNullOrEmpty(path2)) { return false; } // Empty string is not valid path, return false even when both are null if (string.IsNullOrEmpty(path1) && string.IsNullOrEmpty(path2)) { return false; } try { return Normalize(path1) == Normalize(path2); } catch { return false; } } public static bool ContainsInvalidPathChars(string path) { return path.Intersect(Path.GetInvalidPathChars()).Any(); } public static string GetSafePathName(string filename) { var path = string.Join(" ", filename.Split(Path.GetInvalidFileNameChars())); return Regex.Replace(path, @"\s+", " ").Trim(); } public static bool IsFullPath(string path) { if (string.IsNullOrWhiteSpace(path)) { return false; } // Don't use Path.IsPathRooted because it fails on paths starting with one backslash. return Regex.IsMatch(path, @"^([a-zA-Z]:\\|\\\\)"); } public static string GetCommonDirectory(string[] paths) { int k = paths[0].Length; for (int i = 1; i < paths.Length; i++) { k = Math.Min(k, paths[i].Length); for (int j = 0; j < k; j++) { if (paths[i][j] != paths[0][j]) { k = j; break; } } } var common = paths[0].Substring(0, k); if (common.Length == 0) { return string.Empty; } if (common[common.Length -1] == Path.DirectorySeparatorChar) { return common; } else { return common.Substring(0, common.LastIndexOf(Path.DirectorySeparatorChar) + 1); } } public static bool MathcesFilePattern(string filePath, string pattern) { if (filePath.IsNullOrEmpty() || pattern.IsNullOrEmpty()) { return false; } if (pattern.Contains(';')) { return Shlwapi.PathMatchSpecExW(filePath, pattern, MatchPatternFlags.Multiple) == 0; } else { return Shlwapi.PathMatchSpecExW(filePath, pattern, MatchPatternFlags.Normal) == 0; } } public static string FixPathLength(string path, bool forcePrefix = false) { if (path.IsNullOrWhiteSpace()) { return path; } // Relative paths don't support long paths // https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd if (!Paths.IsFullPath(path)) { return path; } // While the MAX_PATH value is 260 characters, a lower value is used because // methods can append "\" and string terminator characters to paths and // make them surpass the limit if ((path.Length >= 258 || forcePrefix) && !path.StartsWith(longPathPrefix)) { if (path.StartsWith(@"\\")) { return longPathUncPrefix + path.Substring(2); } else { return longPathPrefix + path; } } return path; } public static string TrimLongPathPrefix(string path) { if (path.IsNullOrWhiteSpace()) { return path; } if (path.StartsWith(longPathUncPrefix)) { return path.Replace(longPathUncPrefix, @"\\"); } else { return path.Replace(longPathPrefix, string.Empty); } } } } ================================================ FILE: source/Playnite/Common/ProcessMonitor.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Management; using System.Threading; using System.IO; using Playnite.SDK; namespace Playnite.Common { public class MonitorProcess { private readonly Process process; public MonitorProcess(Process process) { this.process = process; } public bool IsProcessRunning() { return !process.HasExited; } } public class MonitorProcessTree { private List relatedIds = new List(); public MonitorProcessTree(int originalId) { relatedIds.Add(originalId); } public bool IsProcessTreeRunning() { if (relatedIds.Count == 0) { return false; } var runningIds = new List(); foreach (var proc in Process.GetProcesses().Where(a => a.SessionId != 0)) { if (proc.TryGetParentId(out var parent)) { if (relatedIds.Contains(parent) && !relatedIds.Contains(proc.Id)) { relatedIds.Add(proc.Id); } } if (relatedIds.Contains(proc.Id)) { runningIds.Add(proc.Id); } } relatedIds = runningIds; return relatedIds.Count > 0; } } public class MonitorProcessNames { private readonly ILogger logger = LogManager.GetLogger(); private readonly List procNames = new List(); private readonly List procNamesNoExt = new List(); public MonitorProcessNames(string directory) { var dir = directory; try { dir = Paths.GetFinalPathName(directory); } catch (Exception e) { logger.Error(e, $"Failed to get target path for a directory {directory}"); } if (FileSystem.DirectoryExists(dir)) { var executables = Directory.GetFiles(dir, "*.exe", SearchOption.AllDirectories); procNames = executables.Select(a => Path.GetFileName(a)).ToList(); procNamesNoExt = executables.Select(a => Path.GetFileNameWithoutExtension(a)).ToList(); } } public bool IsTrackable() { return procNames.Count > 0; } public int IsProcessRunning() { foreach (var process in Process.GetProcesses().Where(a => a.SessionId != 0)) { if (process.TryGetMainModuleFileName(out var procPath)) { if (procNames.Contains(Path.GetFileName(procPath))) { return process.Id; ; } } else if (procNamesNoExt.Contains(process.ProcessName)) { return process.Id; } } return 0; } } public class MonitorDirectory { private readonly ILogger logger = LogManager.GetLogger(); private readonly string dir; public MonitorDirectory(string directory) { dir = directory; try { dir = Paths.GetFinalPathName(directory); } catch (Exception e) { logger.Error(e, $"Failed to get target path for a directory {directory}"); } } public bool IsTrackable() { if (dir.IsNullOrWhiteSpace()) { return false; } return FileSystem.DirectoryExists(dir); } public int IsProcessRunning() { foreach (var process in Process.GetProcesses().Where(a => a.SessionId != 0)) { if (process.TryGetMainModuleFileName(out var procPath) && procPath.IndexOf(dir, StringComparison.OrdinalIgnoreCase) >= 0) { return process.Id; } } return 0; } } } public class MonitorProcessName { public string ProcessName { get; } public MonitorProcessName(string processName) { if (processName.IsNullOrWhiteSpace()) { throw new Exception("Non empty process name must be specified."); } ProcessName = processName; } public int IsProcessRunning() { foreach (var process in Process.GetProcesses().Where(a => a.SessionId != 0)) { using (process) { if (process.ProcessName == ProcessName) { return process.Id; } } } return 0; } } ================================================ FILE: source/Playnite/Common/ProcessStarter.cs ================================================ using Playnite.SDK; using Playnite.Native; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace Playnite.Common { public static class CmdLineTools { public const string TaskKill = "taskkill"; public const string Cmd = "cmd"; public const string IPConfig = "ipconfig"; } public static class ProcessStarter { private static ILogger logger = LogManager.GetLogger(); public static int ShellExecute(string cmdLine) { logger.Debug($"Executing shell command: {cmdLine}"); var startInfo = new STARTUPINFO(); var procInfo = new PROCESS_INFORMATION(); var procAtt = new SECURITY_ATTRIBUTES(); var threadAtt = new SECURITY_ATTRIBUTES(); procAtt.nLength = Marshal.SizeOf(procAtt); threadAtt.nLength = Marshal.SizeOf(threadAtt); try { if (Kernel32.CreateProcess( null, cmdLine, ref procAtt, ref threadAtt, false, 0x0020, IntPtr.Zero, null, ref startInfo, out procInfo)) { return procInfo.dwProcessId; } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } finally { if (procInfo.hProcess != IntPtr.Zero) { Kernel32.CloseHandle(procInfo.hProcess); } if (procInfo.hThread != IntPtr.Zero) { Kernel32.CloseHandle(procInfo.hThread); } } } public static Process StartUrl(string url) { logger.Debug($"Opening URL: {url}"); try { return Process.Start(url); } catch (Exception e) { // There are some crash report with 0x80004005 error when opening standard URL. logger.Error(e, "Failed to open URL."); return Process.Start(CmdLineTools.Cmd, $"/C start {url}"); } } public static Process StartProcess(string path, bool asAdmin = false) { return StartProcess(path, string.Empty, string.Empty, asAdmin); } public static Process StartProcess(string path, string arguments, bool asAdmin = false) { return StartProcess(path, arguments, string.Empty, asAdmin); } public static Process StartProcess(string path, string arguments, string workDir, bool asAdmin = false) { logger.Debug($"Starting process: {path}, {arguments}, {workDir}, {asAdmin}"); if (path.IsNullOrWhiteSpace()) { throw new ArgumentNullException("Cannot start process, executable path is specified."); } var startupPath = path; if (path.Contains("..")) { startupPath = Path.GetFullPath(path); } var info = new ProcessStartInfo(startupPath) { Arguments = arguments, WorkingDirectory = string.IsNullOrEmpty(workDir) ? (new FileInfo(startupPath)).Directory.FullName : workDir }; if (asAdmin) { info.Verb = "runas"; } return Process.Start(info); } public static int StartProcessWait(string path, string arguments, string workDir, bool noWindow = false) { logger.Debug($"Starting process: {path}, {arguments}, {workDir}"); if (path.IsNullOrWhiteSpace()) { throw new ArgumentNullException("Cannot start process, executable path is specified."); } var startupPath = path; if (path.Contains("..")) { startupPath = Path.GetFullPath(path); } var info = new ProcessStartInfo(startupPath) { Arguments = arguments, WorkingDirectory = string.IsNullOrEmpty(workDir) ? (new FileInfo(startupPath)).Directory.FullName : workDir }; if (noWindow) { info.CreateNoWindow = true; info.UseShellExecute = false; } using (var proc = Process.Start(info)) { proc.WaitForExit(); return proc.ExitCode; } } public static int StartProcessWait( string path, string arguments, string workDir, out string stdOutput, out string stdError) { logger.Debug($"Starting process: {path}, {arguments}, {workDir}"); if (path.IsNullOrWhiteSpace()) { throw new ArgumentNullException("Cannot start process, executable path is specified."); } var startupPath = path; if (path.Contains("..")) { startupPath = Path.GetFullPath(path); } var info = new ProcessStartInfo(startupPath) { Arguments = arguments, WorkingDirectory = string.IsNullOrEmpty(workDir) ? (new FileInfo(startupPath)).Directory.FullName : workDir, RedirectStandardError = true, RedirectStandardOutput = true, CreateNoWindow = true, UseShellExecute = false }; var stdout = string.Empty; var stderr = string.Empty; using (var proc = new Process()) { proc.StartInfo = info; proc.OutputDataReceived += (_, e) => { if (e.Data != null) { stdout += e.Data + Environment.NewLine; } }; proc.ErrorDataReceived += (_, e) => { if (e.Data != null) { stderr += e.Data + Environment.NewLine; } }; proc.Start(); proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); proc.WaitForExit(); stdOutput = stdout; stdError = stderr; return proc.ExitCode; } } } } ================================================ FILE: source/Playnite/Common/Programs.cs ================================================ using Microsoft.Win32; using Playnite.SDK; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Security.AccessControl; using System.Security.Principal; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace Playnite.Common { public class Program { public string Path { get; set; } public string Arguments { get; set; } public string Icon { get; set; } public int IconIndex { get; set; } public string WorkDir { get; set; } public string Name { get; set; } public string AppId { get; set; } public override string ToString() { return Name; } } public class UninstallProgram { public string DisplayIcon { get; set; } public string DisplayName { get; set; } public string DisplayVersion { get; set; } public string InstallLocation { get; set; } public string Publisher { get; set; } public string UninstallString { get; set; } public string URLInfoAbout { get; set; } public string RegistryKeyName { get; set; } public string Path { get; set; } public override string ToString() { return DisplayName ?? RegistryKeyName; } } public partial class Programs { private static readonly string[] scanFileExclusionMasks = new string[] { "uninst", "setup", @"unins\d+", "Config", "DXSETUP", @"vc_redist\.x64", @"vc_redist\.x86", @"^UnityCrashHandler32\.exe$", @"^UnityCrashHandler64\.exe$", @"^notification_helper\.exe$", @"^python\.exe$", @"^pythonw\.exe$", @"^zsync\.exe$", @"^zsyncmake\.exe$" }; private static ILogger logger = LogManager.GetLogger(); public static bool IsFileScanExcluded(string path) { return scanFileExclusionMasks.Any(a => Regex.IsMatch(path, a, RegexOptions.IgnoreCase)); } public static void CreateUrlShortcut(string url, string iconPath, string shortcutPath) { FileSystem.PrepareSaveFile(shortcutPath); var content = @"[InternetShortcut] IconIndex=0"; if (!iconPath.IsNullOrEmpty()) { content += Environment.NewLine + $"IconFile={iconPath}"; } content += Environment.NewLine + $"URL={url}"; File.WriteAllText(shortcutPath, content); } private static List GetUninstallProgsFromView(RegistryView view) { var rootString = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"; void SearchRoot(RegistryHive hive, List programs) { using (var root = RegistryKey.OpenBaseKey(hive, view)) { var keyList = root.OpenSubKey(rootString); if (keyList == null) { return; } foreach (var key in keyList.GetSubKeyNames()) { try { using (var prog = root.OpenSubKey(rootString + key)) { if (prog == null) { continue; } var program = new UninstallProgram() { DisplayIcon = prog.GetValue("DisplayIcon")?.ToString(), DisplayVersion = prog.GetValue("DisplayVersion")?.ToString(), DisplayName = prog.GetValue("DisplayName")?.ToString(), InstallLocation = prog.GetValue("InstallLocation")?.ToString(), Publisher = prog.GetValue("Publisher")?.ToString(), UninstallString = prog.GetValue("UninstallString")?.ToString(), URLInfoAbout = prog.GetValue("URLInfoAbout")?.ToString(), Path = prog.GetValue("Path")?.ToString(), RegistryKeyName = key }; programs.Add(program); } } catch (System.Security.SecurityException e) { logger.Warn(e, $"Failed to read registry key {rootString + key}"); } } } } var progs = new List(); SearchRoot(RegistryHive.LocalMachine, progs); SearchRoot(RegistryHive.CurrentUser, progs); return progs; } public static List GetUnistallProgramsList() { var progs = new List(); if (Environment.Is64BitOperatingSystem) { progs.AddRange(GetUninstallProgsFromView(RegistryView.Registry64)); } progs.AddRange(GetUninstallProgsFromView(RegistryView.Registry32)); return progs; } } } ================================================ FILE: source/Playnite/Common/Programs2.cs ================================================ using Microsoft.Win32; using Playnite.SDK; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Security.Principal; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml; using Windows.ApplicationModel; using Windows.Management.Deployment; namespace Playnite.Common { public partial class Programs { public static async Task> GetExecutablesFromFolder(string path, SearchOption searchOption, CancellationToken cancelToken) { return await Task.Run(() => { var execs = new List(); var files = new SafeFileEnumerator(path, "*.*", SearchOption.AllDirectories); foreach (var file in files) { if (cancelToken.IsCancellationRequested == true) { return null; } if (file.Attributes.HasFlag(FileAttributes.Directory)) { continue; } if (IsFileScanExcluded(file.Name)) { continue; } if (file.Extension.IsNullOrEmpty()) { continue; } if (file.Extension.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) == true || file.Extension.EndsWith(".lnk", StringComparison.OrdinalIgnoreCase) == true || file.Extension.EndsWith(".bat", StringComparison.OrdinalIgnoreCase) == true) { execs.Add(GetProgramData(file.FullName)); } } return execs; }); } public static Program GetProgramData(string filePath) { var file = new FileInfo(filePath); if (file.Extension?.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) == true) { var versionInfo = FileVersionInfo.GetVersionInfo(file.FullName); var programName = !string.IsNullOrEmpty(versionInfo.ProductName?.Trim()) ? versionInfo.ProductName : new DirectoryInfo(Path.GetDirectoryName(file.FullName)).Name; return new Program { Path = file.FullName, Icon = file.FullName, WorkDir = Path.GetDirectoryName(file.FullName), Name = programName, AppId = filePath.MD5() }; } else if (file.Extension?.EndsWith(".lnk", StringComparison.OrdinalIgnoreCase) == true) { var data = GetLnkShortcutData(file.FullName); var name = Path.GetFileNameWithoutExtension(file.Name); var program = new Program { Path = data.Path, WorkDir = data.WorkDir, Arguments = data.Arguments, Name = name, AppId = filePath.MD5() }; if (!data.Icon.IsNullOrEmpty()) { var reg = Regex.Match(data.Icon, @"^(.+),(\d+)$"); if (reg.Success) { program.Icon = reg.Groups[1].Value; program.IconIndex = int.Parse(reg.Groups[2].Value); } else { program.Icon = data.Icon; } } else { program.Icon = data.Path; } return program; } else if (file.Extension?.EndsWith(".bat", StringComparison.OrdinalIgnoreCase) == true) { return new Program { Path = file.FullName, Name = Path.GetFileNameWithoutExtension(file.FullName), WorkDir = Path.GetDirectoryName(file.FullName), AppId = filePath.MD5() }; } throw new NotSupportedException("Only exe, bat and lnk files are supported."); } public static void CreateShortcut(string executablePath, string arguments, string iconPath, string shortcutPath) { var shell = new IWshRuntimeLibrary.WshShell(); var link = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(shortcutPath); link.TargetPath = executablePath; link.WorkingDirectory = Path.GetDirectoryName(executablePath); link.Arguments = arguments; link.IconLocation = string.IsNullOrEmpty(iconPath) ? executablePath + ",0" : iconPath; link.Save(); } public static Program GetLnkShortcutData(string lnkPath) { var shell = new IWshRuntimeLibrary.WshShell(); var link = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(lnkPath); return new Program() { Path = link.TargetPath, Icon = link.IconLocation == ",0" ? link.TargetPath : link.IconLocation, Arguments = link.Arguments, WorkDir = link.WorkingDirectory, Name = link.FullName, AppId = lnkPath.MD5() }; } public static async Task> GetShortcutProgramsFromFolder(string path, CancellationTokenSource cancelToken = null) { return await Task.Run(() => { var folderExceptions = new string[] { @"\Accessibility\", @"\Accessories\", @"\Administrative Tools\", @"\Maintenance\", @"\StartUp\", @"\Windows ", @"\Microsoft ", }; var pathExceptions = new string[] { @"\system32\", @"\windows\", }; var shell = new IWshRuntimeLibrary.WshShell(); var apps = new List(); var shortucts = new SafeFileEnumerator(path, "*.lnk", SearchOption.AllDirectories); foreach (var shortcut in shortucts) { if (cancelToken?.IsCancellationRequested == true) { return null; } if (shortcut.Attributes.HasFlag(FileAttributes.Directory)) { continue; } var fileName = shortcut.Name; var Directory = Path.GetDirectoryName(shortcut.FullName); if (folderExceptions.FirstOrDefault(a => shortcut.FullName.IndexOf(a, StringComparison.OrdinalIgnoreCase) >= 0) != null) { continue; } var link = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(shortcut.FullName); var target = link.TargetPath; if (pathExceptions.FirstOrDefault(a => target.IndexOf(a, StringComparison.OrdinalIgnoreCase) >= 0) != null) { continue; } // Ignore uninstallers, config, redistributables and game engine executables if (IsFileScanExcluded(Path.GetFileName(target))) { continue; } // Ignore duplicates if (apps.FirstOrDefault(a => a.Path == target) != null) { continue; } // Ignore non-application links if (Path.GetExtension(target) != ".exe") { continue; } var app = new Program() { Path = target, Icon = link.IconLocation, Name = Path.GetFileNameWithoutExtension(shortcut.Name), WorkDir = link.WorkingDirectory, AppId = path.MD5() }; apps.Add(app); } return apps; }); } public static async Task> GetInstalledPrograms(CancellationToken cancelToken) { var apps = new List(); // Get apps from All Users var allPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu), "Programs"); var allApps = await GetShortcutProgramsFromFolder(allPath); if (cancelToken.IsCancellationRequested == true) { return null; } else { apps.AddRange(allApps); } // Get current user apps var userPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Programs"); var userApps = await GetShortcutProgramsFromFolder(userPath); if (cancelToken.IsCancellationRequested == true) { return null; } else { apps.AddRange(userApps); } return apps; } private static string GetUWPGameIcon(string defPath) { if (File.Exists(defPath)) { return defPath; } var folder = Path.GetDirectoryName(defPath); var fileMask = Path.GetFileNameWithoutExtension(defPath) + ".scale*.png"; var files = Directory.GetFiles(folder, fileMask); if (files == null || files.Count() == 0) { return string.Empty; } else { var icons = files.Where(a => Regex.IsMatch(a, @"\.scale-\d+\.png")); if (icons.Any()) { return icons.OrderBy(a => a).Last(); } return string.Empty; } } public static List GetUWPApps() { var apps = new List(); try { var manager = new PackageManager(); IEnumerable packages = manager.FindPackagesForUser(WindowsIdentity.GetCurrent().User.Value); foreach (var package in packages) { if (package.IsFramework || package.IsResourcePackage || package.SignatureKind != PackageSignatureKind.Store) { continue; } try { if (package.InstalledLocation == null) { continue; } } catch { // InstalledLocation accessor may throw Win32 exception for unknown reason continue; } try { string manifestPath; if (package.IsBundle) { manifestPath = @"AppxMetadata\AppxBundleManifest.xml"; } else { manifestPath = "AppxManifest.xml"; } manifestPath = Path.Combine(package.InstalledLocation.Path, manifestPath); var manifest = new XmlDocument(); using (var stream = new FileStream(manifestPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { manifest.Load(stream); } var apxApp = manifest.SelectSingleNode(@"/*[local-name() = 'Package']/*[local-name() = 'Applications']//*[local-name() = 'Application'][1]"); if (apxApp.Attributes["Id"] == null) { continue; } var appId = apxApp.Attributes["Id"].Value; var visuals = apxApp.SelectSingleNode(@"//*[local-name() = 'VisualElements']"); var iconPath = visuals.Attributes["Square150x150Logo"]?.Value; if (iconPath.IsNullOrEmpty()) { iconPath = visuals.Attributes["Square70x70Logo"]?.Value; if (iconPath.IsNullOrEmpty()) { iconPath = visuals.Attributes["Square44x44Logo"]?.Value; if (iconPath.IsNullOrEmpty()) { iconPath = visuals.Attributes["Logo"]?.Value; } } } if (!iconPath.IsNullOrEmpty()) { iconPath = Path.Combine(package.InstalledLocation.Path, iconPath); iconPath = GetUWPGameIcon(iconPath); } var name = manifest.SelectSingleNode(@"/*[local-name() = 'Package']/*[local-name() = 'Properties']/*[local-name() = 'DisplayName']").InnerText; if (name.StartsWith("ms-resource")) { name = Resources.GetIndirectResourceString(package.Id.FullName, package.Id.Name, name); if (name.IsNullOrEmpty()) { name = manifest.SelectSingleNode(@"/*[local-name() = 'Package']/*[local-name() = 'Identity']").Attributes["Name"].Value; } } var app = new Program() { Name = StringExtensions.NormalizeGameName(name), WorkDir = package.InstalledLocation.Path, Path = "explorer.exe", Arguments = $"shell:AppsFolder\\{package.Id.FamilyName}!{appId}", Icon = iconPath, AppId = package.Id.FamilyName }; apps.Add(app); } catch (Exception e) { logger.Error(e, $"Failed to parse UWP app {package.Id.FullName} info."); } } } catch (Exception e) when (!Debugger.IsAttached) { logger.Error(e, "Failed to get list of installed UWP apps."); } return apps; } } } ================================================ FILE: source/Playnite/Common/Resources.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using Playnite.Native; namespace Playnite.Common { public class Resources { public static void ExtractResource(string path, string name, string type, string destination) { IntPtr hMod = Kernel32.LoadLibraryEx(path, IntPtr.Zero, 0x00000002); IntPtr hRes = Kernel32.FindResource(hMod, name, type); uint size = Kernel32.SizeofResource(hMod, hRes); IntPtr pt = Kernel32.LoadResource(hMod, hRes); byte[] bPtr = new byte[size]; Marshal.Copy(pt, bPtr, 0, (int)size); using (MemoryStream m = new MemoryStream(bPtr)) { File.WriteAllBytes(destination, m.ToArray()); } } public static long GetUriPackFileSize(string packUri) { var info = Application.GetResourceStream(new Uri(packUri)); using (var stream = info.Stream) { return stream.Length; } } public static string ReadFileFromResource(string resource) { using (var stream = Assembly.GetCallingAssembly().GetManifestResourceStream(resource)) { var tr = new StreamReader(stream); return tr.ReadToEnd(); } } public static string GetIndirectResourceString(string fullName, string packageName, string resource) { var resUri = new Uri(resource); var resourceString = string.Empty; if (resource.StartsWith("ms-resource://")) { resourceString = $"@{{{fullName}? {resource}}}"; } else if (resource.Contains('/')) { resourceString = $"@{{{fullName}? ms-resource://{packageName}/{resource.Replace("ms-resource:", "").Trim('/')}}}"; } else { resourceString = $"@{{{fullName}? ms-resource://{packageName}/resources/{resUri.Segments.Last()}}}"; } var sb = new StringBuilder(1024); var result = Shlwapi.SHLoadIndirectString(resourceString, sb, sb.Capacity, IntPtr.Zero); if (result == 0) { return sb.ToString(); } resourceString = $"@{{{fullName}? ms-resource://{packageName}/{resUri.Segments.Last()}}}"; result = Shlwapi.SHLoadIndirectString(resourceString, sb, sb.Capacity, IntPtr.Zero); if (result == 0) { return sb.ToString(); } return string.Empty; } } } ================================================ FILE: source/Playnite/Common/Roman.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { // Courtesy of https://stackoverflow.com/questions/7040289/converting-integers-to-roman-numerals public static class Roman { public static readonly Dictionary RomanNumberDictionary; public static readonly Dictionary NumberRomanDictionary; static Roman() { RomanNumberDictionary = new Dictionary { { 'I', 1 }, { 'V', 5 }, { 'X', 10 }, { 'L', 50 }, { 'C', 100 }, { 'D', 500 }, { 'M', 1000 }, }; NumberRomanDictionary = new Dictionary { { 1000, "M" }, { 900, "CM" }, { 500, "D" }, { 400, "CD" }, { 100, "C" }, { 90, "XC" }, { 50, "L" }, { 40, "XL" }, { 10, "X" }, { 9, "IX" }, { 5, "V" }, { 4, "IV" }, { 1, "I" }, }; } public static string To(int number) { var roman = new StringBuilder(); foreach (var item in NumberRomanDictionary) { while (number >= item.Key) { roman.Append(item.Value); number -= item.Key; } } return roman.ToString(); } public static int From(string roman) { int total = 0; int current, previous = 0; char currentRoman, previousRoman = '\0'; for (int i = 0; i < roman.Length; i++) { currentRoman = roman[i]; previous = previousRoman != '\0' ? RomanNumberDictionary[previousRoman] : '\0'; current = RomanNumberDictionary[currentRoman]; if (previous != 0 && current > previous) { total = total - (2 * previous) + current; } else { total += current; } previousRoman = currentRoman; } return total; } } } ================================================ FILE: source/Playnite/Common/SafeFileEnumerator.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { // Originally from https://stackoverflow.com/questions/9746538/fastest-safest-file-finding-parsing public class SafeFileEnumerator : IEnumerable { /// /// Helper class to enumerate the file system. /// private class Enumerator : IEnumerator { // Core enumerator that we will be walking though private IEnumerator fileEnumerator; // Directory enumerator to capture access errors private IEnumerator directoryEnumerator; private DirectoryInfo root; private string pattern; private SearchOption searchOption; private IList errors; public Enumerator(DirectoryInfo root, string pattern, SearchOption option, IList errors) { this.root = root; this.pattern = pattern; this.errors = errors; this.searchOption = option; Reset(); } /// /// Current item the primary itterator is pointing to /// public FileSystemInfo Current { get { //if (fileEnumerator == null) throw new ObjectDisposedException("FileEnumerator"); return fileEnumerator.Current as FileSystemInfo; } } object System.Collections.IEnumerator.Current { get { return Current; } } public void Dispose() { Dispose(true, true); } private void Dispose(bool file, bool dir) { if (file) { if (fileEnumerator != null) fileEnumerator.Dispose(); fileEnumerator = null; } if (dir) { if (directoryEnumerator != null) directoryEnumerator.Dispose(); directoryEnumerator = null; } } public bool MoveNext() { // Enumerate the files in the current folder if ((fileEnumerator != null) && (fileEnumerator.MoveNext())) return true; // Don't go recursive... if (searchOption == SearchOption.TopDirectoryOnly) { return false; } while ((directoryEnumerator != null) && (directoryEnumerator.MoveNext())) { Dispose(true, false); try { fileEnumerator = new SafeFileEnumerator( directoryEnumerator.Current, pattern, SearchOption.AllDirectories, errors ).GetEnumerator(); } catch (Exception ex) { errors.Add(ex); continue; } // Open up the current folder file enumerator if (fileEnumerator.MoveNext()) return true; } Dispose(true, true); return false; } public void Reset() { Dispose(true, true); // Safely get the enumerators, including in the case where the root is not accessable if (root != null) { try { fileEnumerator = root.GetFileSystemInfos(pattern, SearchOption.TopDirectoryOnly).AsEnumerable().GetEnumerator(); } catch (Exception ex) { errors.Add(ex); fileEnumerator = null; } try { directoryEnumerator = root.GetDirectories("*", SearchOption.TopDirectoryOnly).AsEnumerable().GetEnumerator(); } catch (Exception ex) { errors.Add(ex); directoryEnumerator = null; } } } } /// /// Starting directory to search from /// private DirectoryInfo root; /// /// Filter pattern /// private string pattern; /// /// Indicator if search is recursive or not /// private SearchOption searchOption; /// /// Any errors captured /// private IList errors; /// /// Create an Enumerator that will scan the file system, skipping directories where access is denied /// /// Starting Directory /// Filter pattern /// Recursive or not public SafeFileEnumerator(string root, string pattern, SearchOption option) : this(new DirectoryInfo(root), pattern, option) { } /// /// Create an Enumerator that will scan the file system, skipping directories where access is denied /// /// Starting Directory /// Filter pattern /// Recursive or not public SafeFileEnumerator(DirectoryInfo root, string pattern, SearchOption option) : this(root, pattern, option, new List()) { } // Internal constructor for recursive itterator private SafeFileEnumerator(DirectoryInfo root, string pattern, SearchOption option, IList errors) { if (root == null || !root.Exists) { throw new ArgumentException("Root directory is not set or does not exist.", "root"); } this.root = root; this.searchOption = option; this.pattern = String.IsNullOrEmpty(pattern) ? "*" : pattern; this.errors = errors; } /// /// Errors captured while parsing the file system. /// public Exception[] Errors { get { return errors.ToArray(); } } public IEnumerator GetEnumerator() { return new Enumerator(root, pattern, searchOption, errors); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } } } ================================================ FILE: source/Playnite/Common/Serialization.cs ================================================ using Nett; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Playnite.SDK; using Playnite.SDK.Data; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using YamlDotNet.Serialization; namespace Playnite.Common { public static class SerializationExtensions { public static string ToJson(this object obj, bool formatted = false) { return Serialization.ToJson(obj, formatted); } } public class JsonResolver : DefaultContractResolver { public static JsonResolver Global { get; } = new JsonResolver(); protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var prop = base.CreateProperty(member, memberSerialization); if (Attribute.IsDefined(member, typeof(SerializationPropertyNameAttribute))) { var att = (SerializationPropertyNameAttribute)Attribute.GetCustomAttribute(member, typeof(SerializationPropertyNameAttribute)); prop.PropertyName = att.PropertyName; } return prop; } protected override List GetSerializableMembers(Type objectType) { return objectType. GetMembers(BindingFlags.Public | BindingFlags.Instance). Where(a => a is PropertyInfo || a is FieldInfo). Where(a => !Attribute.IsDefined(a, typeof(DontSerializeAttribute)) && !Attribute.IsDefined(a, typeof(JsonIgnoreAttribute))). ToList(); } } public class DataSerializer : IDataSerializer { public string ToYaml(object obj) { return Serialization.ToYaml(obj); } public T FromYaml(string yaml) where T : class { return Serialization.FromYaml(yaml); } public T FromYamlFile(string filePath) where T : class { return Serialization.FromYamlFile(filePath); } public string ToJson(object obj, bool formatted = false) { return Serialization.ToJson(obj, formatted); } public void ToJsonStream(object obj, Stream stream, bool formatted = false) { Serialization.ToJsonStream(obj, stream, formatted); } public T FromJson(string json) where T : class { return Serialization.FromJson(json); } public T FromJsonStream(Stream stream) where T : class { return Serialization.FromJsonStream(stream); } public T FromJsonFile(string filePath) where T : class { return Serialization.FromJsonFile(filePath); } public T FromToml(string toml) where T : class { return Serialization.FromToml(toml); } public T FromTomlFile(string filePath) where T : class { return Serialization.FromTomlFile(filePath); } public bool TryFromYaml(string yaml, out T content) where T : class { return Serialization.TryFromYaml(yaml, out content); } public bool TryFromYaml(string yaml, out T content, out Exception error) where T : class { return Serialization.TryFromYaml(yaml, out content, out error); } public bool TryFromYamlFile(string filePath, out T content) where T : class { return Serialization.TryFromYamlFile(filePath, out content); } public bool TryFromYamlFile(string filePath, out T content, out Exception error) where T : class { return Serialization.TryFromYamlFile(filePath, out content, out error); } public bool TryFromJson(string json, out T content) where T : class { return Serialization.TryFromJson(json, out content); } public bool TryFromJson(string json, out T content, out Exception error) where T : class { return Serialization.TryFromJson(json, out content, out error); } public bool TryFromJsonStream(Stream stream, out T content) where T : class { return Serialization.TryFromJsonStream(stream, out content); } public bool TryFromJsonStream(Stream stream, out T content, out Exception error) where T : class { return Serialization.TryFromJsonStream(stream, out content, out error); } public bool TryFromJsonFile(string filePath, out T content) where T : class { return Serialization.TryFromJsonFile(filePath, out content); } public bool TryFromJsonFile(string filePath, out T content, out Exception error) where T : class { return Serialization.TryFromJsonFile(filePath, out content, out error); } public bool TryFromToml(string toml, out T content) where T : class { return Serialization.TryFromToml(toml, out content); } public bool TryFromToml(string toml, out T content, out Exception error) where T : class { return Serialization.TryFromToml(toml, out content, out error); } public bool TryFromTomlFile(string filePath, out T content) where T : class { return Serialization.TryFromTomlFile(filePath, out content); } public bool TryFromTomlFile(string filePath, out T content, out Exception error) where T : class { return Serialization.TryFromTomlFile(filePath, out content, out error); } public bool AreObjectsEqual(object object1, object object2) { return object1.IsEqualJson(object2); } public T GetClone(T source) where T : class { return source.GetClone(); } public U GetClone(T source) where T : class where U : class { return source.GetClone(); } } public static class Serialization { private static readonly ILogger logger = LogManager.GetLogger(); private static readonly JsonSerializerSettings jsonDesSettings = new JsonSerializerSettings { ContractResolver = JsonResolver.Global, MaxDepth = 128 }; public static string ToYaml(object obj) { var serializer = new SerializerBuilder().Build(); return serializer.Serialize(obj); } public static T FromYaml(string yaml) where T : class { try { var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); return deserializer.Deserialize(yaml); } catch (Exception e) { logger.Error(e, $"Failed to deserialize {typeof(T).FullName} from yaml:"); logger.Debug(yaml); throw; } } public static bool TryFromYaml(string yaml, out T deserialized) where T : class { try { var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); deserialized = deserializer.Deserialize(yaml); return true; } catch { deserialized = null; return false; } } public static bool TryFromYaml(string yaml, out T deserialized, out Exception error) where T : class { try { var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); deserialized = deserializer.Deserialize(yaml); error = null; return true; } catch (Exception e) { deserialized = null; error = e; return false; } } public static T FromYamlFile(string filePath) where T : class { return FromYaml(FileSystem.ReadStringFromFile(filePath)); } public static bool TryFromYamlFile(string filePath, out T deserialized) where T : class { try { deserialized = FromYaml(FileSystem.ReadStringFromFile(filePath)); return true; } catch { deserialized = null; return false; } } public static bool TryFromYamlFile(string filePath, out T deserialized, out Exception error) where T : class { try { deserialized = FromYaml(FileSystem.ReadStringFromFile(filePath)); error = null; return true; } catch (Exception e) { deserialized = null; error = e; return false; } } public static T FromYamlStream(Stream stream) where T : class { using (var sr = new StreamReader(stream, true)) { var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); return deserializer.Deserialize(sr); } } public static string ToJson(object obj, bool formatted = false, params JsonConverter[] converters) { return JsonConvert.SerializeObject(obj, new JsonSerializerSettings() { Formatting = formatted ? Formatting.Indented : Formatting.None, NullValueHandling = NullValueHandling.Ignore, ContractResolver = JsonResolver.Global, Converters = converters, MaxDepth = 128 }); } public static void ToJsonStream(object obj, Stream stream, bool formatted = false) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 4096, true)) using (var writer = new JsonTextWriter(sw)) { var ser = JsonSerializer.Create(new JsonSerializerSettings() { Formatting = formatted ? Formatting.Indented : Formatting.None, NullValueHandling = NullValueHandling.Ignore, ContractResolver = JsonResolver.Global, MaxDepth = 128 }); ser.Serialize(writer, obj); } } public static T FromJson(string json) where T : class { try { return JsonConvert.DeserializeObject(json, jsonDesSettings); } catch (Exception e) { logger.Error(e, $"Failed to deserialize {typeof(T).FullName} from json:"); logger.Debug(json); throw; } } public static T FromJsonStream(Stream stream) where T : class { using (var sr = new StreamReader(stream)) using (var reader = new JsonTextReader(sr)) { return JsonSerializer.Create(jsonDesSettings).Deserialize(reader); } } public static bool TryFromJsonStream(Stream stream, out T deserialized) where T : class { try { using (var sr = new StreamReader(stream)) using (var reader = new JsonTextReader(sr)) { deserialized = JsonSerializer.Create(jsonDesSettings).Deserialize(reader); } return true; } catch { deserialized = null; return false; } } public static bool TryFromJsonStream(Stream stream, out T deserialized, out Exception error) where T : class { try { using (var sr = new StreamReader(stream)) using (var reader = new JsonTextReader(sr)) { deserialized = JsonSerializer.Create(jsonDesSettings).Deserialize(reader); } error = null; return true; } catch (Exception e) { deserialized = null; error = e; return false; } } public static T FromJsonFile(string filePath) where T : class { filePath = Paths.FixPathLength(filePath); using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { return FromJsonStream(fs); } } public static bool TryFromJsonFile(string filePath, out T deserialized) where T : class { try { filePath = Paths.FixPathLength(filePath); using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { deserialized = FromJsonStream(fs); } return true; } catch { deserialized = null; return false; } } public static bool TryFromJsonFile(string filePath, out T deserialized, out Exception error) where T : class { try { filePath = Paths.FixPathLength(filePath); using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { deserialized = FromJsonStream(fs); } error = null; return true; } catch (Exception e) { deserialized = null; error = e; return false; } } public static bool TryFromJson(string json, out T deserialized) where T : class { try { deserialized = JsonConvert.DeserializeObject(json, jsonDesSettings); return true; } catch { deserialized = null; return false; } } public static bool TryFromJson(string json, out T deserialized, out Exception error) where T : class { try { deserialized = JsonConvert.DeserializeObject(json, jsonDesSettings); error = null; return true; } catch (Exception e) { deserialized = null; error = e; return false; } } public static T FromToml(string toml) where T : class { try { return Toml.ReadString(toml); } catch (Exception e) { logger.Error(e, $"Failed to deserialize {typeof(T).FullName} from toml:"); logger.Debug(toml); throw; } } public static bool TryFromToml(string toml, out T deserialized) where T : class { try { deserialized = Toml.ReadString(toml); return true; } catch { deserialized = null; return false; } } public static bool TryFromToml(string toml, out T deserialized, out Exception error) where T : class { try { deserialized = Toml.ReadString(toml); error = null; return true; } catch (Exception e) { deserialized = null; error = e; return false; } } public static T FromTomlFile(string filePath) where T : class { return FromToml(FileSystem.ReadStringFromFile(filePath)); } public static bool TryFromTomlFile(string filePath, out T deserialized) where T : class { try { deserialized = FromToml(FileSystem.ReadStringFromFile(filePath)); return true; } catch { deserialized = null; return false; } } public static bool TryFromTomlFile(string filePath, out T deserialized, out Exception error) where T : class { try { deserialized = FromToml(FileSystem.ReadStringFromFile(filePath)); error = null; return true; } catch (Exception e) { deserialized = null; error = e; return false; } } } } ================================================ FILE: source/Playnite/Common/SigningTools.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using Playnite.Native; namespace Playnite.Common { public class SigningTools { private static uint WinVerifyTrust(string fileName) { Guid wintrust_action_generic_verify_v2 = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}"); uint result = 0; using (WINTRUST_FILE_INFO fileInfo = new WINTRUST_FILE_INFO(fileName, Guid.Empty)) using (Wintrust.UnmanagedPointer guidPtr = new Wintrust.UnmanagedPointer( Marshal.AllocHGlobal( Marshal.SizeOf(typeof(Guid))), WINTRUST_DATA.AllocMethod.HGlobal)) using (Wintrust.UnmanagedPointer wvtDataPtr = new Wintrust.UnmanagedPointer( Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_DATA))), WINTRUST_DATA.AllocMethod.HGlobal)) { WINTRUST_DATA data = new WINTRUST_DATA(fileInfo); IntPtr pGuid = guidPtr; IntPtr pData = wvtDataPtr; Marshal.StructureToPtr( wintrust_action_generic_verify_v2, pGuid, true); Marshal.StructureToPtr( data, pData, true); result = User32.WinVerifyTrust( IntPtr.Zero, pGuid, pData); } return result; } public static bool IsTrusted(string path) { return WinVerifyTrust(path) == 0; } } } ================================================ FILE: source/Playnite/Common/Sizes.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Data; namespace Playnite.Common { public class AspectRatioTypeConverter : TypeConverter { public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string ratio) { var regex = Regex.Match(ratio, @"(\d+):(\d+)"); if (regex.Success) { return new AspectRatio( Convert.ToInt32(regex.Groups[1].Value), Convert.ToInt32(regex.Groups[2].Value)); } else { // For cases where this is called from designer element preview via binding expression return new AspectRatio(); } } throw new NotSupportedException($"Cannot convert {value} to AspectRatio."); } public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { return sourceType == typeof(string); } } [TypeConverter(typeof(AspectRatioTypeConverter))] public class AspectRatio : IEquatable { public int Width { get; set; } public int Height { get; set; } public AspectRatio() { } public AspectRatio(int width, int height) { Width = width; Height = height; } public override bool Equals(object obj) => Equals(obj as AspectRatio); public bool Equals(AspectRatio other) { return other != null && Width == other.Width && Height == other.Height; } public override int GetHashCode() { return base.GetHashCode(); } public static bool operator ==(AspectRatio obj1, AspectRatio obj2) { return obj1?.Equals(obj2) == true; } public static bool operator !=(AspectRatio obj1, AspectRatio obj2) { return obj1?.Equals(obj2) == false; } public override string ToString() { return $"{Width}:{Height}"; } public double GetWidth(double height) { return ((double)Width / Height) * height; } public double GetHeight(double width) { return ((double)Height / Width) * width; } } public class Sizes { public static AspectRatio GetAspectRatio(Rectangle rect) { return GetAspectRatio(rect.Width, rect.Height); } public static AspectRatio GetAspectRatio(int width, int height) { var gcd = GetGreatestCommonDivisor(width, height); return new AspectRatio(width / gcd, height / gcd); } static int GetGreatestCommonDivisor(int a, int b) { return b == 0 ? a : GetGreatestCommonDivisor(b, a % b); } public static double GetMegapixelsFromRes(int width, int height) { return Math.Round((double)(width * height) / 1000000, 3); } public static double GetMegapixelsFromRes(ImageProperties props) { return GetMegapixelsFromRes(props.Width, props.Height); } } } ================================================ FILE: source/Playnite/Common/Sqlite.cs ================================================ using Playnite.SDK.Data; using SqlNado; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class Sqlite : ISQLite, IDisposable { private SQLiteDatabase db; public Sqlite(string dbPath, SqliteOpenFlags openFlags) { db = new SQLiteDatabase(dbPath, (SQLiteOpenOptions)openFlags); } public List Query(string query, params object[] args) where T : new() { return db.Load(query, args).ToList(); } public void Dispose() { db.Dispose(); } } } ================================================ FILE: source/Playnite/Common/SystemDialogs.cs ================================================ using Microsoft.Win32; using Microsoft.WindowsAPICodePack.Dialogs; using Playnite.SDK; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; namespace Playnite.Common { public class SystemDialogs { public static string SaveFile(Window owner, string filter, bool promptOverwrite, string initialDir = null) { var dialog = new SaveFileDialog() { Filter = filter, OverwritePrompt = promptOverwrite }; if (initialDir != null && Directory.Exists(initialDir)) dialog.InitialDirectory = initialDir; var dialogResult = owner == null ? dialog.ShowDialog() : dialog.ShowDialog(owner); if (dialogResult == true) { return dialog.FileName; } else { return string.Empty; } } public static string SaveFile(Window owner, string filter, string initialDir = null) { return SaveFile(owner, filter, true, initialDir); } public static string SaveFile(string filter, bool promptOverwrite, string initialDir = null) { return SaveFile(null, filter, promptOverwrite, initialDir); } public static string SaveFile(string filter, string initialDir = null) { return SaveFile(null, filter, true, initialDir); } public static string SelectFolder(Window owner, string initialDir = null) { var dialog = new CommonOpenFileDialog() { IsFolderPicker = true }; if (initialDir != null && Directory.Exists(initialDir)) dialog.InitialDirectory = initialDir; var dialogResult = owner == null ? dialog.ShowDialog() : dialog.ShowDialog(owner); if (dialogResult == CommonFileDialogResult.Ok) { return dialog.FileName; } else { return string.Empty; } } public static List SelectFiles(Window owner, string filter, string initialDir = null) { var dialog = new OpenFileDialog() { Filter = filter, Multiselect = true }; if (!initialDir.IsNullOrWhiteSpace() && Directory.Exists(initialDir)) dialog.InitialDirectory = initialDir; var dialogResult = owner == null ? dialog.ShowDialog() : dialog.ShowDialog(owner); if (dialogResult == true) { return dialog.FileNames.ToList(); } else { return null; } } public static List SelectFiles(string filter) { return SelectFiles(null, filter); } public static string SelectFile(Window owner, string filter, string initialDir = null) { var dialog = new OpenFileDialog() { Filter = filter }; if (!initialDir.IsNullOrWhiteSpace() && Directory.Exists(initialDir)) dialog.InitialDirectory = initialDir; var dialogResult = owner == null ? dialog.ShowDialog() : dialog.ShowDialog(owner); if (dialogResult == true) { return dialog.FileName; } else { return string.Empty; } } public static string SelectFile(string filter, string initialDir = null) { return SelectFile(null, filter, initialDir); } public static string SelectIconFile(Window owner, string initialDir = null) { return SelectFile(owner, "Icon Files|*.bmp;*.jpg*;*.jpeg*;*.png;*.gif;*.ico;*.tga;*.exe;*.tif;*.webp;*.avif", initialDir); } public static string SelectIconFile(string initialDir = null) { return SelectIconFile(null, initialDir); } public static string SelectImageFile(Window owner, string initialDir = null) { return SelectFile(owner, "Image Files|*.bmp;*.jpg*;*.jpeg*;*.png;*.gif;*.tga;*.tif;*.webp;*.avif", initialDir); } public static string SelectImageFile(string initialDir = null) { return SelectIconFile(null, initialDir); } } } ================================================ FILE: source/Playnite/Common/TGASharpLib.cs ================================================ /* MIT License Copyright (c) 2017 TGASharpLib Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Runtime.InteropServices; namespace TGASharpLib { #region Enums /// /// The first 128 Color Map Type codes are reserved for use by Truevision, /// while the second set of 128 Color Map Type codes(128 to 255) may be used for /// developer applications. /// True-Color images do not normally make use of the color map field, but some current /// applications store palette information or developer-defined information in this field. /// It is best to check Field 3, Image Type, to make sure you have a file which can use the /// data stored in the Color Map Field. /// Otherwise ignore the information. When saving or creating files for True-Color /// images do not use this field and set it to Zero to ensure compatibility. Please refer /// to the Developer Area specification for methods of storing developer defined information. /// public enum TgaColorMapType : byte { NoColorMap = 0, ColorMap = 1, Truevision_2, Truevision_3, Truevision_4, Truevision_5, Truevision_6, Truevision_7, Truevision_8, Truevision_9, Truevision_10, Truevision_11, Truevision_12, Truevision_13, Truevision_14, Truevision_15, Truevision_16, Truevision_17, Truevision_18, Truevision_19, Truevision_20, Truevision_21, Truevision_22, Truevision_23, Truevision_24, Truevision_25, Truevision_26, Truevision_27, Truevision_28, Truevision_29, Truevision_30, Truevision_31, Truevision_32, Truevision_33, Truevision_34, Truevision_35, Truevision_36, Truevision_37, Truevision_38, Truevision_39, Truevision_40, Truevision_41, Truevision_42, Truevision_43, Truevision_44, Truevision_45, Truevision_46, Truevision_47, Truevision_48, Truevision_49, Truevision_50, Truevision_51, Truevision_52, Truevision_53, Truevision_54, Truevision_55, Truevision_56, Truevision_57, Truevision_58, Truevision_59, Truevision_60, Truevision_61, Truevision_62, Truevision_63, Truevision_64, Truevision_65, Truevision_66, Truevision_67, Truevision_68, Truevision_69, Truevision_70, Truevision_71, Truevision_72, Truevision_73, Truevision_74, Truevision_75, Truevision_76, Truevision_77, Truevision_78, Truevision_79, Truevision_80, Truevision_81, Truevision_82, Truevision_83, Truevision_84, Truevision_85, Truevision_86, Truevision_87, Truevision_88, Truevision_89, Truevision_90, Truevision_91, Truevision_92, Truevision_93, Truevision_94, Truevision_95, Truevision_96, Truevision_97, Truevision_98, Truevision_99, Truevision_100, Truevision_101, Truevision_102, Truevision_103, Truevision_104, Truevision_105, Truevision_106, Truevision_107, Truevision_108, Truevision_109, Truevision_110, Truevision_111, Truevision_112, Truevision_113, Truevision_114, Truevision_115, Truevision_116, Truevision_117, Truevision_118, Truevision_119, Truevision_120, Truevision_121, Truevision_122, Truevision_123, Truevision_124, Truevision_125, Truevision_126, Truevision_127, Other_128, Other_129, Other_130, Other_131, Other_132, Other_133, Other_134, Other_135, Other_136, Other_137, Other_138, Other_139, Other_140, Other_141, Other_142, Other_143, Other_144, Other_145, Other_146, Other_147, Other_148, Other_149, Other_150, Other_151, Other_152, Other_153, Other_154, Other_155, Other_156, Other_157, Other_158, Other_159, Other_160, Other_161, Other_162, Other_163, Other_164, Other_165, Other_166, Other_167, Other_168, Other_169, Other_170, Other_171, Other_172, Other_173, Other_174, Other_175, Other_176, Other_177, Other_178, Other_179, Other_180, Other_181, Other_182, Other_183, Other_184, Other_185, Other_186, Other_187, Other_188, Other_189, Other_190, Other_191, Other_192, Other_193, Other_194, Other_195, Other_196, Other_197, Other_198, Other_199, Other_200, Other_201, Other_202, Other_203, Other_204, Other_205, Other_206, Other_207, Other_208, Other_209, Other_210, Other_211, Other_212, Other_213, Other_214, Other_215, Other_216, Other_217, Other_218, Other_219, Other_220, Other_221, Other_222, Other_223, Other_224, Other_225, Other_226, Other_227, Other_228, Other_229, Other_230, Other_231, Other_232, Other_233, Other_234, Other_235, Other_236, Other_237, Other_238, Other_239, Other_240, Other_241, Other_242, Other_243, Other_244, Other_245, Other_246, Other_247, Other_248, Other_249, Other_250, Other_251, Other_252, Other_253, Other_254, Other_255 } /// /// Establishes the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used. /// When working with VDA or VDA/D cards it is preferred that you select 16 bits(5 bits /// per primary with 1 bit to select interrupt control) and set the 16th bit to 0 so that the /// interrupt bit is disabled. Even if this field is set to 15 bits(5 bits per primary) you /// must still parse the color map data 16 bits at a time and ignore the 16th bit. /// When working with a TARGA M8 card you would select 24 bits (8 bits per primary) /// since the color map is defined as 256 entries of 24 bit color values. /// When working with a TrueVista card(ATVista or NuVista) you would select 24-bit(8 bits per /// primary) or 32-bit(8 bits per primary including Alpha channel) depending on your /// application’s use of look-up tables. It is suggested that when working with 16-bit and /// 32-bit color images, you store them as True-Color images and do not use the color map /// field to store look-up tables. Please refer to the TGA Extensions for fields better suited /// to storing look-up table information. /// public enum TgaColorMapEntrySize : byte { Other = 0, X1R5G5B5 = 15, A1R5G5B5 = 16, R8G8B8 = 24, A8R8G8B8 = 32 } /// /// Truevision has currently defined seven image types: /// 0 - No Image Data Included; /// 1 - Uncompressed, Color-mapped Image; /// 2 - Uncompressed, True-color Image; /// 3 - Uncompressed, Black-and-white Image; /// 9 - Run-length encoded, Color-mapped Image; /// 10 - Run-length encoded, True-color Image; /// 11 - Run-length encoded, Black-and-white Image. /// Image Data Type codes 0 to 127 are reserved for use by Truevision for general applications. /// Image Data Type codes 128 to 255 may be used for developer applications. /// public enum TgaImageType : byte { NoImageData = 0, Uncompressed_ColorMapped = 1, Uncompressed_TrueColor, Uncompressed_BlackWhite, _Truevision_4, _Truevision_5, _Truevision_6, _Truevision_7, _Truevision_8, RLE_ColorMapped = 9, RLE_TrueColor, RLE_BlackWhite, _Truevision_12, _Truevision_13, _Truevision_14, _Truevision_15, _Truevision_16, _Truevision_17, _Truevision_18, _Truevision_19, _Truevision_20, _Truevision_21, _Truevision_22, _Truevision_23, _Truevision_24, _Truevision_25, _Truevision_26, _Truevision_27, _Truevision_28, _Truevision_29, _Truevision_30, _Truevision_31, _Truevision_32, _Truevision_33, _Truevision_34, _Truevision_35, _Truevision_36, _Truevision_37, _Truevision_38, _Truevision_39, _Truevision_40, _Truevision_41, _Truevision_42, _Truevision_43, _Truevision_44, _Truevision_45, _Truevision_46, _Truevision_47, _Truevision_48, _Truevision_49, _Truevision_50, _Truevision_51, _Truevision_52, _Truevision_53, _Truevision_54, _Truevision_55, _Truevision_56, _Truevision_57, _Truevision_58, _Truevision_59, _Truevision_60, _Truevision_61, _Truevision_62, _Truevision_63, _Truevision_64, _Truevision_65, _Truevision_66, _Truevision_67, _Truevision_68, _Truevision_69, _Truevision_70, _Truevision_71, _Truevision_72, _Truevision_73, _Truevision_74, _Truevision_75, _Truevision_76, _Truevision_77, _Truevision_78, _Truevision_79, _Truevision_80, _Truevision_81, _Truevision_82, _Truevision_83, _Truevision_84, _Truevision_85, _Truevision_86, _Truevision_87, _Truevision_88, _Truevision_89, _Truevision_90, _Truevision_91, _Truevision_92, _Truevision_93, _Truevision_94, _Truevision_95, _Truevision_96, _Truevision_97, _Truevision_98, _Truevision_99, _Truevision_100, _Truevision_101, _Truevision_102, _Truevision_103, _Truevision_104, _Truevision_105, _Truevision_106, _Truevision_107, _Truevision_108, _Truevision_109, _Truevision_110, _Truevision_111, _Truevision_112, _Truevision_113, _Truevision_114, _Truevision_115, _Truevision_116, _Truevision_117, _Truevision_118, _Truevision_119, _Truevision_120, _Truevision_121, _Truevision_122, _Truevision_123, _Truevision_124, _Truevision_125, _Truevision_126, _Truevision_127, _Other_128, _Other_129, _Other_130, _Other_131, _Other_132, _Other_133, _Other_134, _Other_135, _Other_136, _Other_137, _Other_138, _Other_139, _Other_140, _Other_141, _Other_142, _Other_143, _Other_144, _Other_145, _Other_146, _Other_147, _Other_148, _Other_149, _Other_150, _Other_151, _Other_152, _Other_153, _Other_154, _Other_155, _Other_156, _Other_157, _Other_158, _Other_159, _Other_160, _Other_161, _Other_162, _Other_163, _Other_164, _Other_165, _Other_166, _Other_167, _Other_168, _Other_169, _Other_170, _Other_171, _Other_172, _Other_173, _Other_174, _Other_175, _Other_176, _Other_177, _Other_178, _Other_179, _Other_180, _Other_181, _Other_182, _Other_183, _Other_184, _Other_185, _Other_186, _Other_187, _Other_188, _Other_189, _Other_190, _Other_191, _Other_192, _Other_193, _Other_194, _Other_195, _Other_196, _Other_197, _Other_198, _Other_199, _Other_200, _Other_201, _Other_202, _Other_203, _Other_204, _Other_205, _Other_206, _Other_207, _Other_208, _Other_209, _Other_210, _Other_211, _Other_212, _Other_213, _Other_214, _Other_215, _Other_216, _Other_217, _Other_218, _Other_219, _Other_220, _Other_221, _Other_222, _Other_223, _Other_224, _Other_225, _Other_226, _Other_227, _Other_228, _Other_229, _Other_230, _Other_231, _Other_232, _Other_233, _Other_234, _Other_235, _Other_236, _Other_237, _Other_238, _Other_239, _Other_240, _Other_241, _Other_242, _Other_243, _Other_244, _Other_245, _Other_246, _Other_247, _Other_248, _Other_249, _Other_250, _Other_251, _Other_252, _Other_253, _Other_254, _Other_255 } /// /// Number of bits per pixel. This number includes the Attribute or Alpha channel bits. /// Common values are 8, 16, 24 and 32 but other pixel depths could be used. /// public enum TgaPixelDepth : byte { Other = 0, Bpp8 = 8, Bpp16 = 16, Bpp24 = 24, Bpp32 = 32 } /// /// Used to indicate the order in which pixel data is transferred from the file to the screen. /// (Bit 4 (bit 0 in enum) is for left-to-right ordering and bit 5 (bit 1 in enum) is for /// topto-bottom ordering as shown below.) /// public enum TgaImgOrigin : byte { BottomLeft = 0, BottomRight, TopLeft, TopRight } /// /// Contains a value which specifies the type of Alpha channel /// data contained in the file. Value Meaning: /// 0: no Alpha data included (bits 3-0 of field 5.6 should also be set to zero) /// 1: undefined data in the Alpha field, can be ignored /// 2: undefined data in the Alpha field, but should be retained /// 3: useful Alpha channel data is present /// 4: pre-multiplied Alpha(see description below) /// 5 -127: RESERVED /// 128-255: Un-assigned /// Pre-multiplied Alpha Example: Suppose the Alpha channel data is being used to specify the /// opacity of each pixel(for use when the image is overlayed on another image), where 0 indicates /// that the pixel is completely transparent and a value of 1 indicates that the pixel is /// completely opaque(assume all component values have been normalized). /// A quadruple(a, r, g, b) of( 0.5, 1, 0, 0) would indicate that the pixel is pure red with a /// transparency of one-half. For numerous reasons(including image compositing) is is better to /// pre-multiply the individual color components with the value in the Alpha channel. /// A pre-multiplication of the above would produce a quadruple(0.5, 0.5, 0, 0). /// A value of 3 in the Attributes Type Field(field 23) would indicate that the color components /// of the pixel have already been scaled by the value in the Alpha channel. /// public enum TgaAttrType : byte { NoAlpha = 0, UndefinedAlphaCanBeIgnored, UndefinedAlphaButShouldBeRetained, UsefulAlpha, PreMultipliedAlpha, _Reserved_5, _Reserved_6, _Reserved_7, _Reserved_8, _Reserved_9, _Reserved_10, _Reserved_11, _Reserved_12, _Reserved_13, _Reserved_14, _Reserved_15, _Reserved_16, _Reserved_17, _Reserved_18, _Reserved_19, _Reserved_20, _Reserved_21, _Reserved_22, _Reserved_23, _Reserved_24, _Reserved_25, _Reserved_26, _Reserved_27, _Reserved_28, _Reserved_29, _Reserved_30, _Reserved_31, _Reserved_32, _Reserved_33, _Reserved_34, _Reserved_35, _Reserved_36, _Reserved_37, _Reserved_38, _Reserved_39, _Reserved_40, _Reserved_41, _Reserved_42, _Reserved_43, _Reserved_44, _Reserved_45, _Reserved_46, _Reserved_47, _Reserved_48, _Reserved_49, _Reserved_50, _Reserved_51, _Reserved_52, _Reserved_53, _Reserved_54, _Reserved_55, _Reserved_56, _Reserved_57, _Reserved_58, _Reserved_59, _Reserved_60, _Reserved_61, _Reserved_62, _Reserved_63, _Reserved_64, _Reserved_65, _Reserved_66, _Reserved_67, _Reserved_68, _Reserved_69, _Reserved_70, _Reserved_71, _Reserved_72, _Reserved_73, _Reserved_74, _Reserved_75, _Reserved_76, _Reserved_77, _Reserved_78, _Reserved_79, _Reserved_80, _Reserved_81, _Reserved_82, _Reserved_83, _Reserved_84, _Reserved_85, _Reserved_86, _Reserved_87, _Reserved_88, _Reserved_89, _Reserved_90, _Reserved_91, _Reserved_92, _Reserved_93, _Reserved_94, _Reserved_95, _Reserved_96, _Reserved_97, _Reserved_98, _Reserved_99, _Reserved_100, _Reserved_101, _Reserved_102, _Reserved_103, _Reserved_104, _Reserved_105, _Reserved_106, _Reserved_107, _Reserved_108, _Reserved_109, _Reserved_110, _Reserved_111, _Reserved_112, _Reserved_113, _Reserved_114, _Reserved_115, _Reserved_116, _Reserved_117, _Reserved_118, _Reserved_119, _Reserved_120, _Reserved_121, _Reserved_122, _Reserved_123, _Reserved_124, _Reserved_125, _Reserved_126, _Reserved_127, _UnAssigned_128, _UnAssigned_129, _UnAssigned_130, _UnAssigned_131, _UnAssigned_132, _UnAssigned_133, _UnAssigned_134, _UnAssigned_135, _UnAssigned_136, _UnAssigned_137, _UnAssigned_138, _UnAssigned_139, _UnAssigned_140, _UnAssigned_141, _UnAssigned_142, _UnAssigned_143, _UnAssigned_144, _UnAssigned_145, _UnAssigned_146, _UnAssigned_147, _UnAssigned_148, _UnAssigned_149, _UnAssigned_150, _UnAssigned_151, _UnAssigned_152, _UnAssigned_153, _UnAssigned_154, _UnAssigned_155, _UnAssigned_156, _UnAssigned_157, _UnAssigned_158, _UnAssigned_159, _UnAssigned_160, _UnAssigned_161, _UnAssigned_162, _UnAssigned_163, _UnAssigned_164, _UnAssigned_165, _UnAssigned_166, _UnAssigned_167, _UnAssigned_168, _UnAssigned_169, _UnAssigned_170, _UnAssigned_171, _UnAssigned_172, _UnAssigned_173, _UnAssigned_174, _UnAssigned_175, _UnAssigned_176, _UnAssigned_177, _UnAssigned_178, _UnAssigned_179, _UnAssigned_180, _UnAssigned_181, _UnAssigned_182, _UnAssigned_183, _UnAssigned_184, _UnAssigned_185, _UnAssigned_186, _UnAssigned_187, _UnAssigned_188, _UnAssigned_189, _UnAssigned_190, _UnAssigned_191, _UnAssigned_192, _UnAssigned_193, _UnAssigned_194, _UnAssigned_195, _UnAssigned_196, _UnAssigned_197, _UnAssigned_198, _UnAssigned_199, _UnAssigned_200, _UnAssigned_201, _UnAssigned_202, _UnAssigned_203, _UnAssigned_204, _UnAssigned_205, _UnAssigned_206, _UnAssigned_207, _UnAssigned_208, _UnAssigned_209, _UnAssigned_210, _UnAssigned_211, _UnAssigned_212, _UnAssigned_213, _UnAssigned_214, _UnAssigned_215, _UnAssigned_216, _UnAssigned_217, _UnAssigned_218, _UnAssigned_219, _UnAssigned_220, _UnAssigned_221, _UnAssigned_222, _UnAssigned_223, _UnAssigned_224, _UnAssigned_225, _UnAssigned_226, _UnAssigned_227, _UnAssigned_228, _UnAssigned_229, _UnAssigned_230, _UnAssigned_231, _UnAssigned_232, _UnAssigned_233, _UnAssigned_234, _UnAssigned_235, _UnAssigned_236, _UnAssigned_237, _UnAssigned_238, _UnAssigned_239, _UnAssigned_240, _UnAssigned_241, _UnAssigned_242, _UnAssigned_243, _UnAssigned_244, _UnAssigned_245, _UnAssigned_246, _UnAssigned_247, _UnAssigned_248, _UnAssigned_249, _UnAssigned_250, _UnAssigned_251, _UnAssigned_252, _UnAssigned_253, _UnAssigned_254, _UnAssigned_255 } #endregion #region Classes public class TgaColorKey : ICloneable { byte a = 0; byte r = 0; byte g = 0; byte b = 0; public TgaColorKey() { } /// /// Make from ARGB bytes. /// /// Alpha value. /// Red value. /// Green value. /// Blue value. public TgaColorKey(byte A, byte R, byte G, byte B) { a = A; r = R; g = G; b = B; } /// /// Make from ARGB bytes. /// /// Array of bytes(byte[4]). public TgaColorKey(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!"); Color color = Color.FromArgb(BitConverter.ToInt32(Bytes, 0)); a = color.A; r = color.R; g = color.G; b = color.B; } /// /// Make from . /// /// 32bit ARGB integer color value. public TgaColorKey(int ARGB) { Color ColorARGB = Color.FromArgb(ARGB); a = ColorARGB.A; r = ColorARGB.R; g = ColorARGB.G; b = ColorARGB.B; } /// /// Make from . /// /// GDI+ value. public TgaColorKey(Color color) { a = color.A; r = color.R; g = color.G; b = color.B; } /// /// Gets or sets alpha color value. /// public byte A { get { return a; } set { a = value; } } /// /// Gets or sets red color value. /// public byte R { get { return r; } set { r = value; } } /// /// Gets or sets green color value. /// public byte G { get { return g; } set { g = value; } } /// /// Gets or sets blue color value. /// public byte B { get { return b; } set { b = value; } } /// /// Gets TGA Field size in bytes. /// public const int Size = 4; /// /// Make full independed copy of . /// /// Copy of public TgaColorKey Clone() { return new TgaColorKey(a, r, g, b); } /// /// Make full independed copy of . /// /// Copy of object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaColorKey) ? Equals((TgaColorKey)obj) : false); } public bool Equals(TgaColorKey item) { return (a == item.a && r == item.r && g == item.g && b == item.b); } public static bool operator ==(TgaColorKey item1, TgaColorKey item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaColorKey item1, TgaColorKey item2) { return !(item1 == item2); } public override int GetHashCode() { return ToInt().GetHashCode(); } /// /// Gets like string. /// /// String in ARGB format. public override string ToString() { return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}", nameof(A), a, nameof(R), r, nameof(G), g, nameof(B), b); } /// /// Convert to byte array. /// /// Byte array with length = 4. public byte[] ToBytes() { return BitConverter.GetBytes(ToInt()); } /// /// Gets like GDI+ . /// /// value of . public Color ToColor() { return Color.FromArgb(a, r, g, b); } /// /// Gets like ARGB . /// /// ARGB value of . public int ToInt() { return ToColor().ToArgb(); } } /// /// This field (5 bytes) and its sub-fields describe the color map (if any) used for the image. /// If the Color Map Type field is set to zero, indicating that no color map exists, then /// these 5 bytes should be set to zero. These bytes always must be written to the file. /// public class TgaColorMapSpec : ICloneable { ushort firstEntryIndex = 0; ushort colorMapLength = 0; TgaColorMapEntrySize colorMapEntrySize = TgaColorMapEntrySize.Other; /// /// Make new . /// public TgaColorMapSpec() { } /// /// Make from bytes. /// /// Array of bytes(byte[5]). public TgaColorMapSpec(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!"); firstEntryIndex = BitConverter.ToUInt16(Bytes, 0); colorMapLength = BitConverter.ToUInt16(Bytes, 2); colorMapEntrySize = (TgaColorMapEntrySize)Bytes[4]; } /// /// Field 4.1 (2 bytes): /// Index of the first color map entry. Index refers to the starting entry in loading /// the color map. /// Example: If you would have 1024 entries in the entire color map but you only /// need to store 72 of those entries, this field allows you to start in the middle of /// the color-map (e.g., position 342). /// public ushort FirstEntryIndex { get { return firstEntryIndex; } set { firstEntryIndex = value; } } /// /// Field 4.2 (2 bytes): /// Total number of color map entries included. /// public ushort ColorMapLength { get { return colorMapLength; } set { colorMapLength = value; } } /// /// Field 4.3 (1 byte): /// Establishes the number of bits per entry. Typically 15, 16, 24 or 32-bit values are used. /// When working with VDA or VDA/D cards it is preferred that you select 16 bits(5 bits /// per primary with 1 bit to select interrupt control) and set the 16th bit to 0 so that the /// interrupt bit is disabled. Even if this field is set to 15 bits(5 bits per primary) you /// must still parse the color map data 16 bits at a time and ignore the 16th bit. /// When working with a TARGA M8 card you would select 24 bits (8 bits per primary) /// since the color map is defined as 256 entries of 24 bit color values. /// When working with a TrueVista card(ATVista or NuVista) you would select 24-bit(8 bits per /// primary) or 32-bit(8 bits per primary including Alpha channel) depending on your /// application’s use of look-up tables. It is suggested that when working with 16-bit and /// 32-bit color images, you store them as True-Color images and do not use the color map /// field to store look-up tables. Please refer to the TGA Extensions for fields better suited /// to storing look-up table information. /// public TgaColorMapEntrySize ColorMapEntrySize { get { return colorMapEntrySize; } set { colorMapEntrySize = value; } } /// /// Gets TGA Field size in bytes. /// public const int Size = 5; /// /// Make full independed copy of . /// /// Copy of public TgaColorMapSpec Clone() { return new TgaColorMapSpec(ToBytes()); } /// /// Make full independed copy of . /// /// Copy of object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaColorMapSpec) ? Equals((TgaColorMapSpec)obj) : false); } public bool Equals(TgaColorMapSpec item) { return (firstEntryIndex == item.firstEntryIndex && colorMapLength == item.colorMapLength && colorMapEntrySize == item.colorMapEntrySize); } public static bool operator ==(TgaColorMapSpec item1, TgaColorMapSpec item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaColorMapSpec item1, TgaColorMapSpec item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { return (firstEntryIndex << 16 | colorMapLength).GetHashCode() ^ colorMapEntrySize.GetHashCode(); } } public override string ToString() { return String.Format("{0}={1}, {2}={3}, {4}={5}", nameof(FirstEntryIndex), FirstEntryIndex, nameof(ColorMapLength), ColorMapLength, nameof(ColorMapEntrySize), ColorMapEntrySize); } /// /// Convert ColorMapSpec to byte array. /// /// Byte array with length = 5. public byte[] ToBytes() { return BitConverterExt.ToBytes(firstEntryIndex, colorMapLength, (byte)colorMapEntrySize); } } public class TgaComment : ICloneable { const int StrNLen = 80; //80 ASCII chars + 1 '\0' = 81 per SrtN! string origString = String.Empty; char blankSpaceChar = TgaString.DefaultBlankSpaceChar; public TgaComment() { } public TgaComment(string Str, char BlankSpaceChar = '\0') { if (Str == null) throw new ArgumentNullException(nameof(Str) + " = null!"); origString = Str; blankSpaceChar = BlankSpaceChar; } public TgaComment(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!"); string s = Encoding.ASCII.GetString(Bytes, 0, StrNLen); s += Encoding.ASCII.GetString(Bytes, 81, StrNLen); s += Encoding.ASCII.GetString(Bytes, 162, StrNLen); s += Encoding.ASCII.GetString(Bytes, 243, StrNLen); switch (s[s.Length - 1]) { case '\0': case ' ': blankSpaceChar = s[s.Length - 1]; origString = s.TrimEnd(new char[] { s[s.Length - 1] }); break; default: origString = s; break; } } /// /// Gets TGA Field size in bytes. /// public const int Size = 81 * 4; public string OriginalString { get { return origString; } set { origString = value; } } public char BlankSpaceChar { get { return blankSpaceChar; } set { blankSpaceChar = value; } } /// /// Make full independed copy of . /// /// Copy of public TgaComment Clone() { return new TgaComment(origString, blankSpaceChar); } /// /// Make full independed copy of . /// /// Copy of object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaComment) ? Equals((TgaComment)obj) : false); } public bool Equals(TgaComment item) { return (origString == item.origString && blankSpaceChar == item.blankSpaceChar); } public static bool operator ==(TgaComment item1, TgaComment item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaComment item1, TgaComment item2) { return !(item1 == item2); } public override int GetHashCode() { return origString.GetHashCode() ^ blankSpaceChar.GetHashCode(); } /// /// Get ASCII-Like string with string-terminators, example: "Line1 \0\0 Line2 \0\0\0". /// /// String with replaced string-terminators to "\0". public override string ToString() { return Encoding.ASCII.GetString(ToBytes()).Replace("\0", @"\0"); } /// /// Get ASCII-Like string to first string-terminator, example: /// "Some string \0 Some Data \0" - > "Some string". /// /// String to first string-terminator. public string GetString() { String Str = Encoding.ASCII.GetString(ToBytes()); for (int i = 1; i < 4; i++) Str = Str.Insert((StrNLen + 1) * i + i - 1, "\n"); return Str.Replace("\0", String.Empty).TrimEnd(new char[] { '\n' }); } /// /// Convert to byte array. /// /// Byte array, every byte is ASCII symbol. public byte[] ToBytes() { return ToBytes(origString, blankSpaceChar); } /// /// Convert to byte array. /// /// Input string. /// Char for filling blank space in string. /// Byte array, every byte is ASCII symbol. public static byte[] ToBytes(string Str, char BlankSpaceChar = '\0') { char[] C = new char[81 * 4]; for (int i = 0; i < C.Length; i++) { if ((i + 82) % 81 == 0) C[i] = TgaString.DefaultEndingChar; else { int Index = i - i / 81; C[i] = (Index < Str.Length ? Str[Index] : BlankSpaceChar); } } return Encoding.ASCII.GetBytes(C); } } public class TgaDateTime : ICloneable { ushort month = 0; ushort day = 0; ushort year = 0; ushort hour = 0; ushort minute = 0; ushort second = 0; /// /// Make empty . /// public TgaDateTime() { } /// /// Make from . /// /// Some variable. public TgaDateTime(DateTime DateAndTime) { month = (ushort)DateAndTime.Month; day = (ushort)DateAndTime.Day; year = (ushort)DateAndTime.Year; hour = (ushort)DateAndTime.Hour; minute = (ushort)DateAndTime.Minute; second = (ushort)DateAndTime.Second; } /// /// Make from ushort values. /// /// Month (1 - 12). /// Day (1 - 31). /// Year (4 digit, ie. 1989). /// Hour (0 - 23). /// Minute (0 - 59). /// Second (0 - 59). public TgaDateTime(ushort Month, ushort Day, ushort Year, ushort Hour, ushort Minute, ushort Second) { month = Month; day = Day; year = Year; hour = Hour; minute = Minute; second = Second; } /// /// Make from bytes. /// /// Array of bytes(byte[12]). public TgaDateTime(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); else if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be equal " + Size + "!"); month = BitConverter.ToUInt16(Bytes, 0); day = BitConverter.ToUInt16(Bytes, 2); year = BitConverter.ToUInt16(Bytes, 4); hour = BitConverter.ToUInt16(Bytes, 6); minute = BitConverter.ToUInt16(Bytes, 8); second = BitConverter.ToUInt16(Bytes, 10); } /// /// Gets or Sets month (1 - 12). /// public ushort Month { get { return month; } set { month = value; } } /// /// Gets or Sets day (1 - 31). /// public ushort Day { get { return day; } set { day = value; } } /// /// Gets or Sets year (4 digit, ie. 1989). /// public ushort Year { get { return year; } set { year = value; } } /// /// Gets or Sets hour (0 - 23). /// public ushort Hour { get { return hour; } set { hour = value; } } /// /// Gets or Sets minute (0 - 59). /// public ushort Minute { get { return minute; } set { minute = value; } } /// /// Gets or Sets second (0 - 59). /// public ushort Second { get { return second; } set { second = value; } } /// /// Gets TGA Field size in bytes. /// public const int Size = 12; /// /// Make full independed copy of . /// /// Copy of public TgaDateTime Clone() { return new TgaDateTime(month, day, year, hour, minute, second); } /// /// Make full independed copy of . /// /// Copy of object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaDateTime) ? Equals((TgaDateTime)obj) : false); } public bool Equals(TgaDateTime item) { return ( month == item.month && day == item.day && year == item.year && hour == item.hour && minute == item.minute && second == item.second); } public static bool operator ==(TgaDateTime item1, TgaDateTime item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaDateTime item1, TgaDateTime item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + (month << 16 | hour).GetHashCode(); hash = hash * 23 + (day << 16 | minute).GetHashCode(); hash = hash * 23 + (year << 16 | second).GetHashCode(); return hash; } } /// /// Gets like string. /// /// String in "1990.01.23 1:02:03" format. public override string ToString() { return String.Format("{0:D4}.{1:D2}.{2:D2} {3}:{4:D2}:{5:D2}", year, month, day, hour, minute, second); } /// /// Convert to byte array. /// /// Byte array with length = 12. public byte[] ToBytes() { return BitConverterExt.ToBytes(month, day, year, hour, minute, second); } /// /// Gets like . /// /// value of . public DateTime ToDateTime() { return new DateTime(year, month, day, hour, minute, second); } } public class TgaDevEntry : ICloneable { // Directory ushort fieldTag = 0; uint fieldFileOffset = 0; // Field byte[] data = null; /// /// Make empty . /// public TgaDevEntry() { } /// /// Make from other . /// /// Some variable. public TgaDevEntry(TgaDevEntry Entry) { if (Entry == null) throw new ArgumentNullException(); fieldTag = Entry.fieldTag; fieldFileOffset = Entry.fieldFileOffset; data = BitConverterExt.ToBytes(Entry.data); } /// /// Make from , and . /// /// TAG ID (0 - 65535). See . /// TAG file offset in bytes. See . /// This is DevEntry Field Data. See . public TgaDevEntry(ushort Tag, uint Offset, byte[] Data = null) { fieldTag = Tag; fieldFileOffset = Offset; data = Data; } /// /// Make from bytes. /// /// Array of bytes(byte[6] or bigger, if exist). public TgaDevEntry(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); else if (Bytes.Length < 6) throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be >= 6!"); fieldTag = BitConverter.ToUInt16(Bytes, 0); fieldFileOffset = BitConverter.ToUInt32(Bytes, 2); if (Bytes.Length > 6) data = BitConverterExt.GetElements(Bytes, 6, Bytes.Length - 6); } /// /// Each TAG is a value in the range of 0 to 65535. Values from 0 - 32767 are available for developer use, /// while values from 32768 - 65535 are reserved for Truevision. /// public ushort Tag { get { return fieldTag; } set { fieldTag = value; } } /// /// This OFFSET is a number of bytes from the beginning of the file to the start of the field /// referenced by the tag. /// public uint Offset { get { return fieldFileOffset; } set { fieldFileOffset = value; } } /// /// Field DATA. /// Although the size and format of the actual Developer Area fields are totally up to the developer, /// please define your formats to address future considerations you might have concerning your fields. /// This means that if you anticipate changing a field, build flexibility into the format to make these /// changes easy on other developers.Major changes to an existing TAG’s definition should never happen. /// public byte[] Data { get { return data; } set { data = value; } } /// /// The FIELD SIZE is a number of bytes in the field. Same like: , /// if is null, return -1. /// public int FieldSize { get { if (Data == null) return -1; return Data.Length; } } /// /// Gets TGA size in bytes (Always constant and equal 10!). /// It is not ! It is just size of entry sizeof(ushort + uint + uint). /// public const int Size = 10; /// /// Make full independed copy of . /// /// Copy of public TgaDevEntry Clone() { return new TgaDevEntry(this); } /// /// Make full independed copy of . /// /// Copy of object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaDevEntry) ? Equals((TgaDevEntry)obj) : false); } public bool Equals(TgaDevEntry item) { return (fieldTag == item.fieldTag && fieldFileOffset == item.fieldFileOffset && BitConverterExt.IsArraysEqual(data, item.data)); } public static bool operator ==(TgaDevEntry item1, TgaDevEntry item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaDevEntry item1, TgaDevEntry item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + fieldTag.GetHashCode(); hash = hash * 23 + fieldFileOffset.GetHashCode(); if (data != null) for (int i = 0; i < data.Length; i++) hash = hash * 23 + data[i].GetHashCode(); return hash; } } /// /// Gets like string. /// /// String in "Tag={0}, Offset={1}, FieldSize={2}" format. public override string ToString() { return String.Format("{0}={1}, {1}={2}, {3}={4}", nameof(Tag), fieldTag, nameof(Offset), fieldFileOffset, nameof(FieldSize), FieldSize); } /// /// Convert to byte array. (Not include !). /// /// Byte array with length = 10. public byte[] ToBytes() { return BitConverterExt.ToBytes(fieldTag, fieldFileOffset, (data == null ? 0 : data.Length)); } } //Not full ToBytes() public class TgaFraction : ICloneable { ushort numerator = 0; ushort denominator = 0; /// /// Make from and . /// /// Numerator value. /// Denominator value. public TgaFraction(ushort Numerator = 0, ushort Denominator = 0) { numerator = Numerator; denominator = Denominator; } /// /// Make from bytes. /// /// Array of bytes(byte[4]). public TgaFraction(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!"); numerator = BitConverter.ToUInt16(Bytes, 0); denominator = BitConverter.ToUInt16(Bytes, 2); } /// /// Gets or sets numerator value. /// public ushort Numerator { get { return numerator; } set { numerator = value; } } /// /// Gets or sets denominator value. /// public ushort Denominator { get { return denominator; } set { denominator = value; } } /// /// Get aspect ratio = / . /// public float AspectRatio { get { if (numerator == denominator) return 1f; return numerator / (float)denominator; } } /// /// Gets Empty , all values are 0. /// public static readonly TgaFraction Empty = new TgaFraction(); /// /// Gets One , all values are 1 (ones, 1 / 1 = 1). /// public static readonly TgaFraction One = new TgaFraction(1, 1); /// /// Gets TGA Field size in bytes. /// public const int Size = 4; /// /// Make full independed copy of . /// /// Copy of public TgaFraction Clone() { return new TgaFraction(numerator, denominator); } /// /// Make full independed copy of . /// /// Copy of object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaFraction) ? Equals((TgaFraction)obj) : false); } public bool Equals(TgaFraction item) { return (numerator == item.numerator && denominator == item.denominator); } public static bool operator ==(TgaFraction item1, TgaFraction item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaFraction item1, TgaFraction item2) { return !(item1 == item2); } public override int GetHashCode() { return (numerator << 16 | denominator).GetHashCode(); } /// /// Gets like string. /// /// String in "Numerator=1, Denominator=2" format. public override string ToString() { return String.Format("{0}={1}, {2}={3}", nameof(Numerator), numerator, nameof(Denominator), denominator); } /// /// Convert to byte array. /// /// Byte array with length = 4. public byte[] ToBytes() { return BitConverterExt.ToBytes(numerator, denominator); } } /// /// Contains image origin bits and alpha channel bits(or number of overlay bits) /// public class TgaImageDescriptor : ICloneable { TgaImgOrigin imageOrigin = 0; //bits 5-4 byte alphaChannelBits = 0; //bits 3-0 /// /// Make empty . /// public TgaImageDescriptor() { } /// /// Make from bytes. /// /// ImageDescriptor byte with reserved 7-6 bits, bits 5-4 used for /// , 3-0 used as alpha channel bits or number of overlay bits. public TgaImageDescriptor(byte b) { imageOrigin = (TgaImgOrigin)((b & 0x30) >> 4); alphaChannelBits = (byte)(b & 0x0F); } /// /// Gets or Sets Image Origin bits (select from enum only, don'n use 5-4 bits!). /// public TgaImgOrigin ImageOrigin { get { return imageOrigin; } set { imageOrigin = value; } } /// /// Gets or Sets alpha channel bits or number of overlay bits. /// public byte AlphaChannelBits { get { return alphaChannelBits; } set { alphaChannelBits = value; } } /// /// Gets TGA Field size in bytes. /// public const int Size = 1; /// /// Make full copy of . /// /// Full independent copy of . public TgaImageDescriptor Clone() { return new TgaImageDescriptor(ToByte()); } /// /// Make full copy of . /// /// Full independent copy of . object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaImageDescriptor) ? Equals((TgaImageDescriptor)obj) : false); } public bool Equals(TgaImageDescriptor item) { return (imageOrigin == item.imageOrigin && alphaChannelBits == item.alphaChannelBits); } public static bool operator ==(TgaImageDescriptor item1, TgaImageDescriptor item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaImageDescriptor item1, TgaImageDescriptor item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { return ((int)ImageOrigin << 4 | alphaChannelBits).GetHashCode(); } } public override string ToString() { return String.Format("{0}={1}, {2}={3}, ImageDescriptor_AsByte={4}", nameof(ImageOrigin), imageOrigin, nameof(AlphaChannelBits), alphaChannelBits, ToByte()); } /// /// Gets ImageDescriptor byte. /// /// ImageDescriptor byte with reserved 7-6 bits, bits 5-4 used for imageOrigin, /// 3-0 used as alpha channel bits or number of overlay bits. public byte ToByte() { return (byte)(((int)imageOrigin << 4) | alphaChannelBits); } } /// /// Image Specification - Field 5 (10 bytes): /// This field and its sub-fields describe the image screen location, size and pixel depth. /// These information is always written to the file. /// public class TgaImageSpec : ICloneable { ushort x_Origin = 0; ushort y_Origin = 0; ushort imageWidth = 0; ushort imageHeight = 0; TgaPixelDepth pixelDepth = TgaPixelDepth.Other; TgaImageDescriptor imageDescriptor = new TgaImageDescriptor(); public TgaImageSpec() { } /// /// Make ImageSpec from values. /// /// These specify the absolute horizontal coordinate for the lower /// left corner of the image as it is positioned on a display device having an origin at /// the lower left of the screen(e.g., the TARGA series). /// These specify the absolute vertical coordinate for the lower /// left corner of the image as it is positioned on a display device having an origin at /// the lower left of the screen(e.g., the TARGA series). /// This field specifies the width of the image in pixels. /// This field specifies the height of the image in pixels. /// This field indicates the number of bits per pixel. This number /// includes the Attribute or Alpha channel bits. Common values are 8, 16, 24 and 32 but /// other pixel depths could be used. /// Contains image origin bits and alpha channel bits /// (or number of overlay bits). public TgaImageSpec(ushort X_Origin, ushort Y_Origin, ushort ImageWidth, ushort ImageHeight, TgaPixelDepth PixelDepth, TgaImageDescriptor ImageDescriptor) { x_Origin = X_Origin; y_Origin = Y_Origin; imageWidth = ImageWidth; imageHeight = ImageHeight; pixelDepth = PixelDepth; imageDescriptor = ImageDescriptor; } /// /// Make ImageSpec from bytes. /// /// Array of bytes(byte[10]). public TgaImageSpec(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!"); x_Origin = BitConverter.ToUInt16(Bytes, 0); y_Origin = BitConverter.ToUInt16(Bytes, 2); imageWidth = BitConverter.ToUInt16(Bytes, 4); imageHeight = BitConverter.ToUInt16(Bytes, 6); pixelDepth = (TgaPixelDepth)Bytes[8]; imageDescriptor = new TgaImageDescriptor(Bytes[9]); } /// /// These specify the absolute horizontal coordinate for the lower left corner of the image /// as it is positioned on a display device having an origin at the lower left of the /// screen(e.g., the TARGA series). /// public ushort X_Origin { get { return x_Origin; } set { x_Origin = value; } } /// /// These specify the absolute vertical coordinate for the lower left corner of the image /// as it is positioned on a display device having an origin at the lower left of the /// screen(e.g., the TARGA series). /// public ushort Y_Origin { get { return y_Origin; } set { y_Origin = value; } } /// /// This field specifies the width of the image in pixels. /// public ushort ImageWidth { get { return imageWidth; } set { imageWidth = value; } } /// /// This field specifies the height of the image in pixels. /// public ushort ImageHeight { get { return imageHeight; } set { imageHeight = value; } } /// /// This field indicates the number of bits per pixel. This number includes the Attribute or /// Alpha channel bits. Common values are 8, 16, 24 and 32 but other pixel depths could be used. /// public TgaPixelDepth PixelDepth { get { return pixelDepth; } set { pixelDepth = value; } } /// /// Contains image origin bits and alpha channel bits(or number of overlay bits). /// public TgaImageDescriptor ImageDescriptor { get { return imageDescriptor; } set { imageDescriptor = value; } } /// /// Gets TGA Field size in bytes. /// public const int Size = 10; /// /// Make full copy of . /// /// public TgaImageSpec Clone() { return new TgaImageSpec(ToBytes()); } /// /// Make full copy of . /// /// object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaImageSpec) ? Equals((TgaImageSpec)obj) : false); } public bool Equals(TgaImageSpec item) { return ( x_Origin == item.x_Origin && y_Origin == item.y_Origin && imageWidth == item.imageWidth && imageHeight == item.imageHeight && pixelDepth == item.pixelDepth && imageDescriptor == item.imageDescriptor); } public static bool operator ==(TgaImageSpec item1, TgaImageSpec item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaImageSpec item1, TgaImageSpec item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + x_Origin.GetHashCode(); hash = hash * 23 + y_Origin.GetHashCode(); hash = hash * 23 + imageWidth.GetHashCode(); hash = hash * 23 + imageHeight.GetHashCode(); hash = hash * 23 + pixelDepth.GetHashCode(); if (imageDescriptor != null) hash = hash * 23 + imageDescriptor.GetHashCode(); return hash; } } public override string ToString() { return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}, {8}={9}, {10}={11}", nameof(X_Origin), x_Origin, nameof(Y_Origin), y_Origin, nameof(ImageWidth), imageWidth, nameof(ImageHeight), imageHeight, nameof(PixelDepth), pixelDepth, nameof(ImageDescriptor), imageDescriptor); } /// /// Convert to byte array. /// /// Byte array with length = 10. public byte[] ToBytes() { return BitConverterExt.ToBytes(x_Origin, y_Origin, imageWidth, imageHeight, (byte)pixelDepth, (imageDescriptor == null ? byte.MinValue : imageDescriptor.ToByte())); } } /// /// Postage Stamp Image (MaxSize 64x64, uncompressed, PixelDepth like in full image). /// public class TgaPostageStampImage : ICloneable { byte width = 0; byte height = 0; byte[] data = null; public TgaPostageStampImage() { } /// /// Make from bytes array. /// /// Bytes array, first 2 bytes are and , /// next bytes - image data. public TgaPostageStampImage(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length < 2) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be >= " + 2 + "!"); width = Bytes[0]; height = Bytes[1]; if (Bytes.Length > 2) data = BitConverterExt.GetElements(Bytes, 2, Bytes.Length - 2); } /// /// Make from bytes and size. /// /// Image Width. /// Image Height. /// Postage Stamp Image Data. public TgaPostageStampImage(byte Width, byte Height, byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); width = Width; height = Height; data = Bytes; } /// /// Postage Stamp Image Data /// public byte[] Data { get { return data; } set { data = value; } } /// /// Postage Stamp Image Width (maximum = 64). /// public byte Width { get { return width; } set { width = value; } } /// /// Postage Stamp Image Height (maximum = 64). /// public byte Height { get { return height; } set { height = value; } } /// /// Make full copy of . /// /// Full independent copy of . public TgaPostageStampImage Clone() { return new TgaPostageStampImage(width, height, BitConverterExt.ToBytes(data)); } /// /// Make full copy of . /// /// Full independent copy of . object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaPostageStampImage) ? Equals((TgaPostageStampImage)obj) : false); } public bool Equals(TgaPostageStampImage item) { return width == item.width && height == item.height && BitConverterExt.IsArraysEqual(data, item.data); } public static bool operator ==(TgaPostageStampImage item1, TgaPostageStampImage item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaPostageStampImage item1, TgaPostageStampImage item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 27; hash = (13 * hash) + width.GetHashCode(); hash = (13 * hash) + height.GetHashCode(); if (data != null) for (int i = 0; i < data.Length; i++) hash = (13 * hash) + data[i].GetHashCode(); return hash; } } public override string ToString() { return String.Format("{0}={1}, {2}={3}, DataLength={4}", nameof(Width), width, nameof(Height), height, (data == null ? -1 : data.Length)); } /// /// Convert to byte array. /// /// Byte array. public byte[] ToBytes() { return BitConverterExt.ToBytes(width, height, data); } } public class TgaSoftVersion : ICloneable { ushort versionNumber = 0; char versionLetter = ' '; /// /// Gets Empty , = ' ' (space). /// public TgaSoftVersion() { } /// /// Make from string. /// /// Input string, example: "123d". public TgaSoftVersion(string Str) { if (Str == null) throw new ArgumentNullException(); if (Str.Length < 3 || Str.Length > 4) throw new ArgumentOutOfRangeException(nameof(Str.Length) + " must be equal 3 or 4!"); bool Res = ushort.TryParse(Str.Substring(0, 3), out versionNumber); if (Res && Str.Length == 4) versionLetter = Str[3]; } /// /// Make from bytes. /// /// Bytes array (byte[3]). public TgaSoftVersion(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!"); versionNumber = BitConverter.ToUInt16(Bytes, 0); versionLetter = Encoding.ASCII.GetString(Bytes, 2, 1)[0]; } public TgaSoftVersion(ushort VersionNumber, char VersionLetter = ' ') { versionNumber = VersionNumber; versionLetter = VersionLetter; } public ushort VersionNumber { get { return versionNumber; } set { versionNumber = value; } } public char VersionLetter { get { return versionLetter; } set { versionLetter = value; } } /// /// Gets TGA Field size in bytes. /// public const int Size = 3; /// /// Make full copy of . /// /// public TgaSoftVersion Clone() { return new TgaSoftVersion(versionNumber, versionLetter); } /// /// Make full copy of . /// /// object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaSoftVersion) ? Equals((TgaSoftVersion)obj) : false); } public bool Equals(TgaSoftVersion item) { return (versionNumber == item.versionNumber && versionLetter == item.versionLetter); } public static bool operator ==(TgaSoftVersion item1, TgaSoftVersion item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaSoftVersion item1, TgaSoftVersion item2) { return !(item1 == item2); } public override int GetHashCode() { return versionNumber.GetHashCode() ^ versionLetter.GetHashCode(); } public override string ToString() { return (versionNumber.ToString("000") + versionLetter).TrimEnd(new char[] { ' ', '\0' }); } /// /// Convert to byte array. /// /// Byte array, (2 bytes) and /// (ASCII symbol). public byte[] ToBytes() { return ToBytes(versionNumber, versionLetter); } /// /// Convert to byte array. /// /// Set 123 for 1.23 version. /// Version letter, example: for 'a' - "1.23a". /// Byte array, (2 bytes) and (ASCII symbol). public static byte[] ToBytes(ushort VersionNumber, char VersionLetter = ' ') { return BitConverterExt.ToBytes(VersionNumber, Encoding.ASCII.GetBytes(VersionLetter.ToString())); } } /// /// Use it for working with ASCII strings in TGA files. /// public class TgaString : ICloneable { public const string XFileSignatuteConst = "TRUEVISION-XFILE"; public const string DotSymbolConst = "."; string origString = String.Empty; int length = 0; char blankSpaceChar = DefaultBlankSpaceChar; bool useEnding = false; public TgaString(bool UseEnding = false) { useEnding = UseEnding; } public TgaString(byte[] Bytes, bool UseEnding = false) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); length = Bytes.Length; useEnding = UseEnding; string s = Encoding.ASCII.GetString(Bytes, 0, Bytes.Length - (useEnding ? 1 : 0)); if (s.Length > 0) switch (s[s.Length - 1]) { case '\0': case ' ': blankSpaceChar = s[s.Length - 1]; origString = s.TrimEnd(new char[] { s[s.Length - 1] }); break; default: origString = s; break; } } public TgaString(int Length, bool UseEnding = false) { length = Length; useEnding = UseEnding; } public TgaString(string Str, int Length, bool UseEnding = false, char BlankSpaceChar = '\0') { if (Str == null) throw new ArgumentNullException(nameof(Str) + " = null!"); origString = Str; length = Length; blankSpaceChar = BlankSpaceChar; useEnding = UseEnding; } public string OriginalString { get { return origString; } set { origString = value; } } public int Length { get { return length; } set { length = value; } } public char BlankSpaceChar { get { return blankSpaceChar; } set { blankSpaceChar = value; } } public bool UseEndingChar { get { return useEnding; } set { useEnding = value; } } /// /// Gets ending char, default '\0'. /// public static readonly char DefaultEndingChar = '\0'; /// /// Gets blank space char, value = '\0'. /// public static readonly char DefaultBlankSpaceChar = '\0'; /// /// Gets Empty . /// public static readonly TgaString Empty = new TgaString(); /// /// Gets with = '\0' and = true. /// public static readonly TgaString ZeroTerminator = new TgaString(true); /// /// Gets "." with dot (period) symbol. /// public static readonly TgaString DotSymbol = new TgaString(DotSymbolConst, DotSymbolConst.Length); /// /// Gets "TRUEVISION-XFILE" (TGA File Format Version 2.0 signatute). /// public static readonly TgaString XFileSignatute = new TgaString(XFileSignatuteConst, XFileSignatuteConst.Length); /// /// Make full independed copy of . /// /// Copy of public TgaString Clone() { return new TgaString(origString, length, useEnding, blankSpaceChar); } /// /// Make full independed copy of . /// /// Copy of object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaString) ? Equals((TgaString)obj) : false); } public bool Equals(TgaString item) { return ( origString == item.origString && length == item.length && blankSpaceChar == item.blankSpaceChar && useEnding == item.useEnding); } public static bool operator ==(TgaString item1, TgaString item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaString item1, TgaString item2) { return !(item1 == item2); } public static TgaString operator +(TgaString item1, TgaString item2) { if (ReferenceEquals(item1, null) || ReferenceEquals(item2, null)) throw new ArgumentNullException(); return new TgaString(BitConverterExt.ToBytes(item1.ToBytes(), item2.ToBytes())); } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + origString.GetHashCode(); hash = hash * 23 + length.GetHashCode(); hash = hash * 23 + blankSpaceChar.GetHashCode(); hash = hash * 23 + useEnding.GetHashCode(); return hash; } } /// /// Get ASCII-Like string with string-terminators, example: "Some string\0\0\0\0\0". /// /// String with replaced string-terminators to "\0". public override string ToString() { return Encoding.ASCII.GetString(ToBytes()).Replace("\0", @"\0"); } /// /// Get ASCII-Like string to first string-terminator, example: /// "Some string \0 Some Data \0" - > "Some string". /// /// String to first string-terminator. public string GetString() { String Str = Encoding.ASCII.GetString(ToBytes()); int EndIndex = Str.IndexOf('\0'); if (EndIndex != -1) Str = Str.Substring(0, EndIndex); return Str; } /// /// Convert to byte array. /// /// Byte array, every byte is ASCII symbol. public byte[] ToBytes() { return ToBytes(origString, length, useEnding, blankSpaceChar); } /// /// Convert to byte array. /// /// Input string. /// Length of output ASCII string with Ending char (if used). /// Add to string or not? /// Char for filling blank space in string. If this char is '-' (only for example!), /// for string "ABC" with = 7, with = true, /// is '\0', result string is "ABC---\0". /// Byte array, every byte is ASCII symbol. public static byte[] ToBytes(string str, int Length, bool UseEnding = true, char BlankSpaceChar = '\0') { char[] C = new char[Math.Max(Length, (UseEnding ? 1 : 0))]; for (int i = 0; i < C.Length; i++) C[i] = (i < str.Length ? str[i] : BlankSpaceChar); if (UseEnding) C[C.Length - 1] = DefaultEndingChar; return Encoding.ASCII.GetBytes(C); } } public class TgaTime : ICloneable { ushort hours = 0; ushort minutes = 0; ushort seconds = 0; /// /// Make empty . /// public TgaTime() { } /// /// Make from . /// /// Some variable. public TgaTime(TimeSpan Time) { hours = (ushort)Time.TotalHours; minutes = (ushort)Time.Minutes; seconds = (ushort)Time.Seconds; } /// /// Make from ushort values. /// /// Hour (0 - 65535). /// Minute (0 - 59). /// Second (0 - 59). public TgaTime(ushort Hours, ushort Minutes, ushort Seconds) { hours = Hours; minutes = Minutes; seconds = Seconds; } /// /// Make from bytes. /// /// Array of bytes(byte[6]). public TgaTime(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); else if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes) + " must be equal " + Size + "!"); hours = BitConverter.ToUInt16(Bytes, 0); minutes = BitConverter.ToUInt16(Bytes, 2); seconds = BitConverter.ToUInt16(Bytes, 4); } /// /// Gets or Sets hour (0 - 65535). /// public ushort Hours { get { return hours; } set { hours = value; } } /// /// Gets or Sets minute (0 - 59). /// public ushort Minutes { get { return minutes; } set { minutes = value; } } /// /// Gets or Sets second (0 - 59). /// public ushort Seconds { get { return seconds; } set { seconds = value; } } /// /// Gets TGA Field size in bytes. /// public const int Size = 6; /// /// Make full independed copy of . /// /// Copy of public TgaTime Clone() { return new TgaTime(hours, minutes, seconds); } /// /// Make full independed copy of . /// /// Copy of object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaTime) ? Equals((TgaTime)obj) : false); } public bool Equals(TgaTime item) { return (hours == item.hours && minutes == item.minutes && seconds == item.seconds); } public static bool operator ==(TgaTime item1, TgaTime item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaTime item1, TgaTime item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + hours.GetHashCode(); hash = hash * 23 + (minutes << 16 | seconds).GetHashCode(); return hash; } } /// /// Gets like string. /// /// String in "H:M:S" format. public override string ToString() { return String.Format("{0}:{1}:{2}", hours, minutes, seconds); } /// /// Convert to byte array. /// /// Byte array with length = 6. public byte[] ToBytes() { return BitConverterExt.ToBytes(hours, minutes, seconds); } /// /// Gets like . /// /// value of . public TimeSpan ToTimeSpan() { return new TimeSpan(hours, minutes, seconds); } } //////////////////////////////////////////////////////////////////////////////////////////////// /// /// File Header Area (18 bytes) /// public class TgaHeader : ICloneable { byte idLength = 0; TgaColorMapType colorMapType = TgaColorMapType.NoColorMap; TgaImageType imageType = TgaImageType.NoImageData; TgaColorMapSpec colorMapSpec = new TgaColorMapSpec(); TgaImageSpec imageSpec = new TgaImageSpec(); /// /// Make empty . /// public TgaHeader() { } /// /// Make from bytes. /// /// Bytes array (byte[18]). public TgaHeader(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!"); idLength = Bytes[0]; colorMapType = (TgaColorMapType)Bytes[1]; imageType = (TgaImageType)Bytes[2]; colorMapSpec = new TgaColorMapSpec(BitConverterExt.GetElements(Bytes, 3, TgaColorMapSpec.Size)); imageSpec = new TgaImageSpec(BitConverterExt.GetElements(Bytes, 8, TgaImageSpec.Size)); } /// /// ID Length - Field 1 (1 byte): /// This field identifies the number of bytes contained in the Field. /// The maximum number of characters is 255. A value of zero indicates that no Image ID /// field is included with the image. /// public byte IDLength { get { return idLength; } set { idLength = value; } } /// /// Color Map Type - Field 2 (1 byte): /// This field indicates the type of color map (if any) included with the image. /// There are currently 2 defined values for this field: /// 0 - indicates that no color-map data is included with this image; /// 1 - indicates that a color-map is included with this image. /// public TgaColorMapType ColorMapType { get { return colorMapType; } set { colorMapType = value; } } /// /// Image Type - Field 3 (1 byte): /// The TGA File Format can be used to store Pseudo-Color, True-Color and Direct-Color images /// of various pixel depths. /// public TgaImageType ImageType { get { return imageType; } set { imageType = value; } } /// /// Color Map Specification - Field 4 (5 bytes): /// This field and its sub-fields describe the color map (if any) used for the image. /// If the Color Map Type field is set to zero, indicating that no color map exists, then /// these 5 bytes should be set to zero. These bytes always must be written to the file. /// public TgaColorMapSpec ColorMapSpec { get { return colorMapSpec; } set { colorMapSpec = value; } } /// /// Image Specification - Field 5 (10 bytes): /// This field and its sub-fields describe the image screen location, size and pixel depth. /// These information is always written to the file. /// public TgaImageSpec ImageSpec { get { return imageSpec; } set { imageSpec = value; } } /// /// Gets TGA Header Section size in bytes. /// public const int Size = 18; /// /// Make full copy of . /// /// Full independent copy of . public TgaHeader Clone() { return new TgaHeader(ToBytes()); } /// /// Make full copy of . /// /// Full independent copy of . object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaHeader) ? Equals((TgaHeader)obj) : false); } public bool Equals(TgaHeader item) { return (idLength == item.idLength && colorMapType == item.colorMapType && imageType == item.imageType && colorMapSpec == item.colorMapSpec && imageSpec == item.imageSpec); } public static bool operator ==(TgaHeader item1, TgaHeader item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaHeader item1, TgaHeader item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + (idLength << 24 | (byte)colorMapType << 8 | (byte)imageType).GetHashCode(); if (colorMapSpec != null) hash = hash * 23 + colorMapSpec.GetHashCode(); if (imageSpec != null) hash = hash * 23 + imageSpec.GetHashCode(); return hash; } } public override string ToString() { return String.Format("{0}={1}, {2}={3}, {4}={5}, {6}={7}, {8}={9}", nameof(IDLength), idLength, nameof(ColorMapType), colorMapType, nameof(ImageType), imageType, nameof(ColorMapSpec), colorMapSpec, nameof(ImageSpec), imageSpec); } /// /// Convert to byte array. /// /// Byte array with size equal . public byte[] ToBytes() { return BitConverterExt.ToBytes(idLength, (byte)colorMapType, (byte)imageType, (colorMapSpec == null ? new byte[TgaColorMapSpec.Size] : colorMapSpec.ToBytes()), (imageSpec == null ? new byte[TgaImageSpec.Size] : imageSpec.ToBytes())); } } /// /// Image Or ColorMap Area /// public class TgaImgOrColMap : ICloneable { TgaString imageID = null; byte[] colorMapData = null; byte[] imageData = null; /// /// Make empty . /// public TgaImgOrColMap() { } /// /// Make from arrays. /// /// This optional field contains identifying information about the image. /// The maximum length for this field is 255 bytes. Refer to /// for the length of this field. If field 1 is set to Zero indicating that no Image ID exists /// then these bytes are not written to the file. /// Color Map Data, see description. /// Image Data, see description. public TgaImgOrColMap(TgaString ImageID, byte[] ColorMapData, byte[] ImageData) { imageID = ImageID; colorMapData = ColorMapData; imageData = ImageData; } /// /// Image ID - Field 6 (variable): /// This optional field contains identifying information about the image. The maximum length /// for this field is 255 bytes. Refer to for the length of this /// field. If field 1 is set to Zero indicating that no Image ID exists then these bytes are not /// written to the file. Can have text inside (ASCII). /// public TgaString ImageID { get { return imageID; } set { imageID = value; } } /// /// Color Map Data - Field 7 (variable): /// If the Color Map Type(field 2) field is set to zero indicating that no Color-Map /// exists then this field will not be present (i.e., no bytes written to the file). /// This variable-length field contains the actual color map information (LUT data). /// Field 4.3 specifies the width in bits of each color map entry while Field 4.2 specifies /// the number of color map entries in this field. These two fields together are used to /// determine the number of bytes contained in field 7. /// Each color map entry is stored using an integral number of bytes.The RGB specification /// for each color map entry is stored in successive bit-fields in the multi-byte entries. /// Each color bit-field is assumed to be MIN(Field4.3/3, 8) bits in length. If Field 4.3 /// contains 24, then each color specification is 8 bits in length; if Field 4.3 contains 32, /// then each color specification is also 8 bits (32/3 gives 10, but 8 is smaller). /// Unused bit(s) in the multi-byte entries are assumed to specify attribute bits. The /// attribute bit field is often called the Alpha Channel, Overlay Bit(s) or Interrupt Bit(s). /// For the TARGA M-8, ATVista and NuVista, the number of bits in a color map specification is /// 24 (or 32). The red, green, and blue components are each represented by one byte. /// public byte[] ColorMapData { get { return colorMapData; } set { colorMapData = value; } } /// /// Image Data - Field 8 (variable): /// This field contains (Width)x(Height) pixels. Each pixel specifies image data in one /// of the following formats: /// a single color-map index for Pseudo-Color; /// Attribute, Red, Green and Blue ordered data for True-Color; /// and independent color-map indices for Direct-Color. /// The values for Width and Height are specified in Fields 5.3 and 5.4 respectively. /// The number of attribute and color-definition bits for each pixel are defined in Fields 5.6 /// and 5.5, respectively.Each pixel is stored as an integral number of bytes. /// public byte[] ImageData { get { return imageData; } set { imageData = value; } } /// /// Make full copy of . /// /// Full independed copy of . public TgaImgOrColMap Clone() { return new TgaImgOrColMap( (imageID == null ? null : imageID.Clone()), (colorMapData == null ? null : (byte[])colorMapData.Clone()), (imageData == null ? null : (byte[])imageData.Clone())); } /// /// Make full copy of . /// /// Full independed copy of . object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaImgOrColMap) ? Equals((TgaImgOrColMap)obj) : false); } public bool Equals(TgaImgOrColMap item) { return imageID == item.imageID && BitConverterExt.IsArraysEqual(colorMapData, item.colorMapData) && BitConverterExt.IsArraysEqual(imageData, item.imageData); } public static bool operator ==(TgaImgOrColMap item1, TgaImgOrColMap item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaImgOrColMap item1, TgaImgOrColMap item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 27; if (imageID != null) hash = (13 * hash) + imageID.GetHashCode(); if (colorMapData != null) for (int i = 0; i < colorMapData.Length; i++) hash = (13 * hash) + colorMapData[i].GetHashCode(); if (imageData != null) for (int i = 0; i < imageData.Length; i++) hash = (13 * hash) + imageData[i].GetHashCode(); return hash; } } } //No ToBytes() /// /// Developer Area /// //? public class TgaDevArea : ICloneable { List entries = new List(); public TgaDevArea() { } public TgaDevArea(List Entries) { if (Entries == null) throw new ArgumentNullException(nameof(Entries) + " = null!"); entries = Entries; } /// /// Developer Data - Field 9 (variable): /// public List Entries { get { return entries; } set { entries = value; } } public int Count { get { return entries.Count; } } public TgaDevEntry this[int index] { get { return entries[index]; } set { entries[index] = value; } } /// /// Make full copy of . /// /// Full independent copy of . public TgaDevArea Clone() { if (entries == null) return new TgaDevArea(null); List L = new List(); for (int i = 0; i < entries.Count; i++) L.Add(entries[i].Clone()); return new TgaDevArea(L); } /// /// Make full copy of . /// /// Full independent copy of . object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaDevArea) ? Equals((TgaDevArea)obj) : false); } public bool Equals(TgaDevArea item) { return BitConverterExt.IsListsEqual(entries, item.entries); } public static bool operator ==(TgaDevArea item1, TgaDevArea item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaDevArea item1, TgaDevArea item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 27; if (entries != null) for (int i = 0; i < entries.Count; i++) hash = (13 * hash) + entries[i].GetHashCode(); return hash; } } /// /// Convert (without Fields Data, only Directory!) to byte array. /// /// Byte array, Len = (NUMBER_OF_TAGS_IN_THE_DIRECTORY * 10) + 2 bytes in size. /// The "+ 2" includes the 2 bytes for the number of tags in the directory. public byte[] ToBytes() { if (entries == null) throw new Exception(nameof(Entries) + " = null!"); ushort NumberOfEntries = (ushort)Math.Min(ushort.MaxValue, entries.Count); List DevDir = new List(BitConverter.GetBytes(NumberOfEntries)); for (int i = 0; i < entries.Count; i++) { DevDir.AddRange(BitConverter.GetBytes(entries[i].Tag)); DevDir.AddRange(BitConverter.GetBytes(entries[i].Offset)); DevDir.AddRange(BitConverter.GetBytes(entries[i].FieldSize)); } return DevDir.ToArray(); } } //Not full ToBytes() /// /// Extension Area /// public class TgaExtArea : ICloneable { public const int MinSize = 495; //bytes ushort extensionSize = MinSize; TgaString authorName = new TgaString(41, true); TgaComment authorComments = new TgaComment(); TgaDateTime dateTimeStamp = new TgaDateTime(); TgaString jobNameOrID = new TgaString(41, true); TgaTime jobTime = new TgaTime(); TgaString softwareID = new TgaString(41, true); TgaSoftVersion softVersion = new TgaSoftVersion(); TgaColorKey keyColor = new TgaColorKey(); TgaFraction pixelAspectRatio = TgaFraction.Empty; TgaFraction gammaValue = TgaFraction.Empty; uint colorCorrectionOffset = 0; uint postageStampOffset = 0; uint scanLineOffset = 0; TgaAttrType attributesType = TgaAttrType.NoAlpha; uint[] scanLineTable = null; TgaPostageStampImage postageStampImage = null; ushort[] colorCorrectionTable = null; byte[] otherDataInExtensionArea = null; public TgaExtArea() { } /// /// Make from bytes. Warning: , /// , not included, /// because thea are can be not in the Extension Area of TGA file! /// /// Bytes of . /// Scan Line Table. /// Postage Stamp Image. /// Color Correction Table. public TgaExtArea(byte[] Bytes, uint[] SLT = null, TgaPostageStampImage PostImg = null, ushort[] CCT = null) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length < MinSize) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be >= " + MinSize + "!"); extensionSize = BitConverter.ToUInt16(Bytes, 0); authorName = new TgaString(BitConverterExt.GetElements(Bytes, 2, 41), true); authorComments = new TgaComment(BitConverterExt.GetElements(Bytes, 43, TgaComment.Size)); dateTimeStamp = new TgaDateTime(BitConverterExt.GetElements(Bytes, 367, TgaDateTime.Size)); jobNameOrID = new TgaString(BitConverterExt.GetElements(Bytes, 379, 41), true); jobTime = new TgaTime(BitConverterExt.GetElements(Bytes, 420, TgaTime.Size)); softwareID = new TgaString(BitConverterExt.GetElements(Bytes, 426, 41), true); softVersion = new TgaSoftVersion(BitConverterExt.GetElements(Bytes, 467, TgaSoftVersion.Size)); keyColor = new TgaColorKey(BitConverterExt.GetElements(Bytes, 470, TgaColorKey.Size)); pixelAspectRatio = new TgaFraction(BitConverterExt.GetElements(Bytes, 474, TgaFraction.Size)); gammaValue = new TgaFraction(BitConverterExt.GetElements(Bytes, 478, TgaFraction.Size)); colorCorrectionOffset = BitConverter.ToUInt32(Bytes, 482); postageStampOffset = BitConverter.ToUInt32(Bytes, 486); scanLineOffset = BitConverter.ToUInt32(Bytes, 490); attributesType = (TgaAttrType)Bytes[494]; if (extensionSize > MinSize) otherDataInExtensionArea = BitConverterExt.GetElements(Bytes, 495, Bytes.Length - MinSize); scanLineTable = SLT; postageStampImage = PostImg; colorCorrectionTable = CCT; } #region Properties /// /// Extension Size - Field 10 (2 Bytes): /// This field is a SHORT field which specifies the number of BYTES in the fixedlength portion of /// the Extension Area. For Version 2.0 of the TGA File Format, this number should be set to 495. /// If the number found in this field is not 495, then the file will be assumed to be of a /// version other than 2.0. If it ever becomes necessary to alter this number, the change /// will be controlled by Truevision, and will be accompanied by a revision to the TGA File /// Format with an accompanying change in the version number. /// public ushort ExtensionSize { get { return extensionSize; } set { extensionSize = value; } } /// /// Author Name - Field 11 (41 Bytes): /// Bytes 2-42 - This field is an ASCII field of 41 bytes where the last byte must be a null /// (binary zero). This gives a total of 40 ASCII characters for the name. If the field is used, /// it should contain the name of the person who created the image (author). If the field is not /// used, you may fill it with nulls or a series of blanks(spaces) terminated by a null. /// The 41st byte must always be a null. /// public TgaString AuthorName { get { return authorName; } set { authorName = value; } } /// /// Author Comments - Field 12 (324 Bytes): /// Bytes 43-366 - This is an ASCII field consisting of 324 bytes which are organized as four lines /// of 80 characters, each followed by a null terminator.This field is provided, in addition to the /// original IMAGE ID field(in the original TGA format), because it was determined that a few /// developers had used the IMAGE ID field for their own purposes.This field gives the developer /// four lines of 80 characters each, to use as an Author Comment area. Each line is fixed to 81 /// bytes which makes access to the four lines easy.Each line must be terminated by a null. /// If you do not use all 80 available characters in the line, place the null after the last /// character and blank or null fill the rest of the line. The 81st byte of each of the four /// lines must be null. /// public TgaComment AuthorComments { get { return authorComments; } set { authorComments = value; } } /// /// Date/Time Stamp - Field 13 (12 Bytes): /// Bytes 367-378 - This field contains a series of 6 SHORT values which define the integer /// value for the date and time that the image was saved. This data is formatted as follows: /// SHORT 0: Month(1 - 12) /// SHORT 1: Day(1 - 31) /// SHORT 2: Year(4 digit, ie. 1989) /// SHORT 3: Hour(0 - 23) /// SHORT 4: Minute(0 - 59) /// SHORT 5: Second(0 - 59) /// Even though operating systems typically time- and date-stamp files, this feature is /// provided because the operating system may change the time and date stamp if the file is /// copied. By using this area, you are guaranteed an unmodified region for date and time /// recording. If the fields are not used, you should fill them with binary zeros (0). /// public TgaDateTime DateTimeStamp { get { return dateTimeStamp; } set { dateTimeStamp = value; } } /// /// Job Name/ID - Field 14 (41 Bytes): /// Bytes 379-419 - This field is an ASCII field of 41 bytes where the last byte must be /// a binary zero. This gives a total of 40 ASCII characters for the job name or the ID. /// If the field is used, it should contain a name or id tag which refers to the job with /// which the image was associated.This allows production companies (and others) to tie /// images with jobs by using this field as a job name (i.e., CITY BANK) or job id number /// (i.e., CITY023). If the field is not used, you may fill it with a null terminated series /// of blanks (spaces) or nulls. In any case, the 41st byte must be a null. /// public TgaString JobNameOrID { get { return jobNameOrID; } set { jobNameOrID = value; } } /// /// Job Time - Field 15 (6 Bytes): /// Bytes 420-425 - This field contains a series of 3 SHORT values which define the integer /// value for the job elapsed time when the image was saved.This data is formatted as follows: /// SHORT 0: Hours(0 - 65535) /// SHORT 1: Minutes(0 - 59) /// SHORT 2: Seconds(0 - 59) /// The purpose of this field is to allow production houses (and others) to keep a running total /// of the amount of time invested in a particular image. This may be useful for billing, costing, /// and time estimating. If the fields are not used, you should fill them with binary zeros (0). /// public TgaTime JobTime { get { return jobTime; } set { jobTime = value; } } /// /// Software ID - Field 16 (41 Bytes): /// Bytes 426-466 - This field is an ASCII field of 41 bytes where the last byte must be /// a binary zero (null). This gives a total of 40 ASCII characters for the Software ID. /// The purpose of this field is to allow software to determine and record with what program /// a particular image was created.If the field is not used, you may fill it with a /// null terminated series of blanks (spaces) or nulls. The 41st byte must always be a null. /// public TgaString SoftwareID { get { return softwareID; } set { softwareID = value; } } /// /// Software Version - Field 17 (3 Bytes): /// Bytes 467-469 - This field consists of two sub-fields, a SHORT and an ASCII BYTE. /// The purpose of this field is to define the version of software defined by the /// “Software ID” field above. The SHORT contains the version number as a binary /// integer times 100. /// Therefore, software version 4.17 would be the integer value 417.This allows for /// two decimal positions of sub-version.The ASCII BYTE supports developers who also /// tag a release letter to the end. For example, if the version number is 1.17b, then /// the SHORT would contain 117. and the ASCII BYTE would contain “b”. /// The organization is as follows: /// SHORT (Bytes 0 - 1): Version Number * 100 /// BYTE(Byte 2): Version Letter /// If you do not use this field, set the SHORT to binary zero, and the BYTE to a space(“ “) /// public TgaSoftVersion SoftVersion { get { return softVersion; } set { softVersion = value; } } /// /// Key Color - Field 18 (4 Bytes): /// Bytes 470-473 - This field contains a long value which is the key color in effect at /// the time the image is saved. The format is in A:R:G:B where ‘A’ (most significant byte) /// is the alpha channel key color(if you don’t have an alpha channel in your application, /// keep this byte zero [0]). /// The Key Color can be thought of as the ‘background color’ or ‘transparent color’. /// This is the color of the ‘non image’ area of the screen, and the same color that the /// screen would be cleared to if erased in the application. If you don’t use this field, /// set it to all zeros (0). Setting the field to all zeros is the same as selecting a key /// color of black. /// A good example of a key color is the ‘transparent color’ used in TIPS™ for WINDOW loading/saving. /// public TgaColorKey KeyColor { get { return keyColor; } set { keyColor = value; } } /// /// Pixel Aspect Ratio - Field 19 (4 Bytes): /// Bytes 474-477 - This field contains two SHORT sub-fields, which when taken together /// specify a pixel size ratio.The format is as follows: /// SHORT 0: Pixel Ratio Numerator(pixel width) /// SHORT 1: Pixel Ratio Denominator(pixel height) /// These sub-fields may be used to determine the aspect ratio of a pixel. This is useful when /// it is important to preserve the proper aspect ratio of the saved image. If the two values /// are set to the same non-zero value, then the image is composed of square pixels. A zero /// in the second sub-field (denominator) indicates that no pixel aspect ratio is specified. /// public TgaFraction PixelAspectRatio { get { return pixelAspectRatio; } set { pixelAspectRatio = value; } } /// /// Gamma Value - Field 20 (4 Bytes): /// Bytes 478-481 - This field contains two SHORT sub-fields, which when taken together in a ratio, /// provide a fractional gamma value.The format is as follows: /// SHORT 0: Gamma Numerator /// SHORT 1: Gamma Denominator /// The resulting value should be in the range of 0.0 to 10.0, with only one decimal place of /// precision necessary. An uncorrected image (an image with no gamma) should have the value 1.0 as /// the result.This may be accomplished by placing thesame, non-zero values in both positions /// (i.e., 1/1). If you decide to totally ignore this field, please set the denominator (the second /// SHORT) to the value zero. This will indicate that the Gamma Value field is not being used. /// public TgaFraction GammaValue { get { return gammaValue; } set { gammaValue = value; } } /// /// Color Correction Offset - Field 21 (4 Bytes): /// Bytes 482-485 - This field is a 4-byte field containing a single offset value. This is an offset /// from the beginning of the file to the start of the Color Correction table. This table may be /// written anywhere between the end of the Image Data field (field 8) and the start of the TGA /// File Footer. If the image has no Color Correction Table or if the Gamma Value setting is /// sufficient, set this value to zero and do not write a Correction Table anywhere. /// public uint ColorCorrectionTableOffset { get { return colorCorrectionOffset; } set { colorCorrectionOffset = value; } } /// /// Postage Stamp Offset - Field 22 (4 Bytes): /// Bytes 486-489 - This field is a 4-byte field containing a single offset value. This is an offset /// from the beginning of the file to the start of the Postage Stamp Image. The Postage Stamp Image /// must be written after Field 25 (Scan Line Table) but before the start of the TGA File Footer. /// If no postage stamp is stored, set this field to the value zero (0). /// public uint PostageStampOffset { get { return postageStampOffset; } set { postageStampOffset = value; } } /// /// Scan Line Offset - Field 23 (4 Bytes): /// Bytes 490-493 - This field is a 4-byte field containing a single offset value. This is an /// offset from the beginning of the file to the start of the Scan Line Table. /// public uint ScanLineOffset { get { return scanLineOffset; } set { scanLineOffset = value; } } /// /// Attributes Type - Field 24 (1 Byte): /// Byte 494 - This single byte field contains a value which specifies the type of Alpha channel /// data contained in the file. Value Meaning: /// 0: no Alpha data included (bits 3-0 of field 5.6 should also be set to zero) /// 1: undefined data in the Alpha field, can be ignored /// 2: undefined data in the Alpha field, but should be retained /// 3: useful Alpha channel data is present /// 4: pre-multiplied Alpha(see description below) /// 5 -127: RESERVED /// 128-255: Un-assigned /// Pre-multiplied Alpha Example: Suppose the Alpha channel data is being used to specify the /// opacity of each pixel(for use when the image is overlayed on another image), where 0 indicates /// that the pixel is completely transparent and a value of 1 indicates that the pixel is /// completely opaque(assume all component values have been normalized). /// A quadruple(a, r, g, b) of( 0.5, 1, 0, 0) would indicate that the pixel is pure red with a /// transparency of one-half. For numerous reasons(including image compositing) is is better to /// pre-multiply the individual color components with the value in the Alpha channel. /// A pre-multiplication of the above would produce a quadruple(0.5, 0.5, 0, 0). /// A value of 3 in the Attributes Type Field(field 23) would indicate that the color components /// of the pixel have already been scaled by the value in the Alpha channel. /// public TgaAttrType AttributesType { get { return attributesType; } set { attributesType = value; } } /// /// Scan Line Table - Field 25 (Variable): /// This information is provided, at the developers’ request, for two purposes: /// 1) To make random access of compressed images easy. /// 2) To allow “giant picture” access in smaller “chunks”. /// This table should contain a series of 4-byte offsets.Each offset you write should point to the /// start of the next scan line, in the order that the image was saved (i.e., top down or bottom up). /// The offset should be from the start of the file.Therefore, you will have a four byte value for /// each scan line in your image. This means that if your image is 768 pixels tall, you will have 768, /// 4-byte offset pointers (for a total of 3072 bytes). This size is not extreme, and thus this table /// can be built and maintained in memory, and then written out at the proper time. /// public uint[] ScanLineTable { get { return scanLineTable; } set { scanLineTable = value; } } /// /// Postage Stamp Image - Field 26 (Variable): /// The Postage Stamp area is a smaller representation of the original image. This is useful for /// “browsing” a collection of image files. If your application can deal with a postage stamp image, /// it is recommended that you create one using sub-sampling techniques to create the best /// representation possible. The postage stamp image must be stored in the same format as the normal /// image specified in the file, but without any compression. The first byte of the postage stamp /// image specifies the X size of the stamp in pixels, the second byte of the stamp image specifies the /// Y size of the stamp in pixels. Truevision does not recommend stamps larger than 64x64 pixels, and /// suggests that any stamps stored larger be clipped. Obviously, the storage of the postage stamp /// relies heavily on the storage of the image. The two images are stored using the same format under /// the assumption that if you can read the image, then you can read the postage stamp. If the original /// image is color mapped, DO NOT average the postage stamp, as you will create new colors not in your map. /// public TgaPostageStampImage PostageStampImage { get { return postageStampImage; } set { postageStampImage = value; } } /// /// Color Correction Table - Field 27 (2K Bytes): /// The Color Correction Table is a block of 256 x 4 SHORT values, where each set of /// four contiguous values are the desired A:R:G:B correction for that entry. This /// allows the user to store a correction table for image remapping or LUT driving. /// Since each color in the block is a SHORT, the maximum value for a color gun (i.e., /// A, R, G, B) is 65535, and the minimum value is zero.Therefore, BLACK maps to /// 0, 0, 0, 0 and WHITE maps to 65535, 65535, 65535, 65535. /// public ushort[] ColorCorrectionTable { get { return colorCorrectionTable; } set { colorCorrectionTable = value; } } /// /// Other Data In Extension Area (if > 495). /// public byte[] OtherDataInExtensionArea { get { return otherDataInExtensionArea; } set { otherDataInExtensionArea = value; } } #endregion /// /// Make full copy of . /// /// Full independent copy of . public TgaExtArea Clone() { TgaExtArea NewExtArea = new TgaExtArea(); NewExtArea.extensionSize = extensionSize; NewExtArea.authorName = authorName.Clone(); NewExtArea.authorComments = authorComments.Clone(); NewExtArea.dateTimeStamp = dateTimeStamp.Clone(); NewExtArea.jobNameOrID = jobNameOrID.Clone(); NewExtArea.jobTime = jobTime.Clone(); NewExtArea.softwareID = softwareID.Clone(); NewExtArea.softVersion = softVersion.Clone(); NewExtArea.keyColor = keyColor.Clone(); NewExtArea.pixelAspectRatio = pixelAspectRatio.Clone(); NewExtArea.gammaValue = gammaValue.Clone(); NewExtArea.colorCorrectionOffset = colorCorrectionOffset; NewExtArea.postageStampOffset = postageStampOffset; NewExtArea.scanLineOffset = scanLineOffset; NewExtArea.attributesType = attributesType; if (scanLineTable != null) NewExtArea.scanLineTable = (uint[])scanLineTable.Clone(); if (postageStampImage != null) NewExtArea.postageStampImage = new TgaPostageStampImage(postageStampImage.ToBytes()); if (colorCorrectionTable != null) NewExtArea.colorCorrectionTable = (ushort[])colorCorrectionTable.Clone(); if (otherDataInExtensionArea != null) NewExtArea.otherDataInExtensionArea = (byte[])otherDataInExtensionArea.Clone(); return NewExtArea; } /// /// Make full copy of . /// /// Full independent copy of . object ICloneable.Clone() { return Clone(); } public override bool Equals(object obj) { return ((obj is TgaExtArea) ? Equals((TgaExtArea)obj) : false); } public bool Equals(TgaExtArea item) { return (extensionSize == item.extensionSize && authorName == item.authorName && authorComments == item.authorComments && dateTimeStamp == item.dateTimeStamp && jobNameOrID == item.jobNameOrID && jobTime == item.jobTime && softwareID == item.softwareID && softVersion == item.softVersion && keyColor == item.keyColor && pixelAspectRatio == item.pixelAspectRatio && gammaValue == item.gammaValue && colorCorrectionOffset == item.colorCorrectionOffset && postageStampOffset == item.postageStampOffset && scanLineOffset == item.scanLineOffset && attributesType == item.attributesType && BitConverterExt.IsArraysEqual(scanLineTable, item.scanLineTable) && postageStampImage == item.postageStampImage && BitConverterExt.IsArraysEqual(colorCorrectionTable, item.colorCorrectionTable) && BitConverterExt.IsArraysEqual(otherDataInExtensionArea, item.otherDataInExtensionArea)); } public static bool operator ==(TgaExtArea item1, TgaExtArea item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaExtArea item1, TgaExtArea item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 27; hash = (13 * hash) + extensionSize.GetHashCode(); hash = (13 * hash) + authorName.GetHashCode(); hash = (13 * hash) + authorComments.GetHashCode(); hash = (13 * hash) + dateTimeStamp.GetHashCode(); hash = (13 * hash) + jobNameOrID.GetHashCode(); hash = (13 * hash) + jobTime.GetHashCode(); hash = (13 * hash) + softwareID.GetHashCode(); hash = (13 * hash) + softVersion.GetHashCode(); hash = (13 * hash) + keyColor.GetHashCode(); hash = (13 * hash) + pixelAspectRatio.GetHashCode(); hash = (13 * hash) + gammaValue.GetHashCode(); hash = (13 * hash) + colorCorrectionOffset.GetHashCode(); hash = (13 * hash) + postageStampOffset.GetHashCode(); hash = (13 * hash) + scanLineOffset.GetHashCode(); hash = (13 * hash) + attributesType.GetHashCode(); if (scanLineTable != null) for (int i = 0; i < scanLineTable.Length; i++) hash = (13 * hash) + scanLineTable[i].GetHashCode(); if (postageStampImage != null) hash = (13 * hash) + postageStampImage.GetHashCode(); if (colorCorrectionTable != null) for (int i = 0; i < colorCorrectionTable.Length; i++) hash = (13 * hash) + colorCorrectionTable[i].GetHashCode(); if (otherDataInExtensionArea != null) for (int i = 0; i < otherDataInExtensionArea.Length; i++) hash = (13 * hash) + otherDataInExtensionArea[i].GetHashCode(); return hash; } } /// /// Convert to byte array. Warning: , /// , not included, /// because thea are can be not in the Extension Area of TGA file! /// /// Byte array. public byte[] ToBytes() { #region Exceptions check if (authorName == null) authorName = new TgaString(41, true); if (authorComments == null) authorComments = new TgaComment(); if (dateTimeStamp == null) dateTimeStamp = new TgaDateTime(DateTime.UtcNow); if (jobNameOrID == null) jobNameOrID = new TgaString(41, true); if (jobTime == null) jobTime = new TgaTime(); if (softwareID == null) softwareID = new TgaString(41, true); if (softVersion == null) softVersion = new TgaSoftVersion(); if (keyColor == null) keyColor = new TgaColorKey(); if (pixelAspectRatio == null) pixelAspectRatio = new TgaFraction(); if (gammaValue == null) gammaValue = new TgaFraction(); #endregion return BitConverterExt.ToBytes( extensionSize, authorName.ToBytes(), authorComments.ToBytes(), dateTimeStamp.ToBytes(), jobNameOrID.ToBytes(), jobTime.ToBytes(), softwareID.ToBytes(), softVersion.ToBytes(), keyColor.ToBytes(), pixelAspectRatio.ToBytes(), gammaValue.ToBytes(), colorCorrectionOffset, postageStampOffset, scanLineOffset, (byte)attributesType, otherDataInExtensionArea); } } //Not full ToBytes() /// /// File Footer Area /// public class TgaFooter : ICloneable { uint extAreaOffset = 0; uint devDirOffset = 0; TgaString signature = TgaString.XFileSignatute; TgaString reservedChar = TgaString.DotSymbol; TgaString zeroStrTerminator = TgaString.ZeroTerminator; /// /// Make NewXFile format TGA Footer with = 0 and /// = 0. /// public TgaFooter() { } /// /// Make from values. /// /// Extension Area Offset, offset from the beginning of the file. /// Developer Directory Offset, offset from the beginning of the file. /// New TGA format signature. /// Reserved Character - ASCII character “.” (period). /// Binary Zero Terminator, a binary zero which acts as a final terminator. public TgaFooter(uint ExtOff, uint DevDirOff, TgaString Sign, TgaString ReservChr, TgaString Termin) { extAreaOffset = ExtOff; devDirOffset = DevDirOff; signature = Sign; reservedChar = ReservChr; zeroStrTerminator = Termin; } /// /// Make from bytes (if signature is right). /// /// Bytes array (byte[26]). public TgaFooter(byte[] Bytes) { if (Bytes == null) throw new ArgumentNullException(nameof(Bytes) + " = null!"); if (Bytes.Length != Size) throw new ArgumentOutOfRangeException(nameof(Bytes.Length) + " must be equal " + Size + "!"); extAreaOffset = BitConverter.ToUInt32(Bytes, 0); devDirOffset = BitConverter.ToUInt32(Bytes, 4); signature = new TgaString(BitConverterExt.GetElements(Bytes, 8, TgaString.XFileSignatuteConst.Length)); reservedChar = new TgaString(new byte[] { Bytes[24] }); zeroStrTerminator = new TgaString(new byte[] { Bytes[25] }); } /// /// Byte 0-3 - Extension Area Offset - Field 28 /// The first four bytes (bytes 0-3, the first LONG) of the TGA File Footer contain an /// offset from the beginning of the file to the start of the Extension Area. Simply /// SEEK to this location to position to the start of the Extension Area. If the /// Extension Area Offset is zero, no Extension Area exists in the file. /// public uint ExtensionAreaOffset { get { return extAreaOffset; } set { extAreaOffset = value; } } /// /// Byte 4-7 - Developer Directory Offset - Field 29 /// The next four bytes(bytes 4-7, the second LONG) contain an offset from the /// beginning of the file to the start of the Developer Directory. If the Developer /// Directory Offset is zero, then the Developer Area does not exist. /// public uint DeveloperDirectoryOffset { get { return devDirOffset; } set { devDirOffset = value; } } /// /// Byte 8-23 - Signature - Field 30 /// This string is exactly 16 bytes long and is formatted exactly as shown below /// capital letters), with a hyphen between “TRUEVISION” and “XFILE.” If the /// signature is detected, the file is assumed to be of the New TGA format and MAY, /// therefore, contain the Developer Area and/or the Extension Area fields.If the /// signature is not found, then the file is assumed to be in the Original TGA format. /// public TgaString Signature { get { return signature; } set { signature = value; } } /// /// Byte 24 - Reserved Character - Field 31 /// Byte 24 is an ASCII character “.” (period). This character MUST BE a period or /// the file is not considered a proper TGA file. /// public TgaString ReservedCharacter { get { return reservedChar; } set { reservedChar = value; } } /// /// Byte 25 - Binary Zero String Terminator - Field 32 /// Byte 25 is a binary zero which acts as a final terminator and allows the entire TGA /// File Footer to be read and utilized as a “C” string. /// public TgaString BinaryZeroStringTerminator { get { return zeroStrTerminator; } set { zeroStrTerminator = value; } } /// /// Make full copy of . /// /// public TgaFooter Clone() { return new TgaFooter(extAreaOffset, devDirOffset, signature.Clone(), reservedChar.Clone(), zeroStrTerminator.Clone()); } /// /// Make full copy of . /// /// object ICloneable.Clone() { return Clone(); } /// /// Gets TGA Footer Section size in bytes. /// public const int Size = 26; public override bool Equals(object obj) { return ((obj is TgaFooter) ? Equals((TgaFooter)obj) : false); } public bool Equals(TgaFooter item) { return (extAreaOffset == item.extAreaOffset && devDirOffset == item.devDirOffset && signature == item.signature && reservedChar == item.reservedChar && zeroStrTerminator == item.zeroStrTerminator); } public static bool operator ==(TgaFooter item1, TgaFooter item2) { if (ReferenceEquals(item1, null)) return ReferenceEquals(item2, null); if (ReferenceEquals(item2, null)) return ReferenceEquals(item1, null); return item1.Equals(item2); } public static bool operator !=(TgaFooter item1, TgaFooter item2) { return !(item1 == item2); } public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + extAreaOffset.GetHashCode(); hash = hash * 23 + devDirOffset.GetHashCode(); if (signature != null) hash = hash * 23 + signature.GetHashCode(); if (reservedChar != null) hash = hash * 23 + reservedChar.GetHashCode(); if (zeroStrTerminator != null) hash = hash * 23 + zeroStrTerminator.GetHashCode(); return hash; } } public override string ToString() { return String.Format("{0}={1}, {2}={3}, FullSignature={4}", nameof(ExtensionAreaOffset), extAreaOffset, nameof(DeveloperDirectoryOffset), devDirOffset, (signature + reservedChar + zeroStrTerminator).ToString()); } /// /// Convert to byte array. /// /// Byte array with size equal . public byte[] ToBytes() { return BitConverterExt.ToBytes(extAreaOffset, devDirOffset, signature.ToBytes(), reservedChar.ToBytes(), zeroStrTerminator.ToBytes()); } /// /// Is footer is real footer of TGA File Format Version 2.0? /// Checking by . /// public bool IsFooterCorrect { get { return signature == TgaString.XFileSignatute; } } } //////////////////////////////////////////////////////////////////////////////////////////////// /// /// Simplify ByteConversion operations, like concatination of byte arrays, comparing and other. /// public static class BitConverterExt { /// /// Combine byte, byte[], (u)short, (u)int, (u)long values to byte[] array. /// /// Array of byte, byte[], (u)short, (u)int, (u)long values. /// Array of bytes, null when some object is null. public static byte[] ToBytes(params object[] obj) { if (obj == null) return null; List BytesList = new List(); for (int i = 0; i < obj.Length; i++) { if (obj[i] == null) continue; else if (obj[i] is byte) BytesList.Add((byte)obj[i]); else if (obj[i] is byte[]) BytesList.AddRange((byte[])obj[i]); else if (obj[i] is short) BytesList.AddRange(BitConverter.GetBytes((short)obj[i])); else if (obj[i] is ushort) BytesList.AddRange(BitConverter.GetBytes((ushort)obj[i])); else if (obj[i] is int) BytesList.AddRange(BitConverter.GetBytes((int)obj[i])); else if (obj[i] is uint) BytesList.AddRange(BitConverter.GetBytes((uint)obj[i])); else if (obj[i] is long) BytesList.AddRange(BitConverter.GetBytes((long)obj[i])); else if (obj[i] is ulong) BytesList.AddRange(BitConverter.GetBytes((ulong)obj[i])); } return BytesList.ToArray(); } /// /// Copies a range of elements from an Array starting at the specified source index. /// The length and the index are specified as 32-bit integers. /// /// The that contains the data to copy. /// A 32-bit integer that represents the index in the /// at which copying begins. /// A 32-bit integer that represents the number of elements to copy. /// public static T[] GetElements(T[] SrcArray, int Offset, int Count) { if (SrcArray == null) throw new ArgumentNullException(nameof(SrcArray) + " is null!"); if (Offset >= SrcArray.Length || Offset < 0) throw new ArgumentOutOfRangeException(nameof(Offset) + " has wrong value!"); if (Count <= 0 || Offset + Count > SrcArray.Length) throw new ArgumentOutOfRangeException(nameof(Count) + " has wrong value!"); T[] Buff = new T[Count]; Array.Copy(SrcArray, Offset, Buff, 0, Buff.Length); return Buff; } /// /// Compare N-dimensional Arrays. /// /// Arrays Type. /// First Array. /// Second Array. /// True, if Arrays are equal. public static bool IsArraysEqual(T[] item1, T[] item2) { if (ReferenceEquals(item1, item2)) return true; if (item1 == null || item2 == null) return false; if (item1.Length != item2.Length) return false; EqualityComparer comparer = EqualityComparer.Default; for (int i = 0; i < item1.Length; i++) if (!comparer.Equals(item1[i], item2[i])) return false; return true; } /// /// Compare Lists. /// /// List Type. /// First List. /// Second List. /// True, if Lists are equal. public static bool IsListsEqual(List item1, List item2) { if (ReferenceEquals(item1, item2)) return true; if (item1 == null || item2 == null) return false; if (item1.Count != item2.Count) return false; for (int i = 0; i < item1.Count; i++) if (!item1[i].Equals(item2[i])) return false; return true; } /// /// Compare elements in one Array with different offsets. /// /// Array type. /// Some Array. /// First offset. /// Second offset. /// Elements count which must be compared. /// public static bool IsElementsEqual(T[] Arr, int Offset1, int Offset2, int Count) { if (Arr == null) throw new ArgumentNullException(nameof(Arr) + " is null!"); if (Offset1 >= Arr.Length || Offset1 < 0) throw new ArgumentOutOfRangeException(nameof(Offset1) + " has wrong value!"); if (Offset2 >= Arr.Length || Offset2 < 0) throw new ArgumentOutOfRangeException(nameof(Offset2) + " has wrong value!"); if (Count <= 0 || Offset1 + Count > Arr.Length || Offset2 + Count > Arr.Length) throw new ArgumentOutOfRangeException(nameof(Count) + " has wrong value!"); if (Offset1 == Offset2) return true; for (int i = 0; i < Count; i++) if (!Arr[Offset1 + i].Equals(Arr[Offset2 + i])) return false; return true; } } #endregion public class TGA : ICloneable { public TgaHeader Header = new TgaHeader(); public TgaImgOrColMap ImageOrColorMapArea = new TgaImgOrColMap(); public TgaDevArea DevArea = null; public TgaExtArea ExtArea = null; public TgaFooter Footer = null; #region TGA Creation, Loading, Saving (all are public, have reference to private metods). /// /// Create new empty istance. /// public TGA() { } /// /// Create instance with some params. If it must have ColorMap, /// check all ColorMap fields and settings after. /// /// Image Width. /// Image Height. /// Image Pixel Depth (bits / pixel), set ColorMap bpp after, if needed! /// Image Type (is RLE compressed, ColorMapped or GrayScaled). /// Set numder of Attrbute bits (Alpha channel bits), default: 0, 1, 8. /// Use new 2.0 TGA XFile format? public TGA(ushort Width, ushort Height, TgaPixelDepth PixDepth = TgaPixelDepth.Bpp24, TgaImageType ImgType = TgaImageType.Uncompressed_TrueColor, byte AttrBits = 0, bool NewFormat = true) { if (Width <= 0 || Height <= 0 || PixDepth == TgaPixelDepth.Other) { Width = Height = 0; PixDepth = TgaPixelDepth.Other; ImgType = TgaImageType.NoImageData; AttrBits = 0; } else { int BytesPerPixel = (int)Math.Ceiling((double)PixDepth / 8.0); ImageOrColorMapArea.ImageData = new byte[Width * Height * BytesPerPixel]; if (ImgType == TgaImageType.Uncompressed_ColorMapped || ImgType == TgaImageType.RLE_ColorMapped) { Header.ColorMapType = TgaColorMapType.ColorMap; Header.ColorMapSpec.FirstEntryIndex = 0; Header.ColorMapSpec.ColorMapEntrySize = (TgaColorMapEntrySize)Math.Ceiling((double)PixDepth / 8); } } Header.ImageType = ImgType; Header.ImageSpec.ImageWidth = Width; Header.ImageSpec.ImageHeight = Height; Header.ImageSpec.PixelDepth = PixDepth; Header.ImageSpec.ImageDescriptor.AlphaChannelBits = AttrBits; if (NewFormat) { Footer = new TgaFooter(); ExtArea = new TgaExtArea(); ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow); ExtArea.AttributesType = (AttrBits > 0 ? TgaAttrType.UsefulAlpha : TgaAttrType.NoAlpha); } } /// /// Make from some instance. /// Equal to function. /// /// Original instance. public TGA(TGA tga) { Header = tga.Header.Clone(); ImageOrColorMapArea = tga.ImageOrColorMapArea.Clone(); DevArea = tga.DevArea.Clone(); ExtArea = tga.ExtArea.Clone(); Footer = tga.Footer.Clone(); } /// /// Load from file. /// /// Full path to TGA file. /// Loaded file. public TGA(string filename) { LoadFunc(filename); } /// /// Make from bytes array. /// /// Bytes array (same like TGA File). public TGA(byte[] bytes) { LoadFunc(bytes); } /// /// Make from . /// For file opening better use . /// /// Some stream. You can use a lot of Stream types, but Stream must support: /// and . public TGA(Stream stream) { LoadFunc(stream); } /// /// Make from . /// /// Input Bitmap, supported a lot of bitmaps types: 8/15/16/24/32 Bpp's. /// Use RLE Compression? /// Use new 2.0 TGA XFile format? /// Is Color Map Entry size equal 15 or 16 Bpp, else - 24 or 32. public TGA(Bitmap bmp, bool UseRLE = false, bool NewFormat = true, bool ColorMap2BytesEntry = false) { LoadFunc(bmp, UseRLE, NewFormat, ColorMap2BytesEntry); } /// /// Load from file. /// /// Full path to TGA file. /// Loaded file. public static TGA FromFile(string filename) { return new TGA(filename); } /// /// Make from bytes array. /// /// Bytes array (same like TGA File). public static TGA FromBytes(byte[] bytes) { return new TGA(bytes); } /// /// Make from . /// For file opening better use . /// /// Some stream. You can use a lot of Stream types, but Stream must support: /// and . public static TGA FromStream(Stream stream) { return new TGA(stream); } /// /// Make from . /// /// Input Bitmap, supported a lot of bitmaps types: 8/15/16/24/32 Bpp's. /// Use RLE Compression? /// Use new 2.0 TGA XFile format? /// Is Color Map Entry size equal 15 or 16 Bpp, else - 24 or 32. public static TGA FromBitmap(Bitmap bmp, bool UseRLE = false, bool NewFormat = true, bool ColorMap2BytesEntry = false) { return new TGA(bmp, UseRLE, NewFormat, ColorMap2BytesEntry); } /// /// Save to file. /// /// Full path to file. /// Return "true", if all done or "false", if failed. public bool Save(string filename) { try { bool Result = false; using (FileStream Fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None)) { using (MemoryStream Ms = new MemoryStream()) { Result = SaveFunc(Ms); Ms.WriteTo(Fs); Fs.Flush(); } } return Result; } catch { return false; } } /// /// Save to . /// /// Some stream, it must support: . /// Return "true", if all done or "false", if failed. public bool Save(Stream stream) { return SaveFunc(stream); } #endregion /// /// Gets or Sets Image Width (see ). /// public ushort Width { get { return Header.ImageSpec.ImageWidth; } set { Header.ImageSpec.ImageWidth = value; } } /// /// Gets or Sets Image Height (see ). /// public ushort Height { get { return Header.ImageSpec.ImageHeight; } set { Header.ImageSpec.ImageHeight = value; } } /// /// Gets or Sets image Size. /// public Size Size { get { return new Size(Header.ImageSpec.ImageWidth, Header.ImageSpec.ImageHeight); } set { Header.ImageSpec.ImageWidth = (ushort)value.Width; Header.ImageSpec.ImageHeight = (ushort)value.Height; } } /// /// Make full independed copy of . /// /// Full independed copy of . public TGA Clone() { return new TGA(this); } object ICloneable.Clone() { return Clone(); } /// /// Flip directions, for more info see . /// /// Flip horizontal. /// Flip vertical. public void Flip(bool Horizontal = false, bool Vertical = false) { int NewOrigin = (int)Header.ImageSpec.ImageDescriptor.ImageOrigin; NewOrigin = NewOrigin ^ ((Vertical ? 0x20 : 0) | (Horizontal ? 0x10 : 0)); Header.ImageSpec.ImageDescriptor.ImageOrigin = (TgaImgOrigin)NewOrigin; } /// /// Get information from TGA image. /// /// MultiLine string with info fields (one per line). public string GetInfo() { StringBuilder SB = new StringBuilder(); SB.AppendLine("Header:"); SB.AppendLine("\tID Length = " + Header.IDLength); SB.AppendLine("\tImage Type = " + Header.ImageType); SB.AppendLine("\tHeader -> ImageSpec:"); SB.AppendLine("\t\tImage Width = " + Header.ImageSpec.ImageWidth); SB.AppendLine("\t\tImage Height = " + Header.ImageSpec.ImageHeight); SB.AppendLine("\t\tPixel Depth = " + Header.ImageSpec.PixelDepth); SB.AppendLine("\t\tImage Descriptor (AsByte) = " + Header.ImageSpec.ImageDescriptor.ToByte()); SB.AppendLine("\t\tImage Descriptor -> AttributeBits = " + Header.ImageSpec.ImageDescriptor.AlphaChannelBits); SB.AppendLine("\t\tImage Descriptor -> ImageOrigin = " + Header.ImageSpec.ImageDescriptor.ImageOrigin); SB.AppendLine("\t\tX_Origin = " + Header.ImageSpec.X_Origin); SB.AppendLine("\t\tY_Origin = " + Header.ImageSpec.Y_Origin); SB.AppendLine("\tColorMap Type = " + Header.ColorMapType); SB.AppendLine("\tHeader -> ColorMapSpec:"); SB.AppendLine("\t\tColorMap Entry Size = " + Header.ColorMapSpec.ColorMapEntrySize); SB.AppendLine("\t\tColorMap Length = " + Header.ColorMapSpec.ColorMapLength); SB.AppendLine("\t\tFirstEntry Index = " + Header.ColorMapSpec.FirstEntryIndex); SB.AppendLine("\nImage / Color Map Area:"); if (Header.IDLength > 0 && ImageOrColorMapArea.ImageID != null) SB.AppendLine("\tImage ID = \"" + ImageOrColorMapArea.ImageID.GetString() + "\""); else SB.AppendLine("\tImage ID = null"); if (ImageOrColorMapArea.ImageData != null) SB.AppendLine("\tImage Data Length = " + ImageOrColorMapArea.ImageData.Length); else SB.AppendLine("\tImage Data = null"); if (ImageOrColorMapArea.ColorMapData != null) SB.AppendLine("\tColorMap Data Length = " + ImageOrColorMapArea.ColorMapData.Length); else SB.AppendLine("\tColorMap Data = null"); SB.AppendLine("\nDevelopers Area:"); if (DevArea != null) SB.AppendLine("\tCount = " + DevArea.Count); else SB.AppendLine("\tDevArea = null"); SB.AppendLine("\nExtension Area:"); if (ExtArea != null) { SB.AppendLine("\tExtension Size = " + ExtArea.ExtensionSize); SB.AppendLine("\tAuthor Name = \"" + ExtArea.AuthorName.GetString() + "\""); SB.AppendLine("\tAuthor Comments = \"" + ExtArea.AuthorComments.GetString() + "\""); SB.AppendLine("\tDate / Time Stamp = " + ExtArea.DateTimeStamp); SB.AppendLine("\tJob Name / ID = \"" + ExtArea.JobNameOrID.GetString() + "\""); SB.AppendLine("\tJob Time = " + ExtArea.JobTime); SB.AppendLine("\tSoftware ID = \"" + ExtArea.SoftwareID.GetString() + "\""); SB.AppendLine("\tSoftware Version = \"" + ExtArea.SoftVersion + "\""); SB.AppendLine("\tKey Color = " + ExtArea.KeyColor); SB.AppendLine("\tPixel Aspect Ratio = " + ExtArea.PixelAspectRatio); SB.AppendLine("\tGamma Value = " + ExtArea.GammaValue); SB.AppendLine("\tColor Correction Table Offset = " + ExtArea.ColorCorrectionTableOffset); SB.AppendLine("\tPostage Stamp Offset = " + ExtArea.PostageStampOffset); SB.AppendLine("\tScan Line Offset = " + ExtArea.ScanLineOffset); SB.AppendLine("\tAttributes Type = " + ExtArea.AttributesType); if (ExtArea.ScanLineTable != null) SB.AppendLine("\tScan Line Table = " + ExtArea.ScanLineTable.Length); else SB.AppendLine("\tScan Line Table = null"); if (ExtArea.PostageStampImage != null) SB.AppendLine("\tPostage Stamp Image: " + ExtArea.PostageStampImage.ToString()); else SB.AppendLine("\tPostage Stamp Image = null"); SB.AppendLine("\tColor Correction Table = " + (ExtArea.ColorCorrectionTable != null)); } else SB.AppendLine("\tExtArea = null"); SB.AppendLine("\nFooter:"); if (Footer != null) { SB.AppendLine("\tExtension Area Offset = " + Footer.ExtensionAreaOffset); SB.AppendLine("\tDeveloper Directory Offset = " + Footer.DeveloperDirectoryOffset); SB.AppendLine("\tSignature (Full) = \"" + Footer.Signature.ToString() + Footer.ReservedCharacter.ToString() + Footer.BinaryZeroStringTerminator.ToString() + "\""); } else SB.AppendLine("\tFooter = null"); return SB.ToString(); } /// /// Check and update all fields with data length and offsets. /// /// Return "true", if all OK or "false", if checking failed. public bool CheckAndUpdateOffsets(out string ErrorStr) { ErrorStr = String.Empty; if (Header == null) { ErrorStr = "Header = null"; return false; } if (ImageOrColorMapArea == null) { ErrorStr = "ImageOrColorMapArea = null"; return false; } uint Offset = TgaHeader.Size; // Virtual Offset #region Header if (ImageOrColorMapArea.ImageID != null) { int StrMaxLen = 255; if (ImageOrColorMapArea.ImageID.UseEndingChar) StrMaxLen--; Header.IDLength = (byte)Math.Min(ImageOrColorMapArea.ImageID.OriginalString.Length, StrMaxLen); ImageOrColorMapArea.ImageID.Length = Header.IDLength; Offset += Header.IDLength; } else Header.IDLength = 0; #endregion #region ColorMap if (Header.ColorMapType != TgaColorMapType.NoColorMap) { if (Header.ColorMapSpec == null) { ErrorStr = "Header.ColorMapSpec = null"; return false; } if (Header.ColorMapSpec.ColorMapLength == 0) { ErrorStr = "Header.ColorMapSpec.ColorMapLength = 0"; return false; } if (ImageOrColorMapArea.ColorMapData == null) { ErrorStr = "ImageOrColorMapArea.ColorMapData = null"; return false; } int CmBytesPerPixel = (int)Math.Ceiling((double)Header.ColorMapSpec.ColorMapEntrySize / 8.0); int LenBytes = Header.ColorMapSpec.ColorMapLength * CmBytesPerPixel; if (LenBytes != ImageOrColorMapArea.ColorMapData.Length) { ErrorStr = "ImageOrColorMapArea.ColorMapData.Length has wrong size!"; return false; } Offset += (uint)ImageOrColorMapArea.ColorMapData.Length; } #endregion #region Image Data int BytesPerPixel = 0; if (Header.ImageType != TgaImageType.NoImageData) { if (Header.ImageSpec == null) { ErrorStr = "Header.ImageSpec = null"; return false; } if (Header.ImageSpec.ImageWidth == 0 || Header.ImageSpec.ImageHeight == 0) { ErrorStr = "Header.ImageSpec.ImageWidth = 0 or Header.ImageSpec.ImageHeight = 0"; return false; } if (ImageOrColorMapArea.ImageData == null) { ErrorStr = "ImageOrColorMapArea.ImageData = null"; return false; } BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0); if (Width * Height * BytesPerPixel != ImageOrColorMapArea.ImageData.Length) { ErrorStr = "ImageOrColorMapArea.ImageData.Length has wrong size!"; return false; } if (Header.ImageType >= TgaImageType.RLE_ColorMapped && Header.ImageType <= TgaImageType.RLE_BlackWhite) { byte[] RLE = RLE_Encode(ImageOrColorMapArea.ImageData, Width, Height); if (RLE == null) { ErrorStr = "RLE Compressing error! Check Image Data size."; return false; } Offset += (uint)RLE.Length; RLE = null; } else Offset += (uint)ImageOrColorMapArea.ImageData.Length; } #endregion #region Footer, DevArea, ExtArea if (Footer != null) { #region DevArea if (DevArea != null) { int DevAreaCount = DevArea.Count; for (int i = 0; i < DevAreaCount; i++) if (DevArea[i] == null || DevArea[i].FieldSize <= 0) //Del Empty Entries { DevArea.Entries.RemoveAt(i); DevAreaCount--; i--; } if (DevArea.Count <= 0) Footer.DeveloperDirectoryOffset = 0; if (DevArea.Count > 2) { DevArea.Entries.Sort((a, b) => { return a.Tag.CompareTo(b.Tag); }); for (int i = 0; i < DevArea.Count - 1; i++) if (DevArea[i].Tag == DevArea[i + 1].Tag) { ErrorStr = "DevArea Enties has same Tags!"; return false; } } for (int i = 0; i < DevArea.Count; i++) { DevArea[i].Offset = Offset; Offset += (uint)DevArea[i].FieldSize; } Footer.DeveloperDirectoryOffset = Offset; Offset += (uint)(DevArea.Count * 10 + 2); } else Footer.DeveloperDirectoryOffset = 0; #endregion #region ExtArea if (ExtArea != null) { ExtArea.ExtensionSize = TgaExtArea.MinSize; if (ExtArea.OtherDataInExtensionArea != null) ExtArea.ExtensionSize += (ushort)ExtArea.OtherDataInExtensionArea.Length; ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow); Footer.ExtensionAreaOffset = Offset; Offset += ExtArea.ExtensionSize; #region ScanLineTable if (ExtArea.ScanLineTable == null) ExtArea.ScanLineOffset = 0; else { if (ExtArea.ScanLineTable.Length != Height) { ErrorStr = "ExtArea.ScanLineTable.Length != Height"; return false; } ExtArea.ScanLineOffset = Offset; Offset += (uint)(ExtArea.ScanLineTable.Length * 4); } #endregion #region PostageStampImage if (ExtArea.PostageStampImage == null) ExtArea.PostageStampOffset = 0; else { if (ExtArea.PostageStampImage.Width == 0 || ExtArea.PostageStampImage.Height == 0) { ErrorStr = "ExtArea.PostageStampImage Width or Height is equal 0!"; return false; } if (ExtArea.PostageStampImage.Data == null) { ErrorStr = "ExtArea.PostageStampImage.Data == null"; return false; } int PImgSB = ExtArea.PostageStampImage.Width * ExtArea.PostageStampImage.Height * BytesPerPixel; if (Header.ImageType != TgaImageType.NoImageData && ExtArea.PostageStampImage.Data.Length != PImgSB) { ErrorStr = "ExtArea.PostageStampImage.Data.Length is wrong!"; return false; } ExtArea.PostageStampOffset = Offset; Offset += (uint)(ExtArea.PostageStampImage.Data.Length); } #endregion #region ColorCorrectionTable if (ExtArea.ColorCorrectionTable == null) ExtArea.ColorCorrectionTableOffset = 0; else { if (ExtArea.ColorCorrectionTable.Length != 1024) { ErrorStr = "ExtArea.ColorCorrectionTable.Length != 256 * 4"; return false; } ExtArea.ColorCorrectionTableOffset = Offset; Offset += (uint)(ExtArea.ColorCorrectionTable.Length * 2); } #endregion } else Footer.ExtensionAreaOffset = 0; #endregion #region Footer if (Footer.ToBytes().Length != TgaFooter.Size) { ErrorStr = "Footer.Length is wrong!"; return false; } Offset += TgaFooter.Size; #endregion } #endregion return true; } #region Convert /// /// Convert to . /// /// Force use alpha channel. /// Bitmap or null, on error. public Bitmap ToBitmap(bool ForceUseAlpha = false) { return ToBitmapFunc(ForceUseAlpha, false); } /// /// Convert to bytes array. /// /// Bytes array, (equal to saved file, but in memory) or null (on error). public byte[] ToBytes() { try { byte[] Bytes; using (MemoryStream ms = new MemoryStream()) { Save(ms); Bytes = ms.ToArray(); ms.Flush(); } return Bytes; } catch { return null; } } /// /// Convert TGA Image to new XFile format (v2.0). /// public void ToNewFormat() { if (Footer == null) Footer = new TgaFooter(); if (ExtArea == null) { ExtArea = new TgaExtArea(); ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow); if (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0) ExtArea.AttributesType = TgaAttrType.UsefulAlpha; else ExtArea.AttributesType = TgaAttrType.NoAlpha; } } #endregion #region Private functions bool LoadFunc(string filename) { if (!File.Exists(filename)) throw new FileNotFoundException("File: \"" + filename + "\" not found!"); try { using (FileStream FS = new FileStream(filename, FileMode.Open)) return LoadFunc(FS); } catch { return false; } } bool LoadFunc(byte[] bytes) { if (bytes == null) throw new ArgumentNullException(); try { using (MemoryStream FS = new MemoryStream(bytes, false)) return LoadFunc(FS); } catch { return false; } } bool LoadFunc(Stream stream) { if (stream == null) throw new ArgumentNullException(); if (!(stream.CanRead && stream.CanSeek)) throw new FileLoadException("Stream reading or seeking is not avaiable!"); try { stream.Seek(0, SeekOrigin.Begin); BinaryReader Br = new BinaryReader(stream); Header = new TgaHeader(Br.ReadBytes(TgaHeader.Size)); if (Header.IDLength > 0) ImageOrColorMapArea.ImageID = new TgaString(Br.ReadBytes(Header.IDLength)); if (Header.ColorMapSpec.ColorMapLength > 0) { int CmBytesPerPixel = (int)Math.Ceiling((double)Header.ColorMapSpec.ColorMapEntrySize / 8.0); int LenBytes = Header.ColorMapSpec.ColorMapLength * CmBytesPerPixel; ImageOrColorMapArea.ColorMapData = Br.ReadBytes(LenBytes); } #region Read Image Data int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0); if (Header.ImageType != TgaImageType.NoImageData) { int ImageDataSize = Width * Height * BytesPerPixel; switch (Header.ImageType) { case TgaImageType.RLE_ColorMapped: case TgaImageType.RLE_TrueColor: case TgaImageType.RLE_BlackWhite: int DataOffset = 0; byte PacketInfo; int PacketCount; byte[] RLE_Bytes, RLE_Part; ImageOrColorMapArea.ImageData = new byte[ImageDataSize]; do { PacketInfo = Br.ReadByte(); //1 type bit and 7 count bits. Len = Count + 1. PacketCount = (PacketInfo & 127) + 1; if (PacketInfo >= 128) // bit7 = 1, RLE { RLE_Bytes = new byte[PacketCount * BytesPerPixel]; RLE_Part = Br.ReadBytes(BytesPerPixel); for (int i = 0; i < RLE_Bytes.Length; i++) RLE_Bytes[i] = RLE_Part[i % BytesPerPixel]; } else // RAW format RLE_Bytes = Br.ReadBytes(PacketCount * BytesPerPixel); Buffer.BlockCopy(RLE_Bytes, 0, ImageOrColorMapArea.ImageData, DataOffset, RLE_Bytes.Length); DataOffset += RLE_Bytes.Length; } while (DataOffset < ImageDataSize); RLE_Bytes = null; break; case TgaImageType.Uncompressed_ColorMapped: case TgaImageType.Uncompressed_TrueColor: case TgaImageType.Uncompressed_BlackWhite: ImageOrColorMapArea.ImageData = Br.ReadBytes(ImageDataSize); break; } } #endregion #region Try parse Footer stream.Seek(-TgaFooter.Size, SeekOrigin.End); uint FooterOffset = (uint)stream.Position; TgaFooter MbFooter = new TgaFooter(Br.ReadBytes(TgaFooter.Size)); if (MbFooter.IsFooterCorrect) { Footer = MbFooter; uint DevDirOffset = Footer.DeveloperDirectoryOffset; uint ExtAreaOffset = Footer.ExtensionAreaOffset; #region If Dev Area exist, read it. if (DevDirOffset != 0) { stream.Seek(DevDirOffset, SeekOrigin.Begin); DevArea = new TgaDevArea(); uint NumberOfTags = Br.ReadUInt16(); ushort[] Tags = new ushort[NumberOfTags]; uint[] TagOffsets = new uint[NumberOfTags]; uint[] TagSizes = new uint[NumberOfTags]; for (int i = 0; i < NumberOfTags; i++) { Tags[i] = Br.ReadUInt16(); TagOffsets[i] = Br.ReadUInt32(); TagSizes[i] = Br.ReadUInt32(); } for (int i = 0; i < NumberOfTags; i++) { stream.Seek(TagOffsets[i], SeekOrigin.Begin); var Ent = new TgaDevEntry(Tags[i], TagOffsets[i], Br.ReadBytes((int)TagSizes[i])); DevArea.Entries.Add(Ent); } Tags = null; TagOffsets = null; TagSizes = null; } #endregion #region If Ext Area exist, read it. if (ExtAreaOffset != 0) { stream.Seek(ExtAreaOffset, SeekOrigin.Begin); ushort ExtAreaSize = Math.Max((ushort)TgaExtArea.MinSize, Br.ReadUInt16()); stream.Seek(ExtAreaOffset, SeekOrigin.Begin); ExtArea = new TgaExtArea(Br.ReadBytes(ExtAreaSize)); if (ExtArea.ScanLineOffset > 0) { stream.Seek(ExtArea.ScanLineOffset, SeekOrigin.Begin); ExtArea.ScanLineTable = new uint[Height]; for (int i = 0; i < ExtArea.ScanLineTable.Length; i++) ExtArea.ScanLineTable[i] = Br.ReadUInt32(); } if (ExtArea.PostageStampOffset > 0) { stream.Seek(ExtArea.PostageStampOffset, SeekOrigin.Begin); byte W = Br.ReadByte(); byte H = Br.ReadByte(); int ImgDataSize = W * H * BytesPerPixel; if (ImgDataSize > 0) ExtArea.PostageStampImage = new TgaPostageStampImage(W, H, Br.ReadBytes(ImgDataSize)); } if (ExtArea.ColorCorrectionTableOffset > 0) { stream.Seek(ExtArea.ColorCorrectionTableOffset, SeekOrigin.Begin); ExtArea.ColorCorrectionTable = new ushort[256 * 4]; for (int i = 0; i < ExtArea.ColorCorrectionTable.Length; i++) ExtArea.ColorCorrectionTable[i] = Br.ReadUInt16(); } } #endregion } #endregion Br.Close(); return true; } catch { return false; } } bool LoadFunc(Bitmap bmp, bool UseRLE = false, bool NewFormat = true, bool ColorMap2BytesEntry = false) { if (bmp == null) throw new ArgumentNullException(); try { Header.ImageSpec.ImageWidth = (ushort)bmp.Width; Header.ImageSpec.ImageHeight = (ushort)bmp.Height; Header.ImageSpec.ImageDescriptor.ImageOrigin = TgaImgOrigin.TopLeft; switch (bmp.PixelFormat) { case PixelFormat.Indexed: case PixelFormat.Gdi: case PixelFormat.Alpha: case PixelFormat.Undefined: case PixelFormat.PAlpha: case PixelFormat.Extended: case PixelFormat.Max: case PixelFormat.Canonical: case PixelFormat.Format16bppRgb565: default: throw new FormatException(nameof(PixelFormat) + " is not supported!"); case PixelFormat.Format1bppIndexed: case PixelFormat.Format4bppIndexed: case PixelFormat.Format8bppIndexed: case PixelFormat.Format16bppGrayScale: case PixelFormat.Format16bppRgb555: case PixelFormat.Format16bppArgb1555: case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppRgb: case PixelFormat.Format32bppArgb: case PixelFormat.Format32bppPArgb: case PixelFormat.Format48bppRgb: case PixelFormat.Format64bppArgb: case PixelFormat.Format64bppPArgb: int bpp = Math.Max(8, Image.GetPixelFormatSize(bmp.PixelFormat)); int BytesPP = bpp / 8; if (bmp.PixelFormat == PixelFormat.Format16bppRgb555) bpp = 15; bool IsAlpha = Image.IsAlphaPixelFormat(bmp.PixelFormat); bool IsPreAlpha = IsAlpha && bmp.PixelFormat.ToString().EndsWith("PArgb"); bool IsColorMapped = bmp.PixelFormat.ToString().EndsWith("Indexed"); Header.ImageSpec.PixelDepth = (TgaPixelDepth)(BytesPP * 8); if (IsAlpha) { Header.ImageSpec.ImageDescriptor.AlphaChannelBits = (byte)(BytesPP * 2); if (bmp.PixelFormat == PixelFormat.Format16bppArgb1555) Header.ImageSpec.ImageDescriptor.AlphaChannelBits = 1; } #region ColorMap bool IsGrayImage = (bmp.PixelFormat == PixelFormat.Format16bppGrayScale | IsColorMapped); if (IsColorMapped && bmp.Palette != null) { Color[] Colors = bmp.Palette.Entries; #region Analyze ColorMapType int AlphaSum = 0; bool ColorMapUseAlpha = false; for (int i = 0; i < Colors.Length; i++) { IsGrayImage &= (Colors[i].R == Colors[i].G && Colors[i].G == Colors[i].B); ColorMapUseAlpha |= (Colors[i].A < 248); AlphaSum |= Colors[i].A; } ColorMapUseAlpha &= (AlphaSum > 0); int CMapBpp = (ColorMap2BytesEntry ? 15 : 24) + (ColorMapUseAlpha ? (ColorMap2BytesEntry ? 1 : 8) : 0); int CMBytesPP = (int)Math.Ceiling(CMapBpp / 8.0); #endregion Header.ColorMapSpec.ColorMapLength = Math.Min((ushort)Colors.Length, ushort.MaxValue); Header.ColorMapSpec.ColorMapEntrySize = (TgaColorMapEntrySize)CMapBpp; ImageOrColorMapArea.ColorMapData = new byte[Header.ColorMapSpec.ColorMapLength * CMBytesPP]; byte[] CMapEntry = new byte[CMBytesPP]; const float To5Bit = 32f / 256f; // Scale value from 8 to 5 bits. for (int i = 0; i < Colors.Length; i++) { switch (Header.ColorMapSpec.ColorMapEntrySize) { case TgaColorMapEntrySize.A1R5G5B5: case TgaColorMapEntrySize.X1R5G5B5: int R = (int)(Colors[i].R * To5Bit); int G = (int)(Colors[i].G * To5Bit) << 5; int B = (int)(Colors[i].B * To5Bit) << 10; int A = 0; if (Header.ColorMapSpec.ColorMapEntrySize == TgaColorMapEntrySize.A1R5G5B5) A = ((Colors[i].A & 0x80) << 15); CMapEntry = BitConverter.GetBytes(A | R | G | B); break; case TgaColorMapEntrySize.R8G8B8: CMapEntry[0] = Colors[i].B; CMapEntry[1] = Colors[i].G; CMapEntry[2] = Colors[i].R; break; case TgaColorMapEntrySize.A8R8G8B8: CMapEntry[0] = Colors[i].B; CMapEntry[1] = Colors[i].G; CMapEntry[2] = Colors[i].R; CMapEntry[3] = Colors[i].A; break; case TgaColorMapEntrySize.Other: default: break; } Buffer.BlockCopy(CMapEntry, 0, ImageOrColorMapArea.ColorMapData, i * CMBytesPP, CMBytesPP); } } #endregion #region ImageType if (UseRLE) { if (IsGrayImage) Header.ImageType = TgaImageType.RLE_BlackWhite; else if (IsColorMapped) Header.ImageType = TgaImageType.RLE_ColorMapped; else Header.ImageType = TgaImageType.RLE_TrueColor; } else { if (IsGrayImage) Header.ImageType = TgaImageType.Uncompressed_BlackWhite; else if (IsColorMapped) Header.ImageType = TgaImageType.Uncompressed_ColorMapped; else Header.ImageType = TgaImageType.Uncompressed_TrueColor; } Header.ColorMapType = (IsColorMapped ? TgaColorMapType.ColorMap : TgaColorMapType.NoColorMap); #endregion #region NewFormat if (NewFormat) { Footer = new TgaFooter(); ExtArea = new TgaExtArea(); ExtArea.DateTimeStamp = new TgaDateTime(DateTime.UtcNow); if (IsAlpha) { ExtArea.AttributesType = TgaAttrType.UsefulAlpha; if (IsPreAlpha) ExtArea.AttributesType = TgaAttrType.PreMultipliedAlpha; } else { ExtArea.AttributesType = TgaAttrType.NoAlpha; if (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0) ExtArea.AttributesType = TgaAttrType.UndefinedAlphaButShouldBeRetained; } } #endregion #region Bitmap width is aligned by 32 bits = 4 bytes! Delete it. int StrideBytes = bmp.Width * BytesPP; int PaddingBytes = (int)Math.Ceiling(StrideBytes / 4.0) * 4 - StrideBytes; byte[] ImageData = new byte[(StrideBytes + PaddingBytes) * bmp.Height]; Rectangle Re = new Rectangle(0, 0, bmp.Width, bmp.Height); BitmapData BmpData = bmp.LockBits(Re, ImageLockMode.ReadOnly, bmp.PixelFormat); Marshal.Copy(BmpData.Scan0, ImageData, 0, ImageData.Length); bmp.UnlockBits(BmpData); BmpData = null; if (PaddingBytes > 0) //Need delete bytes align { ImageOrColorMapArea.ImageData = new byte[StrideBytes * bmp.Height]; for (int i = 0; i < bmp.Height; i++) Buffer.BlockCopy(ImageData, i * (StrideBytes + PaddingBytes), ImageOrColorMapArea.ImageData, i * StrideBytes, StrideBytes); } else ImageOrColorMapArea.ImageData = ImageData; ImageData = null; // Not official supported, but works (tested on 16bpp GrayScale test images)! if (bmp.PixelFormat == PixelFormat.Format16bppGrayScale) { for (long i = 0; i < ImageOrColorMapArea.ImageData.Length; i++) ImageOrColorMapArea.ImageData[i] ^= byte.MaxValue; } #endregion break; } return true; } catch { return false; } } bool SaveFunc(Stream stream) { try { if (stream == null) throw new ArgumentNullException(); if (!(stream.CanWrite && stream.CanSeek)) throw new FileLoadException("Stream writing or seeking is not avaiable!"); string CheckResult; if (!CheckAndUpdateOffsets(out CheckResult)) return false; BinaryWriter Bw = new BinaryWriter(stream); Bw.Write(Header.ToBytes()); if (ImageOrColorMapArea.ImageID != null) Bw.Write(ImageOrColorMapArea.ImageID.ToBytes()); if (Header.ColorMapType != TgaColorMapType.NoColorMap) Bw.Write(ImageOrColorMapArea.ColorMapData); #region ImageData if (Header.ImageType != TgaImageType.NoImageData) { if (Header.ImageType >= TgaImageType.RLE_ColorMapped && Header.ImageType <= TgaImageType.RLE_BlackWhite) Bw.Write(RLE_Encode(ImageOrColorMapArea.ImageData, Width, Height)); else Bw.Write(ImageOrColorMapArea.ImageData); } #endregion #region Footer if (Footer != null) { #region DevArea if (DevArea != null) { for (int i = 0; i < DevArea.Count; i++) Bw.Write(DevArea[i].Data); Bw.Write((ushort)DevArea.Count); for (int i = 0; i < DevArea.Count; i++) { Bw.Write(DevArea[i].Tag); Bw.Write(DevArea[i].Offset); Bw.Write(DevArea[i].FieldSize); } } #endregion #region ExtArea if (ExtArea != null) { Bw.Write(ExtArea.ToBytes()); if (ExtArea.ScanLineTable != null) for (int i = 0; i < ExtArea.ScanLineTable.Length; i++) Bw.Write(ExtArea.ScanLineTable[i]); if (ExtArea.PostageStampImage != null) Bw.Write(ExtArea.PostageStampImage.ToBytes()); if (ExtArea.ColorCorrectionTable != null) for (int i = 0; i < ExtArea.ColorCorrectionTable.Length; i++) Bw.Write(ExtArea.ColorCorrectionTable[i]); } #endregion Bw.Write(Footer.ToBytes()); } #endregion Bw.Flush(); stream.Flush(); return true; } catch { return false; } } /// /// Encode image with RLE compression (used RLE per line)! /// /// Image data, bytes array with size = Width * Height * BytesPerPixel. /// Image Width, must be > 0. /// Image Height, must be > 0. /// Bytes array with RLE compressed image data. byte[] RLE_Encode(byte[] ImageData, int Width, int Height) { if (ImageData == null) throw new ArgumentNullException(nameof(ImageData) + "in null!"); if (Width <= 0 || Height <= 0) throw new ArgumentOutOfRangeException(nameof(Width) + " and " + nameof(Height) + " must be > 0!"); int Bpp = ImageData.Length / Width / Height; // Bytes per pixel int ScanLineSize = Width * Bpp; if (ScanLineSize * Height != ImageData.Length) throw new ArgumentOutOfRangeException("ImageData has wrong Length!"); try { int Count = 0; int Pos = 0; bool IsRLE = false; List Encoded = new List(); byte[] RowData = new byte[ScanLineSize]; for (int y = 0; y < Height; y++) { Pos = 0; Buffer.BlockCopy(ImageData, y * ScanLineSize, RowData, 0, ScanLineSize); while (Pos < ScanLineSize) { if (Pos >= ScanLineSize - Bpp) { Encoded.Add(0); Encoded.AddRange(BitConverterExt.GetElements(RowData, Pos, Bpp)); Pos += Bpp; break; } Count = 0; //1 IsRLE = BitConverterExt.IsElementsEqual(RowData, Pos, Pos + Bpp, Bpp); for (int i = Pos + Bpp; i < Math.Min(Pos + 128 * Bpp, ScanLineSize) - Bpp; i += Bpp) { if (IsRLE ^ BitConverterExt.IsElementsEqual(RowData, (IsRLE ? Pos : i), i + Bpp, Bpp)) { //Count--; break; } else Count++; } int CountBpp = (Count + 1) * Bpp; Encoded.Add((byte)(IsRLE ? Count | 128 : Count)); Encoded.AddRange(BitConverterExt.GetElements(RowData, Pos, (IsRLE ? Bpp : CountBpp))); Pos += CountBpp; } } return Encoded.ToArray(); } catch { return null; } } /// /// Convert to . /// /// Force use alpha channel. /// Get Postage Stamp Image (Thumb) or get main image? /// Bitmap or null, on error. Bitmap ToBitmapFunc(bool ForceUseAlpha = false, bool PostageStampImage = false) { try { #region UseAlpha? bool UseAlpha = true; if (ExtArea != null) { switch (ExtArea.AttributesType) { case TgaAttrType.NoAlpha: case TgaAttrType.UndefinedAlphaCanBeIgnored: case TgaAttrType.UndefinedAlphaButShouldBeRetained: UseAlpha = false; break; case TgaAttrType.UsefulAlpha: case TgaAttrType.PreMultipliedAlpha: default: break; } } UseAlpha = (Header.ImageSpec.ImageDescriptor.AlphaChannelBits > 0 && UseAlpha) | ForceUseAlpha; #endregion #region IsGrayImage bool IsGrayImage = Header.ImageType == TgaImageType.RLE_BlackWhite || Header.ImageType == TgaImageType.Uncompressed_BlackWhite; #endregion #region Get PixelFormat PixelFormat PixFormat = PixelFormat.Format24bppRgb; switch (Header.ImageSpec.PixelDepth) { case TgaPixelDepth.Bpp8: PixFormat = PixelFormat.Format8bppIndexed; break; case TgaPixelDepth.Bpp16: if (IsGrayImage) PixFormat = PixelFormat.Format16bppGrayScale; else PixFormat = (UseAlpha ? PixelFormat.Format16bppArgb1555 : PixelFormat.Format16bppRgb555); break; case TgaPixelDepth.Bpp24: PixFormat = PixelFormat.Format24bppRgb; break; case TgaPixelDepth.Bpp32: if (UseAlpha) { var f = Footer; if (ExtArea?.AttributesType == TgaAttrType.PreMultipliedAlpha) PixFormat = PixelFormat.Format32bppPArgb; else PixFormat = PixelFormat.Format32bppArgb; } else PixFormat = PixelFormat.Format32bppRgb; break; default: PixFormat = PixelFormat.Undefined; break; } #endregion ushort BMP_Width = (PostageStampImage ? ExtArea.PostageStampImage.Width : Width); ushort BMP_Height = (PostageStampImage ? ExtArea.PostageStampImage.Height : Height); Bitmap BMP = new Bitmap(BMP_Width, BMP_Height, PixFormat); #region ColorMap and GrayPalette if (Header.ColorMapType == TgaColorMapType.ColorMap && (Header.ImageType == TgaImageType.RLE_ColorMapped || Header.ImageType == TgaImageType.Uncompressed_ColorMapped)) { ColorPalette ColorMap = BMP.Palette; Color[] CMapColors = ColorMap.Entries; switch (Header.ColorMapSpec.ColorMapEntrySize) { case TgaColorMapEntrySize.X1R5G5B5: case TgaColorMapEntrySize.A1R5G5B5: const float To8Bit = 255f / 31f; // Scale value from 5 to 8 bits. for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++) { ushort A1R5G5B5 = BitConverter.ToUInt16(ImageOrColorMapArea.ColorMapData, i * 2); int A = (UseAlpha ? (A1R5G5B5 & 0x8000) >> 15 : 1) * 255; // (0 or 1) * 255 int R = (int)(((A1R5G5B5 & 0x7C00) >> 10) * To8Bit); int G = (int)(((A1R5G5B5 & 0x3E0) >> 5) * To8Bit); int B = (int)((A1R5G5B5 & 0x1F) * To8Bit); CMapColors[i] = Color.FromArgb(A, R, G, B); } break; case TgaColorMapEntrySize.R8G8B8: for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++) { int Index = i * 3; //RGB = 3 bytes int R = ImageOrColorMapArea.ColorMapData[Index + 2]; int G = ImageOrColorMapArea.ColorMapData[Index + 1]; int B = ImageOrColorMapArea.ColorMapData[Index]; CMapColors[i] = Color.FromArgb(R, G, B); } break; case TgaColorMapEntrySize.A8R8G8B8: for (int i = 0; i < Math.Min(CMapColors.Length, Header.ColorMapSpec.ColorMapLength); i++) { int ARGB = BitConverter.ToInt32(ImageOrColorMapArea.ColorMapData, i * 4); CMapColors[i] = Color.FromArgb(UseAlpha ? ARGB | (0xFF << 24) : ARGB); } break; default: ColorMap = null; break; } if (ColorMap != null) BMP.Palette = ColorMap; } if (PixFormat == PixelFormat.Format8bppIndexed && IsGrayImage) { ColorPalette GrayPalette = BMP.Palette; Color[] GrayColors = GrayPalette.Entries; for (int i = 0; i < GrayColors.Length; i++) GrayColors[i] = Color.FromArgb(i, i, i); BMP.Palette = GrayPalette; } #endregion #region Bitmap width must by aligned (align value = 32 bits = 4 bytes)! byte[] ImageData; int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0); int StrideBytes = BMP.Width * BytesPerPixel; int PaddingBytes = (int)Math.Ceiling(StrideBytes / 4.0) * 4 - StrideBytes; if (PaddingBytes > 0) //Need bytes align { ImageData = new byte[(StrideBytes + PaddingBytes) * BMP.Height]; for (int i = 0; i < BMP.Height; i++) Buffer.BlockCopy(PostageStampImage ? ExtArea.PostageStampImage.Data : ImageOrColorMapArea.ImageData, i * StrideBytes, ImageData, i * (StrideBytes + PaddingBytes), StrideBytes); } else ImageData = BitConverterExt.ToBytes(PostageStampImage ? ExtArea.PostageStampImage.Data : ImageOrColorMapArea.ImageData); // Not official supported, but works (tested on 2 test images)! if (PixFormat == PixelFormat.Format16bppGrayScale) { for (long i = 0; i < ImageData.Length; i++) ImageData[i] ^= byte.MaxValue; } #endregion Rectangle Re = new Rectangle(0, 0, BMP.Width, BMP.Height); BitmapData BmpData = BMP.LockBits(Re, ImageLockMode.WriteOnly, BMP.PixelFormat); Marshal.Copy(ImageData, 0, BmpData.Scan0, ImageData.Length); BMP.UnlockBits(BmpData); ImageData = null; BmpData = null; if (ExtArea != null && ExtArea.KeyColor.ToInt() != 0) BMP.MakeTransparent(ExtArea.KeyColor.ToColor()); #region Flip Image switch (Header.ImageSpec.ImageDescriptor.ImageOrigin) { case TgaImgOrigin.BottomLeft: BMP.RotateFlip(RotateFlipType.RotateNoneFlipY); break; case TgaImgOrigin.BottomRight: BMP.RotateFlip(RotateFlipType.RotateNoneFlipXY); break; case TgaImgOrigin.TopLeft: default: break; case TgaImgOrigin.TopRight: BMP.RotateFlip(RotateFlipType.RotateNoneFlipX); break; } #endregion return BMP; } catch { return null; } } #endregion #region Explicit public static explicit operator Bitmap(TGA tga) { return tga.ToBitmap(); } public static explicit operator TGA(Bitmap bmp) { return FromBitmap(bmp); } #endregion #region PostageStamp Image /// /// Convert to . /// /// Force use alpha channel. /// Bitmap or null. public Bitmap GetPostageStampImage(bool ForceUseAlpha = false) { if (ExtArea == null || ExtArea.PostageStampImage == null || ExtArea.PostageStampImage.Data == null || ExtArea.PostageStampImage.Width <= 0 || ExtArea.PostageStampImage.Height <= 0) return null; return ToBitmapFunc(ForceUseAlpha, true); } /// /// Update Postage Stamp Image or set it. /// public void UpdatePostageStampImage() { if (Header.ImageType == TgaImageType.NoImageData) { if (ExtArea != null) ExtArea.PostageStampImage = null; return; } ToNewFormat(); if (ExtArea.PostageStampImage == null) ExtArea.PostageStampImage = new TgaPostageStampImage(); int PS_Width = Header.ImageSpec.ImageWidth; int PS_Height = Header.ImageSpec.ImageHeight; if (Width > 64 || Height > 64) { float AspectRatio = Width / (float)Height; PS_Width = (byte)(64f * (AspectRatio < 1f ? AspectRatio : 1f)); PS_Height = (byte)(64f / (AspectRatio > 1f ? AspectRatio : 1f)); } PS_Width = Math.Max(PS_Width, 4); PS_Height = Math.Max(PS_Height, 4); ExtArea.PostageStampImage.Width = (byte)PS_Width; ExtArea.PostageStampImage.Height = (byte)PS_Height; int BytesPerPixel = (int)Math.Ceiling((double)Header.ImageSpec.PixelDepth / 8.0); ExtArea.PostageStampImage.Data = new byte[PS_Width * PS_Height * BytesPerPixel]; float WidthCoef = Width / (float)PS_Width; float HeightCoef = Height / (float)PS_Height; for (int y = 0; y < PS_Height; y++) { int Y_Offset = (int)(y * HeightCoef) * Width * BytesPerPixel; int y_Offset = y * PS_Width * BytesPerPixel; for (int x = 0; x < PS_Width; x++) { Buffer.BlockCopy(ImageOrColorMapArea.ImageData, Y_Offset + (int)(x * WidthCoef) * BytesPerPixel, ExtArea.PostageStampImage.Data, y_Offset + x * BytesPerPixel, BytesPerPixel); } } } public void DeletePostageStampImage() { if (ExtArea != null) ExtArea.PostageStampImage = null; } #endregion } } ================================================ FILE: source/Playnite/Common/TempDirectory.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class TempDirectory : IDisposable { private bool autoDelete; public string TempPath { get; private set; } public static TempDirectory Create(bool autoDelete = true, string dirName = null) { if (dirName.IsNullOrEmpty()) { var stack = new StackTrace(1); var method = stack.GetFrame(0).GetMethod(); dirName = Paths.GetSafePathName($"{method.DeclaringType.Name}_{method.Name}"); } return new TempDirectory(dirName, autoDelete); } public TempDirectory(string dirName, bool autoDelete = true) { TempPath = Path.Combine(Path.GetTempPath(), "Playnite", dirName); FileSystem.CreateDirectory(TempPath, true); this.autoDelete = autoDelete; } public void Dispose() { if (autoDelete) { FileSystem.DeleteDirectory(TempPath); } } public override string ToString() { return TempPath; } } } ================================================ FILE: source/Playnite/Common/Timer.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public class ExecutionTimer : IDisposable { private static ILogger logger = LogManager.GetLogger(); private string name; private Stopwatch watch = new Stopwatch(); public ExecutionTimer(string name) { this.name = name; watch.Start(); } public void Dispose() { watch.Stop(); logger.Debug($"--- Timer '{name}', {watch.ElapsedMilliseconds} ms to complete."); } } public class Timer { private static Random randomGen = new Random(); public static IDisposable TimeExecution(string name) { return new ExecutionTimer(name); } public static int HoursToMilliseconds(int hours) { return MinutesToMilliseconds(hours * 60); } public static int MinutesToMilliseconds(int minutes) { return SecondsToMilliseconds(minutes * 60); } public static int SecondsToMilliseconds(int seconds) { return seconds * 1000; } public static DateTime GetRandomDateTime() { var startDate = new DateTime(1970, 1, 1); int range = (DateTime.Today - startDate).Days; return startDate.AddDays(randomGen.Next(range)); } } } ================================================ FILE: source/Playnite/Common/Units.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Common { public static class Units { public static long MegaBytesToBytes(long megaBytes) { return megaBytes * 1024 * 1024; } public static long BytesToMegaBytes(long bytes) { return bytes / 1024 / 1024; } } } ================================================ FILE: source/Playnite/Common/Web/Downloader.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite.Common.Web { public interface IDownloader { string DownloadString(IEnumerable mirrors); string DownloadString(string url); string DownloadString(string url, Encoding encoding); string DownloadString(string url, List cookies); string DownloadString(string url, List cookies, Encoding encoding); void DownloadString(string url, string path); void DownloadString(string url, string path, Encoding encoding); byte[] DownloadData(string url); void DownloadFile(string url, string path); void DownloadFile(IEnumerable mirrors, string path); Task DownloadFileAsync(string url, string path, Action progressHandler); Task DownloadFileAsync(IEnumerable mirrors, string path, Action progressHandler); } // The default timeout of WebClient is 100 seconds which is just too much and doesn't make sense. // This is apparatenly the only way to change timeout for the entire WebClient instance. // We should be using HttpClient but that's not very safe changes for P10 right now. public class CustomWebClient : WebClient { protected override WebRequest GetWebRequest(Uri address) { var request = base.GetWebRequest(address); if (request != null) request.Timeout = 15 * 1000; return request; } } public class Downloader : IDownloader { private static ILogger logger = LogManager.GetLogger(); private static readonly string playniteUserAgent = $"Playnite 10"; public Downloader() { } public string DownloadString(IEnumerable mirrors) { logger.Debug($"Downloading string content from multiple mirrors."); foreach (var mirror in mirrors) { try { return DownloadString(mirror); } catch (Exception e) { logger.Error(e, $"Failed to download {mirror} string."); } } throw new Exception("Failed to download string from all mirrors."); } public string DownloadString(string url) { return DownloadString(url, Encoding.UTF8); } public string DownloadString(string url, CancellationToken cancelToken) { logger.Debug($"Downloading string content from {url} using UTF8 encoding."); try { using (var webClient = new CustomWebClient { Encoding = Encoding.UTF8 }) using (var registration = cancelToken.Register(() => webClient.CancelAsync())) { webClient.Headers.Add("User-Agent", playniteUserAgent); return Task.Run(async () => await webClient.DownloadStringTaskAsync(url)).GetAwaiter().GetResult(); } } catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled) { logger.Warn("Download canceled."); return null; } } public string DownloadString(string url, Encoding encoding) { logger.Debug($"Downloading string content from {url} using {encoding} encoding."); using (var webClient = new CustomWebClient { Encoding = encoding }) { webClient.Headers.Add("User-Agent", playniteUserAgent); return webClient.DownloadString(url); } } public string DownloadString(string url, List cookies) { return DownloadString(url, cookies, Encoding.UTF8); } public string DownloadString(string url, List cookies, Encoding encoding) { logger.Debug($"Downloading string content from {url} using cookies and {encoding} encoding."); using (var webClient = new CustomWebClient { Encoding = encoding }) { webClient.Headers.Add("User-Agent", playniteUserAgent); if (cookies?.Any() == true) { var cookieString = string.Join(";", cookies.Select(a => $"{a.Name}={a.Value}")); webClient.Headers.Add(HttpRequestHeader.Cookie, cookieString); } return webClient.DownloadString(url); } } public void DownloadString(string url, string path) { DownloadString(url, path, Encoding.UTF8); } public void DownloadString(string url, string path, Encoding encoding) { logger.Debug($"Downloading string content from {url} to {path} using {encoding} encoding."); using (var webClient = new CustomWebClient { Encoding = encoding }) { webClient.Headers.Add("User-Agent", playniteUserAgent); var data = webClient.DownloadString(url); File.WriteAllText(path, data); } } public byte[] DownloadData(string url) { logger.Debug($"Downloading data from {url}."); using (var webClient = new CustomWebClient()) { webClient.Headers.Add("User-Agent", playniteUserAgent); return webClient.DownloadData(url); } } public byte[] DownloadData(string url, CancellationToken cancelToken) { logger.Debug($"Downloading data from {url}."); try { using (var webClient = new CustomWebClient()) using (var registration = cancelToken.Register(() => webClient.CancelAsync())) { webClient.Headers.Add("User-Agent", playniteUserAgent); return webClient.DownloadData(url); } } catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled) { logger.Warn("Download canceled."); return new byte[0]; } } public void DownloadFile(string url, string path) { logger.Debug($"Downloading data from {url} to {path}."); FileSystem.CreateDirectory(Path.GetDirectoryName(path)); using (var webClient = new CustomWebClient()) { webClient.Headers.Add("User-Agent", playniteUserAgent); webClient.DownloadFile(url, path); } } public void DownloadFile(string url, string path, CancellationToken cancelToken) { logger.Debug($"Downloading data from {url} to {path}."); FileSystem.CreateDirectory(Path.GetDirectoryName(path)); try { using (var webClient = new CustomWebClient()) using (var registration = cancelToken.Register(() => webClient.CancelAsync())) { webClient.Headers.Add("User-Agent", playniteUserAgent); Task.Run(async () => await webClient.DownloadFileTaskAsync(new Uri(url), path)).Wait(); } } catch (WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled) { logger.Warn("Download canceled."); } catch (AggregateException ae) when (ae.InnerException is WebException we && we.Status == WebExceptionStatus.RequestCanceled) { logger.Warn("Download canceled."); } } public async Task DownloadFileAsync(string url, string path, Action progressHandler) { logger.Debug($"Downloading data async from {url} to {path}."); FileSystem.CreateDirectory(Path.GetDirectoryName(path)); using (var webClient = new CustomWebClient()) { webClient.Headers.Add("User-Agent", playniteUserAgent); webClient.DownloadProgressChanged += (s, e) => progressHandler(e); webClient.DownloadFileCompleted += (s, e) => webClient.Dispose(); await webClient.DownloadFileTaskAsync(url, path); } } public async Task DownloadFileAsync(IEnumerable mirrors, string path, Action progressHandler) { logger.Debug($"Downloading data async from multiple mirrors."); foreach (var mirror in mirrors) { try { await DownloadFileAsync(mirror, path, progressHandler); return; } catch (Exception e) { logger.Error(e, $"Failed to download {mirror} file."); } } throw new Exception("Failed to download file from all mirrors."); } public void DownloadFile(IEnumerable mirrors, string path) { logger.Debug($"Downloading data from multiple mirrors."); foreach (var mirror in mirrors) { try { DownloadFile(mirror, path); return; } catch (Exception e) { logger.Error(e, $"Failed to download {mirror} file."); } } throw new Exception("Failed to download file from all mirrors."); } } } ================================================ FILE: source/Playnite/Common/Web/HttpDownloader.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.IO; using System.Net.Http; using Playnite.SDK; using System.Threading; namespace Playnite.Common.Web { public class HttpDownloader { private static ILogger logger = LogManager.GetLogger(); private static readonly HttpClient httpClient = new HttpClient(); private static readonly Downloader downloader = new Downloader(); public static string DownloadString(IEnumerable mirrors) { return downloader.DownloadString(mirrors); } public static string DownloadString(string url) { return downloader.DownloadString(url); } public static string DownloadString(string url, CancellationToken cancelToken) { return downloader.DownloadString(url, cancelToken); } public static string DownloadString(string url, Encoding encoding) { return downloader.DownloadString(url, encoding); } public static string DownloadString(string url, List cookies) { return downloader.DownloadString(url, cookies); } public static string DownloadString(string url, List cookies, Encoding encoding) { return downloader.DownloadString(url, cookies, encoding); } public static void DownloadString(string url, string path) { downloader.DownloadString(url, path); } public static void DownloadString(string url, string path, Encoding encoding) { downloader.DownloadString(url, path, encoding); } public static byte[] DownloadData(string url) { return downloader.DownloadData(url); } public static byte[] DownloadData(string url, CancellationToken cancelToken) { return downloader.DownloadData(url, cancelToken); } public static void DownloadFile(string url, string path) { downloader.DownloadFile(url, path); } public static void DownloadFile(string url, string path, CancellationToken cancelToken) { downloader.DownloadFile(url, path, cancelToken); } public static HttpStatusCode GetResponseCode(string url, CancellationToken cancelToken, out Dictionary headers) { headers = new Dictionary(StringComparer.OrdinalIgnoreCase); try { var response = httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, url), cancelToken).GetAwaiter().GetResult(); foreach (var header in response.Headers) { headers.Add(header.Key, string.Join(",", header.Value)); } foreach (var header in response.Content.Headers) { headers.Add(header.Key, string.Join(",", header.Value)); } return response.StatusCode; } catch (Exception e) { logger.Error(e, $"Failed to get HTTP response for {url}."); return HttpStatusCode.ServiceUnavailable; } } } } ================================================ FILE: source/Playnite/Common/Xaml.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Markup; namespace Playnite.Common { public class Xaml { public static object FromFile(string path) { using (var stream = new StreamReader(path)) { return XamlReader.Load(stream.BaseStream); } } public static T FromFile(string path) { using (var stream = new StreamReader(path)) { return (T)XamlReader.Load(stream.BaseStream); } } public static T FromString(string xaml) { return (T)XamlReader.Parse(xaml); } } } ================================================ FILE: source/Playnite/Common/Xml.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Linq; namespace Playnite.Common { public class Xml { public static bool AreEqual(XElement elem1, XElement elem2) { if (elem1.Name.ToString() != elem2.Name.ToString()) { return false; } if (elem1.Value != elem2.Value) { return false; } var atts1 = elem1.Attributes().OrderBy(a => a.Name.ToString()).ToList(); var atts2 = elem2.Attributes().OrderBy(a => a.Name.ToString()).ToList(); if (atts1.Count != atts2.Count) { return false; } if (atts1.Count > 0) { for (int i = 0; i < atts1.Count; i++) { var att1 = atts1[i]; var att2 = atts2[i]; if (att1.Name.ToString() != att2.Name.ToString()) { return false; } if (att1.Value != att2.Value) { return false; } } } var elems1 = elem1.Elements().ToList(); var elems2 = elem2.Elements().ToList(); if (elems1.Count != elems2.Count) { return false; } if (elems1.Count > 0) { for (int i = 0; i < elems1.Count; i++) { if (!AreEqual(elems1[i], elems2[i])) { return false; } } } return true; } public static bool AreEqual(string xml1, string xml2) { var xdoc1 = XDocument.Parse(xml1); var xdoc2 = XDocument.Parse(xml2); return AreEqual(xdoc1.Root, xdoc2.Root); } } } ================================================ FILE: source/Playnite/Common.config ================================================  ================================================ FILE: source/Playnite/ControlTemplateTools.cs ================================================ using Playnite.Common; using Playnite.Plugins; using Playnite.SDK; using Playnite.SDK.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace Playnite { public class ControlTemplateTools { private static readonly ILogger logger = LogManager.GetLogger(); public static void InitializePluginControls( ExtensionFactory extensions, ControlTemplate template, FrameworkElement templateParent, ApplicationMode mode, object contextSource, string contextPath) { if (DesignerTools.IsInDesignMode) { return; } foreach (var p in extensions.CustomElementList) { foreach (var elemName in p.ElementList) { if (template.FindName($"{p.SourceName}_{elemName}", templateParent) is ContentControl elem) { Control plugControl = null; try { plugControl = p.Source.GetGameViewControl(new SDK.Plugins.GetGameViewControlArgs { Name = elemName, Mode = mode }); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get plugin control: {p.Source}."); } if (plugControl == null) { continue; } if (contextSource == null) { BindingTools.SetBinding(plugControl, plugControl is PluginUserControl ? PluginUserControl.GameContextProperty : Control.DataContextProperty, contextPath); } else { BindingTools.SetBinding(plugControl, plugControl is PluginUserControl ? PluginUserControl.GameContextProperty : Control.DataContextProperty, contextSource, contextPath); } elem.Focusable = false; elem.Content = plugControl; } } } } } } ================================================ FILE: source/Playnite/Controllers/GameControllerFactory.cs ================================================ using Playnite.Database; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Playnite.SDK; using Playnite.SDK.Plugins; using Playnite.Plugins; using Playnite.SDK.Events; namespace Playnite.Controllers { public class GameControllerFactory : IDisposable { private ILogger logger = LogManager.GetLogger(); private readonly GameDatabase database; public List PlayControllers { get; } = new List(); public List InstallControllers { get; } = new List(); public List UninstallControllers { get; } = new List(); // Starting even is not called by a controller, Playnite starts it automatically when a game is being started public event EventHandler Starting; public event EventHandler Started; public event EventHandler Stopped; public event EventHandler Uninstalled; public event EventHandler Installed; public event EventHandler InstallationCancelled; public event EventHandler StartupCancelled; public GameControllerFactory() { } public GameControllerFactory(GameDatabase database) : this() { this.database = database; } public void Dispose() { foreach (var controller in PlayControllers.ToList()) { RemoveController(controller); } foreach (var controller in InstallControllers.ToList()) { RemoveController(controller); } foreach (var controller in UninstallControllers.ToList()) { RemoveController(controller); } } public void AddController(PlayController controller) { controller.Started += Controller_Started; controller.Stopped += Controller_Stopped; PlayControllers.Add(controller); } public void AddController(InstallController controller) { controller.Installed += Controller_Installed; controller.InstallCancelled += Controller_InstallationCancelled; InstallControllers.Add(controller); } public void AddController(UninstallController controller) { controller.Uninstalled += Controller_Uninstalled; UninstallControllers.Add(controller); } public void RemovePlayController(Guid gameId) { var controller = PlayControllers.FirstOrDefault(a => a.Game?.Id == gameId); if (controller != null) { RemoveController(controller); } } public void RemoveInstallController(Guid gameId) { var controller = InstallControllers.FirstOrDefault(a => a.Game?.Id == gameId); if (controller != null) { RemoveController(controller); } } public void RemoveUninstallController(Guid gameId) { var controller = UninstallControllers.FirstOrDefault(a => a.Game?.Id == gameId); if (controller != null) { RemoveController(controller); } } public void RemoveController(PlayController controller) { controller.Started -= Controller_Started; controller.Stopped -= Controller_Stopped; try { controller.Dispose(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to dispose game controller {controller.GetType()}"); } PlayControllers.Remove(controller); } public void RemoveController(InstallController controller) { controller.Installed -= Controller_Installed; controller.InstallCancelled -= Controller_InstallationCancelled; try { controller.Dispose(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to dispose game controller {controller.GetType()}"); } InstallControllers.Remove(controller); } public void RemoveController(UninstallController controller) { controller.Uninstalled -= Controller_Uninstalled; try { controller.Dispose(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to dispose game controller {controller.GetType()}"); } UninstallControllers.Remove(controller); } public PlayController GetPlayController(Guid gameId) { return PlayControllers.FirstOrDefault(a => a.Game.Id == gameId); } public InstallController GetInstallController(Guid gameId) { return InstallControllers.FirstOrDefault(a => a.Game.Id == gameId); } public UninstallController GetUninstallController(Guid gameId) { return UninstallControllers.FirstOrDefault(a => a.Game.Id == gameId); } private void Controller_Stopped(object sender, GameStoppedEventArgs e) { Stopped?.Invoke(this, e); } private void Controller_Started(object sender, GameStartedEventArgs e) { Started?.Invoke(this, e); } private void Controller_Uninstalled(object sender, GameUninstalledEventArgs e) { Uninstalled?.Invoke(this, e); } private void Controller_Installed(object sender, GameInstalledEventArgs e) { Installed?.Invoke(this, e); } private void Controller_InstallationCancelled(object sender, GameInstallationCancelledEventArgs e) { InstallationCancelled?.Invoke(this, e); } internal void InvokeOnStarting(object sender, OnGameStartingEventArgs e) { Starting?.Invoke(this, e); } internal void InvokeOnGameStartupCancelled(object sernder, Game game) { StartupCancelled?.Invoke(this, new OnGameStartupCancelledEventArgs { Game = game }); } } } ================================================ FILE: source/Playnite/Controllers/GenericGameController.cs ================================================ using Playnite.Common; using Playnite.Database; using Playnite.Emulators; using Playnite.Scripting.PowerShell; using Playnite.SDK; using Playnite.SDK.Events; using Playnite.SDK.Exceptions; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed namespace Playnite.Controllers { public class EmulationPlayAction : GameAction { public EmulatorProfile SelectedEmulatorProfile { get; set; } public string SelectedRomPath { get; set; } } public class GenericPlayController : PlayController { protected CancellationTokenSource watcherToken; private GameDatabase database; private static ILogger logger = LogManager.GetLogger(); private readonly IPlayniteAPI playniteApi; private IPowerShellRuntime scriptRuntime; private PowerShellRuntime playRuntime; private Task playTask; private bool isDisposed = false; private EmulatorProfile currentEmuProfile; internal OnGameStartingEventArgs StartingArgs { get; private set; } // These are stored for emulator scripts because they can be executed in non-linear fasion private string startedRomFile; private Emulator startedEmulator; private EmulatorProfile startedEmulatorProfile; private string startedEmulatorDir; private int startedEmuProcessId; public GenericPlayController( GameDatabase db, Game game, IPowerShellRuntime scriptRuntime, IPlayniteAPI playniteApi) : base(game) { execContext = SynchronizationContext.Current; database = db; this.scriptRuntime = scriptRuntime; this.playniteApi = playniteApi; } public override void Play(PlayActionArgs args) { throw new NotSupportedException("This shouldn't be called."); } public void StartEmulator(EmulationPlayAction action, bool asyncExec, OnGameStartingEventArgs startingArgs) { var emulator = database.Emulators[action.EmulatorId]; if (emulator == null) { throw new Exception("Emulator not found."); } currentEmuProfile = null; if (action.SelectedEmulatorProfile is CustomEmulatorProfile customProfile) { currentEmuProfile = customProfile; } else if (action.SelectedEmulatorProfile is BuiltInEmulatorProfile builtinProfile) { currentEmuProfile = builtinProfile; } else { throw new Exception("Uknown play action configuration."); } emulator = emulator.GetClone(); if (!emulator.InstallDir.IsNullOrEmpty()) { emulator.InstallDir = Paths.FixSeparators(emulator.InstallDir.Replace(ExpandableVariables.PlayniteDirectory, PlaynitePaths.ProgramPath)); emulator.InstallDir = CheckPath(emulator.InstallDir, nameof(emulator.InstallDir), FileSystemItem.Directory); emulator.InstallDir = emulator.InstallDir.TrimEnd(Path.DirectorySeparatorChar); } StartingArgs = startingArgs; var startupPath = ""; var startupArgs = ""; var startupDir = ""; var romPath = Game.ExpandVariables(action.SelectedRomPath, true, emulator.InstallDir, null); // This is later passed to an emulator so it's up to it how it processes long paths. romPath = Paths.TrimLongPathPrefix(CheckPath(romPath, "ROM", FileSystemItem.File)); if (currentEmuProfile is CustomEmulatorProfile emuProf) { var expandedProfile = emuProf.ExpandVariables(Game, emulator.InstallDir, romPath); expandedProfile.Executable = CheckPath(expandedProfile.Executable, nameof(expandedProfile.Executable), FileSystemItem.File); expandedProfile.WorkingDirectory = CheckPath(expandedProfile.WorkingDirectory, nameof(expandedProfile.WorkingDirectory), FileSystemItem.Directory); if (!expandedProfile.StartupScript.IsNullOrWhiteSpace()) { expandedProfile.StartupScript = Game.ExpandVariables(expandedProfile.StartupScript, false, emulator.InstallDir, romPath); RunStartScript( $"{emulator.Name} runtime for {Game.Name}", expandedProfile.StartupScript, emulator.InstallDir, new Dictionary { { "Emulator", emulator.GetClone() }, { "EmulatorProfile", expandedProfile.GetClone() }, { "RomPath", romPath } }, asyncExec); } else { if (action.OverrideDefaultArgs) { startupArgs = Game.ExpandVariables(action.Arguments, false, emulator.InstallDir, romPath); } else { startupArgs = expandedProfile.Arguments; if (!action.AdditionalArguments.IsNullOrEmpty()) { startupArgs += " " + Game.ExpandVariables(action.AdditionalArguments, false, emulator.InstallDir, romPath); } } startupDir = expandedProfile.WorkingDirectory; startupPath = expandedProfile.Executable; StartEmulatorProcess(startupPath, startupArgs, startupDir, emulator.InstallDir, romPath, asyncExec, emulator.GetClone(), expandedProfile.GetClone(), expandedProfile.TrackingMode, expandedProfile.TrackingPath); } } else if (currentEmuProfile is BuiltInEmulatorProfile builtIn) { var profileDef = Emulation.GetProfile(emulator.BuiltInConfigId, builtIn.BuiltInProfileName); if (profileDef == null) { throw new Exception($"Can't find built-in {builtIn.BuiltInProfileName} emulator profile."); } if (profileDef.ScriptStartup) { var def = Emulation.GetDefition(emulator.BuiltInConfigId); if (def == null || !FileSystem.FileExists(Emulation.GetStartupScriptPath(def))) { throw new FileNotFoundException(ResourceProvider.GetString(LOC.ErrorEmulatorStartupScriptNotFound)); } RunStartScriptFile( $"{emulator.Name} runtime for {Game.Name}", Emulation.GetStartupScriptPath(def), emulator.InstallDir, new Dictionary { { "Emulator", emulator.GetClone() }, { "EmulatorProfile", profileDef.GetClone() }, { "RomPath", romPath } }, asyncExec); } else { builtIn = builtIn.GetClone(); startupDir = emulator.InstallDir; startupPath = Emulation.GetExecutable(emulator.InstallDir, profileDef, true); if (startupPath.IsNullOrEmpty()) { throw new FileNotFoundException(ResourceProvider.GetString(LOC.ErrorEmulatorExecutableNotFound) + $"\n\nRegular expression lookup: {profileDef.StartupExecutable}"); } if (action.OverrideDefaultArgs) { startupArgs = Game.ExpandVariables(action.Arguments, false, emulator.InstallDir, romPath); } else { if (builtIn.OverrideDefaultArgs) { startupArgs = Game.ExpandVariables(builtIn.CustomArguments, false, emulator.InstallDir, romPath); } else { startupArgs = Game.ExpandVariables(profileDef.StartupArguments, false, emulator.InstallDir, romPath); } if (!action.AdditionalArguments.IsNullOrEmpty()) { startupArgs += " " + Game.ExpandVariables(action.AdditionalArguments, false, emulator.InstallDir, romPath); } } StartEmulatorProcess(startupPath, startupArgs, startupDir, emulator.InstallDir, romPath, asyncExec, emulator.GetClone(), builtIn.GetClone(), TrackingMode.Process); } } else { throw new NotSupportedException(); } } private void StartEmulatorProcess( string path, string args, string workDir, string emulatorDir, string romPath, bool asyncExec, Emulator emulator, EmulatorProfile emuProfile, TrackingMode trackingMode, string trackingPath = null) { startedRomFile = romPath; startedEmulator = emulator; startedEmulatorProfile = emuProfile; startedEmulatorDir = emulatorDir; if (asyncExec) { ExecuteEmulatorScript(currentEmuProfile.PreScript, emulatorDir, romPath, emulator, emuProfile); Process process = null; try { process = ProcessStarter.StartProcess(path, args, workDir); } catch (Win32Exception exc) { // 2 is ERROR_FILE_NOT_FOUND if (exc.NativeErrorCode == 2) { throw new FileNotFoundException(LOC.ErrorEmulatorExecutableNotFound.GetLocalized() + $"\n\n{path} in {workDir}"); } else { throw; } } void gameStarted(int processId) { startedEmuProcessId = processId; ExecuteEmulatorScript(currentEmuProfile.PostScript, emulatorDir, romPath, emulator, emuProfile); InvokeOnStarted(new GameStartedEventArgs { StartedProcessId = startedEmuProcessId }); } if (trackingMode == TrackingMode.Default || trackingMode == TrackingMode.Process) { gameStarted(process.Id); var monitor = new MonitorProcessTree(process.Id); StartTracking( () => monitor.IsProcessTreeRunning(), gameStoppedAction: () => ExecuteEmulatorScript(currentEmuProfile?.ExitScript, startedEmulatorDir, startedRomFile, startedEmulator, startedEmulatorProfile)); } else if (trackingMode == TrackingMode.OriginalProcess) { gameStarted(process.Id); var monitor = new MonitorProcess(process); StartTracking( () => monitor.IsProcessRunning(), gameStoppedAction: () => ExecuteEmulatorScript(currentEmuProfile?.ExitScript, startedEmulatorDir, startedRomFile, startedEmulator, startedEmulatorProfile)); } else if (trackingMode == TrackingMode.Directory) { var watchDir = trackingPath.IsNullOrEmpty() ? emulatorDir : trackingPath; var monitor = new MonitorDirectory(watchDir); if (monitor.IsTrackable()) { StartTracking( () => monitor.IsProcessRunning() > 0, startupCheck: () => monitor.IsProcessRunning(), gameStartedAction: (id) => gameStarted(id), gameStoppedAction: () => ExecuteEmulatorScript(currentEmuProfile?.ExitScript, startedEmulatorDir, startedRomFile, startedEmulator, startedEmulatorProfile)); } else { InvokeOnStopped(new GameStoppedEventArgs()); } } else if (trackingMode == TrackingMode.ProcessName) { if (!trackingPath.IsNullOrWhiteSpace()) { var monitor = new MonitorProcessName(trackingPath); StartTracking( () => monitor.IsProcessRunning() > 0, startupCheck: () => monitor.IsProcessRunning(), gameStartedAction: (id) => gameStarted(id), gameStoppedAction: () => ExecuteEmulatorScript(currentEmuProfile?.ExitScript, startedEmulatorDir, startedRomFile, startedEmulator, startedEmulatorProfile)); } else { InvokeOnStopped(new GameStoppedEventArgs()); } } else { throw new NotSupportedException(); } } else { ExecuteEmulatorScript(currentEmuProfile.PreScript, emulatorDir, romPath, emulator, emuProfile); ProcessStarter.StartProcess(path, args, workDir); ExecuteEmulatorScript(currentEmuProfile.PostScript, emulatorDir, romPath, emulator, emuProfile); ExecuteEmulatorScript(currentEmuProfile.ExitScript, emulatorDir, romPath, emulator, emuProfile); } } private void ExecuteEmulatorScript(string script, string emulatorDir, string romPath, Emulator emulator, EmulatorProfile emuProfile) { if (script.IsNullOrWhiteSpace()) { return; } try { var scriptVars = new Dictionary { { "PlayniteApi", playniteApi }, { "Game", Game.GetClone() }, { "StartingArgs", StartingArgs }, { "SourceAction", StartingArgs.SourceAction }, { "SelectedRomFile", romPath }, { "Emulator", emulator }, { "EmulatorProfile", emuProfile }, { "StartedProcessId", startedEmuProcessId } }; var expandedScript = Game.ExpandVariables(script, false, emulatorDir, romPath); var dir = Game.ExpandVariables(Game.InstallDirectory, true); if (!dir.IsNullOrEmpty() && FileSystem.DirectoryExists(dir)) { scriptRuntime.Execute(expandedScript, dir, scriptVars); } else { scriptRuntime.Execute(expandedScript, variables: scriptVars); } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Emulator script failed."); logger.Debug(script); Dialogs.ShowMessage( exc.Message, LOC.ErrorEmulatorScriptAction, MessageBoxButton.OK, MessageBoxImage.Error); } } private void RunScript(string runtimeName, Action startAction, Dictionary variables, bool asyncExec = true) { variables.Add("Game", Game.GetClone()); variables.Add("PlayniteApi", playniteApi); variables.Add("IsPlayAction", asyncExec); playRuntime = new PowerShellRuntime(runtimeName); var stopWatch = Stopwatch.StartNew(); if (asyncExec) { watcherToken = new CancellationTokenSource(); variables.Add("CancelToken", watcherToken.Token); playTask = Task.Run(() => { try { startAction(playRuntime); if (!isDisposed) // Should not be called when we reached this from cancel state. { execContext.Post((_) => InvokeOnStopped(new GameStoppedEventArgs() { SessionLength = Convert.ToUInt64(stopWatch.Elapsed.TotalSeconds) }), null); } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Play script failed."); execContext.Post((_) => { InvokeOnStopped(new GameStoppedEventArgs() { SessionLength = Convert.ToUInt64(stopWatch.Elapsed.TotalSeconds) }); Dialogs.ShowMessage( exc.Message, LOC.ErrorPlayScriptAction, MessageBoxButton.OK, MessageBoxImage.Error); }, null); } finally { if (!playRuntime.IsDisposed) { playRuntime?.Dispose(); } } }); InvokeOnStarted(new GameStartedEventArgs()); } else { try { startAction(playRuntime); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Play script failed."); InvokeOnStopped(new GameStoppedEventArgs() { SessionLength = Convert.ToUInt64(stopWatch.Elapsed.TotalSeconds) }); Dialogs.ShowMessage( exc.Message, LOC.ErrorPlayScriptAction, MessageBoxButton.OK, MessageBoxImage.Error); } finally { if (!playRuntime.IsDisposed) { playRuntime?.Dispose(); } } } } private void RunStartScriptFile(string runtimeName, string scriptPath, string workDir, Dictionary variables, bool asyncExec = true) { logger.Debug($"Starting game using script file {scriptPath}"); RunScript(runtimeName, r => r.ExecuteFile(scriptPath, workDir, variables), variables, asyncExec); } private void RunStartScript(string runtimeName, string script, string workDir, Dictionary variables, bool asyncExec = true) { logger.Debug($"Starting game using script."); logger.Trace(script); RunScript(runtimeName, r => r.Execute(script, workDir, variables), variables, asyncExec); } public void Start(AutomaticPlayController controller) { var action = new GameAction { Type = controller.Type == AutomaticPlayActionType.Url ? GameActionType.URL : GameActionType.File, Arguments = controller.Arguments, Path = controller.Path, WorkingDir = controller.WorkingDir, TrackingMode = controller.TrackingMode, TrackingPath = controller.TrackingPath, InitialTrackingDelay = controller.InitialTrackingDelay, TrackingFrequency = controller.TrackingFrequency }; Start(action, true, new OnGameStartingEventArgs { SourceAction = action, Game = Game }); } public void Start(GameAction playAction, bool asyncExec, OnGameStartingEventArgs startingArgs) { if (playAction == null) { throw new ArgumentNullException("Cannot start game without play action."); } if (playAction.Type == GameActionType.Emulator) { throw new Exception("Cannot start emulator using this configuration."); } StartingArgs = startingArgs; var gameClone = Game.GetClone(); var action = playAction.GetClone(); action = action.ExpandVariables(gameClone); action.Path = CheckPath(action.Path, nameof(action.Path), FileSystemItem.File); action.WorkingDir = CheckPath(action.WorkingDir, nameof(action.WorkingDir), FileSystemItem.Directory); if (playAction.Type == GameActionType.Script) { if (action.Script.IsNullOrWhiteSpace()) { throw new ArgumentNullException("Game script is not defined."); } action.Script = Game.ExpandVariables(action.Script, false); RunStartScript( $"{Game.Name} play script", action.Script, gameClone.ExpandVariables(gameClone.InstallDirectory, true), new Dictionary(), asyncExec); } else { Process proc; if (action.Type == GameActionType.File) { proc = ProcessStarter.StartProcess(action.Path, action.Arguments, action.WorkingDir); } else if (action.Type == GameActionType.URL) { proc = ProcessStarter.StartUrl(action.Path); } else { throw new NotSupportedException(); } if (action.TrackingMode == TrackingMode.Default) { if (action.Type != GameActionType.URL) { // Handle Windows store apps var uwpMatch = Regex.Match(action.Arguments ?? string.Empty, @"shell:AppsFolder\\(.+)!.+"); if (action.Path == "explorer.exe" && uwpMatch.Success) { var scanDirectory = gameClone.InstallDirectory; if (!gameClone.GameId.IsNullOrEmpty()) { var prg = Programs.GetUWPApps().FirstOrDefault(a => a.AppId == gameClone.GameId); if (prg != null) { scanDirectory = prg.WorkDir; } } // TODO switch to WatchUwpApp once we are building as 64bit app //procMon.WatchUwpApp(uwpMatch.Groups[1].Value, false); var monitor = new MonitorProcessNames(scanDirectory); if (monitor.IsTrackable()) { StartTracking( () => monitor.IsProcessRunning() > 0, startupCheck: () => monitor.IsProcessRunning(), trackingFrequency: action.TrackingFrequency, trackingStartDelay: action.InitialTrackingDelay); } else { InvokeOnStopped(new GameStoppedEventArgs()); } } else { if (proc != null) { InvokeOnStarted(new GameStartedEventArgs() { StartedProcessId = proc.Id }); var monitor = new MonitorProcessTree(proc.Id); StartTracking( () => monitor.IsProcessTreeRunning(), trackingFrequency: action.TrackingFrequency, trackingStartDelay: action.InitialTrackingDelay); } else { InvokeOnStopped(new GameStoppedEventArgs()); } } } else { var monitor = new MonitorDirectory(gameClone.InstallDirectory); if (monitor.IsTrackable()) { StartTracking( () => monitor.IsProcessRunning() > 0, startupCheck: () => monitor.IsProcessRunning(), trackingFrequency: action.TrackingFrequency, trackingStartDelay: action.InitialTrackingDelay); } else { InvokeOnStopped(new GameStoppedEventArgs()); } } } else if (action.TrackingMode == TrackingMode.Process) { if (proc != null) { InvokeOnStarted(new GameStartedEventArgs() { StartedProcessId = proc.Id }); var monitor = new MonitorProcessTree(proc.Id); StartTracking( () => monitor.IsProcessTreeRunning(), trackingFrequency: action.TrackingFrequency, trackingStartDelay: action.InitialTrackingDelay); } else { InvokeOnStopped(new GameStoppedEventArgs()); } } else if (action.TrackingMode == TrackingMode.OriginalProcess) { if (proc != null) { InvokeOnStarted(new GameStartedEventArgs() { StartedProcessId = proc.Id }); var monitor = new MonitorProcess(proc); StartTracking( () => monitor.IsProcessRunning(), trackingFrequency: action.TrackingFrequency, trackingStartDelay: action.InitialTrackingDelay); } else { InvokeOnStopped(new GameStoppedEventArgs()); } } else if (action.TrackingMode == TrackingMode.Directory) { var watchDir = action.TrackingPath.IsNullOrEmpty() ? gameClone.InstallDirectory : action.TrackingPath; var monitor = new MonitorDirectory(watchDir); if (monitor.IsTrackable()) { StartTracking( () => monitor.IsProcessRunning() > 0, startupCheck: () => monitor.IsProcessRunning(), trackingFrequency: action.TrackingFrequency, trackingStartDelay: action.InitialTrackingDelay); } else { InvokeOnStopped(new GameStoppedEventArgs()); } } else if (action.TrackingMode == TrackingMode.ProcessName) { if (!action.TrackingPath.IsNullOrWhiteSpace()) { var monitor = new MonitorProcessName(action.TrackingPath); StartTracking( () => monitor.IsProcessRunning() > 0, startupCheck: () => monitor.IsProcessRunning(), trackingFrequency: action.TrackingFrequency, trackingStartDelay: action.InitialTrackingDelay); } else { InvokeOnStopped(new GameStoppedEventArgs()); } } else { throw new NotSupportedException(); } } } public void StartTracking( Func trackingAction, Func startupCheck = null, Action gameStartedAction = null, Action gameStoppedAction = null, int trackingFrequency = 2000, int trackingStartDelay = 0) { if (watcherToken != null) { throw new Exception("Game is already being tracked."); } watcherToken = new CancellationTokenSource(); Task.Run(async () => { ulong playTimeMs = 0; var trackingWatch = new Stopwatch(); var maxFailCount = 5; var failCount = 0; if (trackingStartDelay > 0) { await Task.Delay(trackingStartDelay, watcherToken.Token).ContinueWith(task => { }); } if (startupCheck != null) { while (true) { if (watcherToken.IsCancellationRequested) { return; } if (failCount >= maxFailCount) { InvokeOnStopped(new GameStoppedEventArgs(0)); return; } try { var id = startupCheck(); if (id > 0) { gameStartedAction?.Invoke(id); InvokeOnStarted(new GameStartedEventArgs { StartedProcessId = id }); break; } } catch (Exception e) { failCount++; logger.Error(e, "Game startup tracking iteration failed."); } await Task.Delay(trackingFrequency, watcherToken.Token).ContinueWith(task => { }); } } while (true) { failCount = 0; if (watcherToken.IsCancellationRequested) { return; } if (failCount >= maxFailCount) { gameStoppedAction?.Invoke(); InvokeOnStopped(new GameStoppedEventArgs(playTimeMs / 1000)); return; } try { trackingWatch.Restart(); if (!trackingAction()) { gameStoppedAction?.Invoke(); InvokeOnStopped(new GameStoppedEventArgs(playTimeMs / 1000)); return; } } catch (Exception e) { failCount++; logger.Error(e, "Game tracking iteration failed."); } await Task.Delay(trackingFrequency, watcherToken.Token).ContinueWith(task => { }); trackingWatch.Stop(); if (trackingWatch.ElapsedMilliseconds > (trackingFrequency + 30_000)) { // This is for cases where system is put into sleep or hibernation. // Realistically speaking, one tracking interation should never take 30+ seconds, // but lets use that as safe value in case this runs super slowly on some weird PCs. continue; } playTimeMs += (ulong)trackingWatch.ElapsedMilliseconds; } }); } public override void Dispose() { isDisposed = true; watcherToken?.Cancel(); playTask?.Wait(5000); // Give startup script time to gracefully shutdown. if (playRuntime?.IsDisposed == false) { playRuntime?.Dispose(); } watcherToken?.Dispose(); currentEmuProfile = null; } private string CheckPath(string sourcePath, string changeProp, FileSystemItem pathType) { if (sourcePath.IsNullOrWhiteSpace()) { return sourcePath; } var exists = false; var newPath = ""; switch (pathType) { case FileSystemItem.File: exists = FileSystem.FileExistsOnAnyDrive(sourcePath, out newPath); break; case FileSystemItem.Directory: exists = FileSystem.DirectoryExistsOnAnyDrive(sourcePath, out newPath); break; default: throw new NotSupportedException(); } if (exists && !string.Equals(newPath, sourcePath, StringComparison.OrdinalIgnoreCase)) { logger.Warn($"Replaced missing {changeProp} with new one:\n{{0}}\n{{1}}".Format(sourcePath, newPath)); return newPath; } return sourcePath; } } } ================================================ FILE: source/Playnite/Controls/ExtendedDataGrid.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.Controls { public class ExtendedDataGrid : DataGrid { static ExtendedDataGrid() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ExtendedDataGrid), new FrameworkPropertyMetadata(typeof(ExtendedDataGrid))); } public ExtendedDataGrid() { SelectionChanged += ExtendedDataGrid_SelectionChanged; } private void ExtendedDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) { SelectedItemsList = (IList)SelectedItems; } public IList SelectedItemsList { get { return (IList)GetValue(SelectedItemsListProperty); } set { SetValue(SelectedItemsListProperty, value); } } public static readonly DependencyProperty SelectedItemsListProperty = DependencyProperty.Register( nameof(SelectedItemsList), typeof(IList), typeof(ExtendedDataGrid), new PropertyMetadata(null)); } } ================================================ FILE: source/Playnite/Controls/ExtendedListBox.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.Controls { public class ExtendedListBox : ListBox { internal bool ignoreSelectedItemsListChanges = false; static ExtendedListBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ExtendedListBox), new FrameworkPropertyMetadata(typeof(ExtendedListBox))); } public ExtendedListBox() { SelectionChanged += ExtendedListBox_SelectionChanged; } private void ExtendedListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { ignoreSelectedItemsListChanges = true; SelectedItemsList = (IList)SelectedItems; ignoreSelectedItemsListChanges = false; } public IList SelectedItemsList { get { return (IList)GetValue(SelectedItemsListProperty); } set { SetValue(SelectedItemsListProperty, value); } } public static readonly DependencyProperty SelectedItemsListProperty = DependencyProperty.Register( nameof(SelectedItemsList), typeof(IList), typeof(ExtendedListBox), new PropertyMetadata(null, SelectedItemsListChanged)); public static void SelectedItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var list = (ExtendedListBox)d; if (list.ignoreSelectedItemsListChanges || list.SelectionMode == SelectionMode.Single) { return; } list.SelectedItems.Clear(); var newValues = e.NewValue as IList; if (newValues.HasItems()) { newValues.ForEach(a => list.SelectedItems.Add(a)); } } } } ================================================ FILE: source/Playnite/Controls/ExtendedListView.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.Controls { public class ExtendedListView : ListView { static ExtendedListView() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ExtendedListView), new FrameworkPropertyMetadata(typeof(ExtendedListView))); } public ExtendedListView() { SelectionChanged += ExtendedListView_SelectionChanged; } private void ExtendedListView_SelectionChanged(object sender, SelectionChangedEventArgs e) { SelectedItemsList = (IList)SelectedItems; } public IList SelectedItemsList { get { return (IList)GetValue(SelectedItemsListProperty); } set { SetValue(SelectedItemsListProperty, value); } } public static readonly DependencyProperty SelectedItemsListProperty = DependencyProperty.Register( nameof(SelectedItemsList), typeof(IList), typeof(ExtendedListView), new PropertyMetadata(null)); } } ================================================ FILE: source/Playnite/Controls/FadeImage.xaml ================================================  ================================================ FILE: source/Playnite/Controls/FadeImage.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Effects; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.Controls { /// /// Interaction logic for FadeImage.xaml /// public partial class FadeImage : UserControl { private enum CurrentImage { Image1, Image2, None } private CurrentImage currentImage = CurrentImage.None; private object currentSource = null; private System.Timers.Timer sourceChangeTimer = new System.Timers.Timer() { AutoReset = false }; private object newSource; private readonly SynchronizationContext context; internal Storyboard Image1FadeIn; internal Storyboard Image2FadeIn; internal Storyboard Image1FadeOut; internal Storyboard Image2FadeOut; internal Storyboard stateAnim; internal Storyboard BorderDarkenFadeOut; public double SourceUpdateDelay { get; set; } = 0; #region AnimationEnabled public static readonly DependencyProperty AnimationEnabledProperty = DependencyProperty.Register( nameof(AnimationEnabled), typeof(bool), typeof(FadeImage), new PropertyMetadata(true)); public bool AnimationEnabled { get { return (bool)GetValue(AnimationEnabledProperty); } set { SetValue(AnimationEnabledProperty, value); } } #endregion AnimationEnabled #region Source public static readonly DependencyProperty SourceProperty = DependencyProperty.Register( nameof(Source), typeof(object), typeof(FadeImage), new PropertyMetadata(null, SourceChanged)); public object Source { get { return GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } } #endregion Source #region ImageOpacityMask public static readonly DependencyProperty ImageOpacityMaskProperty = DependencyProperty.Register( nameof(ImageOpacityMask), typeof(Brush), typeof(FadeImage), new PropertyMetadata()); public Brush ImageOpacityMask { get { return (Brush)GetValue(ImageOpacityMaskProperty); } set { SetValue(ImageOpacityMaskProperty, value); } } #endregion ImageOpacityMask #region ImageDarkeningBrush public static readonly DependencyProperty ImageDarkeningBrushProperty = DependencyProperty.Register( nameof(ImageDarkeningBrush), typeof(Brush), typeof(FadeImage), new PropertyMetadata()); public Brush ImageDarkeningBrush { get { return (Brush)GetValue(ImageDarkeningBrushProperty); } set { SetValue(ImageDarkeningBrushProperty, value); } } #endregion ImageDarkeningBrush #region Stretch public static readonly DependencyProperty StretchProperty = DependencyProperty.Register( nameof(Stretch), typeof(Stretch), typeof(FadeImage), new PropertyMetadata(Stretch.UniformToFill)); public Stretch Stretch { get { return (Stretch)GetValue(StretchProperty); } set { SetValue(StretchProperty, value); } } #endregion Strech #region IsBlurEnabled public static readonly DependencyProperty IsBlurEnabledProperty = DependencyProperty.Register( nameof(IsBlurEnabled), typeof(bool), typeof(FadeImage), new PropertyMetadata(false, BlurSettingChanged)); public bool IsBlurEnabled { get { return (bool)GetValue(IsBlurEnabledProperty); } set { SetValue(IsBlurEnabledProperty, value); } } #endregion IsBlurEnabled #region BlurAmount public static readonly DependencyProperty BlurAmountProperty = DependencyProperty.Register( nameof(BlurAmount), typeof(int), typeof(FadeImage), new PropertyMetadata(10, BlurSettingChanged)); public int BlurAmount { get { return (int)GetValue(BlurAmountProperty); } set { SetValue(BlurAmountProperty, value); } } #endregion IsBlurEnabled #region HighQualityBlur public static readonly DependencyProperty HighQualityBlurProperty = DependencyProperty.Register( nameof(HighQualityBlurProperty), typeof(bool), typeof(FadeImage), new PropertyMetadata(false, BlurSettingChanged)); public bool HighQualityBlur { get { return (bool)GetValue(HighQualityBlurProperty); } set { SetValue(HighQualityBlurProperty, value); } } #endregion HighQualityBlur public FadeImage() { InitializeComponent(); context = SynchronizationContext.Current; Image1FadeIn = (Storyboard)TryFindResource("Image1FadeIn"); Image2FadeIn = (Storyboard)TryFindResource("Image2FadeIn"); Image1FadeOut = (Storyboard)TryFindResource("Image1FadeOut"); Image2FadeOut = (Storyboard)TryFindResource("Image2FadeOut"); BorderDarkenFadeOut = (Storyboard)TryFindResource("BorderDarkenFadeOut"); Image1FadeOut.Completed += Image1FadeOut_Completed; Image2FadeOut.Completed += Image2FadeOut_Completed; BorderDarkenFadeOut.Completed += BorderDarkenOut_Completed; sourceChangeTimer.Elapsed += (_, __) => context.Send((___) => LoadNewSource(), null); } ~FadeImage() { sourceChangeTimer.Dispose(); } private void Image1FadeOut_Completed(object sender, EventArgs e) { Image1.Source = null; Image1.UpdateLayout(); GC.Collect(); } private void Image2FadeOut_Completed(object sender, EventArgs e) { Image2.Source = null; Image2.UpdateLayout(); } private void BorderDarkenOut_Completed(object sender, EventArgs e) { BorderDarken.Opacity = 0; } private static void BlurSettingChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { var control = (FadeImage)obj; if (control.Source == null) { return; } var blurAmount = control.BlurAmount; var blurEnabled = control.IsBlurEnabled; var highQuality = control.HighQualityBlur; if (blurEnabled) { control.ImageHolder.Effect = new BlurEffect() { KernelType = KernelType.Gaussian, Radius = blurAmount, RenderingBias = highQuality ? RenderingBias.Quality : RenderingBias.Performance }; } else { control.ImageHolder.Effect = null; } } private static void SourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { var control = (FadeImage)obj; control.newSource = args.NewValue; if (control.SourceUpdateDelay > 0) { control.sourceChangeTimer.Enabled = false; control.sourceChangeTimer.Interval = control.SourceUpdateDelay; control.sourceChangeTimer.Enabled = true; } else { control.LoadNewSource(); } } private async void LoadNewSource() { sourceChangeTimer.Enabled = false; if (newSource?.Equals(currentSource) == true) { return; } var blurAmount = BlurAmount; var blurEnabled = IsBlurEnabled; var highQuality = HighQualityBlur; BitmapSource image = null; currentSource = newSource; if (newSource != null) { image = await Task.Factory.StartNew(() => { if (newSource is string str) { return ImageSourceManager.GetImage(str, false); } else if (newSource is BitmapLoadProperties props) { return ImageSourceManager.GetImage(props.Source, false, props); } else { return null; } }); } if (blurEnabled) { if (ImageHolder.Effect == null) { ImageHolder.Effect = new BlurEffect() { KernelType = KernelType.Gaussian, Radius = blurAmount, RenderingBias = highQuality ? RenderingBias.Quality : RenderingBias.Performance }; } } else { if (ImageHolder.Effect != null) { ImageHolder.Effect = null; } } if (AnimationEnabled) { if (image == null) { if (currentImage == CurrentImage.None) { return; } if (currentImage == CurrentImage.Image1) { Image1FadeOut.Begin(); BorderDarkenFadeOut.Begin(); } else if (currentImage == CurrentImage.Image2) { Image2FadeOut.Begin(); BorderDarkenFadeOut.Begin(); } currentImage = CurrentImage.None; } else { if (currentImage == CurrentImage.None) { Image1FadeOut.Stop(); Image1.Source = image; Image1FadeIn.Begin(); BorderDarken.Opacity = 1; BorderDarkenFadeOut.Stop(); currentImage = CurrentImage.Image1; } else if (currentImage == CurrentImage.Image1) { Image2FadeOut.Stop(); Image2.Source = image; Image2FadeIn.Begin(); Image1FadeOut.Begin(); BorderDarken.Opacity = 1; BorderDarkenFadeOut.Stop(); currentImage = CurrentImage.Image2; } else if (currentImage == CurrentImage.Image2) { Image1FadeOut.Stop(); Image1.Source = image; Image1FadeIn.Begin(); Image2FadeOut.Begin(); BorderDarken.Opacity = 1; BorderDarkenFadeOut.Stop(); currentImage = CurrentImage.Image1; } } } else { if (currentImage == CurrentImage.Image1) { Image1.Source = image; } else if (currentImage == CurrentImage.Image2) { Image2.Source = image; } else { Image1.Source = image; currentImage = CurrentImage.Image1; } } } } } ================================================ FILE: source/Playnite/Controls/GridEx.cs ================================================ using Playnite.Extensions.Markup; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using TheArtOfDev.HtmlRenderer.WPF; namespace Playnite.Controls { public class GridEx : Grid { #region RowCount Property /// /// Adds the specified number of Rows to RowDefinitions. /// Default Height is Auto /// public static readonly DependencyProperty RowCountProperty = DependencyProperty.Register( "RowCount", typeof(int), typeof(GridEx), new PropertyMetadata(-1, RowCountChanged)); // Get public static int GetRowCount(DependencyObject obj) { return (int)obj.GetValue(RowCountProperty); } // Set public static void SetRowCount(DependencyObject obj, int value) { obj.SetValue(RowCountProperty, value); } // Change Event - Adds the Rows public static void RowCountChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (!(obj is Grid) || (int)e.NewValue < 0) return; Grid grid = (Grid)obj; grid.RowDefinitions.Clear(); for (int i = 0; i < (int)e.NewValue; i++) grid.RowDefinitions.Add( new RowDefinition() { Height = GridLength.Auto }); SetStarRows(grid); } #endregion #region ColumnCount Property /// /// Adds the specified number of Columns to ColumnDefinitions. /// Default Width is Auto /// public static readonly DependencyProperty ColumnCountProperty = DependencyProperty.Register( "ColumnCount", typeof(int), typeof(GridEx), new PropertyMetadata(-1, ColumnCountChanged)); // Get public static int GetColumnCount(DependencyObject obj) { return (int)obj.GetValue(ColumnCountProperty); } // Set public static void SetColumnCount(DependencyObject obj, int value) { obj.SetValue(ColumnCountProperty, value); } // Change Event - Add the Columns public static void ColumnCountChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (!(obj is Grid) || (int)e.NewValue < 0) return; Grid grid = (Grid)obj; grid.ColumnDefinitions.Clear(); for (int i = 0; i < (int)e.NewValue; i++) grid.ColumnDefinitions.Add( new ColumnDefinition() { Width = GridLength.Auto }); SetStarColumns(grid); } #endregion #region StarRows Property /// /// Makes the specified Row's Height equal to Star. /// Can set on multiple Rows /// public static readonly DependencyProperty StarRowsProperty = DependencyProperty.Register( "StarRows", typeof(string), typeof(GridEx), new PropertyMetadata(string.Empty, StarRowsChanged)); // Get public static string GetStarRows(DependencyObject obj) { return (string)obj.GetValue(StarRowsProperty); } // Set public static void SetStarRows(DependencyObject obj, string value) { obj.SetValue(StarRowsProperty, value); } // Change Event - Makes specified Row's Height equal to Star public static void StarRowsChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString())) return; SetStarRows((Grid)obj); } #endregion #region StarColumns Property /// /// Makes the specified Column's Width equal to Star. /// Can set on multiple Columns /// public static readonly DependencyProperty StarColumnsProperty = DependencyProperty.Register( "StarColumns", typeof(string), typeof(GridEx), new PropertyMetadata(string.Empty, StarColumnsChanged)); // Get public static string GetStarColumns(DependencyObject obj) { return (string)obj.GetValue(StarColumnsProperty); } // Set public static void SetStarColumns(DependencyObject obj, string value) { obj.SetValue(StarColumnsProperty, value); } // Change Event - Makes specified Column's Width equal to Star public static void StarColumnsChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString())) return; SetStarColumns((Grid)obj); } #endregion #region AutoLayoutColumns public static readonly DependencyProperty AutoLayoutColumnsProperty = DependencyProperty.Register( "AutoLayoutColumns", typeof(int), typeof(GridEx), new PropertyMetadata(-1, AutoLayoutColumnsChanged)); public static int GetAutoLayoutColumns(DependencyObject obj) { return (int)obj.GetValue(AutoLayoutColumnsProperty); } public static void SetAutoLayoutColumns(DependencyObject obj, int value) { obj.SetValue(AutoLayoutColumnsProperty, value); } public static void AutoLayoutColumnsChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e) { ((GridEx)obj).ArrangeChildren(); } #endregion AutoLayoutColumns public GridEx() : base() { } internal void ArrangeChildren() { var columns = GetAutoLayoutColumns(this); if (columns == -1) { return; } var index = 0; foreach (UIElement elem in Children) { var span = Grid.GetColumnSpan(elem); SetColumn(elem, index % columns); SetRow(elem, index / columns); if (span > 0) { index += span; } else { index++; } } } private static void SetStarColumns(Grid grid) { var starColumns = GetStarColumns(grid).Split(','); for (int i = 0; i < grid.ColumnDefinitions.Count; i++) { if (starColumns.Contains(i.ToString())) { grid.ColumnDefinitions[i].Width = new GridLength(1, GridUnitType.Star); } else { grid.ColumnDefinitions[i].Width = new GridLength(1, GridUnitType.Auto); } } } private static void SetStarRows(Grid grid) { var starRows = GetStarRows(grid).Split(','); for (int i = 0; i < grid.RowDefinitions.Count; i++) { if (starRows.Contains(i.ToString())) { grid.RowDefinitions[i].Height = new GridLength(1, GridUnitType.Star); } else { grid.RowDefinitions[i].Height = new GridLength(1, GridUnitType.Auto); } } } protected override Size MeasureOverride(Size constraint) { ArrangeChildren(); return base.MeasureOverride(constraint); } } } ================================================ FILE: source/Playnite/Controls/HotKeyBox.cs ================================================ using Playnite.Common; using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace Playnite.Controls { public class HotKeyBox : TextBox { public static readonly DependencyProperty HotkeyProperty = DependencyProperty.Register( nameof(Hotkey), typeof(HotKey), typeof(HotKeyBox), new FrameworkPropertyMetadata(default(HotKey), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public HotKey Hotkey { get => (HotKey)GetValue(HotkeyProperty); set => SetValue(HotkeyProperty, value); } public bool ClearWithDeleteKeys { get; set; } = true; static HotKeyBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(HotKeyBox), new FrameworkPropertyMetadata(typeof(HotKeyBox))); } public HotKeyBox() : base() { PreviewKeyDown += HotKeyBox_PreviewKeyDown; IsReadOnly = true; IsReadOnlyCaretVisible = false; IsUndoEnabled = false; BindingTools.SetBinding( this, TextProperty, this, nameof(Hotkey), System.Windows.Data.BindingMode.OneWay, targetNullValue: ResourceProvider.GetResource(LOC.None)); } private void HotKeyBox_PreviewKeyDown(object sender, KeyEventArgs e) { e.Handled = true; var modifiers = Keyboard.Modifiers; var key = e.Key; if (key == Key.System) { key = e.SystemKey; } if (modifiers == ModifierKeys.None && (key == Key.Delete || key == Key.Back || key == Key.Escape) && ClearWithDeleteKeys) { Hotkey = null; return; } if (key == Key.LeftCtrl || key == Key.RightCtrl || key == Key.LeftAlt || key == Key.RightAlt || key == Key.LeftShift || key == Key.RightShift || key == Key.LWin || key == Key.RWin || key == Key.Clear || key == Key.OemClear || key == Key.Apps) { return; } Hotkey = new HotKey(key, modifiers); } } } ================================================ FILE: source/Playnite/Controls/HtmlTextView.cs ================================================ using Playnite.Extensions.Markup; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using TheArtOfDev.HtmlRenderer.WPF; namespace Playnite.Controls { public class HtmlTextView : StackPanel { private const string defaultTemplate = @" Game Description
{text}
"; private int currentLoadedLength = 0; private readonly int loadPartLength = 10_000; internal string templateContent = string.Empty; private readonly HtmlPanel htmlPanel; private readonly Button moreButton; public string TemplatePath { get { return (string)GetValue(TemplatePathProperty); } set { SetValue(TemplatePathProperty, value); } } public static readonly DependencyProperty TemplatePathProperty = DependencyProperty.Register( "TemplatePath", typeof(string), typeof(HtmlTextView), new PropertyMetadata(null, TemplatePathChange)); private static void TemplatePathChange(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as HtmlTextView; if (e.NewValue is string path) { if (path.IsNullOrEmpty()) { obj.templateContent = string.Empty; } else if (File.Exists(path)) { obj.templateContent = File.ReadAllText(path); } obj.UpdateTextContent(); } } public double HtmlFontSize { get { return (double)GetValue(HtmlFontSizeProperty); } set { SetValue(HtmlFontSizeProperty, value); } } public static readonly DependencyProperty HtmlFontSizeProperty = DependencyProperty.Register("HtmlFontSize", typeof(double), typeof(HtmlTextView), new PropertyMetadata(11.0, OnHtmlFontSizeChange)); private static void OnHtmlFontSizeChange(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as HtmlTextView; if (e.NewValue is double size) { obj.UpdateTextContent(); } } public FontFamily HtmlFontFamily { get { return (FontFamily)GetValue(HtmlFontFamilyProperty); } set { SetValue(HtmlFontFamilyProperty, value); } } public static readonly DependencyProperty HtmlFontFamilyProperty = DependencyProperty.Register("HtmlFontFamily", typeof(FontFamily), typeof(HtmlTextView), new PropertyMetadata(new FontFamily("Trebuchet MS"), OnHtmlFontFamilyChange)); private static void OnHtmlFontFamilyChange(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as HtmlTextView; obj.UpdateTextContent(); } public Color LinkForeground { get { return (Color)GetValue(LinkForegroundProperty); } set { SetValue(LinkForegroundProperty, value); } } public static readonly DependencyProperty LinkForegroundProperty = DependencyProperty.Register("LinkForeground", typeof(Color), typeof(HtmlTextView), new PropertyMetadata(Colors.White, OnLinkForegroundChange)); private static void OnLinkForegroundChange(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as HtmlTextView; if (e.NewValue is Color color) { obj.UpdateTextContent(); } } public Color HtmlForeground { get { return (Color)GetValue(HtmlForegroundProperty); } set { SetValue(HtmlForegroundProperty, value); } } public static readonly DependencyProperty HtmlForegroundProperty = DependencyProperty.Register("HtmlForeground", typeof(Color), typeof(HtmlTextView), new PropertyMetadata(Colors.Black, OnHtmlForegroundChange)); private static void OnHtmlForegroundChange(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as HtmlTextView; if (e.NewValue is Color color) { obj.UpdateTextContent(); } } public string HtmlText { get { return (string)GetValue(HtmlTextProperty); } set { SetValue(HtmlTextProperty, value); } } public static readonly DependencyProperty HtmlTextProperty = DependencyProperty.Register("HtmlText", typeof(string), typeof(HtmlTextView), new PropertyMetadata(string.Empty, OnHtmlTextChange)); private static void OnHtmlTextChange(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as HtmlTextView; obj.UpdateTextContent(); } public bool PartialLoadEnabled { get { return (bool)GetValue(PartialLoadEnabledProperty); } set { SetValue(PartialLoadEnabledProperty, value); } } public static readonly DependencyProperty PartialLoadEnabledProperty = DependencyProperty.Register( "PartialLoadEnabled", typeof(bool), typeof(HtmlTextView), new PropertyMetadata(true)); // These are for theme backwards compatibility since this control is no longer Control but FrameworkElement public FontStyle FontStyle { get; set; } public static readonly DependencyProperty FontStyleProperty = DependencyProperty.Register("FontStyle", typeof(FontStyle), typeof(HtmlTextView)); public FontStretch FontStretch { get; set; } public static readonly DependencyProperty FontStretchProperty = DependencyProperty.Register("FontStretch", typeof(FontStretch), typeof(HtmlTextView)); public double FontSize { get; set; } public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register("FontSize", typeof(double), typeof(HtmlTextView)); public FontFamily FontFamily { get; set; } public static readonly DependencyProperty FontFamilyProperty = DependencyProperty.Register("FontFamily", typeof(FontFamily), typeof(HtmlTextView)); public Brush Foreground { get; set; } public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register("Foreground", typeof(Brush), typeof(HtmlTextView)); public Thickness BorderThickness { get; set; } public static readonly DependencyProperty BorderThicknessProperty = DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(HtmlTextView)); public bool IsTabStop { get; set; } public static readonly DependencyProperty IsTabStopProperty = DependencyProperty.Register("IsTabStop", typeof(bool), typeof(HtmlTextView)); public VerticalAlignment VerticalContentAlignment { get; set; } public static readonly DependencyProperty VerticalContentAlignmentProperty = DependencyProperty.Register("VerticalContentAlignment", typeof(VerticalAlignment), typeof(HtmlTextView)); public int TabIndex { get; set; } public static readonly DependencyProperty TabIndexProperty = DependencyProperty.Register("TabIndex", typeof(int), typeof(HtmlTextView)); public Thickness Padding { get; set; } public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register("Padding", typeof(Thickness), typeof(HtmlTextView)); public FontWeight FontWeight { get; set; } public static readonly DependencyProperty FontWeightProperty = DependencyProperty.Register("FontWeight", typeof(FontWeight), typeof(HtmlTextView)); public Brush BorderBrush { get; set; } public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(HtmlTextView)); public HorizontalAlignment HorizontalContentAlignment { get; set; } public static readonly DependencyProperty HorizontalContentAlignmentProperty = DependencyProperty.Register("HorizontalContentAlignment", typeof(HorizontalAlignment), typeof(HtmlTextView)); internal void UpdateTextContent() { currentLoadedLength = loadPartLength; if (PartialLoadEnabled && !HtmlText.IsNullOrEmpty() && HtmlText.Length > loadPartLength) { moreButton.Visibility = Visibility.Visible; SetHtmlContent(HtmlText.Substring(0, loadPartLength)); } else { moreButton.Visibility = Visibility.Hidden; SetHtmlContent(HtmlText ?? string.Empty); }; } internal void SetHtmlContent(string htmlContent) { var content = string.Empty; if (!templateContent.IsNullOrEmpty()) { content = templateContent; } else if (HtmlText?.Contains("") != true) { content = defaultTemplate; } content = content.Replace("{foreground}", HtmlForeground.ToHtml()); content = content.Replace("{link_foreground}", LinkForeground.ToHtml()); content = content.Replace("{font_family}", HtmlFontFamily.ToString()); content = content.Replace("{font_size}", HtmlFontSize.ToString()); htmlPanel.Text = content.Replace("{text}", htmlContent); } static HtmlTextView() { DefaultStyleKeyProperty.OverrideMetadata(typeof(HtmlTextView), new FrameworkPropertyMetadata(typeof(HtmlTextView))); } public HtmlTextView() { htmlPanel = new HtmlPanel(); htmlPanel.Background = Brushes.Transparent; // Always use LTR because HtmlPanel doesn't support RTL properly FlowDirection = FlowDirection.LeftToRight; // This makes performance way better to leave scrolling to be handled by the parent layout ScrollViewer.SetHorizontalScrollBarVisibility(htmlPanel, ScrollBarVisibility.Disabled); ScrollViewer.SetVerticalScrollBarVisibility(htmlPanel, ScrollBarVisibility.Disabled); moreButton = new Button { HorizontalAlignment = HorizontalAlignment.Center, Content = LOC.LoadMore.GetLocalized(), Visibility = Visibility.Hidden, Margin = new Thickness(0, 0, 0, 5) }; moreButton.Click += (_, __) => { currentLoadedLength += loadPartLength; if (currentLoadedLength > HtmlText.Length) { moreButton.Visibility = Visibility.Hidden; SetHtmlContent(HtmlText); } else { SetHtmlContent(HtmlText.Substring(0, currentLoadedLength)); } }; Children.Add(htmlPanel); Children.Add(moreButton); } public override void OnApplyTemplate() { base.OnApplyTemplate(); } } } ================================================ FILE: source/Playnite/Controls/WindowBase.cs ================================================ using Playnite.Common; using Playnite.Native; using Playnite.Windows; using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; namespace Playnite.Controls { public class EmptyWindowAutomationPeer : FrameworkElementAutomationPeer { private static readonly List emptyList = new List(); public EmptyWindowAutomationPeer(FrameworkElement owner) : base(owner) { } protected override string GetNameCore() { return nameof(EmptyWindowAutomationPeer); } protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.Window; } protected override List GetChildrenCore() { return emptyList; } } [TemplatePart(Name = "PART_ButtonMinimize", Type = typeof(Button))] [TemplatePart(Name = "PART_ButtonMaximize", Type = typeof(Button))] [TemplatePart(Name = "PART_ButtonClose", Type = typeof(Button))] [TemplatePart(Name = "PART_TextTitle", Type = typeof(TextBlock))] public class WindowBase : Window, INotifyPropertyChanged { public readonly WindowPositionHandler PositionHandler; private readonly EmptyWindowAutomationPeer emptyAutomationPeer; private HwndSource hwndSource; private readonly Dictionary hotKeyHandlers = new Dictionary(); private Button MinimizeButton; private Button MaximizeButton; private Button CloseButton; private TextBlock TextTitle; public static TextFormattingMode TextFormattingMode { get; private set; } = TextFormattingMode.Ideal; public static TextRenderingMode TextRenderingMode { get; private set; } = TextRenderingMode.Auto; public event PropertyChangedEventHandler PropertyChanged; public readonly Guid Id = Guid.NewGuid(); public static readonly RoutedEvent ClosedRoutedEvent = EventManager.RegisterRoutedEvent( "ClosedRouted", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(WindowBase)); public event RoutedEventHandler ClosedRouted { add { AddHandler(ClosedRoutedEvent, value); } remove { RemoveHandler(ClosedRoutedEvent, value); } } public static readonly RoutedEvent LoadedRoutedEvent = EventManager.RegisterRoutedEvent( "LoadedRouted", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(WindowBase)); public event RoutedEventHandler LoadedRouted { add { AddHandler(LoadedRoutedEvent, value); } remove { RemoveHandler(LoadedRoutedEvent, value); } } public static readonly RoutedEvent ActivatedRoutedEvent = EventManager.RegisterRoutedEvent( "ActivatedRouted", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(WindowBase)); public event RoutedEventHandler ActivatedRouted { add { AddHandler(ActivatedRoutedEvent, value); } remove { RemoveHandler(ActivatedRoutedEvent, value); } } // The reason we currently don't have accessibility/automation interaface enabled is because of performance. // For some reason certain controls, like listviews, degrade a lot performance wise when accessibility is enabled. // It doesn't seem to be an issue in Playnite itself from my testing (content of listview basically doesn't matter), // so most likely WPF bug. protected override AutomationPeer OnCreateAutomationPeer() { var acc = PlayniteApplication.Current?.AppSettings.AccessibilityInterface; if (acc != null) { switch (acc.Value) { case AccessibilityInterfaceOptions.Auto: return Computer.GetScreenReaderActive() ? base.OnCreateAutomationPeer() : emptyAutomationPeer; case AccessibilityInterfaceOptions.AlwaysOn: return base.OnCreateAutomationPeer(); case AccessibilityInterfaceOptions.AlwaysOff: default: return emptyAutomationPeer; } } else { return emptyAutomationPeer; } } public bool HasChildWindow { get => WindowManager.GetHasChild(this); } public bool ShowMinimizeButton { get { return (bool)GetValue(ShowMinimizeButtonProperty); } set { SetValue(ShowMinimizeButtonProperty, value); } } public bool ShowMaximizeButton { get { return (bool)GetValue(ShowMaximizeButtonProperty); } set { SetValue(ShowMaximizeButtonProperty, value); } } public bool ShowCloseButton { get { return (bool)GetValue(ShowCloseButtonProperty); } set { SetValue(ShowCloseButtonProperty, value); } } public bool ShowTitle { get { return (bool)GetValue(ShowTitleProperty); } set { SetValue(ShowTitleProperty, value); } } public bool BlockAltF4 { get { return (bool)GetValue(BlockAltF4Property); } set { SetValue(BlockAltF4Property, value); } } public static readonly DependencyProperty ShowMinimizeButtonProperty = DependencyProperty.Register(nameof(ShowMinimizeButton), typeof(bool), typeof(WindowBase), new PropertyMetadata(true, ShowMinimizeButtonPropertyChanged)); public static readonly DependencyProperty ShowMaximizeButtonProperty = DependencyProperty.Register(nameof(ShowMaximizeButton), typeof(bool), typeof(WindowBase), new PropertyMetadata(true, ShowMaximizeButtonPropertyChanged)); public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register(nameof(ShowCloseButton), typeof(bool), typeof(WindowBase), new PropertyMetadata(true, ShowCloseButtonPropertyChanged)); public static readonly DependencyProperty ShowTitleProperty = DependencyProperty.Register(nameof(ShowTitle), typeof(bool), typeof(WindowBase), new PropertyMetadata(true, ShowTitlePropertyChanged)); public static readonly DependencyProperty BlockAltF4Property = DependencyProperty.Register(nameof(BlockAltF4), typeof(bool), typeof(WindowBase), new PropertyMetadata(false)); public bool IsShown { get; private set; } public bool WasClosed { get; private set; } public IntPtr Handle { get; private set; } public bool? DialogResultFixed { get; set; } = null; static WindowBase() { DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowBase), new FrameworkPropertyMetadata(typeof(WindowBase))); } public WindowBase() : base() { emptyAutomationPeer = new EmptyWindowAutomationPeer(this); Style defaultStyle = (Style)Application.Current?.TryFindResource(typeof(WindowBase)); if (defaultStyle != null) { Style = defaultStyle; } if (Localization.IsRightToLeft) { FlowDirection = FlowDirection.RightToLeft; } TextOptions.SetTextFormattingMode(this, TextFormattingMode); TextOptions.SetTextRenderingMode(this, TextRenderingMode); Closed += (_, __) => { hwndSource?.RemoveHook(HwndHook); IsShown = false; WasClosed = true; RaiseEvent(new RoutedEventArgs(ClosedRoutedEvent)); }; Loaded += (_, __) => { Handle = new WindowInteropHelper(this).Handle; hwndSource = HwndSource.FromHwnd(Handle); hwndSource.AddHook(HwndHook); IsShown = true; RaiseEvent(new RoutedEventArgs(LoadedRoutedEvent)); }; Activated += (_, __) => { RaiseEvent(new RoutedEventArgs(ActivatedRoutedEvent)); }; PreviewKeyDown += (_, e) => { if (e.Key == Key.System && e.SystemKey == Key.F4 && BlockAltF4) { e.Handled = true; } }; // This fixes an issue if SizeToContent is used on windows with custom WindowChrome (all Playnite windows) // https://stackoverflow.com/questions/29207331/wpf-window-with-custom-chrome-has-unwanted-outline-on-right-and-bottom SourceInitialized += (_, e) => { if (SizeToContent == SizeToContent.WidthAndHeight) { InvalidateMeasure(); } }; if (PlayniteApplication.Current?.AppSettings != null) { if (PlayniteApplication.Current.Mode == SDK.ApplicationMode.Fullscreen) { IsHitTestVisible = !PlayniteApplication.Current.AppSettings.Fullscreen.HideMouserCursor; } } } public WindowBase(string savePositionName, bool saveSize = true) : this() { if (PlayniteApplication.Current?.AppSettings != null) { PositionHandler = new WindowPositionHandler(this, savePositionName, PlayniteApplication.Current.AppSettings.WindowPositions, saveSize); } } public static void SetTextRenderingOptions(TextFormattingModeOptions formatting, TextRenderingModeOptions rendering) { TextFormattingMode = (TextFormattingMode)formatting; TextRenderingMode = (TextRenderingMode)rendering; } public override void OnApplyTemplate() { base.OnApplyTemplate(); if (Template != null) { MinimizeButton = Template.FindName("PART_ButtonMinimize", this) as Button; if (MinimizeButton != null) { MinimizeButton.Click += MinimizeButton_Click; MinimizeButton.Visibility = ShowMinimizeButton == true ? Visibility.Visible : Visibility.Collapsed; } MaximizeButton = Template.FindName("PART_ButtonMaximize", this) as Button; if (MaximizeButton != null) { MaximizeButton.Click += MaximizeButton_Click; MaximizeButton.Visibility = ShowMaximizeButton == true ? Visibility.Visible : Visibility.Collapsed; } CloseButton = Template.FindName("PART_ButtonClose", this) as Button; if (CloseButton != null) { CloseButton.Click += CloseButton_Click; CloseButton.Visibility = ShowCloseButton == true ? Visibility.Visible : Visibility.Collapsed; } TextTitle = Template.FindName("PART_TextTitle", this) as TextBlock; if (TextTitle != null) { TextTitle.Visibility = ShowTitle == true ? Visibility.Visible : Visibility.Collapsed; } } } private void CloseButton_Click(object sender, RoutedEventArgs e) { Close(); } private void MinimizeButton_Click(object sender, RoutedEventArgs e) { WindowState = WindowState.Minimized; } private void MaximizeButton_Click(object sender, RoutedEventArgs e) { if (WindowState == WindowState.Maximized) { WindowState = WindowState.Normal; } else { WindowState = WindowState.Maximized; } } private static void ShowTitlePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var window = (WindowBase)sender; if (window.TextTitle != null) { window.TextTitle.Visibility = (bool)e.NewValue == true ? Visibility.Visible : Visibility.Collapsed; } } private static void ShowCloseButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var window = (WindowBase)sender; if (window.CloseButton != null) { window.CloseButton.Visibility = (bool)e.NewValue == true ? Visibility.Visible : Visibility.Collapsed; } } private static void ShowMaximizeButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var window = (WindowBase)sender; if (window.MaximizeButton != null) { window.MaximizeButton.Visibility = (bool)e.NewValue == true ? Visibility.Visible : Visibility.Collapsed; } } private static void ShowMinimizeButtonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var window = (WindowBase)sender; if (window.MinimizeButton != null) { window.MinimizeButton.Visibility = (bool)e.NewValue == true ? Visibility.Visible : Visibility.Collapsed; } } public void OnPropertyChanged([CallerMemberName]string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == Winuser.WM_HOTKEY) { var hotKeyId = wParam.ToInt32(); if (hotKeyHandlers.TryGetValue(hotKeyId, out var handler)) { handler(); handled = true; } } return IntPtr.Zero; } public void RegisterHotKeyHandler(int hotKeyId, HotKey hotKey, Action handler) { var success = User32.RegisterHotKey(Handle, hotKeyId, (uint)hotKey.Modifiers, (uint)KeyInterop.VirtualKeyFromKey(hotKey.Key)); if (success) { hotKeyHandlers.AddOrUpdate(hotKeyId, handler); } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } public void UnregisterHotKeyHandler(int hotKeyId) { if (!hotKeyHandlers.ContainsKey(hotKeyId)) { return; } if (WasClosed) { return; } var success = User32.UnregisterHotKey(Handle, hotKeyId); if (success) { hotKeyHandlers.Remove(hotKeyId); } else { throw new Win32Exception(Marshal.GetLastWin32Error()); } } } } ================================================ FILE: source/Playnite/Converters/BidirectionalEnumAndNumberConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class BidirectionalEnumAndNumberConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return null; } if (targetType.IsEnum) { // Convert int to enum return Enum.ToObject(targetType, value); } if (value.GetType().IsEnum) { // Convert enum to int return System.Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType())); } return null; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // Perform the same conversion in both directions return Convert(value, targetType, parameter, culture); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/BoolToAutoWidthConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class BoolToAutoWidthConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is bool boolValue) return boolValue ? Double.NaN : 0; return 0; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/BoolToYesNoConverter.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class BoolToYesNoConverter : MarkupExtension, IValueConverter { private readonly string yesString; private readonly string noString; public BoolToYesNoConverter() { yesString = ResourceProvider.GetString("LOCYesLabel"); noString = ResourceProvider.GetString("LOCNoLabel"); } public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is bool boolValue) return boolValue ? yesString : noString; return noString; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/BooleanToHiddenConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class BooleanToHiddenConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is bool boolValue) return boolValue ? Visibility.Visible : Visibility.Hidden; return Visibility.Hidden; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is Visibility visibility) return visibility == Visibility.Visible; return false; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/BooleanToVisibilityConverter.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { [ValueConversion(typeof(bool), typeof(Visibility))] public class BooleanToVisibilityConverter : MarkupExtension, IValueConverter { public static BooleanToVisibilityConverter Instance { get; } = new BooleanToVisibilityConverter(); public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) { return Visibility.Collapsed; } return ((bool)value) ? Visibility.Visible : Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return ((Visibility)value) == Visibility.Visible ? true : false; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class MultiBooleanToVisibilityConverter : MarkupExtension, IMultiValueConverter { public static MultiBooleanToVisibilityConverter Instance { get; } = new MultiBooleanToVisibilityConverter(); public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values == null) { return Visibility.Collapsed; } return values.All(a => a is bool val && val == true) ? Visibility.Visible : Visibility.Collapsed; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/CoversZoomToPercentageConverter.cs ================================================ using Playnite.Settings; using System; using System.Globalization; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class CoversZoomToPercentageConverter : MarkupExtension, IValueConverter { private const double OneHundredPercentValue = ViewSettings.DefaultGridItemWidth; // raw pixel value to percentage public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is null) return 100; var source = (double)value; return Math.Round(source / OneHundredPercentValue * 100); } // percentage to raw pixel value public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (value is null) return OneHundredPercentValue; var source = (double)value; return Math.Round(source * OneHundredPercentValue / 100); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/DateTimeToLastPlayedConverter.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class DateTimeToLastPlayedConverter : MarkupExtension, IValueConverter { public static readonly DateTimeToLastPlayedConverter Instance = new DateTimeToLastPlayedConverter(); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var lastPlayed = (DateTime?)value; if (lastPlayed == null) { return LOC.Never.GetLocalized(); } if (parameter is DateFormattingOptions options) { return lastPlayed.Value.ToDisplayString(options); } return lastPlayed.Value.ToString(Common.Constants.DateUiFormat); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/DockToStringConverter.cs ================================================ using Playnite; using Playnite.SDK; using Playnite.Settings; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Markup; using System.Windows.Media; namespace Playnite.Converters { public class DockToStringConverter : MarkupExtension, IValueConverter { public static string GetString(Dock value) { switch (value) { case Dock.Left: return ResourceProvider.GetString("LOCDockLeft"); case Dock.Right: return ResourceProvider.GetString("LOCDockRight"); case Dock.Top: return ResourceProvider.GetString("LOCDockTop"); case Dock.Bottom: return ResourceProvider.GetString("LOCDockBottom"); } return ""; } public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is Dock dockValue) return GetString(dockValue); return ""; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/EnumToBooleanConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class EnumToBooleanConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value.Equals(parameter); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value.Equals(true) ? parameter : Binding.DoNothing; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/EnumToVisibilityConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class EnumToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value.Equals(parameter) ? Visibility.Visible : Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/GenericTypeConverter.cs ================================================ using Playnite.SDK; using Playnite.Settings; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class GenericTypeConverter : MarkupExtension, IValueConverter { public IValueConverter CustomConverter { get; set; } public string StringFormat { get; set; } public bool TestAsFilePath { get; set; } public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { try { object actualValue = value; if (!StringFormat.IsNullOrEmpty() && value is string) { actualValue = string.Format(StringFormat, value); } if (CustomConverter != null) { if (TestAsFilePath && actualValue is string filePath) { if (File.Exists(filePath)) { return CustomConverter.Convert(filePath, targetType, parameter, culture); } else { return DependencyProperty.UnsetValue; } } else { return CustomConverter.Convert(actualValue, targetType, parameter, culture); } } else { var converter = TypeDescriptor.GetConverter(targetType); if (TestAsFilePath && actualValue is string filePath) { if (File.Exists(filePath)) { return converter.ConvertFrom(filePath); } else { return DependencyProperty.UnsetValue; } } else { return converter.ConvertFrom(actualValue); } } } catch { return DependencyProperty.UnsetValue; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/ICollectionNullOrEmptyToVisibilityConverter.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class ICollectionNullOrEmptyToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is ICollection collection) return collection.Count == 0 ? Visibility.Collapsed : Visibility.Visible; return Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/IconToImageSourceConverter.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows; using System.Windows.Markup; namespace Playnite.Converters { public class PluginIconSourceToElementConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return null; } return SdkHelpers.ResolveUiItemIcon(value); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class IconToImageSourceConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return DependencyProperty.UnsetValue; } var icon = (Icon)value; return icon.ToImageSource(); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/ImageStringToImageConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Media.Imaging; using Playnite.Database; using NLog; using System.IO; using Playnite; using System.Windows.Markup; using Playnite.Common.Web; using Playnite.Settings; using System.Collections.Concurrent; using System.Collections.Specialized; using Playnite.Common; using System.Drawing.Imaging; namespace Playnite.Converters { public class ImageStringToImageConverter : MarkupExtension, IValueConverter { public bool Cached { get; set; } public ImageStringToImageConverter() { } public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return DependencyProperty.UnsetValue; } var image = ImageSourceManager.GetImage((string)value, Cached); return image ?? DependencyProperty.UnsetValue; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/IntToVisibilityConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class IntToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is null || parameter is null) return Visibility.Collapsed; var param = (int)parameter; var val = (int)value; if (val == param) { return Visibility.Visible; } else { return Visibility.Collapsed; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class InvertedIntToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is null || parameter is null) return Visibility.Visible; var param = (int)parameter; var val = (int)value; if (val != param) { return Visibility.Visible; } else { return Visibility.Collapsed; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/InvertableBooleanToVisibilityConverter.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { [ValueConversion(typeof(bool), typeof(Visibility))] public class InvertedBooleanToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is bool boolValue) return boolValue ? Visibility.Collapsed : Visibility.Visible; return Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } [ValueConversion(typeof(bool), typeof(Visibility))] public class InvertableBooleanToVisibilityConverter : IValueConverter { enum Parameters { Normal, Inverted } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is bool boolValue) { var direction = (Parameters)Enum.Parse(typeof(Parameters), (string)parameter); if (direction == Parameters.Inverted) { return !boolValue ? Visibility.Visible : Visibility.Collapsed; } return boolValue ? Visibility.Visible : Visibility.Collapsed; } return Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } } } ================================================ FILE: source/Playnite/Converters/InvertedBoolenConverter.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class InvertedBoolenConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is bool boolValue) return !boolValue; return false; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (value is bool boolValue) return !boolValue; return false; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/ListToStringConverter.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class ListSizeToBoolConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is IList list) { return list.Count > 0; } else { return false; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class ListSizeToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is System.Collections.IList list) { return list.Count > 0 ? Visibility.Visible : Visibility.Collapsed; } else { return Visibility.Collapsed; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class NiceListToStringConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return string.Empty; } if (value is IEnumerable) { return string.Join(", ", (IEnumerable)value); } else { return value.ToString(); } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is string stringVal && !stringVal.IsNullOrEmpty()) { var converted = stringVal.Split(new char[] { ',' }).Select(a => a.Trim()); if (targetType == typeof(ComparableList)) { return new ComparableList(converted); } else { return converted.ToList(); } } return null; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class ListToStringConverter : MarkupExtension, IValueConverter { private const string defaultSeperator = ","; public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return string.Empty; } var sep = defaultSeperator; if (parameter is string customSep) { sep = customSep; } if (value is IEnumerable) { return string.Join(sep, (IEnumerable)value); } else { return value.ToString(); } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is string stringVal && !stringVal.IsNullOrEmpty()) { var sep = defaultSeperator; if (parameter is string customSep) { sep = customSep; } var converted = stringVal.Split(new [] { sep }, StringSplitOptions.None); if (targetType == typeof(ComparableList)) { return new ComparableList(converted); } if (targetType == typeof(ObservableCollection)) { return new ObservableCollection(converted); } else { return converted.ToList(); } } return null; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class ListToMultilineStringConverter : MarkupExtension, IValueConverter { private readonly string[] splitter = new string[] { "\n" }; public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return string.Empty; } if (value is IEnumerable) { return string.Join("\n", (IEnumerable)value); } else { return value.ToString(); } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is string stringVal && !stringVal.IsNullOrEmpty()) { var converted = stringVal.Split(splitter, StringSplitOptions.None).Select(a => a.Trim('\r')).ToArray(); if (targetType == typeof(ComparableList)) { return new ComparableList(converted); } if (targetType == typeof(ObservableCollection)) { return new ObservableCollection(converted); } else { return converted.ToList(); } } return null; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/NegateConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class NegateConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is bool boolValue) return !boolValue; return false; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is bool boolValue) return !boolValue; return false; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/NotificationIconConverter.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class NotificationIconConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is NotificationType icon) switch (icon) { case NotificationType.Info: return @"/Images/Icons/info.png"; case NotificationType.Error: return @"/Images/Icons/warn.png"; } return null; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/NullToBoolConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class NullToBoolConverter : MarkupExtension, IValueConverter { public static NullToBoolConverter Instance { get; } = new NullToBoolConverter(); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return false; } else { return true; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/NullToDependencyPropertyUnsetConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class NullToDependencyPropertyUnsetConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value ?? DependencyProperty.UnsetValue; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/NullToVisibilityConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class NullToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return Visibility.Collapsed; } else { return Visibility.Visible; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class InvertedNullToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return Visibility.Visible; } else { return Visibility.Collapsed; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/NullableDateToStringConverter.cs ================================================ using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class NullableDateToStringConverter : MarkupExtension, IValueConverter { public static readonly NullableDateToStringConverter Instance = new NullableDateToStringConverter(); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return string.Empty; } if (value is DateTime date) { if (parameter is DateFormattingOptions options) { return date.ToDisplayString(options); } return date.ToString(Common.Constants.DateUiFormat); } else { return string.Empty; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (string.IsNullOrEmpty(value as string)) { return null; } var sucess = DateTime.TryParseExact(value as string, Common.Constants.DateUiFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var newDate); if (sucess) { return newDate; } else { return DependencyProperty.UnsetValue; } } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class ReleaseDateToStringConverter : MarkupExtension, IValueConverter { public static readonly ReleaseDateToStringConverter Instance = new ReleaseDateToStringConverter(); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is ReleaseDate date) { if (parameter is ReleaseDateFormattingOptions options) { return date.ToDisplayString(options); } else if (date.Month != null && date.Day != null) { return date.Date.ToString(Common.Constants.DateUiFormat); } else { return date.Serialize(); } } else if (value == null) { return string.Empty; } else { throw new NotSupportedException(); } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var str = value as string; if (str.IsNullOrEmpty()) { return null; } return ReleaseDate.Deserialize(str); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class EditingReleaseDateToStringConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is ReleaseDate date) { return date.Serialize(); } else if (value == null) { return null; } else { throw new NotSupportedException(); } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var str = value as string; if (str.IsNullOrEmpty()) { return null; } return ReleaseDate.Deserialize(str); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class ReleaseDateFieldValidation : ValidationRule { private const string InvalidInput = "Release date must be in year-month-day format!"; public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) { if (!string.IsNullOrEmpty((string)value)) { if (!ReleaseDate.TryDeserialize((string)value, out var _)) { return new ValidationResult(false, InvalidInput); } } return new ValidationResult(true, null); } } public class DateTimeFormatToStringValidation : ValidationRule { private const string InvalidFormatInput = "Format does not contain a valid custom format pattern!"; private const string InvalidArgumentRangeInput = "The date and time is outside the range of dates supported!"; private static DateTime TestDate = DateTime.Now; public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) { var str = (string)value; try { TestDate.ToString(str); return new ValidationResult(true, null); } catch (FormatException) { return new ValidationResult(false, InvalidFormatInput); } catch (ArgumentOutOfRangeException) { return new ValidationResult(false, InvalidArgumentRangeInput); } } } } ================================================ FILE: source/Playnite/Converters/NullableUlongBytesSizeToStringConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class NullableUlongBytesSizeToStringConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return string.Empty; } if (value is ulong bytes) { string suffix; double readable; if (bytes >= 0x1000000000000000) // Exabyte { suffix = "EB"; readable = (bytes >> 50); } else if (bytes >= 0x4000000000000) // Petabyte { suffix = "PB"; readable = bytes >> 40; } else if (bytes >= 0x10000000000) // Terabyte { suffix = "TB"; readable = bytes >> 30; } else if (bytes >= 0x40000000) // Gigabyte { suffix = "GB"; readable = bytes >> 20; } else if (bytes >= 0x100000) // Megabyte { suffix = "MB"; readable = bytes >> 10; } else if (bytes >= 0x400) // Kilobyte { suffix = "KB"; readable = bytes; } else { return bytes.ToString("0 B"); // Byte } readable /= 1024; return readable.ToString("0.00# ") + suffix; } else { return string.Empty; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/NumericConverters.cs ================================================ using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { #region ulong public class UlongToStringConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { throw new NotSupportedException(); } else if (value is ulong num) { return num.ToString(); } throw new NotSupportedException(); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var str = (string)value; if (str.IsNullOrEmpty()) { throw new NotSupportedException(); } else { return ulong.Parse(str); } } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class UlongFieldValidation : ValidationRule { private string invalidInput => $"Not an ulong value in {MinValue} to {MaxValue} range!"; public ulong MinValue { get; set; } = 0; public ulong MaxValue { get; set; } = ulong.MaxValue; public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) { if (value == null) { return new ValidationResult(true, null); } else { var str = (string)value; if (str.IsNullOrEmpty()) { return new ValidationResult(false, invalidInput); } if (ulong.TryParse(str, out var intVal) && intVal >= MinValue && intVal <= MaxValue) { return new ValidationResult(true, null); } return new ValidationResult(false, invalidInput); } } } #endregion ulong #region null int public class NullableIntToStringConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return string.Empty; } else if (value is int num) { return num.ToString(); } throw new NotSupportedException(); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var str = (string)value; if (str.IsNullOrEmpty()) { return null; } else { return int.Parse(str); } } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class NullableIntFieldValidation : ValidationRule { private string invalidInput => $"Not an integer value in {MinValue} to {MaxValue} range!"; public int MinValue { get; set; } = 0; public int MaxValue { get; set; } = int.MaxValue; public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) { if (value == null) { return new ValidationResult(true, null); } else { var str = (string)value; if (str.IsNullOrEmpty()) { return new ValidationResult(true, null); } if (int.TryParse(str, out var intVal) && intVal >= MinValue && intVal <= MaxValue) { return new ValidationResult(true, null); } return new ValidationResult(false, invalidInput); } } } #endregion null int #region null ulong public class NullableUlongToStringConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return string.Empty; } else if (value is ulong num) { return num.ToString(); } throw new NotSupportedException(); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var str = (string)value; if (str.IsNullOrEmpty()) { return null; } else { return ulong.Parse(str); } } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class NullableUlongFieldValidation : ValidationRule { private string invalidInput => $"Not a long value in {MinValue} to {MaxValue} range!"; public ulong MinValue { get; set; } = ulong.MinValue; public ulong MaxValue { get; set; } = ulong.MaxValue; public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) { if (value == null) { return new ValidationResult(true, null); } else { var str = (string)value; if (str.IsNullOrEmpty()) { return new ValidationResult(true, null); } if (ulong.TryParse(str, out var ulongVal) && ulongVal >= MinValue && ulongVal <= MaxValue) { return new ValidationResult(true, null); } return new ValidationResult(false, invalidInput); } } } #endregion null ulong } ================================================ FILE: source/Playnite/Converters/ObjectEqualityToBoolConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class ObjectEqualityToBoolConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value?.Equals(parameter) == true; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value?.Equals(true) == true ? parameter : Binding.DoNothing; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class InvertedObjectEqualityToBoolConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value?.Equals(parameter) != true; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value?.Equals(true) != true ? parameter : Binding.DoNothing; } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class ObjectEqualityToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value?.Equals(parameter) == true ? Visibility.Visible : Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } public class InvertedObjectEqualityToVisibilityConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value?.Equals(parameter) != true ? Visibility.Visible : Visibility.Collapsed; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/ObjectToStringConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; using System.Windows.Media; namespace Playnite.Converters { public class ObjectToStringConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return null; } else { if (value is Enum enumVar) { return enumVar.GetDescription(); } else { return value.ToString(); } } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/OpacityBoolConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class OpacityBoolConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is bool boolValue) { if (parameter != null && ((bool)parameter) == true) boolValue = !boolValue; return boolValue ? 1.0 : 0.5; } return 0.5; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is null) throw new NotSupportedException(); var val = (double)value; if (val <= 0.5) { return false; } else { return true; } } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/PlayTimeToStringConverter.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class PlayTimeToStringConverter : MarkupExtension, IValueConverter { public static PlayTimeToStringConverter Instance { get; } = new PlayTimeToStringConverter(); private static string LOCPlayedNoneString; private static string LOCPlayedNone; private static string LOCPlayedSeconds; private static string LOCPlayedMinutes; private static string LOCPlayedHours; private static string LOCPlayedDays; private static void CacheStrings() { if (LOCPlayedNoneString != null) { return; } LOCPlayedNoneString = ResourceProvider.GetString("LOCPlayedNoneString"); LOCPlayedNone = ResourceProvider.GetString("LOCPlayedNone"); LOCPlayedSeconds = ResourceProvider.GetString("LOCPlayedSeconds"); LOCPlayedMinutes = ResourceProvider.GetString("LOCPlayedMinutes"); LOCPlayedHours = ResourceProvider.GetString("LOCPlayedHours"); LOCPlayedDays = ResourceProvider.GetString("LOCPlayedDays"); } public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { CacheStrings(); if (value == null) { return LOCPlayedNone; } var seconds = (ulong)value; if (seconds == 0) { return LOCPlayedNone; } // Can't use TimeSpan from seconds because ulong is too large for it if (seconds < 60) { return string.Format(LOCPlayedSeconds, seconds); } var minutes = seconds / 60; if (minutes < 60) { return string.Format(LOCPlayedMinutes, minutes); } var hours = minutes / 60; if (parameter is bool formatToDays && formatToDays && hours >= 24) { var days = hours / 24; var remainingHours = hours % 24; var remainingMinutes = minutes % 60; return string.Format(LOCPlayedDays, days, remainingHours, remainingMinutes); } return string.Format(LOCPlayedHours, hours, minutes - (hours * 60)); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/SortingOrderToStringConverter.cs ================================================ using Playnite; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class SortingOrderToStringConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is SortOrder order) return order.GetDescription(); return "no sort order passed!"; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/StrechToStringConverter.cs ================================================ using Playnite; using Playnite.SDK; using Playnite.Settings; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; using System.Windows.Media; namespace Playnite.Converters { public class StrechToStringConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is Stretch stretch) switch (stretch) { case Stretch.None: return ResourceProvider.GetString("LOCStrechNone"); case Stretch.Fill: return ResourceProvider.GetString("LOCStrechFill"); case Stretch.Uniform: return ResourceProvider.GetString("LOCStrechUniform"); case Stretch.UniformToFill: return ResourceProvider.GetString("LOCStrechUniformToFill"); } return ""; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/StringNullOrEmptyToBoolConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class StringNullOrEmptyToBoolConverter : MarkupExtension, IValueConverter { enum Parameters { Normal, Inverted } public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var direction = parameter == null ? Parameters.Normal : (Parameters)Enum.Parse(typeof(Parameters), (string)parameter); if (direction == Parameters.Inverted) { return string.IsNullOrEmpty(value as string) ? true : false; } else { return string.IsNullOrEmpty(value as string) ? false : true; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/StringNullOrEmptyToVisibilityConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class StringNullOrEmptyToVisibilityConverter : MarkupExtension, IValueConverter { enum Parameters { Normal, Inverted } public static StringNullOrEmptyToVisibilityConverter Instance { get; } = new StringNullOrEmptyToVisibilityConverter(); public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var direction = parameter == null ? Parameters.Normal : (Parameters)Enum.Parse(typeof(Parameters), (string)parameter); if (direction == Parameters.Inverted) { return string.IsNullOrEmpty(value as string) ? Visibility.Visible : Visibility.Collapsed; } else { return string.IsNullOrEmpty(value as string) ? Visibility.Collapsed : Visibility.Visible; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/StringToUpperCaseConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class StringToUpperCaseConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is string str && !str.IsNullOrEmpty()) return str.ToUpperInvariant(); return string.Empty; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new NotSupportedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/TicksToTimeSpanConverter.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class TicksToTimeSpanConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is int intValue) { return new TimeSpan(intValue); } else if (value is long longValue) { return new TimeSpan(longValue); } else { throw new NotSupportedException(); } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is TimeSpan timeSpan) { return timeSpan.Ticks; } else { throw new NotSupportedException(); } } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Converters/ValueConverterGroup.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; namespace Playnite.Converters { public class ValueConverterGroup : List, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture)); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotSupportedException(); } } } ================================================ FILE: source/Playnite/Converters/WidthToFontSizeConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Converters { public class WidthToFontSizeConverter : MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is null) throw new NotSupportedException(); var width = (double)value; return width / 10; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } } ================================================ FILE: source/Playnite/Database/Collections/AgeRatingsCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class AgeRatingsCollection : ItemCollection { private readonly GameDatabase db; public AgeRatingsCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.AgeRatings) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid ageRatingId) { foreach (var game in db.Games.Where(a => a.AgeRatingIds?.Contains(ageRatingId) == true)) { game.AgeRatingIds.Remove(ageRatingId); db.Games.Update(game); } } public override bool Remove(AgeRating itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } } } ================================================ FILE: source/Playnite/Database/Collections/AppSoftwareCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class AppSoftwareCollection : ItemCollection { private readonly GameDatabase db; public AppSoftwareCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.AppSoftware) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } public override bool Remove(Guid id) { var dbItem = Get(id); db.RemoveFile(dbItem.Icon); return base.Remove(id); } public override bool Remove(AppSoftware item) { return Remove(item.Id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { var dbItem = Get(item.Id); db.RemoveFile(dbItem.Icon); } } return base.Remove(itemsToRemove); } public override void Update(IEnumerable items) { foreach (var item in items) { var dbItem = Get(item.Id); if (!dbItem.Icon.IsNullOrEmpty() && dbItem.Icon != item.Icon) { db.RemoveFile(dbItem.Icon); } } base.Update(items); } public override void Update(AppSoftware item) { var dbItem = Get(item.Id); if (!dbItem.Icon.IsNullOrEmpty() && dbItem.Icon != item.Icon) { db.RemoveFile(dbItem.Icon); } base.Update(item); } } } ================================================ FILE: source/Playnite/Database/Collections/CategoriesCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class CategoriesCollection : ItemCollection { private readonly GameDatabase db; public CategoriesCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Categories) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid categoryId) { foreach (var game in db.Games.Where(a => a.CategoryIds?.Contains(categoryId) == true)) { game.CategoryIds.Remove(categoryId); db.Games.Update(game); } } public override bool Remove(Category itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } } } ================================================ FILE: source/Playnite/Database/Collections/CompaniesCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class CompaniesCollection : ItemCollection { private readonly GameDatabase db; public CompaniesCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Companies) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid companyId) { foreach (var game in db.Games) { var modified = false; if (game.PublisherIds?.Contains(companyId) == true) { game.PublisherIds.Remove(companyId); modified = true; } if (game.DeveloperIds?.Contains(companyId) == true) { game.DeveloperIds.Remove(companyId); modified = true; } if (modified) { db.Games.Update(game); } } } public override bool Remove(Company itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } } } ================================================ FILE: source/Playnite/Database/Collections/CompletionStatusesCollection.cs ================================================ using LiteDB; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class CompletionStatusSettings { [BsonId(false)] public int Id { get; set; } = 0; public Guid DefaultStatus { get; set; } public Guid PlayedStatus { get; set; } } public class CompletionStatusesCollection : ItemCollection { private readonly GameDatabase db; private LiteCollection settingsCollection; private LiteCollection SettingsCollection { get { if (settingsCollection == null) { settingsCollection = liteDb.GetCollection(); } return settingsCollection; } } public CompletionStatusesCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.CompletionStatuses) { db = database; } public CompletionStatusSettings GetSettings() { if (SettingsCollection.Count() == 0) { var settings = new CompletionStatusSettings(); SettingsCollection.Insert(settings); return settings; } else { return SettingsCollection.FindAll().First(); } } public void SetSettings(CompletionStatusSettings settings) { settings.Id = 0; SettingsCollection.Upsert(settings); } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid statusId) { foreach (var game in db.Games.Where(a => a.CompletionStatusId == statusId)) { game.CompletionStatusId = Guid.Empty; db.Games.Update(game); } } public override bool Remove(CompletionStatus itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } } } ================================================ FILE: source/Playnite/Database/Collections/EmulatorsCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class EmulatorsCollection : ItemCollection { private readonly GameDatabase db; public EmulatorsCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Emulators) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity(). Id(a => a.Id, false). Ignore(a => a.SelectableProfiles). Ignore(a => a.AllProfiles); mapper.Entity(). Ignore(a => a.Type); mapper.Entity(). Ignore(a => a.Type); } private void RemoveUsage(Guid id) { foreach (var game in db.Games) { if (game.GameActions.HasItems()) { foreach (var action in game.GameActions) { if (action?.Type == GameActionType.Emulator && action?.EmulatorId == id) { action.EmulatorId = Guid.Empty; action.EmulatorProfileId = null; db.Games.Update(game); } } } } } public override bool Remove(Emulator itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } public override Emulator Add(string itemName) { throw new NotSupportedException(); } public override Emulator Add(string itemName, Func existingComparer) { throw new NotSupportedException(); } public override IEnumerable Add(List items) { throw new NotSupportedException(); } public override IEnumerable Add(List itemsToAdd, Func existingComparer) { throw new NotSupportedException(); } } } ================================================ FILE: source/Playnite/Database/Collections/FeaturesCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class FeaturesCollection : ItemCollection { private readonly GameDatabase db; public FeaturesCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Features) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid id) { foreach (var game in db.Games.Where(a => a.FeatureIds?.Contains(id) == true)) { game.FeatureIds.Remove(id); db.Games.Update(game); } } public override bool Remove(GameFeature itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } } } ================================================ FILE: source/Playnite/Database/Collections/FilterPresetsCollection.cs ================================================ using LiteDB; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using SdkModels = Playnite.SDK.Models; namespace Playnite.Database { public class FilterPresetsSettings { [BsonId(false)] public int Id { get; set; } = 0; public List SortingOrder { get; set; } = new List(); } public class FilterPresetsSettingsUpdateEvent { /// /// Gets or sets old item state. /// public FilterPresetsSettings OldData { get; set; } /// /// Gets or sets new item state. /// public FilterPresetsSettings NewData { get; set; } /// /// Creates new instance of FilterPresetsSettingsUpdateEvent. /// /// Old state. /// New state. public FilterPresetsSettingsUpdateEvent(FilterPresetsSettings oldData, FilterPresetsSettings newData) { OldData = oldData; NewData = newData; } } public class FilterPresetsCollection : ItemCollection { public event EventHandler OnSettingsUpdated; private LiteCollection settingsCollection; private LiteCollection SettingsCollection { get { if (settingsCollection == null) { settingsCollection = liteDb.GetCollection(); } return settingsCollection; } } public FilterPresetsCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.FilterPresets) { } public FilterPresetsSettings GetSettings() { if (SettingsCollection.Count() == 0) { var settings = new FilterPresetsSettings(); SettingsCollection.Insert(settings); return settings; } else { return SettingsCollection.FindAll().First(); } } public void SetSettings(FilterPresetsSettings settings) { var oldSettings = GetSettings(); settings.Id = 0; SettingsCollection.Upsert(settings); OnSettingsUpdated?.Invoke(this, new FilterPresetsSettingsUpdateEvent(oldSettings, settings)); } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } } } ================================================ FILE: source/Playnite/Database/Collections/GameScannersCollection.cs ================================================ using LiteDB; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class GameScannersSettings { [BsonId(false)] public int Id { get; set; } = 0; public List CrcExcludeFileTypes { get; set; } } public class GameScannersCollection : ItemCollection { private readonly GameDatabase db; private LiteCollection settingsCollection; private LiteCollection SettingsCollection { get { if (settingsCollection == null) { settingsCollection = liteDb.GetCollection(); } return settingsCollection; } } public GameScannersCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.GameScanners) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } public GameScannersSettings GetSettings() { if (SettingsCollection.Count() == 0) { var settings = new GameScannersSettings { CrcExcludeFileTypes = new List { "*.chd" } }; SettingsCollection.Insert(settings); return settings; } else { return SettingsCollection.FindAll().First(); } } public void SetSettings(GameScannersSettings settings) { settings.Id = 0; SettingsCollection.Upsert(settings); } } } ================================================ FILE: source/Playnite/Database/Collections/GamesCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class GamesCollection : ItemCollection { private readonly GameDatabase db; public GamesCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base((Game game) => { game.IsInstalling = false; game.IsUninstalling = false; game.IsLaunching = false; game.IsRunning = false; }, mapper, type: GameDatabaseCollection.Games) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.RegisterType ( (date) => date.Serialize(), (bson) => ReleaseDate.Deserialize(bson.AsString) ); mapper.Entity(). Id(a => a.Id, false). Ignore(a => a.Genres). Ignore(a => a.Developers). Ignore(a => a.Publishers). Ignore(a => a.Tags). Ignore(a => a.Features). Ignore(a => a.Categories). Ignore(a => a.Platforms). Ignore(a => a.Series). Ignore(a => a.AgeRatings). Ignore(a => a.Regions). Ignore(a => a.Source). Ignore(a => a.ReleaseYear). Ignore(a => a.UserScoreRating). Ignore(a => a.CommunityScoreRating). Ignore(a => a.CriticScoreRating). Ignore(a => a.UserScoreGroup). Ignore(a => a.CommunityScoreGroup). Ignore(a => a.CriticScoreGroup). Ignore(a => a.LastActivitySegment). Ignore(a => a.AddedSegment). Ignore(a => a.ModifiedSegment). Ignore(a => a.PlaytimeCategory). Ignore(a => a.IsCustomGame). Ignore(a => a.InstallationStatus); } public override Game Add(string itemName) { throw new NotSupportedException(); } public override Game Add(string itemName, Func existingComparer) { throw new NotSupportedException(); } public override IEnumerable Add(List items) { throw new NotSupportedException(); } public override IEnumerable Add(List itemsToAdd, Func existingComparer) { throw new NotSupportedException(); } public override void Add(Game item) { item.Added = DateTime.Now; item.Modified = item.Added; base.Add(item); } public override void Add(IEnumerable items) { foreach (var item in items) { item.Added = DateTime.Now; item.Modified = item.Added; } base.Add(items); } public override bool Remove(Guid id) { var item = Get(id); var result = base.Remove(id); db.RemoveFile(item.Icon); db.RemoveFile(item.CoverImage); if (item.BackgroundImage.IsHttpUrl()) { HttpFileCache.ClearCache(item.BackgroundImage); } else { db.RemoveFile(item.BackgroundImage); } return result; } public override bool Remove(Game item) { return Remove(item.Id); } public override bool Remove(IEnumerable items) { foreach (var item in items) { // Get item from in case that passed platform doesn't have actual metadata. var dbItem = Get(item.Id); db.RemoveFile(dbItem.Icon); db.RemoveFile(dbItem.CoverImage); if (dbItem.BackgroundImage.IsHttpUrl()) { HttpFileCache.ClearCache(dbItem.BackgroundImage); } else { db.RemoveFile(dbItem.BackgroundImage); } } var result = base.Remove(items); return result; } public override void Update(Game itemToUpdate) { var dbItem = Get(itemToUpdate.Id); if (!dbItem.Icon.IsNullOrEmpty() && dbItem.Icon != itemToUpdate.Icon) { db.RemoveFile(dbItem.Icon); } if (!dbItem.CoverImage.IsNullOrEmpty() && dbItem.CoverImage != itemToUpdate.CoverImage) { db.RemoveFile(dbItem.CoverImage); } if (!dbItem.BackgroundImage.IsNullOrEmpty() && !dbItem.BackgroundImage.IsHttpUrl() && dbItem.BackgroundImage != itemToUpdate.BackgroundImage) { db.RemoveFile(dbItem.BackgroundImage); } else if (dbItem.BackgroundImage.IsHttpUrl() && dbItem.BackgroundImage != itemToUpdate.BackgroundImage) { HttpFileCache.ClearCache(dbItem.BackgroundImage); } base.Update(itemToUpdate); } public override void Update(IEnumerable itemsToUpdate) { foreach (var item in itemsToUpdate) { var dbItem = Get(item.Id); if (!dbItem.Icon.IsNullOrEmpty() && dbItem.Icon != item.Icon) { db.RemoveFile(dbItem.Icon); } if (!dbItem.CoverImage.IsNullOrEmpty() && dbItem.CoverImage != item.CoverImage) { db.RemoveFile(dbItem.CoverImage); } if (!dbItem.BackgroundImage.IsNullOrEmpty()) { if (!dbItem.BackgroundImage.IsHttpUrl() && dbItem.BackgroundImage != item.BackgroundImage) { db.RemoveFile(dbItem.BackgroundImage); } else if (dbItem.BackgroundImage.IsHttpUrl() && dbItem.BackgroundImage != item.BackgroundImage) { HttpFileCache.ClearCache(dbItem.BackgroundImage); } } } base.Update(itemsToUpdate); } } } ================================================ FILE: source/Playnite/Database/Collections/GamesSourcesCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class GamesSourcesCollection : ItemCollection { private readonly GameDatabase db; public GamesSourcesCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Sources) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid sourceId) { foreach (var game in db.Games.Where(a => a.SourceId == sourceId)) { game.SourceId = Guid.Empty; db.Games.Update(game); } } public override bool Remove(GameSource itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } } } ================================================ FILE: source/Playnite/Database/Collections/GenresCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class GenresCollection : ItemCollection { private readonly GameDatabase db; public GenresCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Genres) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid genreId) { foreach (var game in db.Games.Where(a => a.GenreIds?.Contains(genreId) == true)) { game.GenreIds.Remove(genreId); db.Games.Update(game); } } public override bool Remove(Genre itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } } } ================================================ FILE: source/Playnite/Database/Collections/ImportExclusionsCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class ImportExclusionsCollection : ItemCollection { public ImportExclusionsCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.ImportExclusions) { } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } } } ================================================ FILE: source/Playnite/Database/Collections/ItemCollection.cs ================================================ using LiteDB; using Playnite.SDK; using Playnite.SDK.Models; using SqlNado; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { // We currently use LiteDB for permanent storage. // We don't use latest LiteDB 5, but instead latest LiteDB 4, because V5 has some issues: // - doesn't allow disabling of memory cache (which in our case just wastes memory) // - write speeds are slower public class ItemCollection : IItemCollection where TItem : DatabaseObject { class EventBufferHandler : IDisposable where T : DatabaseObject { private IItemCollection collection; public EventBufferHandler(IItemCollection collection) { this.collection = collection; collection.BeginBufferUpdate(); } public void Dispose() { collection.EndBufferUpdate(); } } private ILogger logger = LogManager.GetLogger(typeof(TItem).Name + "_coll"); private readonly object collectionLock = new object(); private string storagePath; private readonly Action initMethod; private bool isEventBufferEnabled = false; private int bufferDepth = 0; private List AddedItemsEventBuffer = new List(); private List RemovedItemsEventBuffer = new List(); private Dictionary> ItemUpdatesEventBuffer = new Dictionary>(); private readonly bool isPersistent = true; internal LiteDatabase liteDb { get; private set; } private LiteCollection liteCollection; private BsonMapper mapper; public ConcurrentDictionary Items { get; } public int Count => Items.Count; public bool IsReadOnly => false; public GameDatabaseCollection CollectionType { get; } = GameDatabaseCollection.Uknown; public TItem this[Guid id] { get => Get(id); set { new NotSupportedException(); } } public event EventHandler> ItemCollectionChanged; public event EventHandler> ItemUpdated; internal bool IsEventsEnabled { get; set; } = true; public ItemCollection(BsonMapper mapper, bool isPersistent = true, GameDatabaseCollection type = GameDatabaseCollection.Uknown) { this.isPersistent = isPersistent; this.mapper = mapper; Items = new ConcurrentDictionary(); CollectionType = type; } public ItemCollection(Action initMethod, BsonMapper mapper, bool isPersistent = true, GameDatabaseCollection type = GameDatabaseCollection.Uknown) : this(mapper, isPersistent, type) { this.initMethod = initMethod; } public ItemCollection(string path, BsonMapper mapper, GameDatabaseCollection type = GameDatabaseCollection.Uknown) { this.isPersistent = true; this.mapper = mapper; Items = new ConcurrentDictionary(); InitializeCollection(path); CollectionType = type; } public void Dispose() { liteDb?.Dispose(); } public void InitializeCollection(string path) { if (!string.IsNullOrEmpty(storagePath)) { throw new Exception("Collection already initialized."); } storagePath = path; // This fixes an issue where people mess up their library with custom scripts // which create collection files instead of directories :| if (File.Exists(storagePath)) { File.Delete(storagePath); } var dbPath = path + ".db"; void openDb() { liteDb = new LiteDatabase($"Filename={dbPath};Mode=Exclusive;Cache Size=0", mapper); liteCollection = liteDb.GetCollection(); liteCollection.EnsureIndex(a => a.Id, true); } void loadCollections() { Parallel.ForEach( liteCollection.FindAll(), new ParallelOptions { MaxDegreeOfParallelism = 4 }, (objectFile) => { if (objectFile != null) { initMethod?.Invoke(objectFile); Items.TryAdd(objectFile.Id, objectFile); } }); // Also try to load other collection to see if db is corrupted foreach (var collName in liteDb.GetCollectionNames().Where(a => a != liteCollection.Name)) { var coll = liteDb.GetCollection(collName); // One these would fail for known corruptions coll.Count(); coll.FindAll().ToList(); } } openDb(); try { loadCollections(); } catch (Exception liteEx) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(liteEx, $"DB file {dbPath} is most likely damaged, trying to repair."); Items.Clear(); liteDb.Dispose(); var backupPath = dbPath + ".backup"; File.Copy(dbPath, backupPath, true); try { var oldData = new Dictionary>(); using (var dbStream = File.OpenRead(dbPath)) { var reader = new LiteDBConversion.FileReaderV7(dbStream, null); foreach (var coll in reader.GetCollections()) { oldData.Add(coll, reader.GetDocuments(coll).ToList()); } } File.Delete(dbPath); using (var db = new LiteDatabase($"Filename={dbPath};Mode=Exclusive;Cache Size=0")) { foreach (var collName in oldData.Keys) { db.GetCollection(collName).InsertBulk(oldData[collName]); } } openDb(); loadCollections(); logger.Debug($"{dbPath} restored successfully."); } catch (Exception resExc) { logger.Error(resExc, "Failed to restore data from damaged db file."); File.Delete(dbPath); File.Move(backupPath, dbPath); } } } internal string GetItemFilePath(Guid id) { return Path.Combine(storagePath, $"{id.ToString()}.json"); } internal void SaveItemData(TItem item) { liteCollection.Upsert(item); } internal void SaveItemData(IEnumerable items) { liteCollection.Upsert(items); } internal TItem GetItemData(Guid id) { return liteCollection.FindById(id); } public TItem Get(Guid id) { if (Items.TryGetValue(id, out var item)) { return item; } else { return null; } } public bool ContainsItem(Guid id) { return Items?.ContainsKey(id) == true; } public List Get(IList ids) { var items = new List(ids.Count); foreach (var id in ids) { var item = Get(id); if (item != null) { items.Add(item); } } return items; } public virtual TItem GetOrGenerate(MetadataProperty property) { if (property is MetadataNameProperty nameProp) { var existingItem = this.FirstOrDefault(a => GameFieldComparer.StringEquals(a.Name, nameProp.Name)); if (existingItem != null) { return existingItem; } else { return typeof(TItem).CrateInstance(nameProp.Name); } } else if (property is MetadataIdProperty idProp) { return this[idProp.Id]; } throw new NotSupportedException($"{property.GetType()} property type is not supported in this collection."); } public virtual IEnumerable GetOrGenerate(IEnumerable properties) { var res = new List(); foreach (var property in properties) { res.Add(GetOrGenerate(property)); } return res; } public virtual TItem Add(MetadataProperty property) { if (property is MetadataNameProperty nameProp) { return Add(nameProp.Name, GameFieldComparer.FieldEquals); } else if (property is MetadataIdProperty idProp) { return this[idProp.Id]; } throw new NotSupportedException($"{property.GetType()} property type is not supported in this collection."); } public virtual IEnumerable Add(IEnumerable properties) { var res = new List(); foreach (var property in properties) { res.Add(Add(property)); } return res; } public virtual TItem Add(string itemName, Func existingComparer) { if (string.IsNullOrEmpty(itemName)) throw new ArgumentNullException(nameof(itemName)); var existingItem = this.FirstOrDefault(a => existingComparer(a, itemName)); if (existingItem != null) { return existingItem; } else { var newItem = typeof(TItem).CrateInstance(itemName); Add(newItem); return newItem; } } public virtual TItem Add(string itemName) { return Add(itemName, (existingItem, newName) => existingItem.Name?.Equals(newName, StringComparison.InvariantCultureIgnoreCase) == true); } public virtual IEnumerable Add(List itemsToAdd, Func existingComparer) { var res = new List(); var toAdd = new List(); foreach (var itemName in itemsToAdd) { var existingItem = this.FirstOrDefault(a => existingComparer(a, itemName)); if (existingItem != null) { res.Add(existingItem); } else { var newItem = typeof(TItem).CrateInstance(itemName); toAdd.Add(newItem); res.Add(newItem); } } if (toAdd?.Any() == true) { Add(toAdd); } return res; } public virtual IEnumerable Add(List itemsToAdd) { return Add(itemsToAdd, (existingItem, newName) => existingItem.Name?.Equals(newName, StringComparison.InvariantCultureIgnoreCase) == true); } public virtual void Add(TItem itemToAdd) { if (Items.ContainsKey(itemToAdd.Id)) { throw new Exception($"Item {itemToAdd.Id} already exists."); } lock (collectionLock) { if (isPersistent) { SaveItemData(itemToAdd); } Items.TryAdd(itemToAdd.Id, itemToAdd); } OnCollectionChanged(new List() { itemToAdd }, new List()); } public virtual void Add(IEnumerable itemsToAdd) { if (itemsToAdd?.Any() != true) { return; } lock (collectionLock) { foreach (var item in itemsToAdd) { if (Items.ContainsKey(item.Id)) { throw new Exception($"Item {item.Id} already exists."); } if (isPersistent) { SaveItemData(item); } Items.TryAdd(item.Id, item); } } OnCollectionChanged(itemsToAdd.ToList(), new List()); } public virtual bool Remove(Guid id) { var item = Get(id); if (item == null) { throw new Exception($"Item {item.Id} doesn't exists."); } lock (collectionLock) { if (isPersistent) { liteCollection.Delete(id); } Items.TryRemove(id, out var removed); } OnCollectionChanged(new List(), new List() { item }); return true; } public virtual bool Remove(TItem itemToRemove) { return Remove(itemToRemove.Id); } public virtual bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove?.Any() != true) { return false; } lock (collectionLock) { foreach (var item in itemsToRemove) { var existing = Get(item.Id); if (existing == null) { throw new Exception($"Item {item.Id} doesn't exists."); } if (isPersistent) { liteCollection.Delete(item.Id); } Items.TryRemove(item.Id, out var removed); } } OnCollectionChanged(new List(), itemsToRemove.ToList()); return true; } public virtual void Update(TItem itemToUpdate) { TItem oldData = null; TItem loadedItem; lock (collectionLock) { if (isPersistent) { try { oldData = GetItemData(itemToUpdate.Id); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to read stored item data."); } // This should never ever happen, but there are automatic crash reports of Playnite db files being corrupted. // This happens because of trash launchers from games like Zula, // which mess with Playnite process and dump their log entries to our files. // This will most likely cause some other issues, but at least it won't crash the whole app. if (oldData == null) { logger.Error("Failed to read stored item data."); oldData = this[itemToUpdate.Id].GetClone(); } } else { oldData = Get(itemToUpdate.Id); } if (oldData == null) { throw new Exception($"Item {oldData.Id} doesn't exists."); } if (isPersistent) { SaveItemData(itemToUpdate); } loadedItem = Get(itemToUpdate.Id); if (!ReferenceEquals(loadedItem, itemToUpdate)) { itemToUpdate.CopyDiffTo(loadedItem); } } OnItemUpdated(new List>() { new ItemUpdateEvent(oldData, loadedItem) }); } public virtual void Update(IEnumerable itemsToUpdate) { var updates = new List>(); lock (collectionLock) { foreach (var item in itemsToUpdate) { TItem oldData; if (isPersistent) { try { oldData = GetItemData(item.Id); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { // This should never ever happen, but there are automatic crash reports of Playnite db files being corrupted. // This happens because of trash launchers from games like Zula, // which mess with Playnite process and dump their log entries to our files. // This will most likely cause some other issues, but at least it won't crash the whole app. logger.Error(e, "Failed to read stored item data."); oldData = this[item.Id].GetClone(); } } else { oldData = Get(item.Id); } if (oldData == null) { throw new Exception($"Item {oldData.Id} doesn't exists."); } if (isPersistent) { SaveItemData(item); } var loadedItem = Get(item.Id); if (!ReferenceEquals(loadedItem, item)) { item.CopyDiffTo(loadedItem); } updates.Add(new ItemUpdateEvent(oldData, loadedItem)); } } OnItemUpdated(updates); } public void Clear() { throw new NotSupportedException(); } public bool Contains(TItem item) { return Items.ContainsKey(item.Id); } public void CopyTo(TItem[] array, int arrayIndex) { Items.Values.CopyTo(array, arrayIndex); } public IEnumerator GetEnumerator() { return Items.Values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return Items.Values.GetEnumerator(); } internal void OnCollectionChanged(List addedItems, List removedItems) { if (!IsEventsEnabled) { return; } if (!isEventBufferEnabled) { ItemCollectionChanged?.Invoke(this, new ItemCollectionChangedEventArgs(addedItems, removedItems)); } else { AddedItemsEventBuffer.AddRange(addedItems); RemovedItemsEventBuffer.AddRange(removedItems); } } internal void OnItemUpdated(IEnumerable> updates) { if (!IsEventsEnabled) { return; } if (!isEventBufferEnabled) { ItemUpdated?.Invoke(this, new ItemUpdatedEventArgs(updates)); } else { foreach (var update in updates) { if (ItemUpdatesEventBuffer.TryGetValue(update.NewData.Id, out var existing)) { existing.NewData = update.NewData; } else { ItemUpdatesEventBuffer.Add(update.NewData.Id, update); } } } } public void BeginBufferUpdate() { isEventBufferEnabled = true; bufferDepth++; } public void EndBufferUpdate() { // In case nested buffers are used then we end only when top level one clear. if (bufferDepth >= 1) { bufferDepth--; } if (bufferDepth == 0) { isEventBufferEnabled = false; if (AddedItemsEventBuffer.Count > 0 || RemovedItemsEventBuffer.Count > 0) { OnCollectionChanged(AddedItemsEventBuffer.ToList(), RemovedItemsEventBuffer.ToList()); AddedItemsEventBuffer.Clear(); RemovedItemsEventBuffer.Clear(); } if (ItemUpdatesEventBuffer.Count > 0) { OnItemUpdated(ItemUpdatesEventBuffer.Values); ItemUpdatesEventBuffer.Clear(); } } } public IEnumerable GetClone() { return this.Select(a => a.GetClone()); } public IDisposable BufferedUpdate() { return new EventBufferHandler(this); } } } ================================================ FILE: source/Playnite/Database/Collections/LiteDBFileReaderV7.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using static LiteDBConversion.Constants; // This is copy from LiteDB 5 version and it's only used for recovery of corrupted databases. // We use LiteDB 4 in Playnite, see description of ItemCollection to see why. // LiteDB 4 has some super rare bug that can corrupt database when file is being open and process is hard killed, // even when journal is used and sharing is not enabled. People reported corrupted DBs when hard resettingg PCs while Playnite is running. // The corruption is very weird because V5 can read these files just fine during upgrade from V4 to V5. // So if we detect corruption we try to use this upgrade code to fix the db. // Standard "raw" reading of collection doesn't work when DB is damabged in this way so this is the only way how to recover data. namespace LiteDBConversion { internal class BsonReader { private bool _utcDate = false; public BsonReader(bool utcDate) { _utcDate = utcDate; } /// /// Main method - deserialize using ByteReader helper /// public LiteDB.BsonDocument Deserialize(byte[] bson) { return this.ReadDocument(new ByteReader(bson)); } /// /// Read a BsonDocument from reader /// public LiteDB.BsonDocument ReadDocument(ByteReader reader) { var length = reader.ReadInt32(); var end = reader.Position + length - 5; var obj = new LiteDB.BsonDocument(); while (reader.Position < end) { var value = this.ReadElement(reader, out string name); obj.RawValue[name] = value; } reader.ReadByte(); // zero return obj; } /// /// Read an BsonArray from reader /// public LiteDB.BsonArray ReadArray(ByteReader reader) { var length = reader.ReadInt32(); var end = reader.Position + length - 5; var arr = new LiteDB.BsonArray(); while (reader.Position < end) { var value = this.ReadElement(reader, out string name); arr.Add(value); } reader.ReadByte(); // zero return arr; } /// /// Reads an element (key-value) from an reader /// private LiteDB.BsonValue ReadElement(ByteReader reader, out string name) { var type = reader.ReadByte(); name = reader.ReadCString(); if (type == 0x01) // Double { return reader.ReadDouble(); } else if (type == 0x02) // String { return reader.ReadBsonString(); } else if (type == 0x03) // Document { return this.ReadDocument(reader); } else if (type == 0x04) // Array { return this.ReadArray(reader); } else if (type == 0x05) // Binary { var length = reader.ReadInt32(); var subType = reader.ReadByte(); var bytes = reader.ReadBytes(length); switch (subType) { case 0x00: return bytes; case 0x04: return new Guid(bytes); } } else if (type == 0x07) // ObjectId { return new LiteDB.ObjectId(reader.ReadBytes(12)); } else if (type == 0x08) // Boolean { return reader.ReadBoolean(); } else if (type == 0x09) // DateTime { var ts = reader.ReadInt64(); // catch specific values for MaxValue / MinValue #19 if (ts == 253402300800000) return DateTime.MaxValue; if (ts == -62135596800000) return DateTime.MinValue; var date = LiteDB.BsonValue.UnixEpoch.AddMilliseconds(ts); return _utcDate ? date : date.ToLocalTime(); } else if (type == 0x0A) // Null { return LiteDB.BsonValue.Null; } else if (type == 0x10) // Int32 { return reader.ReadInt32(); } else if (type == 0x12) // Int64 { return reader.ReadInt64(); } else if (type == 0x13) // Decimal { return reader.ReadDecimal(); } else if (type == 0xFF) // MinKey { return LiteDB.BsonValue.MinValue; } else if (type == 0x7F) // MaxKey { return LiteDB.BsonValue.MaxValue; } throw new NotSupportedException("BSON type not supported"); } } internal class ByteReader { private byte[] _buffer; private int _length; private int _pos; public int Position { get { return _pos; } set { _pos = value; } } public ByteReader(byte[] buffer) { _buffer = buffer; _length = buffer.Length; _pos = 0; } public void Skip(int length) { _pos += length; } #region Native data types public Byte ReadByte() { var value = _buffer[_pos]; _pos++; return value; } public Boolean ReadBoolean() { var value = _buffer[_pos]; _pos++; return value == 0 ? false : true; } public UInt16 ReadUInt16() { _pos += 2; return BitConverter.ToUInt16(_buffer, _pos - 2); } public UInt32 ReadUInt32() { _pos += 4; return BitConverter.ToUInt32(_buffer, _pos - 4); } public UInt64 ReadUInt64() { _pos += 8; return BitConverter.ToUInt64(_buffer, _pos - 8); } public Int16 ReadInt16() { _pos += 2; return BitConverter.ToInt16(_buffer, _pos - 2); } public Int32 ReadInt32() { _pos += 4; return BitConverter.ToInt32(_buffer, _pos - 4); } public Int64 ReadInt64() { _pos += 8; return BitConverter.ToInt64(_buffer, _pos - 8); } public Single ReadSingle() { _pos += 4; return BitConverter.ToSingle(_buffer, _pos - 4); } public Double ReadDouble() { _pos += 8; return BitConverter.ToDouble(_buffer, _pos - 8); } public Decimal ReadDecimal() { _pos += 16; var a = BitConverter.ToInt32(_buffer, _pos - 16); var b = BitConverter.ToInt32(_buffer, _pos - 12); var c = BitConverter.ToInt32(_buffer, _pos - 8); var d = BitConverter.ToInt32(_buffer, _pos - 4); return new Decimal(new int[] { a, b, c, d }); } public Byte[] ReadBytes(int count) { var buffer = new byte[count]; System.Buffer.BlockCopy(_buffer, _pos, buffer, 0, count); _pos += count; return buffer; } #endregion #region Extended types public string ReadString() { var length = this.ReadInt32(); var str = Encoding.UTF8.GetString(_buffer, _pos, length); _pos += length; return str; } public string ReadString(int length) { var str = Encoding.UTF8.GetString(_buffer, _pos, length); _pos += length; return str; } /// /// Read BSON string add \0x00 at and of string and add this char in length before /// public string ReadBsonString() { var length = this.ReadInt32(); var str = Encoding.UTF8.GetString(_buffer, _pos, length - 1); _pos += length; return str; } public string ReadCString() { var pos = _pos; var length = 0; while (true) { if (_buffer[pos] == 0x00) { var str = Encoding.UTF8.GetString(_buffer, _pos, length); _pos += length + 1; // read last 0x00 return str; } else if (pos > _length) { return "_"; } pos++; length++; } } public DateTime ReadDateTime() { // fix #921 converting index key into LocalTime // this is not best solution because uctDate must be a global parameter // this will be review in v5 var date = new DateTime(this.ReadInt64(), DateTimeKind.Utc); return date.ToLocalTime(); } public Guid ReadGuid() { return new Guid(this.ReadBytes(16)); } public LiteDB.ObjectId ReadObjectId() { return new LiteDB.ObjectId(this.ReadBytes(12)); } // Legacy PageAddress structure: [uint, ushort] // public PageAddress ReadPageAddress() // { // return new PageAddress(this.ReadUInt32(), this.ReadUInt16()); // } public LiteDB.BsonValue ReadBsonValue(ushort length) { var type = (LiteDB.BsonType)this.ReadByte(); switch (type) { case LiteDB.BsonType.Null: return LiteDB.BsonValue.Null; case LiteDB.BsonType.Int32: return this.ReadInt32(); case LiteDB.BsonType.Int64: return this.ReadInt64(); case LiteDB.BsonType.Double: return this.ReadDouble(); case LiteDB.BsonType.Decimal: return this.ReadDecimal(); case LiteDB.BsonType.String: return this.ReadString(length); case LiteDB.BsonType.Document: return new BsonReader(false).ReadDocument(this); case LiteDB.BsonType.Array: return new BsonReader(false).ReadArray(this); case LiteDB.BsonType.Binary: return this.ReadBytes(length); case LiteDB.BsonType.ObjectId: return this.ReadObjectId(); case LiteDB.BsonType.Guid: return this.ReadGuid(); case LiteDB.BsonType.Boolean: return this.ReadBoolean(); case LiteDB.BsonType.DateTime: return this.ReadDateTime(); case LiteDB.BsonType.MinValue: return LiteDB.BsonValue.MinValue; case LiteDB.BsonType.MaxValue: return LiteDB.BsonValue.MaxValue; } throw new NotImplementedException(); } #endregion } internal static class BsonDocumentExtensions { public static T GetOrDefault(this IDictionary dict, K key, T defaultValue = default(T)) { if (dict.TryGetValue(key, out T result)) { return result; } return defaultValue; } public static LiteDB.BsonValue Index(this LiteDB.BsonValue source, string key) { if (source is LiteDB.BsonDocument doc) { return doc.RawValue.GetOrDefault(key, LiteDB.BsonValue.Null); } throw new NotSupportedException(); } public static void WriteIndex(this LiteDB.BsonValue source, string key, LiteDB.BsonValue value) { if (source is LiteDB.BsonDocument doc) { doc.RawValue[key] = value ?? LiteDB.BsonValue.Null; } else { throw new NotSupportedException(); } } public static LiteDB.BsonValue Index(this LiteDB.BsonValue source, int index) { if (source is LiteDB.BsonArray array) { return array.RawValue[index]; } throw new NotSupportedException(); } public static unsafe bool IsFullZero(this byte[] data) { fixed (byte* bytes = data) { int len = data.Length; int rem = len % (sizeof(long) * 16); long* b = (long*)bytes; long* e = (long*)(bytes + len - rem); while (b < e) { if ((*(b) | *(b + 1) | *(b + 2) | *(b + 3) | *(b + 4) | *(b + 5) | *(b + 6) | *(b + 7) | *(b + 8) | *(b + 9) | *(b + 10) | *(b + 11) | *(b + 12) | *(b + 13) | *(b + 14) | *(b + 15)) != 0) return false; b += 16; } for (int i = 0; i < rem; i++) if (data[len - 1 - i] != 0) return false; return true; } } } internal class IndexInfo { public string Collection { get; set; } public string Name { get; set; } public string Expression { get; set; } public bool Unique { get; set; } } /// /// Interface to read current or old datafile structure - Used to shirnk/upgrade datafile from old LiteDB versions /// interface IFileReader { /// /// Get all collections name from database /// /// IEnumerable GetCollections(); /// /// Get all indexes from collection (except _id index) /// IEnumerable GetIndexes(string name); /// /// Get all documents from a collection /// IEnumerable GetDocuments(string collection); } internal class Constants { public const string HeaderPage_HEADER_INFO = "** This is a LiteDB file **"; public const int PageAddress_SIZE = 5; public const int BasePage_SLOT_SIZE = 4; public const int DataService_MAX_DATA_BYTES_PER_PAGE = PAGE_SIZE - // 8192 PAGE_HEADER_SIZE - // [32 bytes] BasePage_SLOT_SIZE - // [4 bytes] DataBlock_DATA_BLOCK_FIXED_SIZE; // [6 bytes]; public const int DataBlock_DATA_BLOCK_FIXED_SIZE = 1 + // DataIndex PageAddress_SIZE; // NextBlock public const int P_EXTEND = 0; // 00-00 [byte] public const int P_NEXT_BLOCK = 1; // 01-05 [pageAddress] public const int P_BUFFER = 6; // 06-EOF [byte[]] /// /// The size of each page in disk - use 8192 as all major databases /// public const int PAGE_SIZE = 8192; /// /// Header page size /// public const int PAGE_HEADER_SIZE = 32; /// /// Bytes used in encryption salt /// public const int ENCRYPTION_SALT_SIZE = 16; /// /// Define ShareCounter buffer as writable /// public static int BUFFER_WRITABLE = -1; /// /// Define index name max length /// public static int INDEX_NAME_MAX_LENGTH = 32; /// /// Max level used on skip list (index). /// public const int MAX_LEVEL_LENGTH = 32; /// /// Max size of a index entry - usde for string, binary, array and documents. Need fit in 1 byte length /// public const int MAX_INDEX_KEY_LENGTH = 1023; /// /// Get max length of 1 single index node /// public const int MAX_INDEX_LENGTH = 1400; /// /// Get how many slots collection pages will have for free list page (data/index) /// public const int PAGE_FREE_LIST_SLOTS = 5; /// /// Document limit size - 2048 data pages limit (about 16Mb - same size as MongoDB) /// Using 2047 because first/last page can contain less than 8150 bytes. /// public const int MAX_DOCUMENT_SIZE = 2047 * DataService_MAX_DATA_BYTES_PER_PAGE; /// /// Define how many transactions can be open simultaneously /// public const int MAX_OPEN_TRANSACTIONS = 100; /// /// Define how many pages all transaction will consume, in memory, before persist in disk. This amount are shared across all open transactions /// 100,000 ~= 1Gb memory /// public const int MAX_TRANSACTION_SIZE = 100_000; // 100_000 (default) - 1000 (for tests) /// /// Size, in PAGES, for each buffer array (used in MemoryStore) /// It's an array to increase after each extend - limited in highest value /// Each byte array will be created with this size * PAGE_SIZE /// Use minimal 12 to allocate at least 85Kb per segment (will use LOH) /// public static int[] MEMORY_SEGMENT_SIZES = new int[] { 12, 50, 100, 500, 1000 }; // 8Mb per extend /// /// Define how many documents will be keep in memory until clear cache and remove support to orderby/groupby /// public const int VIRTUAL_INDEX_MAX_CACHE = 2000; /// /// Define how many bytes each merge sort container will be created /// public const int CONTAINER_SORT_SIZE = 100 * PAGE_SIZE; } /// /// Internal class to read old LiteDB v4 database version (datafile v7 structure) /// internal class FileReaderV7 : IFileReader { // v7 uses 4k page size private const int V7_PAGE_SIZE = 4096; private readonly Stream _stream; private readonly LiteDB.BsonDocument _header; private byte[] _buffer = new byte[V7_PAGE_SIZE]; public FileReaderV7(Stream stream, string password) { _stream = stream; // only userVersion was avaiable in old file format versions _header = this.ReadPage(0); if (password == null && _header["salt"].AsBinary.IsFullZero() == false) { throw new LiteDB.LiteException("Current data file requires password"); } } /// /// Read all collection based on header page /// public IEnumerable GetCollections() { return _header["collections"].AsDocument.Keys; } /// /// Read all indexes from all collection pages /// public IEnumerable GetIndexes(string collection) { var pageID = (uint)_header["collections"].AsDocument[collection].AsInt32; var page = this.ReadPage(pageID); foreach (var index in page["indexes"].AsArray) { string name = Regex.Replace(index.Index("name").AsString, @"[^a-z0-9]", "", RegexOptions.IgnoreCase | RegexOptions.Compiled); if (name.Length > INDEX_NAME_MAX_LENGTH) { name = name.Substring(0, INDEX_NAME_MAX_LENGTH); } yield return new IndexInfo { Collection = collection, Name = name, Expression = index.Index("expression").AsString, Unique = index.Index("unique").AsBoolean }; } } /// /// Get all document using an indexInfo as start point (_id index). /// public IEnumerable GetDocuments(string collection) { var colPageID = (uint)_header["collections"].AsDocument[collection].AsInt32; var col = this.ReadPage(colPageID); var headPageID = (uint)col.Index("indexes").Index(0).Index("headPageID").AsInt32; var indexPages = this.VisitIndexPages(headPageID); foreach (var indexPageID in indexPages) { var indexPage = this.ReadPage(indexPageID); foreach (var node in indexPage["nodes"].AsArray) { var dataBlock = node.Index("dataBlock"); // if datablock link to a data page if (dataBlock.Index("pageID").AsInt32 != -1) { // read dataPage and data block var dataPage = this.ReadPage((uint)dataBlock.Index("pageID").AsInt32); if (dataPage["pageType"].AsInt32 != 4) continue; var block = dataPage["blocks"].AsArray.FirstOrDefault(x => x.Index("index") == dataBlock.Index("index"))?.AsDocument; if (block == null) continue; // read byte[] from block or from extend pages var data = block["extendPageID"] == -1 ? block["data"].AsBinary : this.ReadExtendData((uint)block["extendPageID"].AsInt32); if (data.Length == 0) continue; // BSON format still same from all version var doc = LiteDB.BsonSerializer.Deserialize(data); // change _id PK in _chunks collection if (collection == "_chunks") { var parts = doc["_id"].AsString.Split('\\'); if (!int.TryParse(parts[1], out var n)) throw new LiteDB.LiteException("_id"); doc["_id"] = new LiteDB.BsonDocument { ["f"] = parts[0], ["n"] = n }; } yield return doc; } } } } /// /// Read all database pages from v7 structure into a flexible BsonDocument - only read what really needs /// private LiteDB.BsonDocument ReadPage(uint pageID) { if (pageID * V7_PAGE_SIZE > _stream.Length) return null; _stream.Position = pageID * V7_PAGE_SIZE; // v7 uses 4k page size _stream.Read(_buffer, 0, V7_PAGE_SIZE); // decrypt encrypted page (except header page - header are plain data) var reader = new ByteReader(_buffer); // reading page header var page = new LiteDB.BsonDocument { ["pageID"] = (int)reader.ReadUInt32(), ["pageType"] = (int)reader.ReadByte(), ["prevPageID"] = (int)reader.ReadUInt32(), ["nextPageID"] = (int)reader.ReadUInt32(), ["itemCount"] = (int)reader.ReadUInt16() }; // skip freeByte + reserved reader.ReadBytes(2 + 8); #region Header (1) // read header if (page["pageType"] == 1) { var info = reader.ReadString(27); var ver = reader.ReadByte(); if (string.CompareOrdinal(info, HeaderPage_HEADER_INFO) != 0 || ver != 7) { throw new LiteDB.LiteException(""); } // skip ChangeID + FreeEmptyPageID + LastPageID reader.ReadBytes(2 + 4 + 4); page["userVersion"] = (int)reader.ReadUInt16(); page["password"] = reader.ReadBytes(20); page["salt"] = reader.ReadBytes(16); page["collections"] = new LiteDB.BsonDocument(); var cols = reader.ReadByte(); for (var i = 0; i < cols; i++) { var name = reader.ReadString(); var colPageID = reader.ReadUInt32(); page["collections"].WriteIndex(name, (int)colPageID); } } #endregion #region Collection (2) // collection page else if (page["pageType"] == 2) { page["collectionName"] = reader.ReadString(); page["indexes"] = new LiteDB.BsonArray(); reader.ReadBytes(12); for (var i = 0; i < 16; i++) { var index = new LiteDB.BsonDocument(); var field = reader.ReadString(); var eq = field.IndexOf('='); if (eq > 0) { index["name"] = field.Substring(0, eq); index["expression"] = field.Substring(eq + 1); } else { index["name"] = field; index["expression"] = "$." + field; } index["unique"] = reader.ReadBoolean(); index["headPageID"] = (int)reader.ReadUInt32(); // skip HeadNode (index) + TailNode + FreeIndexPageID reader.ReadBytes(2 + 6 + 4); if (field.Length > 0) { page["indexes"].AsArray.Add(index); } } } #endregion #region Index (3) else if (page["pageType"] == 3) { page["nodes"] = new LiteDB.BsonArray(); for (var i = 0; i < page["itemCount"].AsInt32; i++) { var node = new LiteDB.BsonDocument { ["index"] = (int)reader.ReadUInt16() }; var levels = reader.ReadByte(); // skip Slot + PrevNode + NextNode reader.ReadBytes(1 + 6 + 6); var length = reader.ReadUInt16(); // skip DataType + KeyValue reader.ReadBytes(1 + length); node["dataBlock"] = new LiteDB.BsonDocument { ["pageID"] = (int)reader.ReadUInt32(), ["index"] = (int)reader.ReadUInt16() }; // reading Prev[0] node["prev"] = new LiteDB.BsonDocument { ["pageID"] = (int)reader.ReadUInt32(), ["index"] = (int)reader.ReadUInt16() }; // reading Next[0] node["next"] = new LiteDB.BsonDocument { ["pageID"] = (int)reader.ReadUInt32(), ["index"] = (int)reader.ReadUInt16() }; // skip Prev/Next[1..N] reader.ReadBytes((levels - 1) * (6 + 6)); page["nodes"].AsArray.Add(node); } } #endregion #region Data (4) else if (page["pageType"] == 4) { page["blocks"] = new LiteDB.BsonArray(); for (var i = 0; i < page["itemCount"].AsInt32; i++) { var block = new LiteDB.BsonDocument { ["index"] = (int)reader.ReadUInt16(), ["extendPageID"] = (int)reader.ReadUInt32() }; var length = reader.ReadUInt16(); block["data"] = reader.ReadBytes(length); page["blocks"].AsArray.Add(block); } } #endregion #region Extend (5) else if (page["pageType"] == 5) { page["data"] = reader.ReadBytes(page["itemCount"].AsInt32); } #endregion return page; } public int UserVersion => (int)_header["userVersion"]; /// /// Read extend data block /// private byte[] ReadExtendData(uint extendPageID) { // read all extended pages and build byte array using (var buffer = new MemoryStream()) { while (extendPageID != uint.MaxValue) { var page = this.ReadPage(extendPageID); if (page["pageType"].AsInt32 != 5) return new byte[0]; buffer.Write(page["data"].AsBinary, 0, page["itemCount"].AsInt32); extendPageID = (uint)page["nextPageID"].AsInt32; } return buffer.ToArray(); } } /// /// Visit all index pages by starting index page. Get a list with all index pages from a collection /// private HashSet VisitIndexPages(uint startPageID) { var toVisit = new HashSet(new uint[] { startPageID }); var visited = new HashSet(); while (toVisit.Count > 0) { var indexPageID = toVisit.First(); toVisit.Remove(indexPageID); var indexPage = this.ReadPage(indexPageID); if (indexPage == null || indexPage["pageType"] != 3) continue; visited.Add(indexPageID); foreach (var node in indexPage["nodes"].AsArray) { var prev = (uint)node.Index("prev").Index("pageID").AsInt32; var next = (uint)node.Index("next").Index("pageID").AsInt32; if (!visited.Contains(prev)) toVisit.Add(prev); if (!visited.Contains(next)) toVisit.Add(next); } } return visited; } } } ================================================ FILE: source/Playnite/Database/Collections/PlatformsCollection.cs ================================================ using Playnite.Emulators; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class PlatformsCollection : ItemCollection { private readonly GameDatabase db; public PlatformsCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Platforms) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid platformId) { foreach (var game in db.Games.Where(a => a.PlatformIds?.Contains(platformId) == true)) { game.PlatformIds.Remove(platformId); db.Games.Update(game); } foreach (var emulator in db.Emulators) { if (!emulator.CustomProfiles.HasItems()) { continue; } var updated = false; foreach (var profile in emulator.CustomProfiles.Where(a => a.Platforms?.Contains(platformId) == true)) { profile.Platforms.Remove(platformId); updated = true; } if (updated) { db.Emulators.Update(emulator); } } } public override bool Remove(Guid id) { RemoveUsage(id); var dbItem = Get(id); db.RemoveFile(dbItem.Icon); db.RemoveFile(dbItem.Cover); db.RemoveFile(dbItem.Background); return base.Remove(id); } public override bool Remove(Platform item) { return Remove(item.Id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); var dbItem = Get(item.Id); db.RemoveFile(dbItem.Icon); db.RemoveFile(dbItem.Cover); db.RemoveFile(dbItem.Background); } } return base.Remove(itemsToRemove); } public override void Update(IEnumerable items) { foreach (var item in items) { var dbItem = Get(item.Id); if (!dbItem.Icon.IsNullOrEmpty() && dbItem.Icon != item.Icon) { db.RemoveFile(dbItem.Icon); } if (!dbItem.Cover.IsNullOrEmpty() && dbItem.Cover != item.Cover) { db.RemoveFile(dbItem.Cover); } if (!dbItem.Background.IsNullOrEmpty() && dbItem.Background != item.Background) { db.RemoveFile(dbItem.Background); } } base.Update(items); } public override void Update(Platform item) { var dbItem = Get(item.Id); if (!dbItem.Icon.IsNullOrEmpty() && dbItem.Icon != item.Icon) { db.RemoveFile(dbItem.Icon); } if (!dbItem.Cover.IsNullOrEmpty() && dbItem.Cover != item.Cover) { db.RemoveFile(dbItem.Cover); } if (!dbItem.Background.IsNullOrEmpty() && dbItem.Background != item.Background) { db.RemoveFile(dbItem.Background); } base.Update(item); } public override Platform Add(MetadataProperty property) { if (property is MetadataSpecProperty specProp) { var exPlat = this.FirstOrDefault(a => a.SpecificationId == specProp.Id); if (exPlat != null) { return exPlat; } var plat = Emulation.Platforms.FirstOrDefault(a => a.Id == specProp.Id || a.Name == specProp.Id); if (plat != null) { exPlat = this.FirstOrDefault(a => a.SpecificationId == plat.Id); if (exPlat != null) { return exPlat; } else { var newPlat = new Platform(plat.Name) { SpecificationId = plat.Id }; Add(newPlat); return newPlat; } } else { var newPlat = new Platform(plat.Id); Add(newPlat); return newPlat; } } else { return base.Add(property); } } public override IEnumerable Add(IEnumerable properties) { foreach (var property in properties) { if (property is MetadataSpecProperty specProp) { yield return Add(specProp); } else { yield return base.Add(property); } } } public override Platform GetOrGenerate(MetadataProperty property) { if (property is MetadataSpecProperty specProp) { var exPlat = this.FirstOrDefault(a => a.SpecificationId == specProp.Id); if (exPlat != null) { return exPlat; } var plat = Emulation.Platforms.FirstOrDefault(a => a.Id == specProp.Id || a.Name == specProp.Id); if (plat != null) { exPlat = this.FirstOrDefault(a => a.SpecificationId == plat.Id); if (exPlat != null) { return exPlat; } else { return new Platform(plat.Name) { SpecificationId = plat.Id }; } } return null; } else { return base.GetOrGenerate(property); } } public override IEnumerable GetOrGenerate(IEnumerable properties) { foreach (var property in properties) { if (property is MetadataSpecProperty specProp) { yield return GetOrGenerate(specProp); } else { yield return base.GetOrGenerate(property); } } } } } ================================================ FILE: source/Playnite/Database/Collections/RegionsCollection.cs ================================================ using Playnite.Emulators; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class RegionsCollection : ItemCollection { private readonly GameDatabase db; public RegionsCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Regions) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid id) { foreach (var game in db.Games.Where(a => a.RegionIds?.Contains(id) == true)) { game.RegionIds.Remove(id); db.Games.Update(game); } } public override bool Remove(Region itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } public override Region Add(MetadataProperty property) { if (property is MetadataSpecProperty specProp) { var exRegion = this.FirstOrDefault(a => a.SpecificationId == specProp.Id); if (exRegion != null) { return exRegion; } var reg = Emulation.Regions.FirstOrDefault(a => a.Id == specProp.Id || a.Name == specProp.Id); if (reg != null) { exRegion = this.FirstOrDefault(a => a.SpecificationId == reg.Id); if (exRegion != null) { return exRegion; } else { var newReg = new Region(reg.Name) { SpecificationId = reg.Id }; Add(newReg); return newReg; } } else { var newReg = new Region(reg.Id); Add(newReg); return newReg; } } else { return base.Add(property); } } public override IEnumerable Add(IEnumerable properties) { foreach (var property in properties) { if (property is MetadataSpecProperty specProp) { yield return Add(specProp); } else { yield return base.Add(property); } } } public override Region GetOrGenerate(MetadataProperty property) { if (property is MetadataSpecProperty specProp) { var exRegion = this.FirstOrDefault(a => a.SpecificationId == specProp.Id); if (exRegion != null) { return exRegion; } var reg = Emulation.Regions.FirstOrDefault(a => a.Id == specProp.Id || a.Name == specProp.Id); if (reg != null) { exRegion = this.FirstOrDefault(a => a.SpecificationId == reg.Id); if (exRegion != null) { return exRegion; } else { return new Region(reg.Name) { SpecificationId = reg.Id }; } } return null; } else { return base.GetOrGenerate(property); } } public override IEnumerable GetOrGenerate(IEnumerable properties) { foreach (var property in properties) { if (property is MetadataSpecProperty specProp) { yield return GetOrGenerate(specProp); } else { yield return base.GetOrGenerate(property); } } } } } ================================================ FILE: source/Playnite/Database/Collections/SeriesCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class SeriesCollection : ItemCollection { private readonly GameDatabase db; public SeriesCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Series) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid id) { foreach (var game in db.Games.Where(a => a.SeriesIds?.Contains(id) == true)) { game.SeriesIds.Remove(id); db.Games.Update(game); } } public override bool Remove(Series itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } } } ================================================ FILE: source/Playnite/Database/Collections/TagsCollection.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class TagsCollection : ItemCollection { private readonly GameDatabase db; public TagsCollection(GameDatabase database, LiteDB.BsonMapper mapper) : base(mapper, type: GameDatabaseCollection.Tags) { db = database; } public static void MapLiteDbEntities(LiteDB.BsonMapper mapper) { mapper.Entity().Id(a => a.Id, false); } private void RemoveUsage(Guid id) { foreach (var game in db.Games.Where(a => a.TagIds?.Contains(id) == true)) { game.TagIds.Remove(id); db.Games.Update(game); } } public override bool Remove(Tag itemToRemove) { RemoveUsage(itemToRemove.Id); return base.Remove(itemToRemove); } public override bool Remove(Guid id) { RemoveUsage(id); return base.Remove(id); } public override bool Remove(IEnumerable itemsToRemove) { if (itemsToRemove.HasItems()) { foreach (var item in itemsToRemove) { RemoveUsage(item.Id); } } return base.Remove(itemsToRemove); } } } ================================================ FILE: source/Playnite/Database/DatabaseExplorer.cs ================================================ using Playnite.Commands; using Playnite.Plugins; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using Playnite.ViewModels; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; namespace Playnite.Database { public enum ExplorerField { [Description(LOC.SettingsTopPanelFilterPresetsItem)] Presets, [Description(LOC.PlatformTitle)] Platform, [Description(LOC.GameProviderTitle)] Library, [Description(LOC.CategoryLabel)] Category, [Description(LOC.GameLastActivityTitle)] LastActivity, [Description(LOC.RecentActivityLabel)] RecentActivity, [Description(LOC.GenreLabel)] Genre, [Description(LOC.GameReleaseYearTitle)] ReleaseYear, [Description(LOC.DeveloperLabel)] Developer, [Description(LOC.PublisherLabel)] Publisher, [Description(LOC.TagLabel)] Tag, [Description(LOC.SeriesLabel)] Series, [Description(LOC.AgeRatingLabel)] AgeRating, [Description(LOC.RegionLabel)] Region, [Description(LOC.SourceLabel)] Source, [Description(LOC.TimePlayed)] PlayTime, [Description(LOC.InstallSizeLabel)] InstallSize, [Description(LOC.CompletionStatus)] CompletionStatus, [Description(LOC.UserScore)] UserScore, [Description(LOC.CriticScore)] CriticScore, [Description(LOC.CommunityScore)] CommunityScore, [Description(LOC.DateAddedLabel)] Added, [Description(LOC.DateModifiedLabel)] Modified, [Description(LOC.FeatureLabel)] Feature, [Description(LOC.GameNameTitle)] Name, [Description(LOC.GameInstallationStatus)] InstallStatus } // TODO: Rewrite this mess. public class DatabaseExplorer : ObservableObject { public class ExplorableField { public ExplorerField Field { get; } public ExplorableField(ExplorerField field) { Field = field; } public override string ToString() { return Field.GetDescription(); } } public enum SelectionObjectType : int { All = 9998, None = 9999 } public class SelectionObject : ObservableObject { public string DisplayName { get; } public object Value { get; } public string Name { get => DisplayName.IsNullOrEmpty() ? Value.ToString() : DisplayName; } public SelectionObject(object value, string displayName) { Value = value; DisplayName = displayName; } public SelectionObject(object value) { Value = value; } } private readonly IGameDatabaseMain database; private readonly ExtensionFactory extensions; private readonly FilterSettings filters; private readonly PlayniteSettings settings; private readonly MainViewModelBase mainModel; private bool ignoreObjectSelectionChanges = false; public List Fields { get; set; } private ExplorableField selectedField; public ExplorableField SelectedField { get => selectedField; set { if (value != selectedField) { if (selectedField != null) { ApplyFilter(selectedField.Field, null); } selectedField = value; if (selectedField != null) { settings.ViewSettings.SelectedExplorerField = selectedField.Field; LoadValues(selectedField.Field); } OnPropertyChanged(); } } } private List fieldValues; public List FieldValues { get => fieldValues; set { fieldValues = value; OnPropertyChanged(); } } private SelectionObject selectedFieldObject; public SelectionObject SelectedFieldObject { get => selectedFieldObject; set { selectedFieldObject = value; if (selectedFieldObject != null && !ignoreObjectSelectionChanges) { ApplyFilter(SelectedField.Field, selectedFieldObject); } OnPropertyChanged(); } } public DatabaseExplorer( IGameDatabaseMain database, ExtensionFactory extensions, PlayniteSettings settings, MainViewModelBase mainModel) { this.database = database; this.extensions = extensions; this.filters = settings.FilterSettings; this.settings = settings; this.mainModel = mainModel; settings.PropertyChanged += Settings_PropertyChanged; Fields = new List(); foreach (ExplorerField val in Enum.GetValues(typeof(ExplorerField))) { Fields.Add(new ExplorableField(val)); } Fields = Fields.OrderBy(a => a.Field.GetDescription()).ToList(); if (database.IsOpen) { if (settings.ExplorerPanelVisible) { SelectedField = Fields.FirstOrDefault(a => a.Field == settings.ViewSettings.SelectedExplorerField); } } else { database.DatabaseOpened += (s, e) => { if (settings.ExplorerPanelVisible) { SelectedField = Fields.FirstOrDefault(a => a.Field == settings.ViewSettings.SelectedExplorerField); } }; } database.Games.ItemUpdated += Games_ItemUpdated; database.Games.ItemCollectionChanged += Games_ItemCollectionChanged; database.Platforms.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.Platform, e); database.Platforms.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.Platform, e); database.Genres.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.Genre, e); database.Genres.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.Genre, e); database.AgeRatings.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.AgeRating, e); database.AgeRatings.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.AgeRating, e); database.Categories.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.Category, e); database.Categories.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.Category, e); database.Regions.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.Region, e); database.Regions.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.Region, e); database.Series.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.Series, e); database.Series.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.Series, e); database.Sources.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.Source, e); database.Sources.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.Source, e); database.Tags.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.Tag, e); database.Tags.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.Tag, e); database.Features.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.Feature, e); database.Features.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.Feature, e); database.FilterPresets.ItemCollectionChanged += (s, e) => DatabaseCollection_ItemCollectionChanged(ExplorerField.Presets, e); database.FilterPresets.ItemUpdated += (s, e) => DatabaseCollection_ItemUpdated(ExplorerField.Presets, e); (database.FilterPresets as FilterPresetsCollection).OnSettingsUpdated += Database_OnFilterSettingsUpdated; database.Companies.ItemCollectionChanged += (s, e) => { DatabaseCollection_ItemCollectionChanged(ExplorerField.Publisher, e); DatabaseCollection_ItemCollectionChanged(ExplorerField.Developer, e); }; database.Companies.ItemUpdated += (s, e) => { DatabaseCollection_ItemUpdated(ExplorerField.Publisher, e); DatabaseCollection_ItemUpdated(ExplorerField.Developer, e); }; database.AgeRatingsInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.AgeRating); database.CategoriesInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Category); database.DevelopersInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Developer); database.FeaturesInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Feature); database.GenresInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Genre); database.PlatformsInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Platform); database.PublishersInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Publisher); database.RegionsInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Region); database.SeriesInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Series); database.SourcesInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Source); database.TagsInUseUpdated += (_, __) => Database_DatabaseCollectionInUseUpdated(ExplorerField.Tag); } private void Database_OnFilterSettingsUpdated(object sender, FilterPresetsSettingsUpdateEvent e) { if (SelectedField?.Field != ExplorerField.Presets) { return; } LoadValues(ExplorerField.Presets); } private void Database_DatabaseCollectionInUseUpdated(ExplorerField field) { if (!settings.UsedFieldsOnlyOnFilterLists) { return; } if (settings.ExplorerPanelVisible && SelectedField.Field == field) { var oldSelection = SelectedFieldObject; ignoreObjectSelectionChanges = true; var refreshSelection = false; LoadValues(field); if (oldSelection != null && FieldValues.FirstOrDefault(a => a.Value.Equals(oldSelection.Value)) != null) { SelectedFieldObject = FieldValues.FirstOrDefault(a => a.Value.Equals(oldSelection.Value)); } else { refreshSelection = true; } ignoreObjectSelectionChanges = false; if (refreshSelection) { SelectedFieldObject = FieldValues[0]; } } } private void DatabaseCollection_ItemUpdated(ExplorerField field, ItemUpdatedEventArgs e) where T : DatabaseObject { if (settings.ExplorerPanelVisible && SelectedField.Field == field) { foreach (var item in FieldValues) { item.OnPropertyChanged(nameof(SelectionObject.Name)); } } } private void DatabaseCollection_ItemCollectionChanged(ExplorerField field, ItemCollectionChangedEventArgs e) where T : DatabaseObject { if (settings.UsedFieldsOnlyOnFilterLists && field != ExplorerField.Presets) { return; } if (settings.ExplorerPanelVisible && SelectedField.Field == field) { var oldSelection = SelectedFieldObject; ignoreObjectSelectionChanges = true; var refreshSelection = false; LoadValues(field); if (oldSelection != null && FieldValues.FirstOrDefault(a => a.Value.Equals(oldSelection.Value)) != null) { SelectedFieldObject = FieldValues.FirstOrDefault(a => a.Value.Equals(oldSelection.Value)); } else { refreshSelection = true; } ignoreObjectSelectionChanges = false; if (refreshSelection) { if (FieldValues.HasItems()) { SelectedFieldObject = FieldValues[0]; } else { SelectedFieldObject = null; } } } } private void Settings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(PlayniteSettings.ExplorerPanelVisible)) { if (settings.ExplorerPanelVisible) { SelectedField = Fields.FirstOrDefault(a => a.Field == settings.ViewSettings.SelectedExplorerField); } else { SelectedField = null; } } else if (e.PropertyName == nameof(PlayniteSettings.UsedFieldsOnlyOnFilterLists) && settings.ExplorerPanelVisible) { DatabaseCollection_ItemCollectionChanged(SelectedField.Field, null); } } private void Games_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { if (e.AddedItems.HasItems()) { ProcessGameDataChages(e.AddedItems); } } private void Games_ItemUpdated(object sender, ItemUpdatedEventArgs e) { ProcessGameDataChages(e.UpdatedItems.Select(a => a.NewData)); } private void ProcessGameDataChages(IEnumerable gameUpdates) { if (!settings.ExplorerPanelVisible) { return; } if (SelectedField.Field == ExplorerField.ReleaseYear) { foreach (var change in gameUpdates) { if (!IsReleaseYearLoaded(change.ReleaseYear)) { UpdateReleaseDateValues(); break; } } } } private bool IsRelevantGameFieldLoaded(Game game) { var relevantFieldChanged = false; switch (SelectedField.Field) { case ExplorerField.Category: relevantFieldChanged = IsExplorableDbObjectLoaded(game.CategoryIds); break; case ExplorerField.Genre: relevantFieldChanged = IsExplorableDbObjectLoaded(game.GenreIds); break; case ExplorerField.Developer: relevantFieldChanged = IsExplorableDbObjectLoaded(game.DeveloperIds); break; case ExplorerField.Publisher: relevantFieldChanged = IsExplorableDbObjectLoaded(game.PublisherIds); break; case ExplorerField.Tag: relevantFieldChanged = IsExplorableDbObjectLoaded(game.TagIds); break; case ExplorerField.Platform: relevantFieldChanged = IsExplorableDbObjectLoaded(game.PlatformIds); break; case ExplorerField.Series: relevantFieldChanged = IsExplorableDbObjectLoaded(game.SeriesIds); break; case ExplorerField.AgeRating: relevantFieldChanged = IsExplorableDbObjectLoaded(game.AgeRatingIds); break; case ExplorerField.Region: relevantFieldChanged = IsExplorableDbObjectLoaded(game.RegionIds); break; case ExplorerField.Source: relevantFieldChanged = IsExplorableDbObjectLoaded(game.SourceId); break; case ExplorerField.ReleaseYear: relevantFieldChanged = IsReleaseYearLoaded(game.ReleaseYear); break; case ExplorerField.Feature: relevantFieldChanged = IsExplorableDbObjectLoaded(game.FeatureIds); break; } return relevantFieldChanged; } private bool IsExplorableDbObjectLoaded(Guid id) { foreach (var val in FieldValues) { if (val.Value is DatabaseObject obj) { if (obj.Id == id) { return true; } } } return false; } private bool IsExplorableDbObjectLoaded(List ids) { if (!ids.HasItems()) { return true; } return FieldValues.Where(a => a.Value is DatabaseObject).Select(a => ((DatabaseObject)a.Value).Id).Contains(ids); } private void UpdateReleaseDateValues() { var oldSelection = SelectedFieldObject; ignoreObjectSelectionChanges = true; LoadValues(ExplorerField.ReleaseYear); if (oldSelection != null) { SelectedFieldObject = FieldValues.FirstOrDefault(a => a.Value.Equals(oldSelection.Value)); } ignoreObjectSelectionChanges = false; } private bool IsReleaseYearLoaded(int? year) { if (year == null) { return true; } foreach (var obj in FieldValues) { if (obj is SelectionObject y) { if (y.Value == null && year == null) { return true; } if (int.TryParse(y.Value.ToString(), out var parsedYear)) { if (parsedYear == year) { return true; } } } } return false; } private IdItemFilterItemProperties GetIdFilter(SelectionObject filter) { if (filter?.Value == null) { return null; } else if (filter.Value is SelectionObjectType flt && flt == SelectionObjectType.All) { return null; } else { return new IdItemFilterItemProperties(((IIdentifiable)filter.Value).Id); } } private string GetStringFilter(SelectionObject filter) { if (filter == null) { return null; } else if (filter.Value is SelectionObjectType ftl && ftl == SelectionObjectType.All) { return null; } else if (filter.Value is DatabaseObject obj && obj.Id == Guid.Empty) { return FilterSettings.MissingFieldString; } else { return filter.Value.ToString(); } } private StringFilterItemProperties GetStringListFilter(SelectionObject filter) { if (filter == null) { return null; } else if (filter.Value is SelectionObjectType flt && flt == SelectionObjectType.All) { return null; } else if (filter.Value is DatabaseObject obj && obj.Id == Guid.Empty) { return new StringFilterItemProperties(FilterSettings.MissingFieldString); } else { return new StringFilterItemProperties(filter.Value.ToString()); } } private EnumFilterItemProperties GetEnumFilter(SelectionObject filter) { if (filter == null) { return null; } else if (filter is SelectionObject flt && (SelectionObjectType)flt.Value == SelectionObjectType.All) { return null; } else { return new EnumFilterItemProperties((int)filter.Value); } } private void ApplyFilter(ExplorerField field, SelectionObject filter) { if (!settings.ExplorerPanelVisible) { return; } switch (field) { case ExplorerField.Library: filters.Library = GetIdFilter(filter); break; case ExplorerField.Category: filters.Category = GetIdFilter(filter); break; case ExplorerField.Genre: filters.Genre = GetIdFilter(filter); break; case ExplorerField.Developer: filters.Developer = GetIdFilter(filter); break; case ExplorerField.Publisher: filters.Publisher = GetIdFilter(filter); break; case ExplorerField.Tag: filters.Tag = GetIdFilter(filter); break; case ExplorerField.Platform: filters.Platform = GetIdFilter(filter); break; case ExplorerField.Series: filters.Series = GetIdFilter(filter); break; case ExplorerField.AgeRating: filters.AgeRating = GetIdFilter(filter); break; case ExplorerField.Region: filters.Region = GetIdFilter(filter); break; case ExplorerField.Source: filters.Source = GetIdFilter(filter); break; case ExplorerField.ReleaseYear: filters.ReleaseYear = GetStringListFilter(filter); break; case ExplorerField.CompletionStatus: filters.CompletionStatuses = GetIdFilter(filter); break; case ExplorerField.UserScore: filters.UserScore = GetEnumFilter(filter); break; case ExplorerField.CommunityScore: filters.CommunityScore = GetEnumFilter(filter); break; case ExplorerField.CriticScore: filters.CriticScore = GetEnumFilter(filter); break; case ExplorerField.LastActivity: filters.LastActivity = GetEnumFilter(filter); break; case ExplorerField.RecentActivity: filters.RecentActivity = GetEnumFilter(filter); break; case ExplorerField.Added: filters.Added = GetEnumFilter(filter); break; case ExplorerField.Modified: filters.Modified = GetEnumFilter(filter); break; case ExplorerField.PlayTime: filters.PlayTime = GetEnumFilter(filter); break; case ExplorerField.InstallSize: filters.InstallSize = GetEnumFilter(filter); break; case ExplorerField.Feature: filters.Feature = GetIdFilter(filter); break; case ExplorerField.Presets: if (filter?.Value != null) { mainModel.ActiveFilterPreset = filter.Value as FilterPreset; } else { mainModel.ActiveFilterPreset = null; } break; case ExplorerField.Name: filters.Name = GetStringFilter(filter); break; case ExplorerField.InstallStatus: var installed = false; var uninstalled = false; if (filter?.Value is bool instStat) { installed = instStat; uninstalled = !instStat; } filters.SuppressFilterChanges = true; filters.IsInstalled = installed; filters.IsUnInstalled = uninstalled; filters.SuppressFilterChanges = false; filters.OnFilterChanged(new List { nameof(filters.IsInstalled), nameof(filters.IsUnInstalled) }); break; default: if (PlayniteEnvironment.ThrowAllErrors) { throw new NotSupportedException(); } else { break; } } } private SelectionObject allObject = new SelectionObject(SelectionObjectType.All, ResourceProvider.GetString("LOCAll")); private SelectionObject noneDbObject = new SelectionObject( new DatabaseObject() { Id = Guid.Empty, Name = ResourceProvider.GetString("LOCNone") }, ResourceProvider.GetString("LOCNone")); private SelectionObject noneObject = new SelectionObject(SelectionObjectType.None, ResourceProvider.GetString("LOCNone")); private void LoadValues(ExplorerField field) { if (!database.IsOpen || !settings.ExplorerPanelVisible) { return; } var values = new List() { allObject }; switch (field) { case ExplorerField.Library: var libs = extensions.LibraryPlugins.ToList(); libs.Add(new FakePlayniteLibraryPlugin()); values.AddRange(libs.Select(a => new SelectionObject(a, a.Name))); break; case ExplorerField.Category: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedCategories.Select(a => database.Categories[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Categories.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.Genre: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedGenres.Select(a => database.Genres[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Genres.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.Developer: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedDevelopers.Select(a => database.Companies[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Companies.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.Publisher: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedPublishers.Select(a => database.Companies[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Companies.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.Tag: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedTags.Select(a => database.Tags[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Tags.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.Platform: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedPlatforms.Select(a => database.Platforms[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Platforms.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.Series: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedSeries.Select(a => database.Series[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Series.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.AgeRating: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedAgeRatings.Select(a => database.AgeRatings[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.AgeRatings.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.Region: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedRegions.Select(a => database.Regions[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Regions.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.Source: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedSources.Select(a => database.Sources[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Sources.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.ReleaseYear: values.Add(noneDbObject); var years = database.Games.Where(a => a.ReleaseYear != null).Select(a => a.ReleaseYear).Distinct().OrderBy(a => a.Value); values.AddRange(years.Select(a => new SelectionObject(a))); break; case ExplorerField.CompletionStatus: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedCompletionStatuses.Select(a => database.CompletionStatuses[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.CompletionStatuses.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.UserScore: case ExplorerField.CriticScore: case ExplorerField.CommunityScore: values.AddRange(GenerateEnumValues(typeof(ScoreGroup))); break; case ExplorerField.LastActivity: case ExplorerField.RecentActivity: case ExplorerField.Added: case ExplorerField.Modified: values.AddRange(GenerateEnumValues(typeof(PastTimeSegment))); break; case ExplorerField.PlayTime: values.AddRange(GenerateEnumValues(typeof(PlaytimeCategory))); break; case ExplorerField.InstallSize: values.AddRange(GenerateEnumValues(typeof(InstallSizeGroup))); break; case ExplorerField.Feature: values.Add(noneDbObject); if (settings.UsedFieldsOnlyOnFilterLists) { values.AddRange(database.UsedFeastures.Select(a => database.Features[a]).OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } else { values.AddRange(database.Features.OrderBy(a => a.Name).Select(a => new SelectionObject(a))); } break; case ExplorerField.Presets: values.Clear(); values.AddRange(database.GetSortedFilterPresets().Select(a => new SelectionObject(a))); break; case ExplorerField.Name: values.Add(new SelectionObject("^#", "#")); values.AddRange(Enumerable.Range('A', 26).Select(a => new SelectionObject("^" + ((char)a).ToString(), ((char)a).ToString()))); break; case ExplorerField.InstallStatus: values.Add(new SelectionObject(true, LOC.GameIsInstalledTitle.GetLocalized())); values.Add(new SelectionObject(false, LOC.GameIsUnInstalledTitle.GetLocalized())); break; default: if (PlayniteEnvironment.ThrowAllErrors) { throw new NotSupportedException(); } else { break; } } FieldValues = values; if (field != ExplorerField.Presets) { SelectedFieldObject = allObject; } } public IEnumerable GenerateEnumValues(Type enumType) { foreach (Enum status in Enum.GetValues(enumType)) { yield return new SelectionObject(status, status.GetDescription()); } } } } ================================================ FILE: source/Playnite/Database/DatabaseFileEvent.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public enum FileEvent { Added, Removed } public class DatabaseFileEventArgs : EventArgs { public string FileId { get; } public FileEvent EventType { get; } public DatabaseFileEventArgs(string fileId, FileEvent eventType) { FileId = fileId; EventType = eventType; } } } ================================================ FILE: source/Playnite/Database/DatabaseFilter.cs ================================================ using Playnite.Plugins; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Data; namespace Playnite.Database { public class DatabaseFilter : ObservableObject { private static readonly ILogger logger = LogManager.GetLogger(); private static object syncLockYears = new object(); private static object syncLockGenres = new object(); private static object syncLockPlatforms = new object(); private static object syncLockAges = new object(); private static object syncLockCategories = new object(); private static object syncLockPublishers = new object(); private static object syncLockDevelopers = new object(); private static object syncLockRegions = new object(); private static object syncLockSeries = new object(); private static object syncLockSources = new object(); private static object syncLockTags = new object(); private static object syncLockFeatures = new object(); private static object syncLockCompletionStatuses = new object(); private readonly SynchronizationContext context; private readonly IGameDatabaseMain database; private readonly FilterSettings filter; private readonly PlayniteSettings settings; private bool missedDbUpdate = false; private readonly List missedCollection = new List(Enum.GetValues(typeof(GameDatabaseCollection)).Length); private bool ignoreDatabaseUpdates = false; public bool IgnoreDatabaseUpdates { get => ignoreDatabaseUpdates; set { ignoreDatabaseUpdates = value; if (value == false && missedDbUpdate) { UpdateAllCollections(missedCollection); } missedDbUpdate = false; missedCollection.Clear(); } } #region Filter lists public SelectableIdItemList Libraries { get; set; } private SelectableDbItemList genres; public SelectableDbItemList Genres { get => genres; private set { genres = value; OnPropertyChanged(); } } private SelectableDbItemList platforms; public SelectableDbItemList Platforms { get => platforms; private set { platforms = value; OnPropertyChanged(); } } private SelectableDbItemList ageRatings; public SelectableDbItemList AgeRatings { get => ageRatings; private set { ageRatings = value; OnPropertyChanged(); } } private SelectableDbItemList categories; public SelectableDbItemList Categories { get => categories; private set { categories = value; OnPropertyChanged(); } } private SelectableDbItemList publishers; public SelectableDbItemList Publishers { get => publishers; private set { publishers = value; OnPropertyChanged(); } } private SelectableDbItemList developers; public SelectableDbItemList Developers { get => developers; private set { developers = value; OnPropertyChanged(); } } private SelectableDbItemList regions; public SelectableDbItemList Regions { get => regions; private set { regions = value; OnPropertyChanged(); } } private SelectableDbItemList series; public SelectableDbItemList Series { get => series; private set { series = value; OnPropertyChanged(); } } private SelectableDbItemList sources; public SelectableDbItemList Sources { get => sources; private set { sources = value; OnPropertyChanged(); } } private SelectableDbItemList tags; public SelectableDbItemList Tags { get => tags; private set { tags = value; OnPropertyChanged(); } } private SelectableObjectList> releaseYears; public SelectableObjectList> ReleaseYears { get => releaseYears; private set { releaseYears = value; OnPropertyChanged(); } } private SelectableDbItemList features; public SelectableDbItemList Features { get => features; private set { features = value; OnPropertyChanged(); } } private SelectableDbItemList completionStatuses; public SelectableDbItemList CompletionStatuses { get => completionStatuses; private set { completionStatuses = value; OnPropertyChanged(); } } #endregion Filter lists public DatabaseFilter(IGameDatabaseMain database, ExtensionFactory extensions, PlayniteSettings settings, FilterSettings filter) { this.context = SynchronizationContext.Current; this.database = database; this.settings = settings; this.filter = filter; this.settings.PropertyChanged += Settings_PropertyChanged; if (database.IsOpen) { LoadFilterCollection(); } else { database.DatabaseOpened += (s, e) => LoadFilterCollection(); } var libs = extensions.LibraryPlugins.OrderBy(a => a.Name).ToList(); libs.Add(new FakePlayniteLibraryPlugin()); Libraries = new SelectableLibraryPluginList(libs); // Remove filters for unloaded plugins var missing = filter.Library?.Ids.Where(a => Libraries.FirstOrDefault(b => b.Item.Id == a) == null)?.ToList(); if (missing?.Any() == true) { if (filter.Library != null) { missing.ForEach(a => filter.Library.Ids.Remove(a)); } } database.Games.ItemCollectionChanged += Games_ItemCollectionChanged; database.Games.ItemUpdated += Games_ItemUpdated; database.Genres.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(Genres, args, database.Genres, filter.Genre); database.Platforms.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(Platforms, args, database.Platforms, filter.Platform); database.AgeRatings.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(AgeRatings, args, database.AgeRatings, filter.AgeRating); database.Categories.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(Categories, args, database.Categories, filter.Category); database.Regions.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(Regions, args, database.Regions, filter.Region); database.Series.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(Series, args, database.Series, filter.Series); database.Sources.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(Sources, args, database.Sources, filter.Source); database.Tags.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(Tags, args, database.Tags, filter.Tag); database.Features.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(Features, args, database.Features, filter.Feature); database.Companies.ItemCollectionChanged += (_, args) => { FullUpdateAvailableFilterList(Publishers, args, database.Companies, filter.Publisher); FullUpdateAvailableFilterList(Developers, args, database.Companies, filter.Developer); }; database.CompletionStatuses.ItemCollectionChanged += (_, args) => FullUpdateAvailableFilterList(CompletionStatuses, args, database.CompletionStatuses, filter.CompletionStatuses); database.AgeRatingsInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(AgeRatings, database.AgeRatings, database.UsedAgeRatings, filter.AgeRating); database.CategoriesInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Categories, database.Categories, database.UsedCategories, filter.Category); database.DevelopersInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Developers, database.Companies, database.UsedDevelopers, filter.Developer); database.FeaturesInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Features, database.Features, database.UsedFeastures, filter.Feature); database.GenresInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Genres, database.Genres, database.UsedGenres, filter.Genre); database.PlatformsInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Platforms, database.Platforms, database.UsedPlatforms, filter.Platform); database.PublishersInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Publishers, database.Companies, database.UsedPublishers, filter.Publisher); database.RegionsInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Regions, database.Regions, database.UsedRegions, filter.Region); database.SeriesInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Series, database.Series, database.UsedSeries, filter.Series); database.SourcesInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Sources, database.Sources, database.UsedSources, filter.Source); database.TagsInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(Tags, database.Tags, database.UsedTags, filter.Tag); database.CompletionStatusesInUseUpdated += (_, __) => InUseOnlyUpdateAvailableFilterList(CompletionStatuses, database.CompletionStatuses, database.UsedCompletionStatuses, filter.CompletionStatuses); } private void Settings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == nameof(PlayniteSettings.UsedFieldsOnlyOnFilterLists)) { UpdateAllCollections(); } } internal void LoadFilterCollection() { var years = database.Games.Where(a => a.ReleaseYear != null).Select(a => a.ReleaseYear).Distinct().OrderBy(a => a.Value). Select(a => new NamedObject(a.ToString())).ToList(); years.Insert(0, new NamedObject(FilterSettings.MissingFieldString, ResourceProvider.GetString(LOC.None))); ReleaseYears = new SelectableObjectList>(years, null); if (settings.UsedFieldsOnlyOnFilterLists) { Genres = new SelectableDbItemList(database.Genres.Get(database.UsedGenres), null, null, true); Platforms = new SelectableDbItemList(database.Platforms.Get(database.UsedPlatforms), null, null, true); AgeRatings = new SelectableDbItemList(database.AgeRatings.Get(database.UsedAgeRatings), null, null, true); Categories = new SelectableDbItemList(database.Categories.Get(database.UsedCategories), null, null, true); Publishers = new SelectableDbItemList(database.Companies.Get(database.UsedPublishers), null, null, true); Developers = new SelectableDbItemList(database.Companies.Get(database.UsedDevelopers), null, null, true); Regions = new SelectableDbItemList(database.Regions.Get(database.UsedRegions), null, null, true); Series = new SelectableDbItemList(database.Series.Get(database.UsedSeries), null, null, true); Sources = new SelectableDbItemList(database.Sources.Get(database.UsedSources), null, null, true); Tags = new SelectableDbItemList(database.Tags.Get(database.UsedTags), null, null, true); Features = new SelectableDbItemList(database.Features.Get(database.UsedFeastures), null, null, true); CompletionStatuses = new SelectableDbItemList(database.CompletionStatuses.Get(database.UsedCompletionStatuses), null, null, true); } else { Genres = new SelectableDbItemList(database.Genres, null, null, true); Platforms = new SelectableDbItemList(database.Platforms, null, null, true); AgeRatings = new SelectableDbItemList(database.AgeRatings, null, null, true); Categories = new SelectableDbItemList(database.Categories, null, null, true); Publishers = new SelectableDbItemList(database.Companies, null, null, true); Developers = new SelectableDbItemList(database.Companies, null, null, true); Regions = new SelectableDbItemList(database.Regions, null, null, true); Series = new SelectableDbItemList(database.Series, null, null, true); Sources = new SelectableDbItemList(database.Sources, null, null, true); Tags = new SelectableDbItemList(database.Tags, null, null, true); Features = new SelectableDbItemList(database.Features, null, null, true); CompletionStatuses = new SelectableDbItemList(database.CompletionStatuses, null, null, true); } context.Send((a) => { BindingOperations.EnableCollectionSynchronization(ReleaseYears, syncLockYears); BindingOperations.EnableCollectionSynchronization(Genres, syncLockGenres); BindingOperations.EnableCollectionSynchronization(Platforms, syncLockPlatforms); BindingOperations.EnableCollectionSynchronization(AgeRatings, syncLockAges); BindingOperations.EnableCollectionSynchronization(Categories, syncLockCategories); BindingOperations.EnableCollectionSynchronization(Publishers, syncLockPublishers); BindingOperations.EnableCollectionSynchronization(Developers, syncLockDevelopers); BindingOperations.EnableCollectionSynchronization(Regions, syncLockRegions); BindingOperations.EnableCollectionSynchronization(Series, syncLockSeries); BindingOperations.EnableCollectionSynchronization(Sources, syncLockSources); BindingOperations.EnableCollectionSynchronization(Tags, syncLockTags); BindingOperations.EnableCollectionSynchronization(Features, syncLockFeatures); BindingOperations.EnableCollectionSynchronization(CompletionStatuses, syncLockCompletionStatuses); }, null); } private void Games_ItemUpdated(object sender, ItemUpdatedEventArgs e) { if (IgnoreDatabaseUpdates) { missedDbUpdate = true; missedCollection.AddMissing(GameDatabaseCollection.Games); return; } foreach (var update in e.UpdatedItems) { if (update.OldData.ReleaseDate != update.NewData.ReleaseDate && update.NewData.ReleaseDate != null) { ReleaseYears.Add(new NamedObject(update.NewData.ReleaseYear.ToString())); } } } private void Games_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { if (IgnoreDatabaseUpdates) { missedDbUpdate = true; missedCollection.AddMissing(GameDatabaseCollection.Games); return; } foreach (var update in e.AddedItems) { if (update.ReleaseDate != null) { ReleaseYears.Add(new NamedObject(update.ReleaseYear.ToString())); } } } internal void UpdateAllCollections(List updateFields) { foreach (var field in updateFields) { UpdateAllCollections(field); } } internal void UpdateAllCollections(GameDatabaseCollection field) { switch (field) { case GameDatabaseCollection.Games: var years = database.Games.Where(a => a.ReleaseYear != null).Select(a => a.ReleaseYear).Distinct().OrderBy(a => a.Value).Select(a => a.ToString()). Select(a => new NamedObject(a.ToString())).ToList(); years.Insert(0, new NamedObject(FilterSettings.MissingFieldString, ResourceProvider.GetString(LOC.None))); ReleaseYears.SetItems(years, years.Where(a => filter.ReleaseYear?.Values?.Contains(a.Value) == true)); break; case GameDatabaseCollection.Platforms: if (settings.UsedFieldsOnlyOnFilterLists) { Platforms.SetItems(database.Platforms.Get(database.UsedPlatforms), filter.Platform?.Ids); } else { Platforms.SetItems(database.Platforms, filter.Platform?.Ids); } break; case GameDatabaseCollection.Genres: if (settings.UsedFieldsOnlyOnFilterLists) { Genres.SetItems(database.Genres.Get(database.UsedGenres), filter.Genre?.Ids); } else { Genres.SetItems(database.Genres, filter.Genre?.Ids); } break; case GameDatabaseCollection.Companies: if (settings.UsedFieldsOnlyOnFilterLists) { Developers.SetItems(database.Companies.Get(database.UsedDevelopers), filter.Developer?.Ids); Publishers.SetItems(database.Companies.Get(database.UsedPublishers), filter.Publisher?.Ids); } else { Developers.SetItems(database.Companies, filter.Developer?.Ids); Publishers.SetItems(database.Companies, filter.Publisher?.Ids); } break; case GameDatabaseCollection.Tags: if (settings.UsedFieldsOnlyOnFilterLists) { Tags.SetItems(database.Tags.Get(database.UsedTags), filter.Tag?.Ids); } else { Tags.SetItems(database.Tags, filter.Tag?.Ids); } break; case GameDatabaseCollection.Categories: if (settings.UsedFieldsOnlyOnFilterLists) { Categories.SetItems(database.Categories.Get(database.UsedCategories), filter.Category?.Ids); } else { Categories.SetItems(database.Categories, filter.Category?.Ids); } break; case GameDatabaseCollection.Series: if (settings.UsedFieldsOnlyOnFilterLists) { Series.SetItems(database.Series.Get(database.UsedSeries), filter.Series?.Ids); } else { Series.SetItems(database.Series, filter.Series?.Ids); } break; case GameDatabaseCollection.AgeRatings: if (settings.UsedFieldsOnlyOnFilterLists) { AgeRatings.SetItems(database.AgeRatings.Get(database.UsedAgeRatings), filter.AgeRating?.Ids); } else { AgeRatings.SetItems(database.AgeRatings, filter.AgeRating?.Ids); } break; case GameDatabaseCollection.Regions: if (settings.UsedFieldsOnlyOnFilterLists) { Regions.SetItems(database.Regions.Get(database.UsedRegions), filter.Region?.Ids); } else { Regions.SetItems(database.Regions, filter.Region?.Ids); } break; case GameDatabaseCollection.Sources: if (settings.UsedFieldsOnlyOnFilterLists) { Sources.SetItems(database.Sources.Get(database.UsedSources), filter.Source?.Ids); } else { Sources.SetItems(database.Sources, filter.Source?.Ids); } break; case GameDatabaseCollection.Features: if (settings.UsedFieldsOnlyOnFilterLists) { Features.SetItems(database.Features.Get(database.UsedFeastures), filter.Feature?.Ids); } else { Features.SetItems(database.Features, filter.Feature?.Ids); } break; case GameDatabaseCollection.CompletionStatuses: if (settings.UsedFieldsOnlyOnFilterLists) { CompletionStatuses.SetItems(database.CompletionStatuses.Get(database.UsedCompletionStatuses), filter.CompletionStatuses?.Ids); } else { CompletionStatuses.SetItems(database.CompletionStatuses, filter.CompletionStatuses?.Ids); } break; default: if (PlayniteEnvironment.ThrowAllErrors) { throw new NotSupportedException(); } else { break; } } } internal void UpdateAllCollections() { UpdateAllCollections(GameDatabaseCollection.Games); UpdateAllCollections(GameDatabaseCollection.Platforms); UpdateAllCollections(GameDatabaseCollection.Genres); UpdateAllCollections(GameDatabaseCollection.Companies); UpdateAllCollections(GameDatabaseCollection.Tags); UpdateAllCollections(GameDatabaseCollection.Categories); UpdateAllCollections(GameDatabaseCollection.Series); UpdateAllCollections(GameDatabaseCollection.AgeRatings); UpdateAllCollections(GameDatabaseCollection.Regions); UpdateAllCollections(GameDatabaseCollection.Sources); UpdateAllCollections(GameDatabaseCollection.Features); UpdateAllCollections(GameDatabaseCollection.CompletionStatuses); } private void InUseOnlyUpdateAvailableFilterList( SelectableDbItemList targetList, IItemCollection sourceColletion, List usedList, IdItemFilterItemProperties filter) where T : DatabaseObject { if (!settings.UsedFieldsOnlyOnFilterLists) { return; } if (IgnoreDatabaseUpdates) { missedDbUpdate = true; missedCollection.AddMissing(sourceColletion.CollectionType); return; } targetList.SetItems(sourceColletion.Get(usedList), filter?.Ids); } private void FullUpdateAvailableFilterList( SelectableDbItemList targetList, ItemCollectionChangedEventArgs args, IItemCollection sourceColletion, IdItemFilterItemProperties filter) where T : DatabaseObject { if (settings.UsedFieldsOnlyOnFilterLists) { return; } if (IgnoreDatabaseUpdates) { missedDbUpdate = true; missedCollection.AddMissing(sourceColletion.CollectionType); return; } if (args.AddedItems.HasItems() || args.RemovedItems.HasItems()) { targetList.SetItems(sourceColletion, filter?.Ids); } } } } ================================================ FILE: source/Playnite/Database/DatabaseSettings.cs ================================================ using LiteDB; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class DatabaseSettings { public int Version { get; set; } public DatabaseSettings() { } } } ================================================ FILE: source/Playnite/Database/DatabaseStats.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Playnite.SDK.Models; using Playnite.Database; using Playnite.SDK; namespace Playnite.Database { public class DatabaseStats : ObservableObject, IDisposable { private static ILogger logger = LogManager.GetLogger(); public int Installed { get; private set; } = 0; public int UnInstalled { get; private set; } = 0; public int Hidden { get; private set; } = 0; public int Favorite { get; private set; } = 0; public int Total { get { if (database == null) { return 0; } else { return database.Games.Count; } } } private IGameDatabaseMain database; public DatabaseStats(IGameDatabaseMain database) { this.database = database; database.Games.ItemUpdated += Database_GameUpdated; database.Games.ItemCollectionChanged += Database_GamesCollectionChanged; database.DatabaseOpened += Database_DatabaseOpened; if (database.IsOpen) { Recalculate(); } } private void Recalculate() { if (database.Games == null) { return; } logger.Info("Completely recalculating database statistics..."); Installed = 0; UnInstalled = 0; Hidden = 0; Favorite = 0; foreach (var game in database.Games) { if (game.IsInstalled) { Installed++; } else { UnInstalled++; } if (game.Hidden) { Hidden++; } if (game.Favorite) { Favorite++; } } NotifiyAllChanged(); } private void NotifiyAllChanged() { OnPropertyChanged(nameof(Installed)); OnPropertyChanged(nameof(UnInstalled)); OnPropertyChanged(nameof(Hidden)); OnPropertyChanged(nameof(Favorite)); OnPropertyChanged(nameof(Total)); } private void Database_DatabaseOpened(object sender, EventArgs e) { Recalculate(); } private void Database_GamesCollectionChanged(object sender, ItemCollectionChangedEventArgs args) { foreach (var game in args.RemovedItems) { IncrementalUpdate(game, -1); } foreach (var game in args.AddedItems) { IncrementalUpdate(game, 1); } NotifiyAllChanged(); } private void Database_GameUpdated(object sender, ItemUpdatedEventArgs args) { foreach (var update in args.UpdatedItems) { if (update.OldData.Hidden != update.NewData.Hidden) { Hidden = Hidden + (1 * (update.NewData.Hidden ? 1 : -1)); } if (update.OldData.Favorite != update.NewData.Favorite) { Favorite = Favorite + (1 * (update.NewData.Favorite ? 1 : -1)); } if (update.OldData.IsInstalled != update.NewData.IsInstalled) { Installed = Installed + (1 * (update.NewData.IsInstalled ? 1 : -1)); UnInstalled = UnInstalled + (1 * (!update.NewData.IsInstalled ? 1 : -1)); } } OnPropertyChanged(nameof(Installed)); OnPropertyChanged(nameof(UnInstalled)); OnPropertyChanged(nameof(Hidden)); OnPropertyChanged(nameof(Favorite)); } private void IncrementalUpdate(Game game, int modifier) { if (game.Hidden) { Hidden = Hidden + (1 * modifier); } if (game.Favorite) { Favorite = Favorite + (1 * modifier); } if (game.IsInstalled) { Installed = Installed + (1 * modifier); } else { UnInstalled = UnInstalled + (1 * modifier); } } public void Dispose() { database.DatabaseOpened -= Database_DatabaseOpened; database.Games.ItemCollectionChanged -= Database_GamesCollectionChanged; database.Games.ItemUpdated -= Database_GameUpdated; } } } ================================================ FILE: source/Playnite/Database/EventBufferHandler.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database { public class EventBufferHandler : IDisposable { private GameDatabase database; public EventBufferHandler(GameDatabase db) { database = db; db.BeginBufferUpdate(); } public void Dispose() { database.EndBufferUpdate(); } } } ================================================ FILE: source/Playnite/Database/GameDatabase.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Windows.Media.Imaging; using Playnite.Emulators; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.Common; using Playnite.Settings; using Playnite.SDK.Plugins; using System.Net; using Playnite.Common.Web; using System.Drawing.Imaging; using System.Threading; using System.Collections.Concurrent; using Playnite.Common.Media.Icons; using System.Reflection; using SdkModels = Playnite.SDK.Models; namespace Playnite.Database { /// Not the greatest way of doing it but it's a superset of SDK exposed IGameDatabase. /// The whole interface should not be exposed in the SDK since it contains a lot of "internal" members. public interface IGameDatabaseMain : IGameDatabase { List UsedPlatforms { get; } List UsedGenres { get; } List UsedDevelopers { get; } List UsedPublishers { get; } List UsedTags { get; } List UsedCategories { get; } List UsedSeries { get; } List UsedAgeRatings { get; } List UsedRegions { get; } List UsedSources { get; } List UsedFeastures { get; } List UsedCompletionStatuses { get; } AppSoftwareCollection SoftwareApps { get; } event EventHandler DatabaseFileChanged; event EventHandler PlatformsInUseUpdated; event EventHandler GenresInUseUpdated; event EventHandler DevelopersInUseUpdated; event EventHandler PublishersInUseUpdated; event EventHandler TagsInUseUpdated; event EventHandler CategoriesInUseUpdated; event EventHandler AgeRatingsInUseUpdated; event EventHandler SeriesInUseUpdated; event EventHandler RegionsInUseUpdated; event EventHandler SourcesInUseUpdated; event EventHandler FeaturesInUseUpdated; event EventHandler CompletionStatusesInUseUpdated; void SetDatabasePath(string path); void OpenDatabase(); string GetFileStoragePath(Guid parentId); string GetFullFilePath(string dbPath); string AddFile(MetadataFile file, Guid parentId, bool isImage, CancellationToken cancelToken); string AddFile(string path, Guid parentId, bool isImage, CancellationToken cancelToken); void RemoveFile(string dbPath); BitmapSource GetFileAsImage(string dbPath, BitmapLoadProperties loadProperties = null); void CopyFile(string dbPath, string targetPath); void BeginBufferUpdate(); void EndBufferUpdate(); IDisposable BufferedUpdate(); List ImportGames(LibraryPlugin library, CancellationToken cancelToken, PlaytimeImportMode playtimeImportMode); CompletionStatusSettings GetCompletionStatusSettings(); FilterPresetsSettings GetFilterPresetsSettings(); List GetSortedFilterPresets(); void SetCompletionStatusSettings(CompletionStatusSettings settings); void SetFilterPresetsSettings(FilterPresetsSettings settings); GameScannersSettings GetGameScannersSettings(); void SetGameScannersSettings(GameScannersSettings settings); HashSet GetImportedRomFiles(string emulatorDir); HashSet GetImportedExeFiles(); bool GetGameMatchesFilter(Game game, FilterSettings filterSettings, bool useFuzzyNameMatch); IEnumerable GetFilteredGames(FilterSettings filterSettings, bool useFuzzyNameMatch); } public partial class GameDatabase : IGameDatabaseMain, IDisposable { public const double MaximumRecommendedIconSize = 0.1; public const double MaximumRecommendedCoverSize = 1; public const double MaximumRecommendedBackgroundSize = 4; private static ILogger logger = LogManager.GetLogger(); private static readonly Dictionary collectionsSpec = new Dictionary { { nameof(Platforms), typeof(PlatformsCollection) }, { nameof(Emulators), typeof(EmulatorsCollection) }, { nameof(Genres), typeof(GenresCollection) }, { nameof(Companies), typeof(CompaniesCollection) }, { nameof(Tags), typeof(TagsCollection) }, { nameof(Categories), typeof(CategoriesCollection) }, { nameof(AgeRatings), typeof(AgeRatingsCollection) }, { nameof(Series), typeof(SeriesCollection) }, { nameof(Regions), typeof(RegionsCollection) }, { nameof(Sources), typeof(GamesSourcesCollection) }, { nameof(Features), typeof(FeaturesCollection) }, { nameof(SoftwareApps), typeof(AppSoftwareCollection) }, { nameof(Games), typeof(GamesCollection) }, { nameof(GameScanners), typeof(GameScannersCollection) }, { nameof(FilterPresets), typeof(FilterPresetsCollection) }, { nameof(ImportExclusions), typeof(ImportExclusionsCollection) }, { nameof(CompletionStatuses), typeof(CompletionStatusesCollection) } }; #region Locks private readonly object databaseConfigFileLock = new object(); private readonly ConcurrentDictionary fileLocks = new ConcurrentDictionary(); #endregion Locks #region Paths public string DatabasePath { get; private set; } internal const string filesDirName = "files"; private const string settingsFileName = "database.json"; private const string gamesDirName = "games"; private const string platformsDirName = "platforms"; private const string emulatorsDirName = "emulators"; private const string genresDirName = "genres"; private const string companiesDirName = "companies"; private const string tagsDirName = "tags"; private const string featuresDirName = "features"; private const string categoriesDirName = "categories"; private const string seriesDirName = "series"; private const string ageRatingsDirName = "ageratings"; private const string regionsDirName = "regions"; private const string sourcesDirName = "sources"; private const string toolsDirName = "tools"; private const string gameScannersDirName = "scanners"; private const string filterPresetsDirName = "filterpresets"; private const string importExclusionsDirName = "importexclusions"; private const string completionStatusesDirName = "completionstatuses"; private string GamesDirectoryPath { get => Path.Combine(DatabasePath, gamesDirName); } private string PlatformsDirectoryPath { get => Path.Combine(DatabasePath, platformsDirName); } private string EmulatorsDirectoryPath { get => Path.Combine(DatabasePath, emulatorsDirName); } private string GenresDirectoryPath { get => Path.Combine(DatabasePath, genresDirName); } private string CompaniesDirectoryPath { get => Path.Combine(DatabasePath, companiesDirName); } private string TagsDirectoryPath { get => Path.Combine(DatabasePath, tagsDirName); } private string CategoriesDirectoryPath { get => Path.Combine(DatabasePath, categoriesDirName); } private string AgeRatingsDirectoryPath { get => Path.Combine(DatabasePath, ageRatingsDirName); } private string SeriesDirectoryPath { get => Path.Combine(DatabasePath, seriesDirName); } private string RegionsDirectoryPath { get => Path.Combine(DatabasePath, regionsDirName); } private string SourcesDirectoryPath { get => Path.Combine(DatabasePath, sourcesDirName); } private string FilesDirectoryPath { get => Path.Combine(DatabasePath, filesDirName); } private string DatabaseFileSettingsPath { get => Path.Combine(DatabasePath, settingsFileName); } private string FeaturesDirectoryPath { get => Path.Combine(DatabasePath, featuresDirName); } private string ToolsDirectoryPath { get => Path.Combine(DatabasePath, toolsDirName); } private string GameScannersDirectoryPath { get => Path.Combine(DatabasePath, gameScannersDirName); } private string FilterPresetsDirectoryPath { get => Path.Combine(DatabasePath, filterPresetsDirName); } private string ImportExclusionsDirectoryPath { get => Path.Combine(DatabasePath, importExclusionsDirName); } private string CompletionStatusesDirectoryPath { get => Path.Combine(DatabasePath, completionStatusesDirName); } #endregion Paths #region Lists public IItemCollection Games { get; private set; } public IItemCollection Platforms { get; private set; } public IItemCollection Emulators { get; private set; } public IItemCollection Genres { get; private set; } public IItemCollection Companies { get; private set; } public IItemCollection Tags { get; private set; } public IItemCollection Categories { get; private set; } public IItemCollection Series { get; private set; } public IItemCollection AgeRatings { get; private set; } public IItemCollection Regions { get; private set; } public IItemCollection Sources { get; private set; } public IItemCollection Features { get; private set; } public AppSoftwareCollection SoftwareApps { get; private set; } public IItemCollection GameScanners { get; private set; } public IItemCollection FilterPresets { get; private set; } public IItemCollection ImportExclusions { get; private set; } public IItemCollection CompletionStatuses { get; private set; } public List UsedPlatforms { get; } = new List(); public List UsedGenres { get; } = new List(); public List UsedDevelopers { get; } = new List(); public List UsedPublishers { get; } = new List(); public List UsedTags { get; } = new List(); public List UsedCategories { get; } = new List(); public List UsedSeries { get; } = new List(); public List UsedAgeRatings { get; } = new List(); public List UsedRegions { get; } = new List(); public List UsedSources { get; } = new List(); public List UsedFeastures { get; } = new List(); public List UsedCompletionStatuses { get; } = new List(); #endregion Lists public static GameDatabase Instance { get; private set; } public bool IsOpen { get; private set; } private DatabaseSettings settings; public DatabaseSettings Settings { get { if (settings == null) { if (File.Exists(DatabaseFileSettingsPath)) { lock (databaseConfigFileLock) { settings = Serialization.FromJson(FileSystem.ReadFileAsStringSafe(DatabaseFileSettingsPath)); if (settings == null) { // This shouldn't in theory happen, but there are some wierd crash reports available for this. settings = new DatabaseSettings() { Version = NewFormatVersion }; } } } else { settings = new DatabaseSettings() { Version = NewFormatVersion }; } } return settings; } set { lock (databaseConfigFileLock) { settings = value; FileSystem.WriteStringToFileSafe(DatabaseFileSettingsPath, Serialization.ToJson(settings)); } } } public static readonly ushort NewFormatVersion = 4; #region Events public event EventHandler DatabaseOpened; public event EventHandler DatabaseFileChanged; public event EventHandler PlatformsInUseUpdated; public event EventHandler GenresInUseUpdated; public event EventHandler DevelopersInUseUpdated; public event EventHandler PublishersInUseUpdated; public event EventHandler TagsInUseUpdated; public event EventHandler CategoriesInUseUpdated; public event EventHandler AgeRatingsInUseUpdated; public event EventHandler SeriesInUseUpdated; public event EventHandler RegionsInUseUpdated; public event EventHandler SourcesInUseUpdated; public event EventHandler FeaturesInUseUpdated; public event EventHandler CompletionStatusesInUseUpdated; #endregion Events #region Initialization private void LoadCollections() { using (var timer = new ExecutionTimer("DatabaseLoadCollections")) { (Platforms as PlatformsCollection).InitializeCollection(PlatformsDirectoryPath); (Emulators as EmulatorsCollection).InitializeCollection(EmulatorsDirectoryPath); (Genres as GenresCollection).InitializeCollection(GenresDirectoryPath); (Companies as CompaniesCollection).InitializeCollection(CompaniesDirectoryPath); (Tags as TagsCollection).InitializeCollection(TagsDirectoryPath); (Categories as CategoriesCollection).InitializeCollection(CategoriesDirectoryPath); (AgeRatings as AgeRatingsCollection).InitializeCollection(AgeRatingsDirectoryPath); (Series as SeriesCollection).InitializeCollection(SeriesDirectoryPath); (Regions as RegionsCollection).InitializeCollection(RegionsDirectoryPath); (Sources as GamesSourcesCollection).InitializeCollection(SourcesDirectoryPath); (Features as FeaturesCollection).InitializeCollection(FeaturesDirectoryPath); (Games as GamesCollection).InitializeCollection(GamesDirectoryPath); SoftwareApps.InitializeCollection(ToolsDirectoryPath); (GameScanners as GameScannersCollection).InitializeCollection(GameScannersDirectoryPath); (FilterPresets as FilterPresetsCollection).InitializeCollection(FilterPresetsDirectoryPath); (ImportExclusions as ImportExclusionsCollection).InitializeCollection(ImportExclusionsDirectoryPath); (CompletionStatuses as CompletionStatusesCollection).InitializeCollection(CompletionStatusesDirectoryPath); Games.ItemUpdated += Games_ItemUpdated; Games.ItemCollectionChanged += Games_ItemCollectionChanged; Platforms.ItemCollectionChanged += Platforms_ItemCollectionChanged; Genres.ItemCollectionChanged += Genres_ItemCollectionChanged; Companies.ItemCollectionChanged += Companies_ItemCollectionChanged; Tags.ItemCollectionChanged += Tags_ItemCollectionChanged; Categories.ItemCollectionChanged += Categories_ItemCollectionChanged; AgeRatings.ItemCollectionChanged += AgeRatings_ItemCollectionChanged; Series.ItemCollectionChanged += Series_ItemCollectionChanged; Regions.ItemCollectionChanged += Regions_ItemCollectionChanged; Sources.ItemCollectionChanged += Sources_ItemCollectionChanged; Features.ItemCollectionChanged += Features_ItemCollectionChanged; CompletionStatuses.ItemCollectionChanged += CompletionStatuses_ItemCollectionChanged; } } private void LoadUsedItems() { foreach (var game in Games) { if (game.PlatformIds.HasItems()) { UsedPlatforms.AddMissing(game.PlatformIds.Where(a => Platforms.ContainsItem(a))); } if (game.GenreIds.HasItems()) { UsedGenres.AddMissing(game.GenreIds.Where(a => Genres.ContainsItem(a))); } if (game.DeveloperIds.HasItems()) { UsedDevelopers.AddMissing(game.DeveloperIds.Where(a => Companies.ContainsItem(a))); } if (game.PublisherIds.HasItems()) { UsedPublishers.AddMissing(game.PublisherIds.Where(a => Companies.ContainsItem(a))); } if (game.TagIds.HasItems()) { UsedTags.AddMissing(game.TagIds.Where(a => Tags.ContainsItem(a))); } if (game.CategoryIds.HasItems()) { UsedCategories.AddMissing(game.CategoryIds.Where(a => Categories.ContainsItem(a))); } if (game.SeriesIds.HasItems()) { UsedSeries.AddMissing(game.SeriesIds.Where(a => Series.ContainsItem(a))); } if (game.AgeRatingIds.HasItems()) { UsedAgeRatings.AddMissing(game.AgeRatingIds.Where(a => AgeRatings.ContainsItem(a))); } if (game.RegionIds.HasItems()) { UsedRegions.AddMissing(game.RegionIds.Where(a => Regions.ContainsItem(a))); } if (game.SourceId != Guid.Empty && Sources.ContainsItem(game.SourceId)) { UsedSources.AddMissing(game.SourceId); } if (game.FeatureIds.HasItems()) { UsedFeastures.AddMissing(game.FeatureIds.Where(a => Features.ContainsItem(a))); } if (game.CompletionStatusId != Guid.Empty && CompletionStatuses.ContainsItem(game.CompletionStatusId)) { UsedCompletionStatuses.AddMissing(game.CompletionStatusId); } } } #endregion Intialization public GameDatabase() : this(null) { } public static LiteDB.BsonMapper GetCollectionMapper() { var mapper = new LiteDB.BsonMapper() { SerializeNullValues = false, TrimWhitespace = false, EmptyStringToNull = true, IncludeFields = false, IncludeNonPublic = false }; foreach (var col in collectionsSpec) { col.Value. GetMethod(nameof(GamesCollection.MapLiteDbEntities), BindingFlags.Public | BindingFlags.Static). Invoke(null, new object[] { mapper }); } return mapper; } public GameDatabase(string path) { var mapper = GetCollectionMapper(); DatabasePath = GetFullDbPath(path); foreach (var col in collectionsSpec) { var collection = Activator.CreateInstance(col.Value, this, mapper); typeof(GameDatabase).GetProperty(col.Key).SetValue(this, collection); } } public void Dispose() { if (!IsOpen) { return; } foreach (var col in collectionsSpec) { var prop = typeof(GameDatabase).GetProperty(col.Key); typeof(IDisposable).GetMethod(nameof(IDisposable.Dispose)).Invoke(prop.GetValue(this), null); } } public static string GetDefaultPath(bool portable, string userDataDirOverride) { if (!userDataDirOverride.IsNullOrWhiteSpace()) { return Path.Combine(userDataDirOverride, "library"); } if (portable) { return ExpandableVariables.PlayniteDirectory + @"\library"; } else { return @"%AppData%\Playnite\library"; } } private void CheckDbState() { if (!IsOpen) { throw new Exception("Database is not opened."); } } internal static DatabaseSettings GetSettingsFromDbPath(string dbPath) { var settingsPath = Path.Combine(dbPath, settingsFileName); return Serialization.FromJson(FileSystem.ReadFileAsStringSafe(settingsPath)); } internal static void SaveSettingsToDbPath(DatabaseSettings settings, string dbPath) { var settingsPath = Path.Combine(dbPath, settingsFileName); FileSystem.WriteStringToFileSafe(settingsPath, Serialization.ToJson(settings)); } // TODO: Remove this, we should only allow path to be set during instantiation. public void SetDatabasePath(string path) { if (IsOpen) { throw new Exception("Cannot change database path when database is open."); } DatabasePath = GetFullDbPath(path); } public static string GetFullDbPath(string path) { if (string.IsNullOrEmpty(path)) { return path; } if (path.Contains(ExpandableVariables.PlayniteDirectory, StringComparison.OrdinalIgnoreCase)) { return path?.Replace(ExpandableVariables.PlayniteDirectory, PlaynitePaths.ProgramPath); } else if (path.Contains("%AppData%", StringComparison.OrdinalIgnoreCase)) { return path?.Replace("%AppData%", Environment.ExpandEnvironmentVariables("%AppData%"), StringComparison.OrdinalIgnoreCase); } else if (!Paths.IsFullPath(path)) { return Path.GetFullPath(path); } else { return path; } } public void OpenDatabase() { if (string.IsNullOrEmpty(DatabasePath)) { throw new Exception("Database path cannot be empty."); } var dbExists = File.Exists(DatabaseFileSettingsPath); logger.Info("Opening db " + DatabasePath); if (!FileSystem.CanWriteToFolder(DatabasePath)) { throw new Exception($"Can't write to \"{DatabasePath}\" folder."); } // This fixes an issue where people mess up their library with custom scripts // which create collection files instead of directories :| if (File.Exists(FilesDirectoryPath)) { File.Delete(FilesDirectoryPath); } if (!dbExists) { FileSystem.CreateDirectory(DatabasePath); FileSystem.CreateDirectory(FilesDirectoryPath); } if (!dbExists) { Settings = new DatabaseSettings() { Version = NewFormatVersion }; } else { if (Settings.Version > NewFormatVersion) { throw new Exception($"Database version {Settings.Version} is not supported."); } if (GetMigrationRequired(DatabasePath)) { throw new Exception("Database must be migrated before opening."); } } LoadCollections(); LoadUsedItems(); // New DB setup if (!dbExists) { // Generate default platforms var platforms = Emulation.Platforms.Where(a => a.IgdbId != 0).Select(a => new Platform(a.Name) { SpecificationId = a.Id }).ToList(); if (platforms.HasItems()) { var col = Platforms as ItemCollection; col.IsEventsEnabled = false; col.Add(platforms); col.IsEventsEnabled = true; } // Generate default regions var regions = Emulation.Regions.Where(a => a.DefaultImport).Select(a => new Region(a.Name) { SpecificationId = a.Id }).ToList(); if (regions.HasItems()) { var col = Regions as ItemCollection; col.IsEventsEnabled = false; col.Add(regions); col.IsEventsEnabled = true; } // Generate default completion statuses var compCol = CompletionStatuses as CompletionStatusesCollection; var defStatuses = new string[] { "Not Played", "Played", "Beaten", "Completed", "Playing", "Abandoned", "On Hold", "Plan to Play" }; foreach (var status in defStatuses) { compCol.IsEventsEnabled = false; compCol.Add(status); compCol.IsEventsEnabled = true; } var set = new CompletionStatusSettings { DefaultStatus = compCol.First(a => a.Name == defStatuses[0]).Id, PlayedStatus = compCol.First(a => a.Name == defStatuses[1]).Id }; compCol.SetSettings(set); // Generate default filter presets var filters = FilterPresets as FilterPresetsCollection; filters.IsEventsEnabled = false; filters.Add(new FilterPreset { Name = "All", ShowInFullscreeQuickSelection = true, GroupingOrder = GroupableField.None, SortingOrder = SortOrder.Name, SortingOrderDirection = SortOrderDirection.Ascending, Settings = new FilterPresetSettings() }); filters.Add(new FilterPreset { Name = "Recently Played", ShowInFullscreeQuickSelection = true, GroupingOrder = GroupableField.None, SortingOrder = SortOrder.LastActivity, SortingOrderDirection = SortOrderDirection.Descending, Settings = new FilterPresetSettings { IsInstalled = true } }); filters.Add(new FilterPreset { Name = "Favorites", ShowInFullscreeQuickSelection = true, GroupingOrder = GroupableField.None, SortingOrder = SortOrder.Name, SortingOrderDirection = SortOrderDirection.Ascending, Settings = new FilterPresetSettings { Favorite = true } }); filters.Add(new FilterPreset { Name = "Most Played", ShowInFullscreeQuickSelection = true, GroupingOrder = GroupableField.None, SortingOrder = SortOrder.Playtime, SortingOrderDirection = SortOrderDirection.Descending, Settings = new FilterPresetSettings() }); filters.IsEventsEnabled = true; } IsOpen = true; if (PlayniteApplication.Current != null) { PlayniteApplication.Current.SyncContext.Send((_) => DatabaseOpened?.Invoke(this, null), null); } else { DatabaseOpened?.Invoke(this, null); } } private void Games_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { if (e.AddedItems.HasItems()) { foreach (var game in e.AddedItems) { UpdateFieldsInUse(game.PlatformIds, UsedPlatforms, PlatformsInUseUpdated, Platforms); UpdateFieldsInUse(game.GenreIds, UsedGenres, GenresInUseUpdated, Genres); UpdateFieldsInUse(game.DeveloperIds, UsedDevelopers, DevelopersInUseUpdated, Companies); UpdateFieldsInUse(game.PublisherIds, UsedPublishers, PublishersInUseUpdated, Companies); UpdateFieldsInUse(game.TagIds, UsedTags, TagsInUseUpdated, Tags); UpdateFieldsInUse(game.CategoryIds, UsedCategories, CategoriesInUseUpdated, Categories); UpdateFieldsInUse(game.AgeRatingIds, UsedAgeRatings, AgeRatingsInUseUpdated, AgeRatings); UpdateFieldsInUse(game.SeriesIds, UsedSeries, SeriesInUseUpdated, Series); UpdateFieldsInUse(game.RegionIds, UsedRegions, RegionsInUseUpdated, Regions); UpdateFieldsInUse(game.SourceId, UsedSources, SourcesInUseUpdated, Sources); UpdateFieldsInUse(game.FeatureIds, UsedFeastures, FeaturesInUseUpdated, Features); UpdateFieldsInUse(game.CompletionStatusId, UsedCompletionStatuses, CompletionStatusesInUseUpdated, CompletionStatuses); } } } private void Games_ItemUpdated(object sender, ItemUpdatedEventArgs e) { foreach (var upd in e.UpdatedItems) { UpdateFieldsInUse(upd.NewData.PlatformIds, UsedPlatforms, PlatformsInUseUpdated, Platforms); UpdateFieldsInUse(upd.NewData.GenreIds, UsedGenres, GenresInUseUpdated, Genres); UpdateFieldsInUse(upd.NewData.DeveloperIds, UsedDevelopers, DevelopersInUseUpdated, Companies); UpdateFieldsInUse(upd.NewData.PublisherIds, UsedPublishers, PublishersInUseUpdated, Companies); UpdateFieldsInUse(upd.NewData.TagIds, UsedTags, TagsInUseUpdated, Tags); UpdateFieldsInUse(upd.NewData.CategoryIds, UsedCategories, CategoriesInUseUpdated, Categories); UpdateFieldsInUse(upd.NewData.AgeRatingIds, UsedAgeRatings, AgeRatingsInUseUpdated, AgeRatings); UpdateFieldsInUse(upd.NewData.SeriesIds, UsedSeries, SeriesInUseUpdated, Series); UpdateFieldsInUse(upd.NewData.RegionIds, UsedRegions, RegionsInUseUpdated, Regions); UpdateFieldsInUse(upd.NewData.SourceId, UsedSources, SourcesInUseUpdated, Sources); UpdateFieldsInUse(upd.NewData.FeatureIds, UsedFeastures, FeaturesInUseUpdated, Features); UpdateFieldsInUse(upd.NewData.CompletionStatusId, UsedCompletionStatuses, CompletionStatusesInUseUpdated, CompletionStatuses); } } private void UpdateFieldsInUse(Guid sourceData, List useCollection, EventHandler handler, IItemCollection dbItems) { if (sourceData != Guid.Empty && dbItems.ContainsItem(sourceData)) { if (useCollection.AddMissing(sourceData)) { handler?.Invoke(this, EventArgs.Empty); } } } private void UpdateFieldsInUse(List sourceData, List useCollection, EventHandler handler, IItemCollection dbItems) { if (sourceData.HasItems()) { if (useCollection.AddMissing(sourceData.Where(a => dbItems.ContainsItem(a)).ToArray())) { handler?.Invoke(this, EventArgs.Empty); } } } private void UpdateRemovedFieldsInUse(List removedObjects, List useCollection, EventHandler handler) where T : DatabaseObject { if (removedObjects.HasItems()) { var someRemoved = false; foreach (var item in removedObjects) { if (useCollection.Remove(item.Id)) { someRemoved = true; } } if (someRemoved) { handler?.Invoke(this, EventArgs.Empty); } } } private void Features_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedFeastures, FeaturesInUseUpdated); } private void Sources_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedSources, SourcesInUseUpdated); } private void Regions_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedRegions, RegionsInUseUpdated); } private void Series_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedSeries, SeriesInUseUpdated); } private void AgeRatings_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedAgeRatings, AgeRatingsInUseUpdated); } private void Categories_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedCategories, CategoriesInUseUpdated); } private void Tags_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedTags, TagsInUseUpdated); } private void Companies_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedDevelopers, DevelopersInUseUpdated); UpdateRemovedFieldsInUse(e.RemovedItems, UsedPublishers, PublishersInUseUpdated); } private void Genres_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedGenres, GenresInUseUpdated); } private void Platforms_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedPlatforms, PlatformsInUseUpdated); } private void CompletionStatuses_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { UpdateRemovedFieldsInUse(e.RemovedItems, UsedCompletionStatuses, CompletionStatusesInUseUpdated); } #region Files public string GetFileStoragePath(Guid parentId) { var path = Path.Combine(FilesDirectoryPath, parentId.ToString()); FileSystem.CreateDirectory(path, false); return path; } public string GetFullFilePath(string dbPath) { return Path.Combine(FilesDirectoryPath, dbPath); } public string AddFile(MetadataFile file, Guid parentId, bool isImage, CancellationToken cancelToken) { if (!file.HasImageData) { logger.Error("Cannot add file to database, no file data provided."); return null; } string localPath = null; try { localPath = file.GetLocalFile(cancelToken); } catch (Exception e) { logger.Error(e, "Failed to get local file from metadata file"); } if (localPath.IsNullOrEmpty()) { return null; } var finalFile = AddFile(localPath, parentId, isImage, cancelToken); if (localPath.StartsWith(PlaynitePaths.TempPath)) { FileSystem.DeleteFile(localPath); } return finalFile; } public string AddFile(string path, Guid parentId, bool isImage, CancellationToken cancelToken) { CheckDbState(); var targetDir = Path.Combine(FilesDirectoryPath, parentId.ToString()); var dbPath = string.Empty; if (path.IsHttpUrl()) { var extension = Path.GetExtension(new Uri(path).AbsolutePath); var fileName = Guid.NewGuid().ToString() + extension; var downPath = Path.Combine(targetDir, fileName); try { HttpDownloader.DownloadFile(path, downPath, cancelToken); if (cancelToken.IsCancellationRequested) { FileSystem.DeleteFile(downPath); return null; } if (FileSystem.GetFileSize(downPath) == 0) { FileSystem.DeleteFile(downPath); return null; } if (isImage) { var converted = Images.ConvertToCompatibleFormat(downPath, Path.Combine(targetDir, Path.GetFileNameWithoutExtension(fileName))); if (converted.IsNullOrEmpty()) { FileSystem.DeleteFile(downPath); return null; } else if (converted == downPath) { dbPath = Path.Combine(parentId.ToString(), fileName); } else { dbPath = Path.Combine(parentId.ToString(), Path.GetFileName(converted)); FileSystem.DeleteFile(downPath); } } else { dbPath = Path.Combine(parentId.ToString(), fileName); } } catch (Exception e) { FileSystem.DeleteFile(downPath); logger.Error(e, $"Failed to add http {path} file to database."); return null; } } else { try { var fileName = Path.GetFileName(path); // Re-use file if already part of db folder, don't copy. if (Paths.AreEqual(targetDir, Path.GetDirectoryName(path))) { dbPath = Path.Combine(parentId.ToString(), fileName); } else { fileName = Guid.NewGuid().ToString() + Path.GetExtension(fileName); if (isImage) { var converted = Images.ConvertToCompatibleFormat(path, Path.Combine(targetDir, Path.GetFileNameWithoutExtension(fileName))); if (converted.IsNullOrEmpty()) { return null; } else if (converted == path) { FileSystem.CopyFile(path, Path.Combine(targetDir, fileName)); dbPath = Path.Combine(parentId.ToString(), fileName); } else { dbPath = Path.Combine(parentId.ToString(), Path.GetFileName(converted)); } } else { FileSystem.CopyFile(path, Path.Combine(targetDir, fileName)); dbPath = Path.Combine(parentId.ToString(), fileName); } } } catch (Exception e) { logger.Error(e, $"Failed to add {path} file to database."); return null; } } DatabaseFileChanged?.Invoke(this, new DatabaseFileEventArgs(dbPath, FileEvent.Added)); return dbPath; } public void RemoveFile(string dbPath) { if (string.IsNullOrEmpty(dbPath)) { return; } CheckDbState(); var filePath = GetFullFilePath(dbPath); if (!File.Exists(filePath)) { return; } try { lock (GetFileLock(dbPath)) { try { FileSystem.DeleteFileSafe(filePath); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to remove old database file {dbPath}."); } try { var dir = Path.GetDirectoryName(filePath); if (FileSystem.IsDirectoryEmpty(dir)) { FileSystem.DeleteDirectory(dir); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { // Getting crash reports from Path.GetDirectoryName for some reason. logger.Error(e, "Failed to clean up directory after removing file"); } } } finally { ReleaseFileLock(dbPath); } DatabaseFileChanged?.Invoke(this, new DatabaseFileEventArgs(dbPath, FileEvent.Removed)); } public BitmapSource GetFileAsImage(string dbPath, BitmapLoadProperties loadProperties = null) { CheckDbState(); var filePath = GetFullFilePath(dbPath); if (!File.Exists(filePath)) { return null; } try { lock (GetFileLock(dbPath)) { using (var fStream = FileSystem.OpenReadFileStreamSafe(filePath)) { return BitmapExtensions.BitmapFromStream(fStream, loadProperties); } } } finally { ReleaseFileLock(dbPath); } } public void CopyFile(string dbPath, string targetPath) { CheckDbState(); var filePath = GetFullFilePath(dbPath); try { lock (GetFileLock(dbPath)) { FileSystem.PrepareSaveFile(targetPath); File.Copy(filePath, targetPath); } } finally { ReleaseFileLock(dbPath); } } #endregion Files public void BeginBufferUpdate() { Platforms.BeginBufferUpdate(); Genres.BeginBufferUpdate(); Companies.BeginBufferUpdate(); Tags.BeginBufferUpdate(); Categories.BeginBufferUpdate(); Series.BeginBufferUpdate(); AgeRatings.BeginBufferUpdate(); Regions.BeginBufferUpdate(); Sources.BeginBufferUpdate(); Emulators.BeginBufferUpdate(); Features.BeginBufferUpdate(); Games.BeginBufferUpdate(); SoftwareApps.BeginBufferUpdate(); GameScanners.BeginBufferUpdate(); FilterPresets.BeginBufferUpdate(); ImportExclusions.BeginBufferUpdate(); CompletionStatuses.BeginBufferUpdate(); } public void EndBufferUpdate() { Platforms.EndBufferUpdate(); Genres.EndBufferUpdate(); Companies.EndBufferUpdate(); Tags.EndBufferUpdate(); Categories.EndBufferUpdate(); Series.EndBufferUpdate(); AgeRatings.EndBufferUpdate(); Regions.EndBufferUpdate(); Sources.EndBufferUpdate(); Emulators.EndBufferUpdate(); Features.EndBufferUpdate(); Games.EndBufferUpdate(); SoftwareApps.EndBufferUpdate(); GameScanners.EndBufferUpdate(); FilterPresets.EndBufferUpdate(); ImportExclusions.EndBufferUpdate(); CompletionStatuses.EndBufferUpdate(); } public IDisposable BufferedUpdate() { return new EventBufferHandler(this); } private Game GameInfoToGame(GameMetadata game, Guid pluginId) { var toAdd = new Game() { PluginId = pluginId, Name = game.Name, GameId = game.GameId, Description = game.Description, InstallDirectory = game.InstallDirectory, SortingName = game.SortingName, GameActions = game.GameActions == null ? null : new ObservableCollection(game.GameActions), ReleaseDate = game.ReleaseDate, Links = game.Links == null ? null : new ObservableCollection(game.Links), Roms = game.Roms == null ? null : new ObservableCollection(game.Roms), IsInstalled = game.IsInstalled, Playtime = game.Playtime, PlayCount = game.PlayCount, LastActivity = game.LastActivity, Version = game.Version, UserScore = game.UserScore, CriticScore = game.CriticScore, CommunityScore = game.CommunityScore, Hidden = game.Hidden, Favorite = game.Favorite, InstallSize = game.InstallSize }; if (game.Platforms?.Any() == true) { toAdd.PlatformIds = Platforms.Add(game.Platforms).Select(a => a.Id).ToList(); } if (game.Regions?.Any() == true) { toAdd.RegionIds = Regions.Add(game.Regions).Select(a => a.Id).ToList(); } if (game.Developers?.Any() == true) { toAdd.DeveloperIds = Companies.Add(game.Developers).Select(a => a.Id).ToList(); } if (game.Publishers?.Any() == true) { toAdd.PublisherIds = Companies.Add(game.Publishers).Select(a => a.Id).ToList(); } if (game.Genres?.Any() == true) { toAdd.GenreIds = Genres.Add(game.Genres).Select(a => a.Id).ToList(); } if (game.Categories?.Any() == true) { toAdd.CategoryIds = Categories.Add(game.Categories).Select(a => a.Id).ToList(); } if (game.Tags?.Any() == true) { toAdd.TagIds = Tags.Add(game.Tags).Select(a => a.Id).ToList(); } if (game.Features?.Any() == true) { toAdd.FeatureIds = Features.Add(game.Features).Select(a => a.Id).ToList(); } if (game.AgeRatings?.Any() == true) { toAdd.AgeRatingIds = AgeRatings.Add(game.AgeRatings).Select(a => a.Id).ToList(); } if (game.Series?.Any() == true) { toAdd.SeriesIds = Series.Add(game.Series).Select(a => a.Id).ToList(); } if (game.Source != null) { toAdd.SourceId = Sources.Add(game.Source).Id; } if (game.CompletionStatus != null) { toAdd.CompletionStatusId = CompletionStatuses.Add(game.CompletionStatus).Id; } return toAdd; } public Game ImportGame(GameMetadata game) { return ImportGame(game, Guid.Empty); } public Game ImportGame(GameMetadata game, LibraryPlugin sourcePlugin) { return ImportGame(game, sourcePlugin.Id); } public Game ImportGame(GameMetadata game, Guid pluginId) { var toAdd = GameInfoToGame(game, pluginId); if (game.Icon != null) { toAdd.Icon = AddFile(game.Icon, toAdd.Id, true, CancellationToken.None); } if (game.CoverImage != null) { toAdd.CoverImage = AddFile(game.CoverImage, toAdd.Id, true, CancellationToken.None); } if (game.BackgroundImage != null) { toAdd.BackgroundImage = AddFile(game.BackgroundImage, toAdd.Id, true, CancellationToken.None); } toAdd.IncludeLibraryPluginAction = true; Games.Add(toAdd); return toAdd; } public List ImportGames(LibraryPlugin library, CancellationToken cancelToken, PlaytimeImportMode playtimeImportMode) { using (BufferedUpdate()) { var statusSettings = GetCompletionStatusSettings(); bool updateCompletionStatus(Game game, CompletionStatusSettings settings) { var updated = false; if ((game.Playtime > 0 && (game.CompletionStatusId == Guid.Empty || game.CompletionStatusId == settings.DefaultStatus)) && game.CompletionStatusId != statusSettings.PlayedStatus) { game.CompletionStatusId = statusSettings.PlayedStatus; updated = true; } else if ((game.Playtime == 0 && game.CompletionStatusId == Guid.Empty) && game.CompletionStatusId != statusSettings.DefaultStatus) { game.CompletionStatusId = statusSettings.DefaultStatus; updated = true; } return updated; } if (library.Properties?.HasCustomizedGameImport == true) { var importedGames = library.ImportGames(new LibraryImportGamesArgs { CancelToken = cancelToken })?.ToList() ?? new List(); foreach (var game in importedGames) { updateCompletionStatus(game, statusSettings); } return importedGames; } else { var addedGames = new List(); foreach (var newGame in library.GetGames(new LibraryGetGamesArgs { CancelToken = cancelToken }) ?? new List()) { if (ImportExclusions[ImportExclusionItem.GetId(newGame.GameId, library.Id)] != null) { logger.Debug($"Excluding {newGame.Name} {library.Name} from import."); continue; } var existingGame = Games.FirstOrDefault(a => a.GameId == newGame.GameId && a.PluginId == library.Id); if (existingGame == null) { logger.Info(string.Format("Adding new game {0} from {1} plugin", newGame.GameId, library.Name)); try { if (newGame.Playtime != 0) { var originalPlaytime = newGame.Playtime; newGame.Playtime = 0; if (playtimeImportMode == PlaytimeImportMode.Always || playtimeImportMode == PlaytimeImportMode.NewImportsOnly) { newGame.Playtime = originalPlaytime; } } var importedGame = ImportGame(newGame, library.Id); addedGames.Add(importedGame); if (updateCompletionStatus(importedGame, statusSettings)) { Games.Update(importedGame); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to import game into database."); } } else { var existingGameUpdated = false; if (!existingGame.IsCustomGame && !existingGame.OverrideInstallState) { if (existingGame.IsInstalled != newGame.IsInstalled) { existingGame.IsInstalled = newGame.IsInstalled; existingGameUpdated = true; } if (string.Equals(existingGame.InstallDirectory, newGame.InstallDirectory, StringComparison.OrdinalIgnoreCase) == false) { existingGame.InstallDirectory = newGame.InstallDirectory; existingGameUpdated = true; } } if (playtimeImportMode == PlaytimeImportMode.Always && newGame.Playtime > 0) { if (existingGame.Playtime != newGame.Playtime) { existingGame.Playtime = newGame.Playtime; existingGameUpdated = true; } // The LastActivity value of the newGame is only applied if newer than // the existing game, to prevent cases of DRM free games being launched without // the client or offline, which would prevent the date from being updated in the service if (newGame.LastActivity != null && (existingGame.LastActivity == null || newGame.LastActivity > existingGame.LastActivity)) { existingGame.LastActivity = newGame.LastActivity; existingGameUpdated = true; } if (updateCompletionStatus(existingGame, statusSettings)) { existingGameUpdated = true; } } if (!existingGame.IsInstalled && newGame.InstallSize != null && newGame.InstallSize > 0 && existingGame.InstallSize != newGame.InstallSize) { existingGame.InstallSize = newGame.InstallSize; existingGameUpdated = true; } if (existingGameUpdated) { Games.Update(existingGame); } } } return addedGames; } } } public List GetSortedFilterPresets() { if (!IsOpen) { return new List(); } var filterPresetsSettings = (FilterPresets as FilterPresetsCollection).GetSettings(); var sortingOrder = filterPresetsSettings.SortingOrder; var savedSortedList = FilterPresets .Where(x => sortingOrder.Contains(x.Id)) .OrderBy(x => sortingOrder.IndexOf(x.Id)) .ToList(); var unsavedSortedList = FilterPresets .Where(x => !sortingOrder.Contains(x.Id)) .OrderBy(x => x.Name); savedSortedList.AddRange(unsavedSortedList); return savedSortedList; } public FilterPresetsSettings GetFilterPresetsSettings() { return (FilterPresets as FilterPresetsCollection).GetSettings(); } public void SetFilterPresetsSettings(FilterPresetsSettings settings) { (FilterPresets as FilterPresetsCollection).SetSettings(settings); } public CompletionStatusSettings GetCompletionStatusSettings() { return (CompletionStatuses as CompletionStatusesCollection).GetSettings(); } public void SetCompletionStatusSettings(CompletionStatusSettings settings) { (CompletionStatuses as CompletionStatusesCollection).SetSettings(settings); } public GameScannersSettings GetGameScannersSettings() { return (GameScanners as GameScannersCollection).GetSettings(); } public void SetGameScannersSettings(GameScannersSettings settings) { (GameScanners as GameScannersCollection).SetSettings(settings); } public static void GenerateSampleData(IGameDatabase database) { database.Platforms.Add("Windows"); database.AgeRatings.Add("18+"); database.Categories.Add("Category"); database.Companies.Add("BioWare"); database.Companies.Add("LucasArts"); database.Genres.Add("RPG"); database.Regions.Add("EU"); database.Series.Add("Star Wars"); database.Sources.Add("Retails"); database.Tags.Add("Star Wars"); database.Features.Add("Single Player"); var designGame = new Game($"Star Wars: Knights of the Old Republic") { ReleaseDate = new ReleaseDate(2009, 9, 5), PlatformIds = new List { database.Platforms.First().Id }, PlayCount = 20, Playtime = 115200, LastActivity = DateTime.Today, IsInstalled = true, AgeRatingIds = new List { database.AgeRatings.First().Id }, CategoryIds = new List { database.Categories.First().Id }, DeveloperIds = new List { database.Companies.First().Id }, PublisherIds = new List { database.Companies.Last().Id }, GenreIds = new List { database.Genres.First().Id }, RegionIds = new List { database.Regions.First().Id }, SeriesIds = new List { database.Series.First().Id }, SourceId = database.Sources.First().Id, TagIds = new List { database.Tags.First().Id }, FeatureIds = new List { database.Features.First().Id }, Description = "Star Wars: Knights of the Old Republic (often abbreviated as KotOR) is the first installment in the Knights of the Old Republic series. KotOR is the first computer role-playing game set in the Star Wars universe.", Version = "1.2", CommunityScore = 95, CriticScore = 50, UserScore = 15, Links = new ObservableCollection { new Link("Wiki", ""), new Link("HomePage", "") } }; database.Games.Add(designGame); } private object GetFileLock(string filePath) { if (fileLocks.TryGetValue(filePath, out object fileLock)) { return fileLock; } else { var lc = new object(); fileLocks.TryAdd(filePath, lc); return lc; } } private void ReleaseFileLock(string filePath) { fileLocks.TryRemove(filePath, out var removed); } public HashSet GetImportedRomFiles(string emulatorDir) { var importedRoms = new HashSet(); foreach (var game in Games.Where(a => a.Roms.HasItems())) { try { foreach (var rom in game.Roms) { if (rom.Path.IsNullOrWhiteSpace()) { continue; } var path = game.ExpandVariables(rom.Path, true, emulatorDir).ToLowerInvariant(); string absPath = null; try { absPath = Path.GetFullPath(path); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get absolute ROM path:\n{rom.Path}\n{path}"); } if (!absPath.IsNullOrEmpty()) { importedRoms.Add(absPath); } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to get roms from a game."); logger.Debug(Serialization.ToJson(game.Roms)); } } return importedRoms; } public HashSet GetImportedExeFiles() { var imported = new HashSet(); foreach (var game in Games.Where(a => a.IsInstalled && a.GameActions.HasItems())) { // Since path can be defined in various partial forms between cation field, // we are going to support only format that's automtically generated by scan folder process. foreach (var action in game.GameActions.Where(a => a.Type == GameActionType.File && !a.Path.IsNullOrWhiteSpace())) { imported.Add(game.ExpandVariables(action.Path, true) + action.Arguments ?? string.Empty); } } return imported; } public void SetAsSingletonInstance() { if (Instance != null) { throw new Exception("Database singleton intance already exists."); } Instance = this; } } } ================================================ FILE: source/Playnite/Database/GameDatabaseMigration.cs ================================================ using LiteDB; using Newtonsoft.Json.Linq; using Playnite.Common; using Playnite.Database.OldModels; using Playnite.Emulators; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using SdkModels = Playnite.SDK.Models; namespace Playnite.Database { public partial class GameDatabase { public static void MigrateNewDatabaseFormat(string databasePath) { var dbSettings = GetSettingsFromDbPath(databasePath); var gamesDir = Path.Combine(databasePath, gamesDirName); // 1 to 2 if (dbSettings.Version == 1 && NewFormatVersion > 1) { void convetList(Dictionary game, string origKey, string newKey, Dictionary convertedList) where T : Ver2_DatabaseObject { if (game.TryGetValue(origKey, out var storedObj)) { if (storedObj == null) { return; } var gameObjs = new List(); var oldLIst = (storedObj as JArray).ToObject>(); foreach (var oldObj in oldLIst) { if (string.IsNullOrEmpty(oldObj)) { continue; } if (convertedList.TryGetValue(oldObj, out var curObj)) { if (!gameObjs.Contains(curObj.Id)) { gameObjs.Add(curObj.Id); } } else { var newObj = typeof(T).CrateInstance(oldObj); gameObjs.Add(newObj.Id); convertedList.Add(oldObj, newObj); } } game.Remove(origKey); game[newKey] = gameObjs; } } void covertObject(Dictionary game, string origKey, string newKey, Dictionary convertedList) where T : Ver2_DatabaseObject { if (game.TryGetValue(origKey, out var storedObj)) { var oldObj = storedObj as string; if (!string.IsNullOrEmpty(oldObj)) { if (convertedList.TryGetValue(oldObj, out var curObj)) { game[newKey] = curObj.Id; } else { var newObj = typeof(T).CrateInstance(oldObj); game[newKey] = newObj.Id; convertedList.Add(oldObj, newObj); } } game.Remove(origKey); } } void saveCollection(Dictionary collection, string collPath) where T : Ver2_DatabaseObject { if (collection.Any()) { foreach (var item in collection.Values) { FileSystem.WriteStringToFileSafe(Path.Combine(collPath, item.Id + ".json"), Serialization.ToJson(item)); } } } var allGenres = new Dictionary(StringComparer.CurrentCultureIgnoreCase); var allCompanies = new Dictionary(StringComparer.CurrentCultureIgnoreCase); var allTags = new Dictionary(StringComparer.CurrentCultureIgnoreCase); var allCategories = new Dictionary(StringComparer.CurrentCultureIgnoreCase); var allSeries = new Dictionary(StringComparer.CurrentCultureIgnoreCase); var allRatings = new Dictionary(StringComparer.CurrentCultureIgnoreCase); var allRegions = new Dictionary(StringComparer.CurrentCultureIgnoreCase); var allSources = new Dictionary(StringComparer.CurrentCultureIgnoreCase); // Convert following object to Id representations and store them in separete lists: foreach (var file in Directory.EnumerateFiles(gamesDir, "*.json")) { var game = Serialization.FromJson>(FileSystem.ReadFileAsStringSafe(file)); if (game == null) { // Some users have 0 sized game files for uknown reason. File.Delete(file); continue; } // Genres convetList(game, nameof(OldModels.NewVer1.OldGame.Genres), nameof(Ver2_Game.GenreIds), allGenres); // Developers convetList(game, nameof(OldModels.NewVer1.OldGame.Developers), nameof(Ver2_Game.DeveloperIds), allCompanies); // Publishers convetList(game, nameof(OldModels.NewVer1.OldGame.Publishers), nameof(Ver2_Game.PublisherIds), allCompanies); // Tags convetList(game, nameof(OldModels.NewVer1.OldGame.Tags), nameof(Ver2_Game.TagIds), allTags); // Categories convetList(game, nameof(OldModels.NewVer1.OldGame.Categories), nameof(Ver2_Game.CategoryIds), allCategories); // Series covertObject(game, nameof(OldModels.NewVer1.OldGame.Series), nameof(Ver2_Game.SeriesId), allSeries); // AgeRating covertObject(game, nameof(OldModels.NewVer1.OldGame.AgeRating), nameof(Ver2_Game.AgeRatingId), allRatings); // Region covertObject(game, nameof(OldModels.NewVer1.OldGame.Region), nameof(Ver2_Game.RegionId), allRegions); // Source covertObject(game, nameof(OldModels.NewVer1.OldGame.Source), nameof(Ver2_Game.SourceId), allSources); FileSystem.WriteStringToFileSafe(file, Serialization.ToJson(game)); } saveCollection(allGenres, Path.Combine(databasePath, genresDirName)); saveCollection(allCompanies, Path.Combine(databasePath, companiesDirName)); saveCollection(allTags, Path.Combine(databasePath, tagsDirName)); saveCollection(allCategories, Path.Combine(databasePath, categoriesDirName)); saveCollection(allSeries, Path.Combine(databasePath, seriesDirName)); saveCollection(allRatings, Path.Combine(databasePath, ageRatingsDirName)); saveCollection(allRegions, Path.Combine(databasePath, regionsDirName)); saveCollection(allSources, Path.Combine(databasePath, sourcesDirName)); dbSettings.Version = 2; SaveSettingsToDbPath(dbSettings, databasePath); } // 2 to 3 if (dbSettings.Version == 2 && NewFormatVersion > 2) { var mapper = new BsonMapper() { SerializeNullValues = false, TrimWhitespace = false, EmptyStringToNull = true, IncludeFields = false, IncludeNonPublic = false }; void convertList(string dir, Action propertyMapper = null) where TOld : Ver2_DatabaseObject where TNew : DatabaseObject { var dbFile = dir + ".db"; if (File.Exists(dbFile)) { logger.Warn($"Migration database file {dbFile} already exists!"); File.Delete(dbFile); } using (var db = new LiteDatabase($"Filename={dbFile};Mode=Exclusive;Journal=false", mapper)) { var col = db.GetCollection(); col.EnsureIndex(a => a.Id, true); foreach (var file in Directory.GetFiles(dir, "*.json")) { if (Guid.TryParse(Path.GetFileNameWithoutExtension(file), out _)) { TOld oldItem = null; try { oldItem = Serialization.FromJsonFile(file); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to load old database file {file}."); continue; } if (oldItem == null) { logger.Warn($"Failed to load old database file {file}, it's empty."); continue; } // No idea how these get created, most likely by some 3rd party extension. // They cause several issues so don't migrate them. if (oldItem.Name.IsNullOrWhiteSpace()) { logger.Warn($"Failed to load old database file {file}, has empty name."); continue; } var newItem = typeof(TNew).CrateInstance(oldItem.Name); newItem.Id = oldItem.Id; propertyMapper?.Invoke(oldItem, newItem); col.Insert(newItem); } } } } LiteDatabase createDb(string dir) where T : DatabaseObject { var dbFile = dir + ".db"; if (File.Exists(dbFile)) { logger.Warn($"Migration database file {dbFile} already exists!"); File.Delete(dbFile); } var db = new LiteDatabase($"Filename={dbFile};Mode=Exclusive;Journal=false", mapper); var col = db.GetCollection(); col.EnsureIndex(a => a.Id, true); return db; } // Convert completion statuses CompletionStatusesCollection.MapLiteDbEntities(mapper); var convertedCompStatuses = new List(); using (var db = createDb(Path.Combine(databasePath, completionStatusesDirName))) { var col = db.GetCollection(); foreach (Ver2_CompletionStatus value in Enum.GetValues(typeof(Ver2_CompletionStatus))) { var newStatus = new CompletionStatus(value.GetDescription()); convertedCompStatuses.Add(newStatus); col.Insert(newStatus); } var setCol = db.GetCollection(); setCol.Insert(new CompletionStatusSettings { DefaultStatus = convertedCompStatuses[0].Id, PlayedStatus = convertedCompStatuses[1].Id }); } // Generate default filter presets FilterPresetsCollection.MapLiteDbEntities(mapper); using (var db = createDb(Path.Combine(databasePath, filterPresetsDirName))) { var col = db.GetCollection(); col.InsertBulk(new List { new FilterPreset { Name = "All", ShowInFullscreeQuickSelection = true, GroupingOrder = GroupableField.None, SortingOrder = SortOrder.Name, SortingOrderDirection = SortOrderDirection.Ascending, Settings = new FilterPresetSettings() }, new FilterPreset { Name = "Recently Played", ShowInFullscreeQuickSelection = true, GroupingOrder = GroupableField.None, SortingOrder = SortOrder.LastActivity, SortingOrderDirection = SortOrderDirection.Descending, Settings = new FilterPresetSettings { IsInstalled = true } }, new FilterPreset { Name = "Favorites", ShowInFullscreeQuickSelection = true, GroupingOrder = GroupableField.None, SortingOrder = SortOrder.Name, SortingOrderDirection = SortOrderDirection.Ascending, Settings = new FilterPresetSettings { Favorite = true } }, new FilterPreset { Name = "Most Played", ShowInFullscreeQuickSelection = true, GroupingOrder = GroupableField.None, SortingOrder = SortOrder.Playtime, SortingOrderDirection = SortOrderDirection.Descending, Settings = new FilterPresetSettings() } }); } // Convert import exclusion list ImportExclusionsCollection.MapLiteDbEntities(mapper); using (var db = createDb(Path.Combine(databasePath, importExclusionsDirName))) { var listPath = Path.Combine(PlaynitePaths.ConfigRootPath, "exclusionList.json"); if (File.Exists(listPath)) { Ver2_ImportExclusionList exclusionList = null; try { exclusionList = Serialization.FromJsonFile(listPath); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to load old exclusion list."); } if (exclusionList != null) { var col = db.GetCollection(); col.Insert(exclusionList.Items.Select(a => new ImportExclusionItem(a.GameId, a.GameName, a.LibraryId, a.LibraryName))); File.Delete(listPath); } } } var dirToConvert = new string[] { "games", "platforms", "emulators", "genres", "companies", "tags", "features", "categories", "series", "ageratings", "regions", "sources", "tools" }; foreach (var dir in Directory.GetDirectories(databasePath)) { switch (Path.GetFileName(dir)) { case "games": GameAction convertAction(Ver2_GameAction oldAction) { var newAction = new GameAction { Name = oldAction.Name, Type = (GameActionType)oldAction.Type, AdditionalArguments = oldAction.AdditionalArguments, Arguments = oldAction.Arguments, EmulatorId = oldAction.EmulatorId, OverrideDefaultArgs = oldAction.OverrideDefaultArgs, Path = oldAction.Path, WorkingDir = oldAction.WorkingDir }; if (oldAction.EmulatorProfileId != Guid.Empty) { newAction.EmulatorProfileId = CustomEmulatorProfile.ProfilePrefix + oldAction.EmulatorProfileId; } return newAction; } string convertScript(string source, Ver2_ScriptLanguage runtime) { if (source.IsNullOrWhiteSpace()) { return null; } if (runtime == Ver2_ScriptLanguage.PowerShell) { return source; } else if (runtime == Ver2_ScriptLanguage.Batch) { source = "$scriptPath = (Join-Path $env:TEMP 'playniteScript.bat')\n@\"\n" + source + "\n\"@ | Out-File $scriptPath -Encoding ascii\n"; source = source + "Start-Process \"cmd.exe\" \"/c $scriptPath\" -Wait"; source = "# Batch support has been removed in Playnite 9\n# This conversion was automatically generated\n" + source; return source; } else { source = "throw \"IronPython support has been removed in Playnite 9\"\n" + source; source = source.Replace("\n", "\n#"); return source; } } GamesCollection.MapLiteDbEntities(mapper); convertList(dir, (oldGame, newGame) => { newGame.BackgroundImage = oldGame.BackgroundImage; newGame.Description = oldGame.Description; newGame.Notes = oldGame.Notes; newGame.GenreIds = oldGame.GenreIds; newGame.Hidden = oldGame.Hidden; newGame.Favorite = oldGame.Favorite; newGame.Icon = oldGame.Icon; newGame.CoverImage = oldGame.CoverImage; newGame.InstallDirectory = oldGame.InstallDirectory; newGame.LastActivity = oldGame.LastActivity; newGame.SortingName = oldGame.SortingName; newGame.GameId = oldGame.GameId; newGame.PluginId = oldGame.PluginId; newGame.PublisherIds = oldGame.PublisherIds; newGame.DeveloperIds = oldGame.DeveloperIds; newGame.CategoryIds = oldGame.CategoryIds; newGame.TagIds = oldGame.TagIds; newGame.FeatureIds = oldGame.FeatureIds; newGame.IsInstalled = oldGame.IsInstalled; newGame.Playtime = (ulong)oldGame.Playtime; newGame.Added = oldGame.Added; newGame.Modified = oldGame.Modified; newGame.PlayCount = (ulong)oldGame.PlayCount; newGame.Version = oldGame.Version; newGame.SourceId = oldGame.SourceId; newGame.UserScore = oldGame.UserScore; newGame.CommunityScore = oldGame.CommunityScore; newGame.CriticScore = oldGame.CriticScore; newGame.UseGlobalGameStartedScript = oldGame.UseGlobalGameStartedScript; newGame.UseGlobalPostScript = oldGame.UseGlobalPostScript; newGame.UseGlobalPreScript = oldGame.UseGlobalPreScript; newGame.Manual = oldGame.Manual; newGame.CompletionStatusId = convertedCompStatuses[(int)oldGame.CompletionStatus].Id; newGame.PreScript = convertScript(oldGame.PreScript, oldGame.ActionsScriptLanguage); newGame.PostScript = convertScript(oldGame.PostScript, oldGame.ActionsScriptLanguage); newGame.GameStartedScript = convertScript(oldGame.GameStartedScript, oldGame.ActionsScriptLanguage); var allActions = new List(); if (oldGame.PlayAction != null) { newGame.IncludeLibraryPluginAction = oldGame.PlayAction.IsHandledByPlugin; if (!oldGame.PlayAction.IsHandledByPlugin) { var playAction = convertAction(oldGame.PlayAction); playAction.Name = "Play"; playAction.IsPlayAction = true; allActions.Add(playAction); } } oldGame.OtherActions?.Where(a => a != null).ForEach(a => allActions.Add(convertAction(a))); if (allActions.HasItems()) { newGame.GameActions = allActions.ToObservable(); } if (!oldGame.GameImagePath.IsNullOrEmpty()) { string romName = null; try { romName = Path.GetFileNameWithoutExtension(oldGame.GameImagePath); } catch (Exception e) { // This sometimes crashes on weird ROM paths logger.Error(e, $"Failed to get rom name from {oldGame.GameImagePath}"); } newGame.Roms = new ObservableCollection { new GameRom(romName ?? oldGame.Name, oldGame.GameImagePath) }; } if (oldGame.ReleaseDate != null) { newGame.ReleaseDate = new ReleaseDate(oldGame.ReleaseDate.Value); } if (oldGame.PlatformId != Guid.Empty) { newGame.PlatformIds = new List { oldGame.PlatformId }; } if (oldGame.SeriesId != Guid.Empty) { newGame.SeriesIds = new List { oldGame.SeriesId }; } if (oldGame.AgeRatingId != Guid.Empty) { newGame.AgeRatingIds = new List { oldGame.AgeRatingId }; } if (oldGame.RegionId != Guid.Empty) { newGame.RegionIds = new List { oldGame.RegionId }; } if (oldGame.Links.HasItems()) { newGame.Links = oldGame.Links.Where(a => a != null).Select(a => new Link(a.Name, a.Url)).ToObservable(); } }); break; case "platforms": PlatformsCollection.MapLiteDbEntities(mapper); convertList(dir, (oldPlat, newPlat) => { newPlat.Icon = oldPlat.Icon; newPlat.Cover = oldPlat.Cover; newPlat.Background = oldPlat.Background; if (newPlat.Name == "PC") { newPlat.Name = "PC (Windows)"; } else if (newPlat.Name == "DOS") { newPlat.Name = "PC (DOS)"; } var platSpec = Emulation.Platforms.FirstOrDefault(a => a.Name.Equals(newPlat.Name, StringComparison.OrdinalIgnoreCase)); if (platSpec != null) { newPlat.SpecificationId = platSpec.Id; } }); break; case "emulators": EmulatorsCollection.MapLiteDbEntities(mapper); convertList(dir, (oldEmu, newEmu) => { if (!oldEmu.Profiles.HasItems()) { return; } newEmu.CustomProfiles = new ObservableCollection(); foreach (var oldProfile in oldEmu.Profiles) { newEmu.CustomProfiles.Add(new CustomEmulatorProfile { Id = CustomEmulatorProfile.ProfilePrefix + oldProfile.Id, Name = oldProfile.Name, Platforms = oldProfile.Platforms, ImageExtensions = oldProfile.ImageExtensions, Executable = oldProfile.Executable, Arguments = oldProfile.Arguments, WorkingDirectory = oldProfile.WorkingDirectory }); } if (!newEmu.CustomProfiles[0].WorkingDirectory.IsNullOrEmpty()) { newEmu.InstallDir = newEmu.CustomProfiles[0].WorkingDirectory; } }); break; case "genres": GenresCollection.MapLiteDbEntities(mapper); convertList(dir); break; case "companies": CompaniesCollection.MapLiteDbEntities(mapper); convertList(dir); break; case "tags": TagsCollection.MapLiteDbEntities(mapper); convertList(dir); break; case "features": FeaturesCollection.MapLiteDbEntities(mapper); convertList(dir); break; case "categories": CategoriesCollection.MapLiteDbEntities(mapper); convertList(dir); break; case "series": SeriesCollection.MapLiteDbEntities(mapper); convertList(dir); break; case "ageratings": AgeRatingsCollection.MapLiteDbEntities(mapper); convertList(dir); break; case "regions": RegionsCollection.MapLiteDbEntities(mapper); convertList(dir, (oldRegion, newRegion) => { var regSpec = Emulation.Regions.FirstOrDefault(a => a.Name.Equals(newRegion.Name, StringComparison.OrdinalIgnoreCase)); if (regSpec != null) { newRegion.SpecificationId = regSpec.Id; } }); break; case "sources": GamesSourcesCollection.MapLiteDbEntities(mapper); convertList(dir); break; case "tools": AppSoftwareCollection.MapLiteDbEntities(mapper); convertList(dir, (oldApp, newApp) => { newApp.Arguments = oldApp.Arguments; newApp.Icon = oldApp.Icon; newApp.Path = oldApp.Path; newApp.WorkingDir = oldApp.WorkingDir; }); break; } } foreach (var dir in Directory.GetDirectories(databasePath)) { if (!dirToConvert.Contains(Path.GetFileName(dir))) { continue; } try { Directory.Delete(dir, true); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to delete old database files."); } } dbSettings.Version = 3; SaveSettingsToDbPath(dbSettings, databasePath); } // 3 to 4 // No data format change, only to cleanup mess caused by bug #2618 if (dbSettings.Version == 3 && NewFormatVersion > 3) { var filesDir = Path.Combine(databasePath, filesDirName); if (Directory.Exists(filesDir)) { foreach (var dir in Directory.GetDirectories(filesDir)) { try { Directory.GetFiles(dir, "*.exe").ForEach(a => { // Only delete files named as guid as those are 99% made by 2618 bug // People sometimes put foreign files into libary folder :|, so we don't want to delete something else. if (Guid.TryParse(Path.GetFileNameWithoutExtension(a), out var _)) { File.Delete(a); } }); } catch (Exception e) { logger.Error(e, "Failed to delete file."); } } } dbSettings.Version = 4; SaveSettingsToDbPath(dbSettings, databasePath); } } public static bool GetMigrationRequired(string databasePath) { if (string.IsNullOrEmpty(databasePath)) { throw new ArgumentNullException(nameof(databasePath)); } var fullPath = GetFullDbPath(databasePath); var settingsPath = Path.Combine(fullPath, "database.json"); if (!File.Exists(settingsPath)) { return false; } var st = Serialization.FromJson(FileSystem.ReadFileAsStringSafe(settingsPath)); if (st == null) { // This shouldn't in theory happen, but there are some wierd crash reports available for this. return false; } else { return st.Version < NewFormatVersion; } } } } ================================================ FILE: source/Playnite/Database/GameDatabase_Filters.cs ================================================ using Playnite.SDK.Models; using Playnite.ViewModels; using System; using System.Collections.Generic; using System.Linq; namespace Playnite.Database { public partial class GameDatabase : IGameDatabaseMain, IDisposable { public bool GetGameMatchesFilter(Game game, FilterPresetSettings filterSettings) { return GetGameMatchesFilter(game, FilterSettings.FromSdkFilterSettings(filterSettings), false); } public IEnumerable GetFilteredGames(FilterPresetSettings filterSettings) { return GetFilteredGames(FilterSettings.FromSdkFilterSettings(filterSettings), false); } public bool GetGameMatchesFilter(Game game, FilterPresetSettings filterSettings, bool useFuzzyNameMatch) { return GetGameMatchesFilter(game, FilterSettings.FromSdkFilterSettings(filterSettings), useFuzzyNameMatch); } public IEnumerable GetFilteredGames(FilterPresetSettings filterSettings, bool useFuzzyNameMatch) { return GetFilteredGames(FilterSettings.FromSdkFilterSettings(filterSettings), useFuzzyNameMatch); } public bool GetGameMatchesFilter(Game game, FilterSettings filterSettings, bool useFuzzyNameMatch) { return new FilterMatcher(filterSettings, useFuzzyNameMatch).Match(game); } public IEnumerable GetFilteredGames(FilterSettings filterSettings, bool useFuzzyNameMatch) { var fm = new FilterMatcher(filterSettings, useFuzzyNameMatch); foreach (var game in Games) { if (fm.Match(game)) { yield return game; } } } } internal class FilterMatcher { private readonly FilterSettings filterSettings; private readonly bool useFuzzyNameMatch; public FilterMatcher(FilterSettings filterSettings, bool useFuzzyNameMatch) { this.filterSettings = filterSettings; this.useFuzzyNameMatch = useFuzzyNameMatch; } public bool Match(Game game) { if (!MatchInstallStatus(game)) return false; if (!MatchFavorite(game)) return false; if (!MatchHidden(game)) return false; if (!MatchLibrary(game)) return false; if (!MatchName(game)) return false; if (!MatchReleaseYear(game)) return false; if (!MatchPlaytime(game)) return false; if (!MatchInstallSize(game)) return false; if (!MatchVersion(game)) return false; if (!MatchCompletionStatus(game)) return false; if (!MatchLastActivity(game)) return false; if (!MatchRecentActivity(game)) return false; if (!MatchDateAdded(game)) return false; if (!MatchDateModified(game)) return false; if (!MatchUserScore(game)) return false; if (!MatchCommunityScore(game)) return false; if (!MatchCriticScore(game)) return false; if (!MatchSeries(game)) return false; if (!MatchRegions(game)) return false; if (!MatchSource(game)) return false; if (!MatchAgeRatings(game)) return false; if (!MatchGenres(game)) return false; if (!MatchPlatforms(game)) return false; if (!MatchPublishers(game)) return false; if (!MatchDevelopers(game)) return false; if (!MatchCategories(game)) return false; if (!MatchTags(game)) return false; if (!MatchFeatures(game)) return false; return true; } private bool MatchInstallStatus(Game game) { if (filterSettings.IsInstalled == filterSettings.IsUnInstalled) return true; if (filterSettings.IsInstalled && game.IsInstalled) return true; if (filterSettings.IsUnInstalled && !game.IsInstalled) return true; return false; } private bool MatchFavorite(Game game) => !filterSettings.Favorite || (filterSettings.Favorite && game.Favorite); private bool MatchHidden(Game game) => filterSettings.Hidden == game.Hidden; private bool MatchLibrary(Game game) { if (filterSettings.Library?.IsSet != true) return true; return filterSettings.Library.Ids.Contains(game.PluginId); } private bool MatchName(Game game) { if (filterSettings.Name.IsNullOrEmpty()) { return true; } if (game.Name.IsNullOrEmpty()) { return false; } if (filterSettings.Name.Length >= 2 && filterSettings.Name[0] == '^') { return game.GetNameGroup() == filterSettings.Name[1]; } if (!useFuzzyNameMatch || filterSettings.Name[0] == '!') { return game.Name.IndexOf(filterSettings.Name.Substring(1), StringComparison.OrdinalIgnoreCase) >= 0; } return SearchViewModel.MatchTextFilter(filterSettings.Name, game.Name, true); } private bool MatchReleaseYear(Game game) { if (filterSettings.ReleaseYear?.IsSet != true) return true; if (game.ReleaseDate == null) { return filterSettings.ReleaseYear.Values.Contains(FilterSettings.MissingFieldString); } return filterSettings.ReleaseYear.Values.Contains(game.ReleaseYear.ToString()); } private bool MatchPlaytime(Game game) => MatchEnumField(filterSettings.PlayTime, (int)game.PlaytimeCategory); private bool MatchInstallSize(Game game) => MatchEnumField(filterSettings.InstallSize, (int)game.InstallSizeGroup); private bool MatchVersion(Game game) { if (filterSettings.Version.IsNullOrEmpty()) return true; return game.Version?.Contains(filterSettings.Version, StringComparison.OrdinalIgnoreCase) == true; } private bool MatchCompletionStatus(Game game) => IsFilterMatchingSingle(filterSettings.CompletionStatuses, game.CompletionStatusId, game.CompletionStatus); private bool MatchLastActivity(Game game) => MatchEnumField(filterSettings.LastActivity, (int)game.LastActivitySegment); private bool MatchRecentActivity(Game game) => MatchEnumField(filterSettings.RecentActivity, (int)game.RecentActivitySegment); private bool MatchDateAdded(Game game) => MatchEnumField(filterSettings.Added, (int)game.AddedSegment); private bool MatchDateModified(Game game) => MatchEnumField(filterSettings.Modified, (int)game.ModifiedSegment); private bool MatchUserScore(Game game) => IsScoreFilterMatching(filterSettings.UserScore, game.UserScoreGroup); private bool MatchCommunityScore(Game game) => IsScoreFilterMatching(filterSettings.CommunityScore, game.CommunityScoreGroup); private bool MatchCriticScore(Game game) => IsScoreFilterMatching(filterSettings.CriticScore, game.CriticScoreGroup); private bool MatchSeries(Game game) => IsFilterMatchingList(filterSettings.Series, game.SeriesIds, game.Series); private bool MatchRegions(Game game) => IsFilterMatchingList(filterSettings.Region, game.RegionIds, game.Regions); private bool MatchSource(Game game) => IsFilterMatchingSingle(filterSettings.Source, game.SourceId, game.Source); private bool MatchAgeRatings(Game game) => IsFilterMatchingList(filterSettings.AgeRating, game.AgeRatingIds, game.AgeRatings); private bool MatchGenres(Game game) => IsFilterMatchingList(filterSettings.Genre, game.GenreIds, game.Genres); private bool MatchPlatforms(Game game) => IsFilterMatchingList(filterSettings.Platform, game.PlatformIds, game.Platforms); private bool MatchPublishers(Game game) => IsFilterMatchingList(filterSettings.Publisher, game.PublisherIds, game.Publishers); private bool MatchDevelopers(Game game) => IsFilterMatchingList(filterSettings.Developer, game.DeveloperIds, game.Developers); private bool MatchCategories(Game game) => IsFilterMatchingList(filterSettings.Category, game.CategoryIds, game.Categories); private bool MatchTags(Game game) => IsFilterMatchingList(filterSettings.Tag, game.TagIds, game.Tags); private bool MatchFeatures(Game game) => IsFilterMatchingList(filterSettings.Feature, game.FeatureIds, game.Features); private static bool MatchEnumField(EnumFilterItemProperties enumFilter, int enumFieldValue) { if (enumFilter?.IsSet != true) return true; return enumFilter.Values.Contains(enumFieldValue); } /// /// Match a filter dropdown selection to a field that has 1 possible value (OR filtering logic) /// /// /// /// /// private static bool IsFilterMatchingSingle(IdItemFilterItemProperties filter, Guid idData, DatabaseObject objectData) { if (filter == null || !filter.IsSet) { return true; } if (!filter.Text.IsNullOrEmpty()) { if (objectData == null) { return false; } return filter.Texts.ContainsPartOfString(objectData.Name); } if (filter.Ids.HasItems()) { return filter.Ids.Contains(idData); } return true; } /// /// Match a filter dropdown selection to a field that has multiple possible values /// /// /// /// /// private bool IsFilterMatchingList(IdItemFilterItemProperties filter, List gamePropertyIds, IReadOnlyCollection gamePropertyObjects) { if (filter == null || !filter.IsSet) { return true; } if (!filter.Text.IsNullOrEmpty()) { if (gamePropertyObjects == null) { return false; } bool gameHasItemWithStringMatch(string t) { return gamePropertyObjects.Any(o => o.Name.Contains(t, StringComparison.InvariantCultureIgnoreCase)); } if (filterSettings.UseAndFilteringStyle) { return filter.Texts.All(gameHasItemWithStringMatch); } else { return filter.Texts.Any(gameHasItemWithStringMatch); } } if (filter.Ids.HasItems()) { bool gameHasItemWithIdMatch(Guid filterId) { if (filterId == Guid.Empty) { return gamePropertyIds == null || gamePropertyIds.Count == 0; } return gamePropertyIds?.Contains(filterId) == true; } if (filterSettings.UseAndFilteringStyle) { return filter.Ids.All(gameHasItemWithIdMatch); } else { return filter.Ids.Any(gameHasItemWithIdMatch); } } return true; } private static bool IsScoreFilterMatching(EnumFilterItemProperties filter, ScoreGroup score) { if (filter?.IsSet != true) return true; return filter.Values?.Contains((int)score) == true; } } } ================================================ FILE: source/Playnite/Database/GameFieldComparer.cs ================================================ using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Playnite.Database { public class GameFieldComparer : IEqualityComparer { private static readonly Regex regex = new Regex(@"[\s-]", RegexOptions.Compiled); public static readonly GameFieldComparer Instance = new GameFieldComparer(); public bool Equals(string x, string y) { return StringEquals(x, y); } public static bool StringEquals(string x, string y) { if (x.IsNullOrEmpty() && y.IsNullOrEmpty()) { return true; } if (!x.IsNullOrEmpty() && y.IsNullOrEmpty()) { return false; } if (x.IsNullOrEmpty() && !y.IsNullOrEmpty()) { return false; } return string.Equals( regex.Replace(x, ""), regex.Replace(y, ""), StringComparison.InvariantCultureIgnoreCase); } public static bool FieldEquals(T x, string y) where T : DatabaseObject { return StringEquals(x.Name, y); } public static bool FieldEquals(T x, T y) where T : DatabaseObject { return StringEquals(x.Name, y.Name); } public int GetHashCode(string obj) { return regex.Replace(obj, "").ToLower().GetHashCode(); } } } ================================================ FILE: source/Playnite/Database/InMemoryGameDatabase.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Drawing.Imaging; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Media.Imaging; namespace Playnite.Database { public class InMemoryItemCollection : ItemCollection where TItem : DatabaseObject { public InMemoryItemCollection() : base(null, false) { } } public class InMemoryGameDatabase : IGameDatabaseMain { public IItemCollection Games { get; } = new InMemoryItemCollection(); public IItemCollection Platforms { get; } = new InMemoryItemCollection(); public IItemCollection Emulators { get; } = new InMemoryItemCollection(); public IItemCollection Genres { get; } = new InMemoryItemCollection(); public IItemCollection Companies { get; } = new InMemoryItemCollection(); public IItemCollection Tags { get; } = new InMemoryItemCollection(); public IItemCollection Categories { get; } = new InMemoryItemCollection(); public IItemCollection Series { get; } = new InMemoryItemCollection(); public IItemCollection AgeRatings { get; } = new InMemoryItemCollection(); public IItemCollection Regions { get; } = new InMemoryItemCollection(); public IItemCollection Sources { get; } = new InMemoryItemCollection(); public IItemCollection Features { get; } = new InMemoryItemCollection(); public IItemCollection GameScanners { get; } = new InMemoryItemCollection(); public IItemCollection CompletionStatuses => new InMemoryItemCollection(); public IItemCollection ImportExclusions => new InMemoryItemCollection(); public IItemCollection FilterPresets => new InMemoryItemCollection(); public bool IsOpen => true; public AppSoftwareCollection SoftwareApps => throw new NotImplementedException(); public List UsedPlatforms => throw new NotImplementedException(); public List UsedGenres => throw new NotImplementedException(); public List UsedDevelopers => throw new NotImplementedException(); public List UsedPublishers => throw new NotImplementedException(); public List UsedTags => throw new NotImplementedException(); public List UsedCategories => throw new NotImplementedException(); public List UsedSeries => throw new NotImplementedException(); public List UsedAgeRatings => throw new NotImplementedException(); public List UsedRegions => throw new NotImplementedException(); public List UsedSources => throw new NotImplementedException(); public List UsedFeastures => throw new NotImplementedException(); public List UsedCompletionStatuses => throw new NotImplementedException(); #pragma warning disable CS0067 public event EventHandler DatabaseOpened; public event EventHandler DatabaseFileChanged; public event EventHandler PlatformsInUseUpdated; public event EventHandler GenresInUseUpdated; public event EventHandler DevelopersInUseUpdated; public event EventHandler PublishersInUseUpdated; public event EventHandler TagsInUseUpdated; public event EventHandler CategoriesInUseUpdated; public event EventHandler AgeRatingsInUseUpdated; public event EventHandler SeriesInUseUpdated; public event EventHandler RegionsInUseUpdated; public event EventHandler SourcesInUseUpdated; public event EventHandler FeaturesInUseUpdated; public event EventHandler CompletionStatusesInUseUpdated; #pragma warning restore CS0067 public InMemoryGameDatabase() { } public Game ImportGame(GameMetadata game) { throw new NotImplementedException(); } public Game ImportGame(GameMetadata game, LibraryPlugin sourcePlugin) { throw new NotImplementedException(); } public void SetDatabasePath(string path) { throw new NotImplementedException(); } public void OpenDatabase() { } public string GetFileStoragePath(Guid parentId) { throw new NotImplementedException(); } public string GetFullFilePath(string dbPath) { throw new NotImplementedException(); } public string AddFile(MetadataFile file, Guid parentId, bool isImage, CancellationToken cancelToken) { throw new NotImplementedException(); } public string AddFile(string path, Guid parentId, bool isImage, CancellationToken cancelToken) { throw new NotImplementedException(); } public void RemoveFile(string dbPath) { throw new NotImplementedException(); } public BitmapSource GetFileAsImage(string dbPath, BitmapLoadProperties loadProperties = null) { throw new NotImplementedException(); } public void CopyFile(string dbPath, string targetPath) { throw new NotImplementedException(); } public void BeginBufferUpdate() { throw new NotImplementedException(); } public void EndBufferUpdate() { throw new NotImplementedException(); } public IDisposable BufferedUpdate() { throw new NotImplementedException(); } public List ImportGames(LibraryPlugin library, CancellationToken cancelToken, PlaytimeImportMode playtimeImportMode) { throw new NotImplementedException(); } public List GetSortedFilterPresets() { throw new NotImplementedException(); } public FilterPresetsSettings GetFilterPresetsSettings() { throw new NotImplementedException(); } public void SetFilterPresetsSettings(FilterPresetsSettings settings) { throw new NotImplementedException(); } public CompletionStatusSettings GetCompletionStatusSettings() { throw new NotImplementedException(); } public void SetCompletionStatusSettings(CompletionStatusSettings settings) { throw new NotImplementedException(); } public GameScannersSettings GetGameScannersSettings() { throw new NotImplementedException(); } public void SetGameScannersSettings(GameScannersSettings settings) { throw new NotImplementedException(); } public HashSet GetImportedRomFiles(string emulatorDir) { throw new NotImplementedException(); } public bool GetGameMatchesFilter(Game game, FilterPresetSettings filterSettings, bool useFuzzyNameMatch) { return true; } public IEnumerable GetFilteredGames(FilterPresetSettings filterSettings) { yield break; } public IEnumerable GetFilteredGames(FilterPresetSettings filterSettings, bool useFuzzyNameMatch) { yield break; } public bool GetGameMatchesFilter(Game game, FilterSettings filterSettings, bool useFuzzyNameMatch) { return true; } public IEnumerable GetFilteredGames(FilterSettings filterSettings, bool useFuzzyNameMatch) { yield break; } public bool GetGameMatchesFilter(Game game, FilterPresetSettings filterSettings) { return true; } public HashSet GetImportedExeFiles() { throw new NotImplementedException(); } } } ================================================ FILE: source/Playnite/Database/OldModels/NewVer1/OldCompletionStatus.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database.OldModels.NewVer1 { /// /// Represents game completion status. /// public enum OldCompletionStatus { /// /// Game has not been played yet. /// NotPlayed, /// /// Game has been played. /// Played, /// /// Main storyline has been beaten. /// Beaten, /// /// Game has been fully completed. /// Completed } } ================================================ FILE: source/Playnite/Database/OldModels/NewVer1/OldDatabaseObject.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database.OldModels.NewVer1 { /// /// Represents base database object item. /// public class OldDatabaseObject : ObservableObject { /// /// Gets or sets identifier of database object. /// public Guid Id { get; set; } /// /// Creates new instance of . /// public OldDatabaseObject() { Id = Guid.NewGuid(); } } } ================================================ FILE: source/Playnite/Database/OldModels/NewVer1/OldGame.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Collections.Concurrent; using Newtonsoft.Json; namespace Playnite.Database.OldModels.NewVer1 { /// /// Represents Playnite game object. /// public class OldGame : OldDatabaseObject { private string backgroundImage; /// /// Gets or sets background image. Local file path, HTTP URL or database file ids are supported. /// public string BackgroundImage { get { return backgroundImage; } set { backgroundImage = value; OnPropertyChanged(); } } private string description; /// /// Gets or sets HTML game description. /// public string Description { get { return description; } set { description = value; OnPropertyChanged(); } } private ComparableList developers; /// /// Gets or sets list of developers. /// public ComparableList Developers { get { return developers; } set { developers = value; OnPropertyChanged(); } } private ComparableList genres; /// /// Gets or sets list of genres. /// public ComparableList Genres { get { return genres; } set { genres = value; OnPropertyChanged(); } } private bool hidden; /// /// Gets or sets value indicating if the game is hidden in library. /// public bool Hidden { get { return hidden; } set { hidden = value; OnPropertyChanged(); } } private bool favorite; /// /// Gets or sets avlue indicating if the game is marked as favorite in library. /// public bool Favorite { get { return favorite; } set { favorite = value; OnPropertyChanged(); } } private string icon; /// /// Gets or sets game icon. Local file path, HTTP URL or database file ids are supported. /// public string Icon { get { return icon; } set { icon = value; OnPropertyChanged(); } } private string coverImage; /// /// Gets or sets game cover image. Local file path, HTTP URL or database file ids are supported. /// public string CoverImage { get { return coverImage; } set { coverImage = value; OnPropertyChanged(); } } private string installDirectory; /// /// Gets or sets game installation directory path. /// public string InstallDirectory { get { if (string.IsNullOrEmpty(installDirectory)) { if (PlayAction != null) { return PlayAction.WorkingDir; } } return installDirectory; } set { installDirectory = value; OnPropertyChanged(); } } private string gameImagePath; /// /// Gets or sets game's ISO, ROM or other type of executable image path. /// public string GameImagePath { get { return gameImagePath; } set { gameImagePath = value; OnPropertyChanged(); } } private DateTime? lastActivity; /// /// Gets or sets last played date. /// public DateTime? LastActivity { get { return lastActivity; } set { lastActivity = value; OnPropertyChanged(); } } private string name; /// /// Gets or sets game name. /// public string Name { get { return name; } set { name = value; OnPropertyChanged(); } } private string sortingName; /// /// Gets or sets optional name used for sorting the game by name. /// public string SortingName { get { return sortingName; } set { sortingName = value; OnPropertyChanged(); } } private string gameId; /// /// Gets or sets provider id. For example game's Steam ID. /// public string GameId { get { return gameId; } set { gameId = value; OnPropertyChanged(); } } private Guid pluginId = Guid.Empty; /// /// Gets or sets id of plugin responsible for handling this game. /// public Guid PluginId { get { return pluginId; } set { pluginId = value; OnPropertyChanged(); } } private ObservableCollection otherActions; /// /// Gets or sets list of additional game actions. /// public ObservableCollection OtherActions { get { return otherActions; } set { otherActions = value; OnPropertyChanged(); } } private OldGameAction playAction; /// /// Gets or sets game action used to starting the game. /// public OldGameAction PlayAction { get { return playAction; } set { playAction = value; OnPropertyChanged(); } } private Guid platformId; /// /// Gets or sets platform id. /// public Guid PlatformId { get { return platformId; } set { platformId = value; OnPropertyChanged(); } } private ComparableList publishers; /// /// Gets or sets list of publishers. /// public ComparableList Publishers { get { return publishers; } set { publishers = value; OnPropertyChanged(); } } private DateTime? releaseDate; /// /// Gets or set game's release date. /// public DateTime? ReleaseDate { get { return releaseDate; } set { releaseDate = value; OnPropertyChanged(); } } private ComparableList categories; /// /// Gets or sets game categories. /// public ComparableList Categories { get { return categories; } set { categories = value; OnPropertyChanged(); } } private ComparableList tags; /// /// Gets or sets list of tags. /// public ComparableList Tags { get { return tags; } set { tags = value; OnPropertyChanged(); } } private ObservableCollection links; /// /// Gets or sets list of game related web links. /// public ObservableCollection Links { get { return links; } set { links = value; OnPropertyChanged(); } } private bool isInstalling; /// /// Gets or sets value indicating wheter a game is being installed.. /// public bool IsInstalling { get => isInstalling; set { isInstalling = value; OnPropertyChanged(); } } private bool isUninstalling; /// /// Gets or sets value indicating wheter a game is being uninstalled. /// public bool IsUninstalling { get => isUninstalling; set { isUninstalling = value; OnPropertyChanged(); } } private bool isLaunching; /// /// Gets or sets value indicating wheter a game is being launched. /// public bool IsLaunching { get => isLaunching; set { isLaunching = value; OnPropertyChanged(); } } private bool isRunning; /// /// Gets or sets value indicating wheter a game is currently running. /// public bool IsRunning { get => isRunning; set { isRunning = value; OnPropertyChanged(); } } private bool isInstalled; /// /// Gets or sets value indicating wheter a game is installed. /// public bool IsInstalled { get => isInstalled; set { isInstalled = value; OnPropertyChanged(); } } /// /// Gets value indicating wheter the game is custom game. /// [JsonIgnore] public bool IsCustomGame { get => PluginId == Guid.Empty; } private long playtime = 0; /// /// Gets or sets played time in seconds. /// public long Playtime { get { return playtime; } set { playtime = value; OnPropertyChanged(); } } private DateTime? added; /// /// Gets or sets date when game was added into library. /// public DateTime? Added { get { return added; } set { added = value; OnPropertyChanged(); } } private DateTime? modified; /// /// Gets or sets date of last modification made to a game. /// public DateTime? Modified { get { return modified; } set { modified = value; OnPropertyChanged(); } } private long playCount = 0; /// /// Gets or sets a number indicating how many times the game has been played. /// public long PlayCount { get { return playCount; } set { playCount = value; OnPropertyChanged(); } } private string series; /// /// Gets or sets game series. /// public string Series { get { return series; } set { series = value; OnPropertyChanged(); } } private string version; /// /// Gets or sets game version. /// public string Version { get { return version; } set { version = value; OnPropertyChanged(); } } private string ageRating; /// /// Gets or sets age rating for a game. /// public string AgeRating { get { return ageRating; } set { ageRating = value; OnPropertyChanged(); } } private string region; /// /// Gets or sets game region. /// public string Region { get { return region; } set { region = value; OnPropertyChanged(); } } private string source; /// /// Gets or sets source of the game. /// public string Source { get { return source; } set { source = value; OnPropertyChanged(); } } private OldCompletionStatus completionStatus = OldCompletionStatus.NotPlayed; /// /// Gets or sets game completion status. /// public OldCompletionStatus CompletionStatus { get { return completionStatus; } set { completionStatus = value; OnPropertyChanged(); } } private int? userScore = null; /// /// Gets or sets user's rating score. /// public int? UserScore { get { return userScore; } set { userScore = value; OnPropertyChanged(); } } private int? criticScore = null; /// /// Gets or sets critic based rating score. /// public int? CriticScore { get { return criticScore; } set { criticScore = value; OnPropertyChanged(); } } private int? communityScore = null; /// /// Gets or sets community rating score. /// public int? CommunityScore { get { return communityScore; } set { communityScore = value; OnPropertyChanged(); } } /// /// Creates new instance of a Game object. /// public OldGame() : base() { GameId = Guid.NewGuid().ToString(); } /// /// Creates new instance of a Game object with specific name. /// /// Game name. public OldGame(string name) : this() { Name = name; } /// /// /// /// public override string ToString() { return Name; } } } ================================================ FILE: source/Playnite/Database/OldModels/NewVer1/OldGameAction.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database.OldModels.NewVer1 { /// /// Represents game action type. /// public enum OldGameActionType : int { /// /// Game action executes a file. /// File = 0, /// /// Game action navigates to a web based URL. /// URL = 1, /// /// Game action starts an emulator. /// Emulator = 2 } /// /// Represents executable game action. /// public class OldGameAction : ObservableObject { private OldGameActionType type; /// /// Gets or sets task type. /// public OldGameActionType Type { get => type; set { type = value; OnPropertyChanged(); } } private string arguments; /// /// Gets or sets executable arguments for File type tasks. /// public string Arguments { get => arguments; set { arguments = value; OnPropertyChanged(); } } private string additionalArguments; /// /// Gets or sets additional executable arguments used for Emulator action type. /// public string AdditionalArguments { get => additionalArguments; set { additionalArguments = value; OnPropertyChanged(); } } private bool overrideDefaultArgs; /// /// Gets or sets value indicating wheter emulator arguments should be completely overwritten with action arguments. /// Applies only to Emulator action type. /// public bool OverrideDefaultArgs { get => overrideDefaultArgs; set { overrideDefaultArgs = value; OnPropertyChanged(); } } private string path; /// /// Gets or sets executable path for File action type or URL for URL action type. /// public string Path { get => path; set { path = value; OnPropertyChanged(); } } private string workingDir; /// /// Gets or sets working directory for File action type executable. /// public string WorkingDir { get => workingDir; set { workingDir = value; OnPropertyChanged(); } } private string name; /// /// Gets or sets action name. /// public string Name { get => name; set { name = value; OnPropertyChanged(); } } private bool isHandledByPlugin; /// /// Gets or sets value indicating wheter a action's execution should be handled by a plugin. /// public bool IsHandledByPlugin { get => isHandledByPlugin; set { isHandledByPlugin = value; OnPropertyChanged(); } } private Guid emulatorId; /// /// Gets or sets emulator id for Emulator action type execution. /// public Guid EmulatorId { get => emulatorId; set { emulatorId = value; OnPropertyChanged(); } } private Guid emulatorProfileId; /// /// Gets or sets emulator profile id for Emulator action type execution. /// public Guid EmulatorProfileId { get => emulatorProfileId; set { emulatorProfileId = value; OnPropertyChanged(); } } /// public override string ToString() { switch (Type) { case OldGameActionType.File: return $"File: {Path}, {Arguments}, {WorkingDir}"; case OldGameActionType.URL: return $"Url: {Path}"; case OldGameActionType.Emulator: return $"Emulator: {EmulatorId}, {EmulatorProfileId}, {OverrideDefaultArgs}, {AdditionalArguments}"; default: return Path; } } } } ================================================ FILE: source/Playnite/Database/OldModels/NewVer1/OldLink.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database.OldModels.NewVer1 { /// /// Represents web link. /// public class OldLink : ObservableObject { private string name; /// /// Gets or sets name of the link. /// public string Name { get => name; set { name = value; OnPropertyChanged(); } } private string url; /// /// Gets or sets web based URL. /// public string Url { get => url; set { url = value; OnPropertyChanged(); } } /// /// Creates new instance of Link. /// public OldLink() { } /// /// Creates new instance of Link with specific values. /// /// Link name. /// Link URL. public OldLink(string name, string url) { Name = name; Url = url; } } } ================================================ FILE: source/Playnite/Database/OldModels/Ver2.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Database.OldModels { public enum Ver2_ScriptLanguage { /// /// Represents PowerShell scripting language. /// PowerShell, /// /// Represents IronPython scripting language. /// IronPython, /// /// Represents Batch scripting language. /// [Description("Batch (.bat script)")] Batch } public class Ver2_ImportExclusionList { public List Items { get; set; } } public class Ver2_ImportExclusionItem { public string GameId { get; set; } public string GameName { get; set; } public Guid LibraryId { get; set; } public string LibraryName { get; set; } } public interface Ver2_IIdentifiable { /// /// Gets unique object identifier. /// Guid Id { get; } } public class Ver2_DatabaseObject : ObservableObject, IComparable, Ver2_IIdentifiable { /// /// Gets or sets identifier of database object. /// public Guid Id { get; set; } private string name; /// /// Gets or sets name. /// public string Name { get { return name; } set { name = value; OnPropertyChanged(); } } /// /// Creates new instance of . /// public Ver2_DatabaseObject() { Id = Guid.NewGuid(); } /// /// Compares Names of database object. /// /// /// public int CompareTo(object obj) { var objName = (obj as Ver2_DatabaseObject).Name; if (string.IsNullOrEmpty(Name) && string.IsNullOrEmpty(objName)) { return 0; } if (string.IsNullOrEmpty(Name)) { return 1; } if (string.IsNullOrEmpty(objName)) { return -1; } return string.Compare(Name, objName, true); } /// /// DO NOT use for actual equality check, this only checks if db Ids are equal! /// /// /// public override bool Equals(object obj) { if (obj is Ver2_DatabaseObject dbObj) { return dbObj.Id == Id; } else { return false; } } /// public override int GetHashCode() { if (Id == Guid.Empty) { return 0; } else { return Id.GetHashCode(); } } /// public override string ToString() { return Name ?? string.Empty; } } public class Ver2_AgeRating : Ver2_DatabaseObject { /// /// Creates new instance of . /// public Ver2_AgeRating() : base() { } /// /// Creates new instance of . /// /// Rating name. public Ver2_AgeRating(string name) : base() { Name = name; } /// /// Gets empty age rating. /// public static readonly Ver2_AgeRating Empty = new Ver2_AgeRating { Id = Guid.Empty, Name = string.Empty }; } public class Ver2_AppSoftware : Ver2_DatabaseObject { private string icon; /// /// Gets or sets application icon. /// public string Icon { get => icon; set { icon = value; OnPropertyChanged(); } } private string arguments; /// /// Gets or sets application arguments. /// public string Arguments { get => arguments; set { arguments = value; OnPropertyChanged(); } } private string path; /// /// Gets or sets application path. /// public string Path { get => path; set { path = value; OnPropertyChanged(); } } private string workingDir; /// /// Gets or sets application working directory. /// public string WorkingDir { get => workingDir; set { workingDir = value; OnPropertyChanged(); } } /// /// Creates new instance of . /// public Ver2_AppSoftware() : base() { } /// /// Creates new instance of . /// /// public Ver2_AppSoftware(string name) : base() { Name = name; } } public class Ver2_Category : Ver2_DatabaseObject { /// /// Creates new instance of . /// public Ver2_Category() : base() { } /// /// Creates new instance of . /// /// Category name. public Ver2_Category(string name) : base() { Name = name; } /// /// Gets empty category. /// public static readonly Ver2_Category Empty = new Ver2_Category { Id = Guid.Empty, Name = string.Empty }; } public class Ver2_Company : Ver2_DatabaseObject { /// /// Creates new instance of . /// public Ver2_Company() : base() { } /// /// Creates new instance of . /// /// public Ver2_Company(string name) : base() { Name = name; } /// /// /// public static readonly Ver2_Company Empty = new Ver2_Company { Id = Guid.Empty, Name = string.Empty }; } public class Ver2_Developer : Ver2_Company { /// /// Creates new instance of . /// public Ver2_Developer() : base() { } /// /// Creates new instance of . /// /// public Ver2_Developer(string name) : base() { } } public class Ver2_Publisher : Ver2_Company { /// /// Creates new instance of . /// public Ver2_Publisher() : base() { } /// /// Creates new instance of . /// /// public Ver2_Publisher(string name) : base() { } } public enum Ver2_CompletionStatus : int { [Description("Not Played")] NotPlayed = 0, /// /// Represents Played completion status. /// [Description("Played")] Played = 1, /// /// Represents Beaten completion status. /// [Description("Beaten")] Beaten = 2, /// /// Represents Completed completion status. /// [Description("Completed")] Completed = 3, /// /// Represents Playing completion status. /// [Description("Playing")] Playing = 4, /// /// Represents Abandoned completion status. /// [Description("Abandoned")] Abandoned = 5, /// /// Represents "On hold" completion status. /// [Description("On Hold")] OnHold = 6, /// /// Represents "Plan to Play" completion status. /// [Description("Plan to Play")] PlanToPlay = 7 } public class Ver2_EmulatorProfile : Ver2_DatabaseObject, IEquatable { private List platforms; /// /// Gets or sets platforms supported by profile. /// public List Platforms { get => platforms; set { platforms = value; OnPropertyChanged(); } } private List imageExtensions; /// /// Gets or sets file extension supported by profile. /// public List ImageExtensions { get => imageExtensions; set { imageExtensions = value; OnPropertyChanged(); } } private string executable; /// /// Gets or sets executable path used to launch emulator. /// public string Executable { get => executable; set { executable = value; OnPropertyChanged(); } } private string arguments; /// /// Gets or sets arguments for emulator executable. /// public string Arguments { get => arguments; set { arguments = value; OnPropertyChanged(); } } private string workingDirectory; /// /// Gets or sets working directory of emulator process. /// public string WorkingDirectory { get => workingDirectory; set { workingDirectory = value; OnPropertyChanged(); } } /// /// /// /// public override string ToString() { return Name; } /// /// Creates new instance of EmulatorProfile. /// public Ver2_EmulatorProfile() : base() { } /// public bool Equals(Ver2_EmulatorProfile other) { if (other is null) { return false; } if (!Platforms.IsListEqual(other.Platforms)) { return false; } if (!ImageExtensions.IsListEqual(other.ImageExtensions)) { return false; } if (!string.Equals(Executable, other.Executable, StringComparison.Ordinal)) { return false; } if (!string.Equals(Arguments, other.Arguments, StringComparison.Ordinal)) { return false; } if (!string.Equals(WorkingDirectory, other.WorkingDirectory, StringComparison.Ordinal)) { return false; } if (!string.Equals(Name, other.Name, StringComparison.Ordinal)) { return false; } return true; } } /// /// Represents system emulator. /// public class Ver2_Emulator : Ver2_DatabaseObject { private ObservableCollection profile; /// /// Gets or sets list of emulator profiles. /// public ObservableCollection Profiles { get => profile; set { profile = value; OnPropertyChanged(); } } /// /// Creates new instance of Emulator. /// public Ver2_Emulator() : base() { } /// /// Creates new instance of Emulator with specific name. /// /// Emulator name. public Ver2_Emulator(string name) : this() { Name = name; } /// /// /// /// public override string ToString() { return Name; } } public enum Ver2_GameField { /// BackgroundImage, /// Description, /// GenreIds, /// Hidden, /// Favorite, /// Icon, /// CoverImage, /// InstallDirectory, /// GameImagePath, /// LastActivity, /// SortingName, /// Gameid, /// PluginId, /// OtherActions, /// PlayAction, /// PlatformId, /// PublisherIds, /// DeveloperIds, /// ReleaseDate, /// CategoryIds, /// TagIds, /// Links, /// IsInstalling, /// IsUninstalling, /// IsLaunching, /// IsRunning, /// IsInstalled, /// IsCustomGame, /// Playtime, /// Added, /// Modified, /// PlayCount, /// SeriesId, /// Version, /// AgeRatingId, /// RegionId, /// SourceId, /// CompletionStatus, /// UserScore, /// CriticScore, /// CommunityScore, /// Genres, /// Developers, /// Publishers, /// Tags, /// Categories, /// Platform, /// Series, /// AgeRating, /// Region, /// Source, /// ReleaseYear, /// ActionsScriptLanguage, /// PreScript, /// PostScript, /// Name, /// Features, /// FeatureIds, /// UseGlobalPostScript, /// UseGlobalPreScript, /// UserScoreRating, /// CommunityScoreRating, /// CriticScoreRating, /// UserScoreGroup, /// CommunityScoreGroup, /// CriticScoreGroup, /// LastActivitySegment, /// AddedSegment, /// ModifiedSegment, /// PlaytimeCategory, /// InstallationStatus, /// None, /// GameStartedScript, /// UseGlobalGameStartedScript, /// Notes, /// Manual } /// /// Represents Playnite game object. /// public class Ver2_Game : Ver2_DatabaseObject { private string backgroundImage; /// /// Gets or sets background image. Local file path, HTTP URL or database file ids are supported. /// public string BackgroundImage { get { return backgroundImage; } set { backgroundImage = value; OnPropertyChanged(); } } private string description; /// /// Gets or sets HTML game description. /// [DefaultValue("")] public string Description { get { return description; } set { description = value; OnPropertyChanged(); } } private string notes; /// /// Gets or sets user notes. /// [DefaultValue("")] public string Notes { get { return notes; } set { notes = value; OnPropertyChanged(); } } private List genreIds; /// /// Gets or sets list of genres. /// public List GenreIds { get { return genreIds; } set { genreIds = value; OnPropertyChanged(); } } private bool hidden; /// /// Gets or sets value indicating if the game is hidden in library. /// public bool Hidden { get { return hidden; } set { hidden = value; OnPropertyChanged(); } } private bool favorite; /// /// Gets or sets avlue indicating if the game is marked as favorite in library. /// public bool Favorite { get { return favorite; } set { favorite = value; OnPropertyChanged(); } } private string icon; /// /// Gets or sets game icon. Local file path, HTTP URL or database file ids are supported. /// public string Icon { get { return icon; } set { icon = value; OnPropertyChanged(); } } private string coverImage; /// /// Gets or sets game cover image. Local file path, HTTP URL or database file ids are supported. /// public string CoverImage { get { return coverImage; } set { coverImage = value; OnPropertyChanged(); } } private string installDirectory; /// /// Gets or sets game installation directory path. /// public string InstallDirectory { get { if (string.IsNullOrEmpty(installDirectory)) { if (PlayAction != null) { return PlayAction.WorkingDir; } } return installDirectory; } set { installDirectory = value; OnPropertyChanged(); } } private string gameImagePath; /// /// Gets or sets game's ISO, ROM or other type of executable image path. /// public string GameImagePath { get { return gameImagePath; } set { gameImagePath = value; OnPropertyChanged(); } } private DateTime? lastActivity; /// /// Gets or sets last played date. /// public DateTime? LastActivity { get { return lastActivity; } set { lastActivity = value; OnPropertyChanged(); } } private string sortingName; /// /// Gets or sets optional name used for sorting the game by name. /// public string SortingName { get { return sortingName; } set { sortingName = value; OnPropertyChanged(); } } private string gameId; /// /// Gets or sets provider id. For example game's Steam ID. /// public string GameId { get { return gameId; } set { gameId = value; OnPropertyChanged(); } } private Guid pluginId = Guid.Empty; /// /// Gets or sets id of plugin responsible for handling this game. /// public Guid PluginId { get { return pluginId; } set { pluginId = value; OnPropertyChanged(); } } private ObservableCollection otherActions; /// /// Gets or sets list of additional game actions. /// public ObservableCollection OtherActions { get { return otherActions; } set { otherActions = value; OnPropertyChanged(); } } private Ver2_GameAction playAction; /// /// Gets or sets game action used to starting the game. /// public Ver2_GameAction PlayAction { get { return playAction; } set { playAction = value; OnPropertyChanged(); } } private Guid platformId; /// /// Gets or sets platform id. /// public Guid PlatformId { get { return platformId; } set { platformId = value; OnPropertyChanged(); OnPropertyChanged(nameof(Ver2_Platform)); } } private List publisherIds; /// /// Gets or sets list of publishers. /// public List PublisherIds { get { return publisherIds; } set { publisherIds = value; OnPropertyChanged(); } } private List developerIds; /// /// Gets or sets list of developers. /// public List DeveloperIds { get { return developerIds; } set { developerIds = value; OnPropertyChanged(); } } private DateTime? releaseDate; /// /// Gets or set game's release date. /// public DateTime? ReleaseDate { get { return releaseDate; } set { releaseDate = value; OnPropertyChanged(); } } private List categoryIds; /// /// Gets or sets game categories. /// public List CategoryIds { get { return categoryIds; } set { categoryIds = value; OnPropertyChanged(); } } private List tagIds; /// /// Gets or sets list of tags. /// public List TagIds { get { return tagIds; } set { tagIds = value; OnPropertyChanged(); } } private List featureIds; /// /// Gets or sets list of game features. /// public List FeatureIds { get { return featureIds; } set { featureIds = value; OnPropertyChanged(); } } private ObservableCollection links; /// /// Gets or sets list of game related web links. /// public ObservableCollection Links { get { return links; } set { links = value; OnPropertyChanged(); } } private bool isInstalling; /// /// Gets or sets value indicating wheter a game is being installed.. /// public bool IsInstalling { get => isInstalling; set { isInstalling = value; OnPropertyChanged(); } } private bool isUninstalling; /// /// Gets or sets value indicating wheter a game is being uninstalled. /// public bool IsUninstalling { get => isUninstalling; set { isUninstalling = value; OnPropertyChanged(); } } private bool isLaunching; /// /// Gets or sets value indicating wheter a game is being launched. /// public bool IsLaunching { get => isLaunching; set { isLaunching = value; OnPropertyChanged(); } } private bool isRunning; /// /// Gets or sets value indicating wheter a game is currently running. /// public bool IsRunning { get => isRunning; set { isRunning = value; OnPropertyChanged(); } } private bool isInstalled; /// /// Gets or sets value indicating wheter a game is installed. /// public bool IsInstalled { get => isInstalled; set { isInstalled = value; OnPropertyChanged(); OnPropertyChanged(nameof(Ver2_InstallationStatus)); } } private long playtime = 0; /// /// Gets or sets played time in seconds. /// public long Playtime { get { return playtime; } set { playtime = value; OnPropertyChanged(); OnPropertyChanged(nameof(Ver2_PlaytimeCategory)); } } private DateTime? added; /// /// Gets or sets date when game was added into library. /// public DateTime? Added { get { return added; } set { added = value; OnPropertyChanged(); } } private DateTime? modified; /// /// Gets or sets date of last modification made to a game. /// public DateTime? Modified { get { return modified; } set { modified = value; OnPropertyChanged(); } } private long playCount = 0; /// /// Gets or sets a number indicating how many times the game has been played. /// public long PlayCount { get { return playCount; } set { playCount = value; OnPropertyChanged(); } } private Guid seriesId; /// /// Gets or sets game series. /// public Guid SeriesId { get { return seriesId; } set { seriesId = value; OnPropertyChanged(); OnPropertyChanged(nameof(Ver2_Series)); } } private string version; /// /// Gets or sets game version. /// public string Version { get { return version; } set { version = value; OnPropertyChanged(); } } private Guid ageRatingId; /// /// Gets or sets age rating for a game. /// public Guid AgeRatingId { get { return ageRatingId; } set { ageRatingId = value; OnPropertyChanged(); } } private Guid regionId; /// /// Gets or sets game region. /// public Guid RegionId { get { return regionId; } set { regionId = value; OnPropertyChanged(); OnPropertyChanged(nameof(Ver2_Region)); } } private Guid sourceId; /// /// Gets or sets source of the game. /// public Guid SourceId { get { return sourceId; } set { sourceId = value; OnPropertyChanged(); } } private Ver2_CompletionStatus completionStatus = Ver2_CompletionStatus.NotPlayed; /// /// Gets or sets game completion status. /// public Ver2_CompletionStatus CompletionStatus { get { return completionStatus; } set { completionStatus = value; OnPropertyChanged(); } } private int? userScore = null; /// /// Gets or sets user's rating score. /// public int? UserScore { get { return userScore; } set { userScore = value; OnPropertyChanged(); } } private int? criticScore = null; /// /// Gets or sets critic based rating score. /// public int? CriticScore { get { return criticScore; } set { criticScore = value; OnPropertyChanged(); } } private int? communityScore = null; /// /// Gets or sets community rating score. /// public int? CommunityScore { get { return communityScore; } set { communityScore = value; OnPropertyChanged(); } } private Ver2_ScriptLanguage actionsScriptLanguage = Ver2_ScriptLanguage.PowerShell; /// /// Gets or sets scripting language for and scripts. /// public Ver2_ScriptLanguage ActionsScriptLanguage { get => actionsScriptLanguage; set { actionsScriptLanguage = value; OnPropertyChanged(); } } private string preScript; /// /// Gets or sets pre-action script. /// [DefaultValue("")] public string PreScript { get => preScript; set { preScript = value; OnPropertyChanged(); } } private string postScript; /// /// Gets or sets post-action script. /// [DefaultValue("")] public string PostScript { get => postScript; set { postScript = value; OnPropertyChanged(); } } private string gameStartedScript; /// /// Gets or sets script to be executed after game started. /// [DefaultValue("")] public string GameStartedScript { get => gameStartedScript; set { gameStartedScript = value; OnPropertyChanged(); } } private bool useGlobalPostScript = true; /// /// Gets or sets value indicating whether global post script should be executed. /// [DefaultValue(true)] public bool UseGlobalPostScript { get => useGlobalPostScript; set { useGlobalPostScript = value; OnPropertyChanged(); } } private bool useGlobalPreScript = true; /// /// Gets or sets value indicating whether global pre script should be executed. /// [DefaultValue(true)] public bool UseGlobalPreScript { get => useGlobalPreScript; set { useGlobalPreScript = value; OnPropertyChanged(); } } private bool useGameStartedScript = true; /// /// Gets or sets value indicating whether global pre script should be executed. /// [DefaultValue(true)] public bool UseGlobalGameStartedScript { get => useGameStartedScript; set { useGameStartedScript = value; OnPropertyChanged(); } } private string manual; /// /// Gets or sets game manual. /// [DefaultValue("")] public string Manual { get { return manual; } set { manual = value; OnPropertyChanged(); } } /// /// Creates new instance of a Game object. /// public Ver2_Game() : base() { GameId = Guid.NewGuid().ToString(); } /// /// Creates new instance of a Game object with specific name. /// /// Game name. public Ver2_Game(string name) : this() { Name = name; } /// public override string ToString() { return Name; } } public enum Ver2_GameActionType : int { /// /// Game action executes a file. /// File = 0, /// /// Game action navigates to a web based URL. /// URL = 1, /// /// Game action starts an emulator. /// Emulator = 2 } /// /// Represents executable game action. /// public class Ver2_GameAction : ObservableObject, IEquatable { private readonly Guid id = Guid.NewGuid(); private Ver2_GameActionType type; /// /// Gets or sets task type. /// public Ver2_GameActionType Type { get => type; set { type = value; OnPropertyChanged(); } } private string arguments; /// /// Gets or sets executable arguments for File type tasks. /// public string Arguments { get => arguments; set { arguments = value; OnPropertyChanged(); } } private string additionalArguments; /// /// Gets or sets additional executable arguments used for Emulator action type. /// public string AdditionalArguments { get => additionalArguments; set { additionalArguments = value; OnPropertyChanged(); } } private bool overrideDefaultArgs; /// /// Gets or sets value indicating wheter emulator arguments should be completely overwritten with action arguments. /// Applies only to Emulator action type. /// public bool OverrideDefaultArgs { get => overrideDefaultArgs; set { overrideDefaultArgs = value; OnPropertyChanged(); } } private string path; /// /// Gets or sets executable path for File action type or URL for URL action type. /// public string Path { get => path; set { path = value; OnPropertyChanged(); } } private string workingDir; /// /// Gets or sets working directory for File action type executable. /// public string WorkingDir { get => workingDir; set { workingDir = value; OnPropertyChanged(); } } private string name; /// /// Gets or sets action name. /// public string Name { get => name; set { name = value; OnPropertyChanged(); } } private bool isHandledByPlugin; /// /// Gets or sets value indicating wheter a action's execution should be handled by a plugin. /// public bool IsHandledByPlugin { get => isHandledByPlugin; set { isHandledByPlugin = value; OnPropertyChanged(); } } private Guid emulatorId; /// /// Gets or sets emulator id for Emulator action type execution. /// public Guid EmulatorId { get => emulatorId; set { emulatorId = value; OnPropertyChanged(); } } private Guid emulatorProfileId; /// /// Gets or sets emulator profile id for Emulator action type execution. /// public Guid EmulatorProfileId { get => emulatorProfileId; set { emulatorProfileId = value; OnPropertyChanged(); } } /// public override string ToString() { switch (Type) { case Ver2_GameActionType.File: return $"File: {Path}, {Arguments}, {WorkingDir}"; case Ver2_GameActionType.URL: return $"Url: {Path}"; case Ver2_GameActionType.Emulator: return $"Emulator: {EmulatorId}, {EmulatorProfileId}, {OverrideDefaultArgs}, {AdditionalArguments}"; default: return Path; } } /// /// Compares two objects for equality. /// /// /// /// public static bool Equals(Ver2_GameAction obj1, Ver2_GameAction obj2) { if (obj1 == null && obj2 == null) { return true; } else { return obj1?.Equals(obj2) == true; } } /// public bool Equals(Ver2_GameAction other) { if (other is null) { return false; } if (Type != other.Type) { return false; } if (!string.Equals(Arguments, other.Arguments, StringComparison.Ordinal)) { return false; } if (!string.Equals(AdditionalArguments, other.AdditionalArguments, StringComparison.Ordinal)) { return false; } if (!string.Equals(Path, other.Path, StringComparison.Ordinal)) { return false; } if (!string.Equals(WorkingDir, other.WorkingDir, StringComparison.Ordinal)) { return false; } if (!string.Equals(Name, other.Name, StringComparison.Ordinal)) { return false; } if (IsHandledByPlugin != other.IsHandledByPlugin) { return false; } if (EmulatorId != other.EmulatorId) { return false; } if (EmulatorProfileId != other.EmulatorProfileId) { return false; } if (OverrideDefaultArgs != other.OverrideDefaultArgs) { return false; } return true; } } public class Ver2_GameFeature : Ver2_DatabaseObject { /// /// Creates new instance of . /// public Ver2_GameFeature() : base() { } /// /// Creates new instance of . /// /// public Ver2_GameFeature(string name) : base() { Name = name; } /// /// Gets empty tag. /// public static readonly Ver2_GameFeature Empty = new Ver2_GameFeature { Id = Guid.Empty, Name = string.Empty }; } public class Ver2_GameSource : Ver2_DatabaseObject { /// /// Creates new instance of . /// public Ver2_GameSource() : base() { } /// /// Creates new instance of . /// /// public Ver2_GameSource(string name) : base() { Name = name; } /// /// Gets empty game source. /// public static readonly Ver2_GameSource Empty = new Ver2_GameSource { Id = Guid.Empty, Name = string.Empty }; } public class Ver2_Genre : Ver2_DatabaseObject { /// /// Creates new instance of . /// public Ver2_Genre() : base() { } /// /// Creates new instance of . /// /// public Ver2_Genre(string name) : base() { Name = name; } /// /// Gets empty genre. /// public static readonly Ver2_Genre Empty = new Ver2_Genre { Id = Guid.Empty, Name = string.Empty }; } public enum Ver2_InstallationStatus { /// /// Game is installed. /// [Description("LOCGameIsInstalledTitle")] Installed = 0, /// /// Game is not installed. /// [Description("LOCGameIsUnInstalledTitle")] Uninstalled = 1 } public class Ver2_Link : ObservableObject, IEquatable { private string name; /// /// Gets or sets name of the link. /// public string Name { get => name; set { name = value; OnPropertyChanged(); } } private string url; /// /// Gets or sets web based URL. /// public string Url { get => url; set { url = value; OnPropertyChanged(); } } /// /// Creates new instance of Link. /// public Ver2_Link() { } /// /// Creates new instance of Link with specific values. /// /// Link name. /// Link URL. public Ver2_Link(string name, string url) { Name = name; Url = url; } /// public bool Equals(Ver2_Link other) { if (other is null) { return false; } if (!string.Equals(Name, other.Name, StringComparison.Ordinal)) { return false; } if (!string.Equals(Url, other.Url, StringComparison.Ordinal)) { return false; } return true; } } public enum Ver2_PastTimeSegment : int { /// /// Idicates time occurig today. /// [Description("LOCToday")] Today = 0, /// /// Idicates time occurig yesterday. /// [Description("LOCYesterday")] Yesterday = 1, /// /// Idicates time occurig past week. /// [Description("LOCPastWeek")] PastWeek = 2, /// /// Idicates time occurig past month. /// [Description("LOCPastMonth")] PastMonth = 3, /// /// Idicates time occurig past year. /// [Description("LOCPastYear")] PastYear = 4, /// /// Idicates time occurig past year. /// [Description("LOCMoreThenYear")] MoreThenYear = 5, /// /// Idicates time that never happened. /// [Description("LOCNever")] Never = 6 } public class Ver2_Platform : Ver2_DatabaseObject { private string icon; /// /// Gets or sets platform icon. /// public string Icon { get => icon; set { icon = value; OnPropertyChanged(); } } private string cover; /// /// Gets or sets default game cover. /// public string Cover { get => cover; set { cover = value; OnPropertyChanged(); } } private string background; /// /// Gets or sets default game background image. /// public string Background { get => background; set { background = value; OnPropertyChanged(); } } /// /// Creates new instance of Platform. /// public Ver2_Platform() : base() { } /// /// Creates new instance of Platform with specific name. /// /// Platform name. public Ver2_Platform(string name) : this() { Name = name; } /// public override string ToString() { return Name; } /// /// Gets empty platform. /// public static readonly Ver2_Platform Empty = new Ver2_Platform { Id = Guid.Empty, Name = string.Empty }; } public enum Ver2_PlaytimeCategory : int { /// /// Not playtime. /// [Description("LOCPlayedNone")] NotPlayed = 0, /// /// Less then an hour played. /// [Description("LOCPLaytimeLessThenAnHour")] LessThenHour = 1, /// /// Played 1 to 10 hours. /// [Description("LOCPLaytime1to10")] O1_10 = 2, /// /// Played 10 to 100 hours. /// [Description("LOCPLaytime10to100")] O10_100 = 3, /// /// Played 100 to 500 hours. /// [Description("LOCPLaytime100to500")] O100_500 = 4, /// /// Played 500 to 1000 hours. /// [Description("LOCPLaytime500to1000")] O500_1000 = 5, /// /// Played more then 1000 hours. /// [Description("LOCPLaytime1000plus")] O1000plus = 6 } public class Ver2_Region : Ver2_DatabaseObject { /// /// Creates new instance of . /// public Ver2_Region() : base() { } /// /// Creates new instance of . /// /// public Ver2_Region(string name) : base() { Name = name; } /// /// Gets empty region. /// public static readonly Ver2_Region Empty = new Ver2_Region { Id = Guid.Empty, Name = string.Empty }; } public enum Ver2_ScoreRating { /// /// No score. /// None, /// /// Negative rating. /// Negative, /// /// Positive rating. /// Positive, /// /// Mixed rating. /// Mixed } /// /// Scpecifies rating score groups. /// public enum Ver2_ScoreGroup : int { /// /// Score rage from 0 to 10. /// [Description("0x")] O0x = 0, /// /// Score rage from 10 to 20. /// [Description("1x")] O1x = 1, /// /// Score rage from 20 to 30. /// [Description("2x")] O2x = 2, /// /// Score rage from 30 to 40. /// [Description("3x")] O3x = 3, /// /// Score rage from 40 to 50. /// [Description("4x")] O4x = 4, /// /// Score rage from 50 to 60. /// [Description("5x")] O5x = 5, /// /// Score rage from 60 to 70. /// [Description("6x")] O6x = 6, /// /// Score rage from 70 to 80. /// [Description("7x")] O7x = 7, /// /// Score rage from 80 to 90. /// [Description("8x")] O8x = 8, /// /// Score rage from 90 to 100. /// [Description("9x")] O9x = 9, /// /// No score. /// [Description("LOCNone")] None = 10 } public class Ver2_Series : Ver2_DatabaseObject { /// /// Creates new instance of . /// public Ver2_Series() : base() { } /// /// Creates new instance of . /// /// public Ver2_Series(string name) : base() { Name = name; } /// /// Gets empty series. /// public static readonly Ver2_Series Empty = new Ver2_Series { Id = Guid.Empty, Name = string.Empty }; } public class Ver2_Tag : Ver2_DatabaseObject { /// /// Creates new instance of . /// public Ver2_Tag() : base() { } /// /// Creates new instance of . /// /// public Ver2_Tag(string name) : base() { Name = name; } /// /// Gets empty tag. /// public static readonly Ver2_Tag Empty = new Ver2_Tag { Id = Guid.Empty, Name = string.Empty }; } } ================================================ FILE: source/Playnite/DateTimes.cs ================================================ using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public static class DateTimes { public interface IDateTimes { DateTime Now { get; } DateTime Today { get; } } public class DefaultDateProvider : IDateTimes { public DateTime Now => DateTime.Now; public DateTime Today => DateTime.Today; } public class TempDateTime : IDisposable { public TempDateTime(IDateTimes customDates) { DateTimes.dateProvider = customDates; } public void Dispose() { DateTimes.dateProvider = DateTimes.defaultDateProvider; } } private static IDateTimes defaultDateProvider = new DefaultDateProvider(); private static IDateTimes dateProvider = defaultDateProvider; public static DateTime Now => dateProvider.Now; public static DateTime Today => dateProvider.Today; public static IDisposable UseCustomDates(IDateTimes dates) { return new TempDateTime(dates); } public static string ToDisplayString(this DateTime date, DateFormattingOptions options = null) { try { if (options == null) { return date.ToString(Common.Constants.DateUiFormat); } if (options.PastWeekRelativeFormat) { var today = Today; var dayDiff = (today - date.Date).TotalDays; if (dayDiff == 0) { return LOC.Today.GetLocalized(); } if (dayDiff == 1) { return LOC.Yesterday.GetLocalized(); } if (dayDiff > 1 && dayDiff < 7) { switch (date.DayOfWeek) { case DayOfWeek.Sunday: return LOC.Sunday.GetLocalized(); case DayOfWeek.Monday: return LOC.Monday.GetLocalized(); case DayOfWeek.Tuesday: return LOC.Tuesday.GetLocalized(); case DayOfWeek.Wednesday: return LOC.Wednesday.GetLocalized(); case DayOfWeek.Thursday: return LOC.Thursday.GetLocalized(); case DayOfWeek.Friday: return LOC.Friday.GetLocalized(); case DayOfWeek.Saturday: return LOC.Saturday.GetLocalized(); } } } return date.ToString(options.Format ?? Common.Constants.DateUiFormat); } catch (ArgumentOutOfRangeException) { // This is for rare cases where this fails on error similar to this: // Specified time is not supported in this calendar. It should be between 04/30/1900 00:00:00 (Gregorian date) and 11/16/2077 23:59:59 (Gregorian date), inclusive. // TODO: handle properly return "unsupported"; } } public static string ToDisplayString(this ReleaseDate date, ReleaseDateFormattingOptions options = null) { if (date.Month == null && date.Day == null) { return date.Year.ToString(); } if (date.Month != null && date.Day == null) { return date.Date.ToString(options.PartialFormat ?? Common.Constants.DefaultPartialReleaseDateTimeFormat); } return date.Date.ToDisplayString(options); } } } ================================================ FILE: source/Playnite/Diagnostic.cs ================================================ using Playnite.Common; using Playnite.SDK; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Text; namespace Playnite { public static class Diagnostic { private static ILogger logger = LogManager.GetLogger(); private static List GetPlayniteFilesList() { var progPath = PlaynitePaths.ProgramPath; var allFiles = new List(); foreach (var file in Directory.GetFiles(progPath, "*.*", SearchOption.TopDirectoryOnly)) { try { var fInfo = new FileInfo(file); allFiles.Add(fInfo.Name + ", " + fInfo.Length); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get information about Playnite file {file}"); } } return allFiles; } private static string GetManifestInfo(string rootDir, string manFileName) { if (!Directory.Exists(rootDir)) { return string.Empty; } var total = new StringBuilder(); foreach (var dir in Directory.GetDirectories(rootDir)) { var manifest = Path.Combine(dir, manFileName); if (File.Exists(manifest)) { total.Append(Path.GetFileName(dir)); total.AppendLine(); total.AppendLine("--------------------------"); total.Append(File.ReadAllText(manifest)); total.AppendLine(); total.AppendLine(); } } return total.ToString(); } public static void CreateDiagPackage(string path, string userActionsDescription, DiagnosticPackageInfo packageInfo) { var diagTemp = Path.Combine(PlaynitePaths.TempPath, "diag"); FileSystem.CreateDirectory(diagTemp, true); FileSystem.DeleteFile(path); ZipFile.CreateFromDirectory(diagTemp, path); using (FileStream zipToOpen = new FileStream(path, FileMode.Open)) { using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update)) { // Package info var packagePath = Path.Combine(diagTemp, DiagnosticPackageInfo.PackageInfoFileName); File.WriteAllText(packagePath, Serialization.ToJson(packageInfo)); archive.CreateEntryFromFile(packagePath, Path.GetFileName(packagePath)); // Config if (Directory.Exists(PlaynitePaths.ConfigRootPath)) { foreach (var cfg in Directory.GetFiles(PlaynitePaths.ConfigRootPath, "*.json")) { var fileInfo = new FileInfo(cfg); archive.CreateEntryFromFile(cfg, fileInfo.Name); } } // Extension configs if (Directory.Exists(PlaynitePaths.ExtensionsDataPath)) { foreach (var cfg in Directory.GetFiles(PlaynitePaths.ExtensionsDataPath, "config.json", SearchOption.AllDirectories)) { var fileInfo = new FileInfo(cfg); archive.CreateEntryFromFile(cfg, Path.Combine("extensions", fileInfo.Directory.Name, fileInfo.Name)); } } // Installed extensions/themes try { var extensionsPath = Path.Combine(diagTemp, "extensions.txt"); File.AppendAllText(extensionsPath, "----- User data extensions: -----\n\n"); File.AppendAllText(extensionsPath, GetManifestInfo(PlaynitePaths.ExtensionsUserDataPath, PlaynitePaths.ExtensionManifestFileName)); File.AppendAllText(extensionsPath, GetManifestInfo(PlaynitePaths.ThemesUserDataPath, PlaynitePaths.ThemeManifestFileName)); if (PlayniteSettings.IsPortable) { File.AppendAllText(extensionsPath, "\n\n----- Program dir extensions: -----\n\n"); File.AppendAllText(extensionsPath, GetManifestInfo(PlaynitePaths.ExtensionsProgramPath, PlaynitePaths.ExtensionManifestFileName)); File.AppendAllText(extensionsPath, GetManifestInfo(PlaynitePaths.ThemesProgramPath, PlaynitePaths.ThemeManifestFileName)); } archive.CreateEntryFromFile(extensionsPath, Path.GetFileName(extensionsPath)); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to package extensions list."); } // System Info try { var infoPath = Path.Combine(diagTemp, "sysinfo.txt"); File.WriteAllText(infoPath, Serialization.ToJson(Computer.GetSystemInfo(), true)); archive.CreateEntryFromFile(infoPath, Path.GetFileName(infoPath)); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed gather system info."); } // Playnite info var playnitePath = Path.Combine(diagTemp, "playniteInfo.txt"); var playniteInfo = new Dictionary { { "Version", Updater.CurrentVersion.ToString() }, { "Portable", PlayniteSettings.IsPortable }, { "Memory", (PlayniteProcess.WorkingSetMemory / 1024f) / 1024f }, { "Path", PlayniteProcess.Path }, { "Cmdline", PlayniteProcess.Cmdline }, { "Elevated", PlayniteEnvironment.IsElevated }, { "Playnite.DesktopApp.exe_MD5", FileSystem.GetMD5(PlaynitePaths.DesktopExecutablePath) }, { "Playnite.FullscreenApp.exe_MD5", FileSystem.GetMD5(PlaynitePaths.FullscreenExecutablePath) }, { "Playnite.dll_MD5", FileSystem.GetMD5(PlaynitePaths.PlayniteAssemblyPath) }, { "Playnite.SDK.dll_MD5", FileSystem.GetMD5(PlaynitePaths.PlayniteSDKAssemblyPath) } }; File.WriteAllText(playnitePath, Serialization.ToJson(playniteInfo, true)); archive.CreateEntryFromFile(playnitePath, Path.GetFileName(playnitePath)); // Program file list try { var fileListPath = Path.Combine(diagTemp, "fileList.txt"); File.WriteAllText(fileListPath, string.Join(Environment.NewLine, GetPlayniteFilesList())); archive.CreateEntryFromFile(fileListPath, Path.GetFileName(fileListPath)); } catch (Exception e) { logger.Error(e, "Failed to pack app file list."); } // User actions description if (!string.IsNullOrWhiteSpace(userActionsDescription)) { var descriptionPath = Path.Combine(diagTemp, "userActions.txt"); File.WriteAllText(descriptionPath, userActionsDescription); archive.CreateEntryFromFile(descriptionPath, Path.GetFileName(descriptionPath)); } void addCefLog(string logPath, ZipArchive archiveObj) { try { var cefEntry = archive.CreateEntry(Path.GetFileName(logPath)); using (var cefS = new FileStream(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) using (var writer = new StreamWriter(cefEntry.Open())) { cefS.CopyTo(writer.BaseStream); } } catch (Exception e) { logger.Error(e, "Failed to pack CEF log."); } } // Add log files foreach (var logFile in Directory.GetFiles(PlaynitePaths.ConfigRootPath, "*.log", SearchOption.TopDirectoryOnly)) { if (Path.GetFileName(logFile) == "cef.log" || Path.GetFileName(logFile) == "debug.log") { addCefLog(logFile, archive); } else { archive.CreateEntryFromFile(logFile, Path.GetFileName(logFile)); } } } } FileSystem.DeleteDirectory(diagTemp); } public static void CreateLogPackage(string path) { FileSystem.DeleteFile(path); using (FileStream zipToOpen = new FileStream(path, FileMode.Create)) using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update)) { foreach (var logFile in Directory.GetFiles(PlaynitePaths.ConfigRootPath, "*.log", SearchOption.TopDirectoryOnly)) { if (Path.GetFileName(logFile) == "cef.log" || Path.GetFileName(logFile) == "debug.log") { continue; } else { archive.CreateEntryFromFile(logFile, Path.GetFileName(logFile)); } } } } public static bool IsHResultCloudError(int hresult) { return hresult == unchecked((int)0x80070166) || // ERROR_CLOUD_FILE_SYNC_ROOT_METADATA_CORRUPT hresult == unchecked((int)0x8007016A) || // ERROR_CLOUD_FILE_PROVIDER_NOT_RUNNING hresult == unchecked((int)0x8007016B) || // ERROR_CLOUD_FILE_METADATA_CORRUPT hresult == unchecked((int)0x8007016C) || // ERROR_CLOUD_FILE_METADATA_TOO_LARGE hresult == unchecked((int)0x8007016D) || // ERROR_CLOUD_FILE_PROPERTY_BLOB_TOO_LARGE hresult == unchecked((int)0x8007016E) || // ERROR_CLOUD_FILE_PROPERTY_BLOB_CHECKSUM_MISMATCH hresult == unchecked((int)0x80070176) || // ERROR_CLOUD_FILE_TOO_MANY_PROPERTY_BLOBS hresult == unchecked((int)0x80070177) || // ERROR_CLOUD_FILE_PROPERTY_VERSION_NOT_SUPPORTED hresult == unchecked((int)0x80070179) || // ERROR_CLOUD_FILE_NOT_IN_SYNC hresult == unchecked((int)0x8007017A) || // ERROR_CLOUD_FILE_ALREADY_CONNECTED hresult == unchecked((int)0x8007017B) || // ERROR_CLOUD_FILE_NOT_SUPPORTED hresult == unchecked((int)0x8007017C) || // ERROR_CLOUD_FILE_INVALID_REQUEST hresult == unchecked((int)0x8007017D) || // ERROR_CLOUD_FILE_READ_ONLY_VOLUME hresult == unchecked((int)0x8007017E) || // ERROR_CLOUD_FILE_CONNECTED_PROVIDER_ONLY hresult == unchecked((int)0x8007017F) || // ERROR_CLOUD_FILE_VALIDATION_FAILED hresult == unchecked((int)0x80070182) || // ERROR_CLOUD_FILE_AUTHENTICATION_FAILED hresult == unchecked((int)0x80070183) || // ERROR_CLOUD_FILE_INSUFFICIENT_RESOURCES hresult == unchecked((int)0x80070184) || // ERROR_CLOUD_FILE_NETWORK_UNAVAILABLE hresult == unchecked((int)0x80070185) || // ERROR_CLOUD_FILE_UNSUCCESSFUL hresult == unchecked((int)0x80070186) || // ERROR_CLOUD_FILE_NOT_UNDER_SYNC_ROOT hresult == unchecked((int)0x80070187) || // ERROR_CLOUD_FILE_IN_USE hresult == unchecked((int)0x80070188) || // ERROR_CLOUD_FILE_PINNED hresult == unchecked((int)0x80070189) || // ERROR_CLOUD_FILE_REQUEST_ABORTED hresult == unchecked((int)0x8007018A) || // ERROR_CLOUD_FILE_PROPERTY_CORRUPT hresult == unchecked((int)0x8007018B) || // ERROR_CLOUD_FILE_ACCESS_DENIED hresult == unchecked((int)0x8007018C) || // ERROR_CLOUD_FILE_INCOMPATIBLE_HARDLINKS hresult == unchecked((int)0x8007018D) || // ERROR_CLOUD_FILE_PROPERTY_LOCK_CONFLICT hresult == unchecked((int)0x8007018E) || // ERROR_CLOUD_FILE_REQUEST_CANCELED hresult == unchecked((int)0x80070194) || // ERROR_CLOUD_FILE_PROVIDER_TERMINATED hresult == unchecked((int)0x800701AA) || // ERROR_CLOUD_FILE_REQUEST_TIMEOUT hresult == unchecked((int)0x800701B2) || // ERROR_CLOUD_FILE_DEHYDRATION_DISALLOWED hresult == unchecked((int)0x800701DB); // ERROR_CLOUD_FILE_US_MESSAGE_TIMEOUT } } } ================================================ FILE: source/Playnite/DiagnosticPackageInfo.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class DiagnosticPackageInfo { public static readonly string PackageInfoFileName = "packageInfo.txt"; public string PlayniteVersion { get; set; } public bool IsCrashPackage { get; set; } = false; } } ================================================ FILE: source/Playnite/DialogsFactory.cs ================================================ using Playnite.Common; using Playnite.SDK; using Playnite.Windows; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace Playnite { public class Dialogs { public static IDialogsFactory DialogsHandler { get; private set; } public static void SetHandler(IDialogsFactory factory) { DialogsHandler = factory; } public static MessageBoxResult ShowErrorMessage(string messageBoxText, string caption) { return DialogsHandler.ShowErrorMessage(messageBoxText, caption); } public static MessageBoxResult ShowMessage(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon) { return DialogsHandler.ShowMessage(messageBoxText, caption, button, icon); } public static MessageBoxResult ShowMessage(string messageBoxText, string caption, MessageBoxButton button) { return DialogsHandler.ShowMessage(messageBoxText, caption, button); } public static MessageBoxResult ShowMessage(string messageBoxText, string caption) { return DialogsHandler.ShowMessage(messageBoxText, caption); } public static MessageBoxResult ShowMessage(string messageBoxText) { return DialogsHandler.ShowMessage(messageBoxText); } public static string SelectFolder() { return DialogsHandler.SelectFolder(); } public static string SelectFile(string filter) { return DialogsHandler.SelectFile(filter); } public static List SelectFiles(string filter) { return DialogsHandler.SelectFiles(filter); } public static string SelectIconFile() { return DialogsHandler.SelectIconFile(); } public static string SelectImagefile() { return DialogsHandler.SelectImagefile(); } public static string SaveFile(string filter) { return DialogsHandler.SaveFile(filter); } public static string SaveFile(string filter, bool promptOverwrite) { return DialogsHandler.SaveFile(filter, promptOverwrite); } public static StringSelectionDialogResult SelectString(string messageBoxText, string caption, string defaultInput) { return DialogsHandler.SelectString(messageBoxText, caption, defaultInput); } public static StringSelectionDialogResult SelectString(string messageBoxText, string caption, string defaultInput, List options) { return DialogsHandler.SelectString(messageBoxText, caption, defaultInput, options); } public static void ShowSelectableString(string messageBoxText, string caption, string defaultInput) { DialogsHandler.ShowSelectableString(messageBoxText, caption, defaultInput); } public static GlobalProgressResult ActivateGlobalProgress(Action progresAction, GlobalProgressOptions progressOptions) { return DialogsHandler.ActivateGlobalProgress(progresAction, progressOptions); } public static GlobalProgressResult ActivateGlobalProgress(Func progresAction, GlobalProgressOptions progressOptions) { return DialogsHandler.ActivateGlobalProgress(progresAction, progressOptions); } } } ================================================ FILE: source/Playnite/DiscordManager.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite { public class DiscordManager : IDisposable { private static readonly ILogger logger = LogManager.GetLogger(); private DiscordRPC.DiscordRpcClient discord; private bool isConnected = false; private bool isConnecting = false; public bool IsPresenceEnabled { get; set; } = false; public DiscordManager(bool presenceEnabled = false) { IsPresenceEnabled = presenceEnabled; if (IsPresenceEnabled) { InitializeDiscord(); } } private void InitializeDiscord() { discord = new DiscordRPC.DiscordRpcClient("689105200262414377"); discord.OnError += (a, s) => { logger.Error($"{s.Code}, {s.Type}, {s.Message}"); }; discord.OnConnectionEstablished += (a, s) => { isConnected = true; isConnecting = false; }; discord.OnConnectionFailed += (a, s) => { isConnecting = false; isConnected = false; discord.Deinitialize(); discord.Dispose(); logger.Error("Discord connection failed"); }; isConnecting = true; discord.Initialize(); } public void SetPresence(string gameName) { if (!IsPresenceEnabled) { return; } try { if (!isConnected && !isConnecting) { InitializeDiscord(); } var pres = discord.CurrentPresence; discord.SetPresence(new DiscordRPC.RichPresence { Details = gameName, Assets = new DiscordRPC.Assets { LargeImageKey = "playnite-avatar", LargeImageText = "Playnite game library manager." } }); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to set Discord presence"); } } public void ClearPresence() { if (!IsPresenceEnabled) { return; } try { if (!isConnected && !isConnecting) { InitializeDiscord(); } var pres = discord.CurrentPresence; discord.ClearPresence(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to clear Discord presence"); } } public void Dispose() { discord?.Dispose(); } } } ================================================ FILE: source/Playnite/EasyAntiCheat.cs ================================================ using Newtonsoft.Json; using Playnite.Common; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class EasyAntiCheatLauncherSettings { [JsonProperty(PropertyName = "title")] public string Title { get; set; } [JsonProperty(PropertyName = "executable")] public string Executable { get; set; } [JsonProperty(PropertyName = "logo_position")] public string LogoPosition { get; set; } [JsonProperty(PropertyName = "parameters")] public string Parameters { get; set; } [JsonProperty(PropertyName = "use_cmdline_parameters")] public string UseCmdlineParameters { get; set; } [JsonProperty(PropertyName = "working_directory")] public string WorkingDirectory { get; set; } [JsonProperty(PropertyName = "wait_for_game_process_exit")] public string WaitForGameProcessExit { get; set; } [JsonProperty(PropertyName = "hide_splash_screen")] public string HideSplashScreen { get; set; } [JsonProperty(PropertyName = "ide_ui_controls")] public string IdeUiControls { get; set; } } public class EasyAntiCheat { public static EasyAntiCheatLauncherSettings GetLauncherSettings(string gameDirectory) { var settingsPath = Path.Combine(gameDirectory, "EasyAntiCheat", "Launcher", "Settings.json"); if (!File.Exists(settingsPath)) { throw new FileNotFoundException($"EAC launcher settings not found: {settingsPath}"); } return Serialization.FromJson(File.ReadAllText(settingsPath)); } } } ================================================ FILE: source/Playnite/ElementTreeHelper.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; namespace Playnite { public class ElementTreeHelper { public static IEnumerable FindVisualChildren(DependencyObject depObj) where T : DependencyObject { if (depObj == null) { yield return null; } for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); if (child != null && child is T) { yield return (T)child; } foreach (T childOfChild in FindVisualChildren(child)) { yield return childOfChild; } } } public static IEnumerable FindLogicalChildren(DependencyObject depObj) { if (depObj == null) { yield return null; } foreach (var child in LogicalTreeHelper.GetChildren(depObj)) { if (child is DependencyObject) { yield return child as DependencyObject; foreach (var childOfChild in FindLogicalChildren(child as DependencyObject)) { yield return childOfChild; } } } } } } ================================================ FILE: source/Playnite/Emulation/Emulators/4DO/emulator.yaml ================================================ Id: 4do Name: 4DO Website: 'https://sourceforge.net/projects/fourdo/' Profiles: - Name: Default StartupArguments: '-StartLoadFile "{ImagePath}" --StartFullScreen' Platforms: [3do] ImageExtensions: [cue, bin, iso] InstallationFile: ^4do\.exe$ StartupExecutable: ^4do\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Altirra/emulator.yaml ================================================ Id: altirra Name: Altirra Website: 'http://www.virtualdub.org/altirra.html' Profiles: - Name: Atari 8-bit StartupArguments: '"{ImagePath}"' Platforms: [atari_8bit] ImageExtensions: [atr, atx, xfd, dcm, pro, cas, wav, rom, bin, car, xex, obx, com, arc, sap, zip] StartupExecutable: ^altirra\.exe$ - Name: Atari 5200 StartupArguments: '/hardware:5200 /kernel:5200 "{ImagePath}"' Platforms: [atari_5200] ImageExtensions: [rom, bin, car, a52, zip] StartupExecutable: ^altirra\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Atari800/emulator.yaml ================================================ Id: atari800 Name: Atari800 Website: 'https://atari800.github.io/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [atari_8bit, atari_5200] ImageExtensions: [xfd, atr, atx, cdm, cas, bin, a52] StartupExecutable: ^atari800\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/BGB/emulator.yaml ================================================ Id: bgb Name: BGB Website: 'https://bgb.bircd.org' Profiles: - Name: Game Boy StartupArguments: '-rom "{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [zip, gb] StartupExecutable: ^bgb\.exe$ - Name: Game Boy Color StartupArguments: '-rom "{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [zip, gbc] StartupExecutable: ^bgb\.exe$ - Name: Game Boy 64-bit StartupArguments: '-rom "{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [zip, gb] StartupExecutable: ^bgb64\.exe$ - Name: Game Boy Color 64-bit StartupArguments: '-rom "{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [zip, gbc] StartupExecutable: ^bgb64\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/BigPEmu/emulator.yaml ================================================ Id: bigpemu Name: BigPEmu Website: 'https://www.richwhitehouse.com/jaguar/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [atari_jaguar] ImageExtensions: [j64, cof, rom, jag, abs, zip, cue, cdi] StartupExecutable: ^BigPEmu\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/BizHawk/emulator.yaml ================================================ Id: bizhawk Name: BizHawk Website: 'http://tasvideos.org/Bizhawk.html/' Profiles: - Name: Apple II StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [apple_2] ImageExtensions: [dsk, do, po, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Atari 2600 StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [atari_2600] ImageExtensions: [a26, zip, rar, 7z, gz, bin] StartupExecutable: ^EmuHawk\.exe$ - Name: Atari 7800 StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [atari_7800] ImageExtensions: [a78, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Atari Jaguar StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [atari_jaguar] ImageExtensions: [j64, jag, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Atari Lynx StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [atari_lynx] ImageExtensions: [lnx, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Coleco ColecoVision StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [coleco_vision] ImageExtensions: [col, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Commodore 64 StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [commodore_64] ImageExtensions: [prg, d64, g64, crt, tap, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: GCE Vectrex StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [vectrex] ImageExtensions: [vec, zip] StartupExecutable: ^EmuHawk\.exe$ - Name: Magnavox Odyssey 2 StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [magnavox_odyssey_2] ImageExtensions: [o2, bin, zip] StartupExecutable: ^EmuHawk\.exe$ - Name: Mattel Intellivision StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [mattel_intellivision] ImageExtensions: [int, bin, rom, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: NEC TurboGrafx 16 StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nec_turbografx_16] ImageExtensions: [pce, cue, ccd, mds, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: NEC TurboGrafx-CD StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nec_turbografx_cd] ImageExtensions: [cue, ccd, mds] StartupExecutable: ^EmuHawk\.exe$ - Name: Nintendo DS/DSi StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nintendo_ds, nintendo_dsi] ImageExtensions: [nds, zip] StartupExecutable: ^EmuHawk\.exe$ - Name: Nintendo Entertainment System StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nintendo_nes] ImageExtensions: [nes, fds, unf, nsf, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Nintendo Game Boy StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nintendo_gameboy] ImageExtensions: [gb, sgb, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Nintendo Game Boy Color StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nintendo_gameboycolor] ImageExtensions: [gbc, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Nintendo Game Boy Advance StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nintendo_gameboyadvance] ImageExtensions: [gba, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Nintendo Virtual Boy StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nintendo_virtualboy] ImageExtensions: [vb, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Nintendo 64 StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nintendo_64] ImageExtensions: [z64, v64, n64, zip] StartupExecutable: ^EmuHawk\.exe$ - Name: PC Engine SuperGrafx StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nec_supergrafx] ImageExtensions: [sgx, zip, rar, 7z, gz, pce] StartupExecutable: ^EmuHawk\.exe$ - Name: Sega Game Gear StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [sega_gamegear] ImageExtensions: [gg, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Sega Genesis StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [sega_genesis] ImageExtensions: [gen, md, smd, 32x, bin, cue, ccd, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Sega Master System StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [sega_mastersystem] ImageExtensions: [sms, gg, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Sega Saturn StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [sega_saturn] ImageExtensions: [iso, bin, cue] StartupExecutable: ^EmuHawk\.exe$ - Name: SNK Neo Geo Pocket StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [snk_neogeopocket] ImageExtensions: [ngp, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: SNK Neo Geo Pocket Color StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [snk_neogeopocket_color] ImageExtensions: [ngc, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Sony PlayStation StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [sony_playstation] ImageExtensions: [cue, ccd, mds, m3u] StartupExecutable: ^EmuHawk\.exe$ - Name: Super Nintendo Entertainment System StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [nintendo_super_nes] ImageExtensions: [smc, sfc, xml, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Uzebox StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [uzebox] ImageExtensions: [uze, zip] StartupExecutable: ^EmuHawk\.exe$ - Name: WonderSwan StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [bandai_wonderswan] ImageExtensions: [ws, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: WonderSwan Color StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [bandai_wonderswan_color] ImageExtensions: [wsc, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Sinclair ZX Spectrum StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [sinclair_zxspectrum] ImageExtensions: [tzx, tap, dsk, pzx, csw, wav, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: Texas Instruments TI-83 StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [ti_83] ImageExtensions: [83g, 83l, 83p, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ - Name: TIC-80 StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [tic_80] ImageExtensions: [tic, zip, rar, 7z, gz] StartupExecutable: ^EmuHawk\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/BlastEm/emulator.yaml ================================================ Id: blastem Name: BlastEm Website: 'https://www.retrodev.com/blastem/' Profiles: - Name: Default StartupArguments: '-fullscreen "{ImagePath}"' Platforms: [sega_genesis] ImageExtensions: [bin, md, zip] StartupExecutable: ^blastem\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Cemu/emulator.yaml ================================================ Id: cemu Name: Cemu Website: 'https://cemu.info/' Profiles: - Name: Default StartupArguments: '-g "{ImagePath}" -f' Platforms: [nintendo_wiiu] ImageExtensions: [wud, wux, rpx, wua] StartupExecutable: ^cemu.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Citra/emulator.yaml ================================================ Id: citra Name: Citra Website: 'https://citra-emu.org/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_3ds] ImageExtensions: [3ds, 3dsx, cci, cxi, elf, cia] StartupExecutable: ^citra-qt\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Cxbx-Reloaded/emulator.yaml ================================================ Id: cxbx-reloaded Name: Cxbx-Reloaded Website: 'https://cxbx-reloaded.co.uk' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [xbox] ImageExtensions: [xbe] StartupExecutable: ^cxbx\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/DOSBox/emulator.yaml ================================================ Id: dosbox Name: DOSBox Website: 'https://www.dosbox.com/' Profiles: - Name: Default StartupArguments: '-conf "{ImagePath}" -noconsole' Platforms: [pc_dos] StartupExecutable: ^dosbox\.exe$ ImageExtensions: [conf] ================================================ FILE: source/Playnite/Emulation/Emulators/DeSmuME/emulator.yaml ================================================ Id: desmume Name: DeSmuME Website: 'https://desmume.org/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_ds] ImageExtensions: [nds, zip, 7z, rar, gz] StartupExecutable: ^DeSmuME.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Dolphin/emulator.yaml ================================================ Id: dolphin Name: Dolphin Website: 'https://dolphin-emu.org/' Profiles: - Name: Nintendo GameCube StartupArguments: '--exec="{ImagePath}" --batch' Platforms: [nintendo_gamecube] ImageExtensions: [elf, dol, gcm, tgc, ciso, gcz, iso, wad, dff, rvz, m3u] StartupExecutable: ^Dolphin\.exe$ - Name: Nintendo Wii StartupArguments: '--exec="{ImagePath}" --batch' Platforms: [nintendo_wii] ImageExtensions: [elf, dol, tgc, wbfs, ciso, gcz, iso, wad, dff, rvz, wia, m3u] StartupExecutable: ^Dolphin\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/DuckStation/emulator.yaml ================================================ Id: duckstation Name: DuckStation Website: 'https://github.com/stenzek/duckstation' Profiles: - Name: Default Platforms: [sony_playstation] StartupArguments: '-batch -fullscreen "{ImagePath}"' ImageExtensions: [bin, img, exe, chd, psexe, m3u, cue, pbp, iso] StartupExecutable: ^duckstation-qt-x64-ReleaseLTCG\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/FCEUX/emulator.yaml ================================================ Id: fceux Name: FCEUX Website: 'http://www.fceux.com' Profiles: - Name: NES 32bit StartupArguments: '"{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [7z, zip, rar, tar, nes, fds, nsf, unf, nez, unif] StartupExecutable: ^fceux\.exe$ - Name: NES 64bit StartupArguments: '"{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [7z, zip, rar, tar, nes, fds, nsf, unf, nez, unif] StartupExecutable: ^fceux64\.exe$ - Name: QT 64bit StartupArguments: '"{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [7z, zip, rar, tar, nes, fds, nsf, unf, nez, unif] StartupExecutable: ^qfceux\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/FS-UAE/emulator.yaml ================================================ Id: fs-uae Name: FS-UAE Website: 'https://fs-uae.net/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [commodore_amiga, commodore_amiga_cd32] ImageExtensions: [iso, ccd, cue, chd, mds, nrg, adf, adz, gz, dms, ipf, scp, fdi, lha, lzx] StartupExecutable: ^fs-uae\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/FlashPlayerProjector/emulator.yaml ================================================ Id: flashplayerprojector Name: Flash Player Projector Website: 'https://www.adobe.com/support/flashplayer/debug_downloads.html' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [adobe_flash] ImageExtensions: [swf] StartupExecutable: ^flashplayer.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Flycast/emulator.yaml ================================================ Id: flycast Name: Flycast Website: 'https://github.com/flyinghead/flycast' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [sega_dreamcast] ImageExtensions: [gdi, cdi, chd, zip] StartupExecutable: ^flycast\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Fuse/emulator.yaml ================================================ Id: fuse Name: Fuse Website: 'http://fuse-emulator.sourceforge.net/' Profiles: - Name: Sinclair ZX Spectrum StartupArguments: '"{ImagePath}"' Platforms: [sinclair_zxspectrum] ImageExtensions: [tzx, tap, z80, rzx, scl, trd] StartupExecutable: ^fuse\.exe$ - Name: Sinclair ZX Spectrum +3 StartupArguments: '"{ImagePath}"' Platforms: [sinclair_zxspectrum3] ImageExtensions: [tzx, tap, z80, rzx, scl, trd] StartupExecutable: ^fuse\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/GBE+/emulator.yaml ================================================ Id: gbe+ Name: GBE+ Website: 'https://github.com/shonumi/gbe-plus' Profiles: - Name: Game Boy StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [gb] StartupExecutable: ^gbe_plus_qt*\.exe$ - Name: Game Boy Color StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [gbc] StartupExecutable: ^gbe_plus_qt*\.exe$ - Name: Game Boy Advance StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [gba] StartupExecutable: ^gbe_plus_qt*\.exe$ - Name: Nintendo DS StartupArguments: '"{ImagePath}"' Platforms: [nintendo_ds] ImageExtensions: [nds] StartupExecutable: ^gbe_plus_qt*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Gambatte/emulator.yaml ================================================ Id: gambatte Name: Gambatte Website: 'https://github.com/sinamas/gambatte' Profiles: - Name: Game Boy StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [gb, dmg, zip, gz] StartupExecutable: ^gambatte\.exe$ - Name: Game Boy Color StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [gbc, sgb, zip, gz] StartupExecutable: ^gambatte\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/KegaFusion/emulator.yaml ================================================ Id: kegafusion Name: Kega Fusion Website: 'https://www.carpeludum.com/kega-fusion/' Profiles: - Name: Sega Genesis StartupArguments: '"{ImagePath}" -auto -fullscreen' Platforms: [sega_genesis] ImageExtensions: [bin, smd, md, raw, gen, zip] StartupExecutable: ^Fusion\.exe$ - Name: Sega 32X StartupArguments: '"{ImagePath}" -auto -fullscreen' Platforms: [sega_32x] ImageExtensions: [32x, raw, zip] StartupExecutable: ^Fusion\.exe$ - Name: Sega CD StartupArguments: '"{ImagePath}" -auto -fullscreen' Platforms: [sega_cd] ImageExtensions: [cue, bin, iso, raw, zip] StartupExecutable: ^Fusion\.exe$ - Name: Sega Master System StartupArguments: '"{ImagePath}" -auto -fullscreen' Platforms: [sega_mastersystem] ImageExtensions: [sms, sg, sc, mv, bin, raw, zip] StartupExecutable: ^Fusion\.exe$ - Name: Sega Game Gear StartupArguments: '"{ImagePath}" -auto -fullscreen' Platforms: [sega_gamegear] ImageExtensions: [bin, gg, raw, zip] StartupExecutable: ^Fusion\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Lime3DS/emulator.yaml ================================================ Id: lime3ds Name: Lime3DS Website: 'https://github.com/Lime3DS/lime3ds-archive' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_3ds] ImageExtensions: [3ds, 3dsx, cci, cxi, elf, cia] StartupExecutable: ^lime3ds\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/LocaleEmulator/emulator.yaml ================================================ Id: localeemulator Name: Locale Emulator Website: 'https://pooi.moe/Locale-Emulator/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [pc_windows] ImageExtensions: [exe] StartupExecutable: ^LEProc\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/M64Py/emulator.yaml ================================================ Id: m64py Name: M64Py Website: 'https://m64py.sourceforge.net/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_64] StartupExecutable: ^m64py\.exe$ ImageExtensions: [gzip, bzip2, zip, rar, 7z, n64, z64, v64, ndd, d64] ================================================ FILE: source/Playnite/Emulation/Emulators/Mednafen/emulator.yaml ================================================ Id: mednafen Name: Mednafen Website: 'https://mednafen.github.io/' Profiles: - Name: Apple II StartupArguments: '"{ImagePath}"' Platforms: [apple_2] ImageExtensions: [d13, sk, do, po, woz, zip] StartupExecutable: ^mednafen\.exe$ - Name: Atari Lynx StartupArguments: '"{ImagePath}"' Platforms: [atari_lynx] ImageExtensions: [lnx, zip] StartupExecutable: ^mednafen\.exe$ - Name: NEC TurboGrafx 16 StartupArguments: '"{ImagePath}"' Platforms: [nec_turbografx_16] ImageExtensions: [pce, zip, m3u] StartupExecutable: ^mednafen\.exe$ - Name: NEC TurboGrafx-CD StartupArguments: '"{ImagePath}"' Platforms: [nec_turbografx_cd] ImageExtensions: [cue, ccd, chd, m3u] StartupExecutable: ^mednafen\.exe$ - Name: NEC PC-FX StartupArguments: '"{ImagePath}"' Platforms: [nec_pcfx] ImageExtensions: [cue, ccd, chd, zip] StartupExecutable: ^mednafen\.exe$ - Name: Nintendo Entertainment System StartupArguments: '"{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [nes, zip] StartupExecutable: ^mednafen\.exe$ - Name: Nintendo Game Boy StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [gb, zip] StartupExecutable: ^mednafen\.exe$ - Name: Nintendo Game Boy Color StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [gbc, zip] StartupExecutable: ^mednafen\.exe$ - Name: Nintendo Game Boy Advance StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [gba, zip] StartupExecutable: ^mednafen\.exe$ - Name: Nintendo Virtual Boy StartupArguments: '"{ImagePath}"' Platforms: [nintendo_virtualboy] ImageExtensions: [vb, zip] StartupExecutable: ^mednafen\.exe$ - Name: PC Engine SuperGrafx StartupArguments: '"{ImagePath}"' Platforms: [nec_supergrafx] ImageExtensions: [sgx, zip] StartupExecutable: ^mednafen\.exe$ - Name: Sega Game Gear StartupArguments: '"{ImagePath}"' Platforms: [sega_gamegear] ImageExtensions: [sgg, zip] StartupExecutable: ^mednafen\.exe$ - Name: Sega Genesis StartupArguments: '"{ImagePath}"' Platforms: [sega_genesis] ImageExtensions: [gen, md, bin, zip] StartupExecutable: ^mednafen\.exe$ - Name: Sega Master System StartupArguments: '"{ImagePath}"' Platforms: [sega_mastersystem] ImageExtensions: [sms, zip] StartupExecutable: ^mednafen\.exe$ - Name: Sega Saturn StartupArguments: '"{ImagePath}"' Platforms: [sega_saturn] ImageExtensions: [chd, iso, bin, cue, m3u] StartupExecutable: ^mednafen\.exe$ - Name: SNK Neo Geo Pocket StartupArguments: '"{ImagePath}"' Platforms: [snk_neogeopocket] ImageExtensions: [ngp, zip] StartupExecutable: ^mednafen\.exe$ - Name: SNK Neo Geo Pocket Color StartupArguments: '"{ImagePath}"' Platforms: [snk_neogeopocket_color] ImageExtensions: [ngc, zip] StartupExecutable: ^mednafen\.exe$ - Name: Sony PlayStation StartupArguments: '"{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [chd, cue, ccd, mds, m3u] StartupExecutable: ^mednafen\.exe$ - Name: Super Nintendo Entertainment System StartupArguments: '"{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [smc, sfc, zip] StartupExecutable: ^mednafen\.exe$ - Name: Bandai WonderSwan StartupArguments: '"{ImagePath}"' Platforms: [bandai_wonderswan] ImageExtensions: [ws, zip] StartupExecutable: ^mednafen\.exe$ - Name: Bandai WonderSwan Color StartupArguments: '"{ImagePath}"' Platforms: [bandai_wonderswan_color] ImageExtensions: [wsc, zip] StartupExecutable: ^mednafen\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Mesen/emulator.yaml ================================================ Id: mesen Name: Mesen Website: 'https://www.mesen.ca/' Profiles: - Name: Default StartupArguments: '/fullscreen "{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [nes, unf, fds, nsf, nsfe, zip, 7z, ips, bps, ups] StartupExecutable: ^Mesen\.exe$ - Name: Game Boy StartupArguments: '/fullscreen "{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [gb, gbs, zip] StartupExecutable: ^Mesen\.exe$ - Name: Game Boy Color StartupArguments: '/fullscreen "{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [gbc, zip] StartupExecutable: ^Mesen\.exe$ - Name: NES StartupArguments: '/fullscreen "{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [nes, fds, unif, studybox, nsf, nsfe, zip] StartupExecutable: ^Mesen\.exe$ - Name: PC Engine StartupArguments: '/fullscreen "{ImagePath}"' Platforms: [nec_turbografx_16] ImageExtensions: [pce, hes, zip] StartupExecutable: ^Mesen\.exe$ - Name: PC Engine CD StartupArguments: '/fullscreen "{ImagePath}"' Platforms: [nec_turbografx_cd] ImageExtensions: [cue, zip] StartupExecutable: ^Mesen\.exe$ - Name: PC Engine SuperGrafx StartupArguments: '/fullscreen "{ImagePath}"' Platforms: [nec_supergrafx] ImageExtensions: [pce, sgx, zip] StartupExecutable: ^Mesen\.exe$ - Name: SNES StartupArguments: '/fullscreen "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [sfc, fig, smc, spc, zip] StartupExecutable: ^Mesen\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Mesen-S/emulator.yaml ================================================ Id: mesen-s Name: Mesen-S Website: 'https://github.com/SourMesen/Mesen-S' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [sfc, smc, zip, 7z] StartupExecutable: ^Mesen-S\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/NanoboyAdvance/emulator.yaml ================================================ Id: nanoboyadvance Name: NanoboyAdvance Website: 'https://github.com/fleroviux/NanoboyAdvance' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [gba, zip] StartupExecutable: ^NanoboyAdvance\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Nestopia/emulator.yaml ================================================ Id: nestopia Name: Nestopia Website: 'http://nestopia.sourceforge.net/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [nes, unf, fds, nsf, zip] StartupExecutable: ^nestopia\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/NullDC/emulator.yaml ================================================ Id: nulldc Name: NullDC Website: 'http://segaretro.org/NullDC' Profiles: - Name: Default StartupArguments: '-config nullDC_GUI:Fullscreen=1 -config nullDC:Emulator.Autostart=1 -config ImageReader:LoadDefaultImage=1 -config ImageReader:DefaultImage="{ImagePath}"' Platforms: [sega_dreamcast] ImageExtensions: [iso, bwt, cdi, b5t, b6t, ccd, cue, mds, nrg, pdi, gdi] StartupExecutable: ^nullDC.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/PCSX2/emulator.yaml ================================================ Id: pcsx2 Name: PCSX2 Website: 'https://pcsx2.net/' Profiles: - Name: Default 32bit StartupArguments: '"{ImagePath}" --nogui --fullboot --fullscreen' Platforms: [sony_playstation2] ImageExtensions: [iso, bin, mdf, nrg, img, gz, cso, chd, m3u] StartupExecutable: ^pcsx2\.exe$ - Name: Default 64bit StartupArguments: '"{ImagePath}" --nogui --fullboot --fullscreen' Platforms: [sony_playstation2] ImageExtensions: [iso, bin, mdf, nrg, img, gz, cso, chd, m3u] StartupExecutable: ^pcsx2x64\.exe$ - Name: Default QT StartupArguments: '-fullscreen -slowboot -- {ImagePath}' Platforms: [sony_playstation2] ImageExtensions: [iso, bin, mdf, nrg, img, gz, cso, chd, m3u] StartupExecutable: ^pcsx2-qt\.exe$ - Name: 64bit AVX2 StartupArguments: '"{ImagePath}" --nogui --fullboot --fullscreen' Platforms: [sony_playstation2] ImageExtensions: [iso, bin, mdf, nrg, img, gz, cso, chd, m3u] StartupExecutable: ^pcsx2x64-avx2\.exe$ - Name: 64bit AVX2 QT StartupArguments: '-fullscreen -slowboot -- {ImagePath}' Platforms: [sony_playstation2] ImageExtensions: [iso, bin, mdf, nrg, img, gz, cso, chd, m3u] StartupExecutable: ^pcsx2-qtx64-avx2\.exe$ - Name: 64bit SSE4 QT StartupArguments: '-fullscreen -slowboot -- {ImagePath}' Platforms: [sony_playstation2] ImageExtensions: [iso, bin, mdf, nrg, img, gz, cso, chd, m3u] StartupExecutable: ^pcsx2-qtx64\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/PCSXR-PGXP/emulator.yaml ================================================ Id: pcsxr-pgxp Name: PCSXR-PGXP Website: 'https://github.com/iCatButler/pcsxr/' Profiles: - Name: Default StartupArguments: '-cdfile "{ImagePath}" -nogui -slowboot' Platforms: [sony_playstation] ImageExtensions: [bin, iso, img, cue] StartupExecutable: ^pcsxr-pgxp\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/PCem/emulator.yaml ================================================ Id: pcem Name: PCem Website: 'https://pcem-emulator.co.uk/' Profiles: - Name: Default StartupArguments: '--config "{ImagePath}"' Platforms: [pc_windows, pc_dos] ImageExtensions: [cfg] StartupExecutable: ^pcem\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/PPSSPP/emulator.yaml ================================================ Id: ppsspp Name: PPSSPP Website: 'https://www.ppsspp.org/' Profiles: - Name: 64bit StartupArguments: '"{ImagePath}" --pause-menu-exit --fullscreen' Platforms: [sony_psp] ImageExtensions: [iso, cso, pbp, chd] StartupExecutable: ^PPSSPPWindows64\.exe$ - Name: 32bit StartupArguments: '"{ImagePath}" --pause-menu-exit --fullscreen' Platforms: [sony_psp] ImageExtensions: [iso, cso, pbp, chd] StartupExecutable: ^PPSSPPWindows\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Project64/emulator.yaml ================================================ Id: project64 Name: Project64 Website: 'https://www.pj64-emu.com/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' ImageExtensions: [rom, n64, v64, z64, jap, pal, usa, zip, 7z] Platforms: [nintendo_64] StartupExecutable: ^Project64\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/RPCS3/emulator.yaml ================================================ Id: rpcs3 Name: RPCS3 Website: 'https://rpcs3.net/' Profiles: - Name: Default Platforms: [sony_playstation3] ProfileFiles: ['qt\plugins\platforms\qwindows.dll'] StartupExecutable: ^rpcs3.*\.exe$ StartupArguments: '"{ImagePath}"' ScriptGameImport: true - Name: Default QT6 Platforms: [sony_playstation3] ProfileFiles: ['qt6\plugins\platforms\qwindows.dll'] StartupExecutable: ^rpcs3.*\.exe$ StartupArguments: '"{ImagePath}"' ScriptGameImport: true ================================================ FILE: source/Playnite/Emulation/Emulators/RPCS3/importGames.ps1 ================================================ param( $ImportArgs ) if (-not [System.IO.Directory]::Exists($ImportArgs.ScanDirectory)) { return } function Get-NullTerminatedString { param([Array]$bytes, [int]$offset) $strBytes = @() for ($j = $offset; $j -lt $bytes.Count; $j++) { $b = $bytes[$j] if($b -eq 0) { break } #strings end on null terminator $strBytes += $b } return [System.Text.Encoding]::UTF8.GetString($strBytes) } function Get-ParamSfoValue { param([string]$path, [string]$key) #thanks to https://psdevwiki.com/ps3/PARAM.SFO [byte[]]$bytes = Get-Content -LiteralPath $path -Encoding Byte -Raw $keyTableOffset = [System.BitConverter]::ToUInt32($bytes, 0x08) $dataTableOffset = [System.BitConverter]::ToUInt32($bytes, 0x0c) $indexTableOffset = 0x14 $indexRowLength = 0x10 #go through each index table row for ($i = $indexTableOffset; $i -lt $keyTableOffset; $i += $indexRowLength) { $relativeKeyOffset = [System.BitConverter]::ToUInt16($bytes, $i) $foundKey = Get-NullTerminatedString $bytes ($keyTableOffset + $relativeKeyOffset) if($foundKey -ne $key) { continue } $dataFormat = [System.BitConverter]::ToUInt16($bytes, $i + 0x02) $dataLength = [System.BitConverter]::ToUInt32($bytes, $i + 0x04) $relativeDataOffset = [System.BitConverter]::ToUInt32($bytes, $i + 0x0c) if ($dataFormat -eq 1028) #uint32 { $data = [System.BitConverter]::ToUInt32($bytes, $dataTableOffset + $relativeDataOffset) } else #string (usually null-terminated) { $data = [System.Text.Encoding]::UTF8.GetString($bytes, $dataTableOffset + $relativeDataOffset, $dataLength).Trim("`0") } return $data } return $null } [array]$games = Get-ChildItem -LiteralPath $ImportArgs.ScanDirectory -Recurse | Where { $_.Name -eq "ISO.BIN.EDAT" -or $_.Name -eq "EBOOT.BIN" -or $_.Extension -ieq ".iso" } foreach ($game in $games) { $anyFunc = [Func[string,bool]]{ param($a) $a.Equals($game.FullName, 'OrdinalIgnoreCase') } if ([System.Linq.Enumerable]::Any($ImportArgs.ImportedFiles, $anyFunc)) { continue } $scannedGame = New-Object "Playnite.Emulators.ScriptScannedGame" $scannedGame.Path = $game.FullName if ($game.Extension -ieq '.iso') { try { $DiskImage = Mount-DiskImage -ImagePath $game.FullName -StorageType ISO -NoDriveLetter -PassThru New-PSDrive -Name ISOFile -PSProvider FileSystem -Root (Get-Volume -DiskImage $DiskImage).UniqueId | Out-Null Push-Location ISOFile: | Out-Null try { $paramSfoPath = (Get-ChildItem ISOFile: -Filter "param.sfo" -Recurse -File | Where { $_.DirectoryName -like "*PS3_GAME*" })[0].FullName $scannedGame.Serial = Get-ParamSfoValue $paramSfoPath "TITLE_ID" if ($null -ne $scannedGame.Serial) { $scannedGame.Name = Get-ParamSfoValue $paramSfoPath "TITLE" } } finally { Pop-Location | Out-Null Remove-PSDrive ISOFile | Out-Null Dismount-DiskImage -StorageType ISO -ImagePath $DiskImage.ImagePath | Out-Null } } catch { $__logger.Error($_.Exception, "Failed to scan PS3 .iso PARAM.SFO file $paramSfoPath") $__logger.Error($_.ScriptStackTrace) $scannedGame.Name = [System.IO.Path]::GetFileNameWithoutExtension($game.Name) if ($game.Name -match '(BLUS|BLES|NPUB|NPEB)\d{5}') { $scannedGame.Serial = $matches[0] } } $scannedGame } else { $parentDir = $game.Directory.Parent.FullName $paramSfoPath = Join-Path $parentDir "PARAM.SFO" if (Test-Path -LiteralPath $paramSfoPath -PathType Leaf) { try { $scannedGame.Serial = Get-ParamSfoValue $paramSfoPath "TITLE_ID" if ($null -ne $scannedGame.Serial) { $scannedGame.Name = Get-ParamSfoValue $paramSfoPath "TITLE" $scannedGame } } catch { $__logger.Error($_.Exception, "Failed to scan PS3 PARAM.SFO file $paramSfoPath") $__logger.Error($_.ScriptStackTrace) } } } } ================================================ FILE: source/Playnite/Emulation/Emulators/Reicast/emulator.yaml ================================================ Id: reicast Name: Reicast Website: 'https://reicast.com/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [sega_dreamcast] ImageExtensions: [gdi, cdi, chd] StartupExecutable: ^reicast.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/RetroArch/emulator.yaml ================================================ Id: retroarch Name: RetroArch Website: 'http://www.retroarch.com/' Profiles: - Name: 4DO StartupArguments: '-L ".\cores\4do_libretro.dll" "{ImagePath}"' Platforms: [3do] ImageExtensions: [7z, bin, chd, cue, iso, zip] ProfileFiles: ['cores\4do_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: a5200 StartupArguments: '-L ".\cores\a5200_libretro.dll" "{ImagePath}"' Platforms: [atari_5200] ImageExtensions: [7z, a52, bin, zip] ProfileFiles: ['cores\a5200_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Atari800 StartupArguments: '-L ".\cores\atari800_libretro.dll" "{ImagePath}"' Platforms: [atari_5200, atari_8bit] ImageExtensions: [7z, a52, atr, atx, bin, car, cas, com, dcm, m3u, rom, xex, xfd, zip] ProfileFiles: ['cores\atari800_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle bsnes StartupArguments: '-L ".\cores\mednafen_snes_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bs, fig, sfc, smc, st, zip] ProfileFiles: ['cores\mednafen_snes_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle GBA StartupArguments: '-L ".\cores\mednafen_gba_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [7z, agb, bin, gba, zip] ProfileFiles: ['cores\mednafen_gba_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle Lynx StartupArguments: '-L ".\cores\mednafen_lynx_libretro.dll" "{ImagePath}"' Platforms: [atari_lynx] ImageExtensions: [7z, lnx, lyx, o, zip] ProfileFiles: ['cores\mednafen_lynx_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle NeoPop StartupArguments: '-L ".\cores\mednafen_ngp_libretro.dll" "{ImagePath}"' Platforms: [snk_neogeopocket, snk_neogeopocket_color] ImageExtensions: [7z, ngc, ngp, ngpc, npc, zip] ProfileFiles: ['cores\mednafen_ngp_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle PC-FX StartupArguments: '-L ".\cores\mednafen_pcfx_libretro.dll" "{ImagePath}"' Platforms: [nec_pcfx] ImageExtensions: [7z, ccd, chd, cue, toc, zip] ProfileFiles: ['cores\mednafen_pcfx_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle PCE StartupArguments: '-L ".\cores\mednafen_pce_libretro.dll" "{ImagePath}"' Platforms: [nec_supergrafx, nec_turbografx_16, nec_turbografx_cd] ImageExtensions: [7z, ccd, chd, cue, m3u, pce, sgx, toc, zip] ProfileFiles: ['cores\mednafen_pce_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle PCE Fast StartupArguments: '-L ".\cores\mednafen_pce_fast_libretro.dll" "{ImagePath}"' Platforms: [nec_turbografx_16, nec_turbografx_cd] ImageExtensions: [7z, ccd, chd, cue, m3u, pce, toc, zip] ProfileFiles: ['cores\mednafen_pce_fast_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle PSX StartupArguments: '-L ".\cores\mednafen_psx_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, ccd, chd, cue, exe, m3u, pbp, toc, zip] ProfileFiles: ['cores\mednafen_psx_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle PSX HW StartupArguments: '-L ".\cores\mednafen_psx_hw_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, ccd, chd, cue, exe, m3u, pbp, toc, zip] ProfileFiles: ['cores\mednafen_psx_hw_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle Saturn StartupArguments: '-L ".\cores\mednafen_saturn_libretro.dll" "{ImagePath}"' Platforms: [sega_saturn] ImageExtensions: [7z, ccd, chd, cue, m3u, toc, zip] ProfileFiles: ['cores\mednafen_saturn_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle Supafaust StartupArguments: '-L ".\cores\mednafen_supafaust_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bsx, dx2, fig, gd3, gd7, sfc, smc, swc, zip] ProfileFiles: ['cores\mednafen_supafaust_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle SuperGrafx StartupArguments: '-L ".\cores\mednafen_supergrafx_libretro.dll" "{ImagePath}"' Platforms: [nec_supergrafx, nec_turbografx_16, nec_turbografx_cd] ImageExtensions: [7z, ccd, chd, cue, pce, sgx, zip] ProfileFiles: ['cores\mednafen_supergrafx_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle VB StartupArguments: '-L ".\cores\mednafen_vb_libretro.dll" "{ImagePath}"' Platforms: [nintendo_virtualboy] ImageExtensions: [7z, bin, vb, vboy, zip] ProfileFiles: ['cores\mednafen_vb_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Beetle WonderSwan StartupArguments: '-L ".\cores\mednafen_wswan_libretro.dll" "{ImagePath}"' Platforms: [bandai_wonderswan, bandai_wonderswan_color] ImageExtensions: [7z, pc2, pcv2, ws, wsc, zip] ProfileFiles: ['cores\mednafen_wswan_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: BlastEm StartupArguments: '-L ".\cores\blastem_libretro.dll" "{ImagePath}"' Platforms: [sega_genesis] ImageExtensions: [68k, 7z, bin, gen, md, sgd, smd, zip] ProfileFiles: ['cores\blastem_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: blueMSX StartupArguments: '-L ".\cores\bluemsx_libretro.dll" "{ImagePath}"' Platforms: [microsoft_msx, microsoft_msx2] ImageExtensions: [7z, cas, col, dsk, m3u, mx1, mx2, ri, rom, sc, sf, sg, zip] ProfileFiles: ['cores\bluemsx_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bnes/higan StartupArguments: '-L ".\cores\bnes_libretro.dll" "{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [7z, nes, zip] ProfileFiles: ['cores\bnes_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Boytacean StartupArguments: '-L ".\cores\boytacean_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor] ImageExtensions: [7z, gb, gbc, zip] ProfileFiles: ['cores\boytacean_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes StartupArguments: '-L ".\cores\bsnes_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bs, fig, gb, gbc, sfc, smc, swc, zip] ProfileFiles: ['cores\bsnes_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes 2014 Accuracy StartupArguments: '-L ".\cores\bsnes2014_accuracy_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bml, bs, gb, gbc, sfc, smc, st, zip] ProfileFiles: ['cores\bsnes2014_accuracy_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes 2014 Balanced StartupArguments: '-L ".\cores\bsnes2014_balanced_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bml, bs, gb, gbc, sfc, smc, st, zip] ProfileFiles: ['cores\bsnes2014_balanced_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes 2014 Performance StartupArguments: '-L ".\cores\bsnes2014_performance_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bml, bs, gb, gbc, sfc, smc, st, zip] ProfileFiles: ['cores\bsnes2014_performance_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes C++98 (v085) StartupArguments: '-L ".\cores\bsnes_cplusplus98_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bs, gb, gbc, sfc, smc, st, zip] ProfileFiles: ['cores\bsnes_cplusplus98_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes-hd beta StartupArguments: '-L ".\cores\bsnes_hd_beta_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bs, fig, gb, gbc, sfc, smc, swc, zip] ProfileFiles: ['cores\bsnes_hd_beta_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes-jg StartupArguments: '-L ".\cores\bsnes-jg_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bs, gb, gbc, sfc, smc, st, zip] ProfileFiles: ['cores\bsnes-jg_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes-mercury Accuracy StartupArguments: '-L ".\cores\bsnes_mercury_accuracy_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bml, bs, gb, gbc, sfc, smc, st, zip] ProfileFiles: ['cores\bsnes_mercury_accuracy_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes-mercury Balanced StartupArguments: '-L ".\cores\bsnes_mercury_balanced_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bml, bs, gb, gbc, sfc, smc, st, zip] ProfileFiles: ['cores\bsnes_mercury_balanced_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: bsnes-mercury Performance StartupArguments: '-L ".\cores\bsnes_mercury_performance_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bml, bs, gb, gbc, sfc, smc, st, zip] ProfileFiles: ['cores\bsnes_mercury_performance_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Caprice32 StartupArguments: '-L ".\cores\cap32_libretro.dll" "{ImagePath}"' Platforms: [amstrad_cpc] ImageExtensions: [7z, cdt, cpr, dsk, m3u, sna, tap, voc, zip] ProfileFiles: ['cores\cap32_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: ChimeraSNES StartupArguments: '-L ".\cores\chimerasnes_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bs, bsx, dx2, fig, gd3, gd7, sfc, smc, st, swc, zip] ProfileFiles: ['cores\chimerasnes_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Citra StartupArguments: '-L ".\cores\citra_libretro.dll" "{ImagePath}"' Platforms: [nintendo_3ds] ImageExtensions: [3ds, 3dsx, 7z, app, axf, cci, cxi, elf, zip] ProfileFiles: ['cores\citra_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Citra 2018 StartupArguments: '-L ".\cores\citra2018_libretro.dll" "{ImagePath}"' Platforms: [nintendo_3ds] ImageExtensions: [3ds, 3dsx, 7z, app, axf, cci, cxi, elf, zip] ProfileFiles: ['cores\citra2018_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Citra Canary/Experimental StartupArguments: '-L ".\cores\citra_canary_libretro.dll" "{ImagePath}"' Platforms: [nintendo_3ds] ImageExtensions: [3ds, 3dsx, 7z, app, axf, cci, cxi, elf, zip] ProfileFiles: ['cores\citra_canary_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: CrocoDS StartupArguments: '-L ".\cores\crocods_libretro.dll" "{ImagePath}"' Platforms: [amstrad_cpc] ImageExtensions: [7z, dsk, kcr, sna, zip] ProfileFiles: ['cores\crocods_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DeSmuME StartupArguments: '-L ".\cores\desmume_libretro.dll" "{ImagePath}"' Platforms: [nintendo_ds] ImageExtensions: [7z, bin, ids, nds, zip] ProfileFiles: ['cores\desmume_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DeSmuME 2015 StartupArguments: '-L ".\cores\desmume2015_libretro.dll" "{ImagePath}"' Platforms: [nintendo_ds] ImageExtensions: [7z, bin, ids, nds, zip] ProfileFiles: ['cores\desmume2015_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DirectXBox StartupArguments: '-L ".\cores\directxbox_libretro.dll" "{ImagePath}"' Platforms: [xbox] ImageExtensions: [7z, iso, zip] ProfileFiles: ['cores\directxbox_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Dolphin StartupArguments: '-L ".\cores\dolphin_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gamecube, nintendo_wii] ImageExtensions: [7z, ciso, dff, dol, elf, gcm, gcz, iso, m3u, rvz, tgc, wad, wbfs, wia, zip] ProfileFiles: ['cores\dolphin_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Dolphin Launcher StartupArguments: '-L ".\cores\dolphin_launcher_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gamecube, nintendo_wii] ImageExtensions: [7z, ciso, dol, elf, gcm, gcz, iso, wad, wbfs, zip] ProfileFiles: ['cores\dolphin_launcher_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DOSBox StartupArguments: '-L ".\cores\dosbox_libretro.dll" "{ImagePath}"' Platforms: [pc_dos] ImageExtensions: [7z, bat, com, conf, exe, zip] ProfileFiles: ['cores\dosbox_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DOSBox-core StartupArguments: '-L ".\cores\dosbox_core_libretro.dll" "{ImagePath}"' Platforms: [pc_dos] ImageExtensions: [/, 7z, bat, com, conf, cue, exe, img, iso, zip] ProfileFiles: ['cores\dosbox_core_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DOSBox-Pure StartupArguments: '-L ".\cores\dosbox_pure_libretro.dll" "{ImagePath}"' Platforms: [pc_dos] ImageExtensions: [/, 7z, bat, chd, com, conf, cue, dosz, exe, ima, img, ins, iso, jrc, m3u, m3u8, tc, vhd, zip] ProfileFiles: ['cores\dosbox_pure_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DOSBox-SVN StartupArguments: '-L ".\cores\dosbox_svn_libretro.dll" "{ImagePath}"' Platforms: [pc_dos] ImageExtensions: [/, 7z, bat, com, conf, cue, exe, img, iso, zip] ProfileFiles: ['cores\dosbox_svn_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DOSBox-SVN CE StartupArguments: '-L ".\cores\dosbox_svn_ce_libretro.dll" "{ImagePath}"' Platforms: [pc_dos] ImageExtensions: [7z, bat, com, conf, cue, exe, iso, zip] ProfileFiles: ['cores\dosbox_svn_ce_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DoubleCherryGB StartupArguments: '-L ".\cores\DoubleCherryGB_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor] ImageExtensions: [7z, cgb, dmg, gb, gbc, sgb, zip] ProfileFiles: ['cores\DoubleCherryGB_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: DuckStation StartupArguments: '-L ".\cores\duckstation_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, bin, chd, cue, ecm, exe, img, iso, m3u, mds, pbp, psexe, psf, zip] ProfileFiles: ['cores\duckstation_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Emux GB StartupArguments: '-L ".\cores\emux_gb_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor] ImageExtensions: [7z, bin, gb, gbc, rom, zip] ProfileFiles: ['cores\emux_gb_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Emux NES StartupArguments: '-L ".\cores\emux_nes_libretro.dll" "{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [7z, bin, nes, rom, zip] ProfileFiles: ['cores\emux_nes_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Emux SMS StartupArguments: '-L ".\cores\emux_sms_libretro.dll" "{ImagePath}"' Platforms: [sega_mastersystem] ImageExtensions: [7z, bin, bms, rom, sms, zip] ProfileFiles: ['cores\emux_sms_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: FCEUmm StartupArguments: '-L ".\cores\fceumm_libretro.dll" "{ImagePath}"' Platforms: [nintendo_famicom_disk, nintendo_nes] ImageExtensions: [7z, fds, nes, unf, unif, zip] ProfileFiles: ['cores\fceumm_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: fixGB StartupArguments: '-L ".\cores\fixgb_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor] ImageExtensions: [7z, gb, gbc, gbs, zip] ProfileFiles: ['cores\fixgb_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: fixNES StartupArguments: '-L ".\cores\fixnes_libretro.dll" "{ImagePath}"' Platforms: [nintendo_famicom_disk, nintendo_nes] ImageExtensions: [7z, fds, nes, nsf, qd, zip] ProfileFiles: ['cores\fixnes_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Flycast StartupArguments: '-L ".\cores\flycast_libretro.dll" "{ImagePath}"' Platforms: [sega_dreamcast] ImageExtensions: [7z, bin, cdi, chd, cue, dat, elf, gdi, lst, m3u, zip] ProfileFiles: ['cores\flycast_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Flycast GLES2 StartupArguments: '-L ".\cores\flycast_gles2_libretro.dll" "{ImagePath}"' Platforms: [sega_dreamcast] ImageExtensions: [7z, bin, cdi, chd, cue, dat, elf, gdi, iso, lst, m3u, zip] ProfileFiles: ['cores\flycast_gles2_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: fMSX StartupArguments: '-L ".\cores\fmsx_libretro.dll" "{ImagePath}"' Platforms: [microsoft_msx, microsoft_msx2] ImageExtensions: [7z, cas, dsk, fdi, m3u, mx1, mx2, rom, zip] ProfileFiles: ['cores\fmsx_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: FreeIntv StartupArguments: '-L ".\cores\freeintv_libretro.dll" "{ImagePath}"' Platforms: [mattel_intellivision] ImageExtensions: [7z, bin, int, rom, zip] ProfileFiles: ['cores\freeintv_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Frodo StartupArguments: '-L ".\cores\frodo_libretro.dll" "{ImagePath}"' Platforms: [commodore_64] ImageExtensions: [7z, d64, lnx, lyx, p00, t64, x64, zip] ProfileFiles: ['cores\frodo_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: FS-UAE StartupArguments: '-L ".\cores\fsuae_libretro.dll" "{ImagePath}"' Platforms: [commodore_amiga] ImageExtensions: [7z, adf, fs-uae, ipf, zip] ProfileFiles: ['cores\fsuae_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Fuse StartupArguments: '-L ".\cores\fuse_libretro.dll" "{ImagePath}"' Platforms: [sinclair_zxspectrum, sinclair_zxspectrum3] ImageExtensions: [7z, dck, dsk, rzx, scl, sna, szx, tap, trd, tzx, z80, zip] ProfileFiles: ['cores\fuse_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Gambatte StartupArguments: '-L ".\cores\gambatte_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor] ImageExtensions: [7z, dmg, gb, gbc, zip] ProfileFiles: ['cores\gambatte_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Gearboy StartupArguments: '-L ".\cores\gearboy_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor] ImageExtensions: [7z, cgb, dmg, gb, gbc, sgb, zip] ProfileFiles: ['cores\gearboy_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Gearcoleco StartupArguments: '-L ".\cores\gearcoleco_libretro.dll" "{ImagePath}"' Platforms: [coleco_vision] ImageExtensions: [7z, bin, col, cv, rom, zip] ProfileFiles: ['cores\gearcoleco_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Gearsystem StartupArguments: '-L ".\cores\gearsystem_libretro.dll" "{ImagePath}"' Platforms: [coleco_vision, sega_gamegear, sega_mastersystem, sega_sg1000] ImageExtensions: [7z, bin, gg, rom, sg, sms, zip] ProfileFiles: ['cores\gearsystem_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Genesis Plus GX StartupArguments: '-L ".\cores\genesis_plus_gx_libretro.dll" "{ImagePath}"' Platforms: [sega_cd, sega_gamegear, sega_genesis, sega_mastersystem] ImageExtensions: [68k, 7z, bin, bms, chd, cue, gen, gg, iso, m3u, md, mdx, sg, sgd, smd, sms, zip] ProfileFiles: ['cores\genesis_plus_gx_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Genesis Plus GX Wide StartupArguments: '-L ".\cores\genesis_plus_gx_wide_libretro.dll" "{ImagePath}"' Platforms: [sega_cd, sega_gamegear, sega_genesis, sega_mastersystem] ImageExtensions: [68k, 7z, bin, bms, chd, cue, gen, gg, iso, m3u, md, mdx, sg, sgd, smd, sms, zip] ProfileFiles: ['cores\genesis_plus_gx_wide_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: gpSP StartupArguments: '-L ".\cores\gpsp_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [7z, bin, gba, zip] ProfileFiles: ['cores\gpsp_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Handy StartupArguments: '-L ".\cores\handy_libretro.dll" "{ImagePath}"' Platforms: [atari_lynx] ImageExtensions: [7z, lnx, lyx, o, zip] ProfileFiles: ['cores\handy_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Hatari StartupArguments: '-L ".\cores\hatari_libretro.dll" "{ImagePath}"' Platforms: [atari_falcon030, atari_st] ImageExtensions: [7z, dim, gem, ide, ipf, m3u, msa, st, stx, vhd, zip] ProfileFiles: ['cores\hatari_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: higan (Super Famicom Accuracy) StartupArguments: '-L ".\cores\higan_sfc_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor, nintendo_super_nes] ImageExtensions: [7z, bml, gb, gbc, rom, sfc, smc, zip] ProfileFiles: ['cores\higan_sfc_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Holani StartupArguments: '-L ".\cores\holani_libretro.dll" "{ImagePath}"' Platforms: [atari_lynx] ImageExtensions: [7z, lnx, o, zip] ProfileFiles: ['cores\holani_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Ishiiruka StartupArguments: '-L ".\cores\ishiiruka_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gamecube, nintendo_wii] ImageExtensions: [7z, ciso, dff, dol, elf, gcm, gcz, iso, tgc, wad, wbfs, zip] ProfileFiles: ['cores\ishiiruka_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Kronos StartupArguments: '-L ".\cores\kronos_libretro.dll" "{ImagePath}"' Platforms: [sega_saturn] ImageExtensions: [7z, ccd, chd, cue, iso, m3u, mds, zip] ProfileFiles: ['cores\kronos_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: melonDS StartupArguments: '-L ".\cores\melonds_libretro.dll" "{ImagePath}"' Platforms: [nintendo_ds] ImageExtensions: [7z, dsi, ids, nds, zip] ProfileFiles: ['cores\melonds_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: melonDS DS StartupArguments: '-L ".\cores\melondsds_libretro.dll" "{ImagePath}"' Platforms: [nintendo_ds] ImageExtensions: [7z, dsi, ids, nds, zip] ProfileFiles: ['cores\melondsds_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Mesen StartupArguments: '-L ".\cores\mesen_libretro.dll" "{ImagePath}"' Platforms: [nintendo_famicom_disk, nintendo_nes] ImageExtensions: [7z, fds, nes, unf, unif, zip] ProfileFiles: ['cores\mesen_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Mesen-S StartupArguments: '-L ".\cores\mesen-s_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor, nintendo_super_nes] ImageExtensions: [7z, bs, fig, gb, gbc, sfc, smc, swc, zip] ProfileFiles: ['cores\mesen-s_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Meteor StartupArguments: '-L ".\cores\meteor_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [7z, gba, zip] ProfileFiles: ['cores\meteor_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: mGBA StartupArguments: '-L ".\cores\mgba_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboyadvance, nintendo_gameboycolor] ImageExtensions: [7z, gb, gba, gbc, zip] ProfileFiles: ['cores\mgba_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Mupen64Plus-Next StartupArguments: '-L ".\cores\mupen64plus_next_libretro.dll" "{ImagePath}"' Platforms: [nintendo_64] ImageExtensions: [7z, bin, n64, ndd, u1, v64, z64, zip] ProfileFiles: ['cores\mupen64plus_next_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Mupen64Plus-Next - Nintendo - Nintendo 64 (Mupen64Plus-Next GLES2) StartupArguments: '-L ".\cores\mupen64plus_next_gles2_libretro.dll" "{ImagePath}"' Platforms: [nintendo_64] ImageExtensions: [7z, bin, n64, ndd, u1, v64, z64, zip] ProfileFiles: ['cores\mupen64plus_next_gles2_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Mupen64Plus-Next (Develop) StartupArguments: '-L ".\cores\mupen64plus_next_develop_libretro.dll" "{ImagePath}"' Platforms: [nintendo_64] ImageExtensions: [7z, bin, n64, ndd, u1, v64, z64, zip] ProfileFiles: ['cores\mupen64plus_next_develop_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Mupen64Plus-Next GLES3 StartupArguments: '-L ".\cores\mupen64plus_next_gles3_libretro.dll" "{ImagePath}"' Platforms: [nintendo_64] ImageExtensions: [7z, bin, n64, ndd, u1, v64, z64, zip] ProfileFiles: ['cores\mupen64plus_next_gles3_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Neko Project II StartupArguments: '-L ".\cores\nekop2_libretro.dll" "{ImagePath}"' Platforms: [nec_pc98] ImageExtensions: [2hd, 7z, 88d, 98d, cmd, d88, d98, dup, fdd, fdi, hdd, hdi, hdm, nhd, tfd, thd, xdf, zip] ProfileFiles: ['cores\nekop2_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Neko Project II Kai StartupArguments: '-L ".\cores\np2kai_libretro.dll" "{ImagePath}"' Platforms: [nec_pc98] ImageExtensions: [2hd, 7z, 88d, 98d, cmd, d88, d98, dup, fdd, fdi, hdd, hdi, hdm, hdn, nhd, tfd, thd, xdf, zip] ProfileFiles: ['cores\np2kai_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: NeoCD StartupArguments: '-L ".\cores\neocd_libretro.dll" "{ImagePath}"' Platforms: [snk_neogeo_cd] ImageExtensions: [7z, chd, cue, zip] ProfileFiles: ['cores\neocd_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: nes StartupArguments: '-L ".\cores\nes_libretro.dll" "{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [7z, nes, zip] ProfileFiles: ['cores\nes_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Nestopia StartupArguments: '-L ".\cores\nestopia_libretro.dll" "{ImagePath}"' Platforms: [nintendo_famicom_disk, nintendo_nes] ImageExtensions: [7z, fds, nes, unf, unif, zip] ProfileFiles: ['cores\nestopia_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: NooDS StartupArguments: '-L ".\cores\noods_libretro.dll" "{ImagePath}"' Platforms: [nintendo_ds] ImageExtensions: [7z, nds, zip] ProfileFiles: ['cores\noods_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: nSide (Super Famicom Balanced) StartupArguments: '-L ".\cores\higan_sfc_balanced_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor, nintendo_super_nes] ImageExtensions: [7z, bml, gb, gbc, rom, sfc, smc, zip] ProfileFiles: ['cores\higan_sfc_balanced_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Opera StartupArguments: '-L ".\cores\opera_libretro.dll" "{ImagePath}"' Platforms: [3do] ImageExtensions: [7z, bin, chd, cue, iso, zip] ProfileFiles: ['cores\opera_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Panda3DS StartupArguments: '-L ".\cores\panda3ds_libretro.dll" "{ImagePath}"' Platforms: [nintendo_3ds] ImageExtensions: [3ds, 3dsx, 7z, app, axf, cci, cxi, elf, zip] ProfileFiles: ['cores\panda3ds_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: ParaLLEl (Debug) StartupArguments: '-L ".\cores\parallel_n64_debug_libretro.dll" "{ImagePath}"' Platforms: [nintendo_64] ImageExtensions: [7z, bin, n64, ndd, u1, v64, z64, zip] ProfileFiles: ['cores\parallel_n64_debug_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: ParaLLEl N64 StartupArguments: '-L ".\cores\parallel_n64_libretro.dll" "{ImagePath}"' Platforms: [nintendo_64] ImageExtensions: [7z, bin, n64, ndd, u1, v64, z64, zip] ProfileFiles: ['cores\parallel_n64_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PCem StartupArguments: '-L ".\cores\pcem_libretro.dll" "{ImagePath}"' Platforms: [pc_dos] ImageExtensions: [7z, bat, com, conf, exe, zip] ProfileFiles: ['cores\pcem_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PCSX ReARMed [Interpreter] StartupArguments: '-L ".\cores\pcsx_rearmed_interpreter_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, bin, cbn, chd, cue, img, mdf, pbp, toc, zip] ProfileFiles: ['cores\pcsx_rearmed_interpreter_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PCSX ReARMed [NEON] StartupArguments: '-L ".\cores\pcsx_rearmed_libretro_neon.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, bin, cbn, chd, cue, img, m3u, mdf, pbp, toc, zip] ProfileFiles: ['cores\pcsx_rearmed_libretro_neon.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PCSX ReARMed [NEON] - Sony - PlayStation (PCSX ReARMed) [NEON] StartupArguments: '-L ".\cores\pcsx_rearmed_neon_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, bin, cbn, chd, cue, img, m3u, mdf, pbp, toc, zip] ProfileFiles: ['cores\pcsx_rearmed_neon_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PCSX-ReARMed StartupArguments: '-L ".\cores\pcsx_rearmed_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, bin, cbn, ccd, chd, cue, exe, img, iso, m3u, mdf, pbp, toc, zip] ProfileFiles: ['cores\pcsx_rearmed_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PCSX1 StartupArguments: '-L ".\cores\pcsx1_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, bin, cbn, cue, img, m3u, mdf, pbp, toc, zip] ProfileFiles: ['cores\pcsx1_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PCSX2 StartupArguments: '-L ".\cores\pcsx2_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation2] ImageExtensions: [7z, bin, chd, ciso, cso, cue, dump, elf, gz, img, iso, m3u, mdf, nrg, zip] ProfileFiles: ['cores\pcsx2_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PicoDrive StartupArguments: '-L ".\cores\picodrive_libretro.dll" "{ImagePath}"' Platforms: [sega_32x, sega_cd, sega_genesis, sega_mastersystem] ImageExtensions: [32x, 68k, 7z, bin, chd, cue, gen, gg, iso, m3u, md, pco, sc, sg, sgd, smd, sms, zip] ProfileFiles: ['cores\picodrive_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Play! StartupArguments: '-L ".\cores\play_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation2] ImageExtensions: [7z, chd, cso, cue, elf, iso, isz, zip] ProfileFiles: ['cores\play_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Potator StartupArguments: '-L ".\cores\potator_libretro.dll" "{ImagePath}"' Platforms: [watara_supervision] ImageExtensions: [7z, bin, sv, zip] ProfileFiles: ['cores\potator_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PPSSPP StartupArguments: '-L ".\cores\ppsspp_libretro.dll" "{ImagePath}"' Platforms: [sony_psp] ImageExtensions: [7z, chd, cso, elf, iso, pbp, prx, zip] ProfileFiles: ['cores\ppsspp_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: ProSystem StartupArguments: '-L ".\cores\prosystem_libretro.dll" "{ImagePath}"' Platforms: [atari_7800] ImageExtensions: [7z, a78, bin, cdf, zip] ProfileFiles: ['cores\prosystem_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PUAE StartupArguments: '-L ".\cores\puae_libretro.dll" "{ImagePath}"' Platforms: [commodore_amiga] ImageExtensions: [7z, adf, adz, ccd, chd, cue, dms, fdi, hdf, hdz, info, ipf, iso, lha, m3u, mds, nrg, rp9, slave, uae, zip] ProfileFiles: ['cores\puae_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PUAE 2021 StartupArguments: '-L ".\cores\puae2021_libretro.dll" "{ImagePath}"' Platforms: [commodore_amiga] ImageExtensions: [7z, adf, adz, ccd, chd, cue, dms, fdi, hdf, hdz, info, ipf, iso, lha, m3u, mds, nrg, rp9, slave, uae, zip] ProfileFiles: ['cores\puae2021_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: PX68k StartupArguments: '-L ".\cores\px68k_libretro.dll" "{ImagePath}"' Platforms: [sharp_x68000] ImageExtensions: [2hd, 7z, 88d, cmd, d88, dim, dup, hdf, hdm, img, m3u, xdf, zip] ProfileFiles: ['cores\px68k_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: QuickNES StartupArguments: '-L ".\cores\quicknes_libretro.dll" "{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [7z, nes, zip] ProfileFiles: ['cores\quicknes_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: RACE StartupArguments: '-L ".\cores\race_libretro.dll" "{ImagePath}"' Platforms: [snk_neogeopocket, snk_neogeopocket_color] ImageExtensions: [7z, ngc, ngp, ngpc, npc, zip] ProfileFiles: ['cores\race_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Redream StartupArguments: '-L ".\cores\redream_libretro.dll" "{ImagePath}"' Platforms: [sega_dreamcast] ImageExtensions: [7z, cdi, chd, gdi, zip] ProfileFiles: ['cores\redream_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: RetroDream StartupArguments: '-L ".\cores\retrodream_libretro.dll" "{ImagePath}"' Platforms: [sega_dreamcast] ImageExtensions: [7z, cdi, chd, gdi, zip] ProfileFiles: ['cores\retrodream_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Rustation StartupArguments: '-L ".\cores\rustation_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, ccd, cue, exe, m3u, toc, zip] ProfileFiles: ['cores\rustation_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: SameBoy StartupArguments: '-L ".\cores\sameboy_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor] ImageExtensions: [7z, gb, gbc, zip] ProfileFiles: ['cores\sameboy_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Sinclair ZX 81 StartupArguments: '-L ".\cores\81_libretro.dll" "{ImagePath}"' Platforms: [sinclair_zx81] ImageExtensions: [7z, p, t81, tzx, zip] ProfileFiles: ['cores\81_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: SMS Plus GX StartupArguments: '-L ".\cores\smsplus_libretro.dll" "{ImagePath}"' Platforms: [coleco_vision, sega_gamegear, sega_mastersystem, sega_sg1000] ImageExtensions: [7z, bin, col, gg, rom, sg, sms, zip] ProfileFiles: ['cores\smsplus_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Snes9x StartupArguments: '-L ".\cores\snes9x_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bs, fig, sfc, smc, st, swc, zip] ProfileFiles: ['cores\snes9x_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Snes9x 2002 StartupArguments: '-L ".\cores\snes9x2002_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bsx, dx2, fig, gd3, gd7, sfc, smc, swc, zip] ProfileFiles: ['cores\snes9x2002_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Snes9x 2005 StartupArguments: '-L ".\cores\snes9x2005_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bsx, dx2, fig, gd3, gd7, sfc, smc, swc, zip] ProfileFiles: ['cores\snes9x2005_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Snes9x 2005 Plus StartupArguments: '-L ".\cores\snes9x2005_plus_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bsx, dx2, fig, gd3, gd7, sfc, smc, swc, zip] ProfileFiles: ['cores\snes9x2005_plus_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Snes9x 2010 StartupArguments: '-L ".\cores\snes9x2010_libretro.dll" "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [7z, bsx, dx2, fig, gd3, gd7, sfc, smc, swc, zip] ProfileFiles: ['cores\snes9x2010_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Stella StartupArguments: '-L ".\cores\stella_libretro.dll" "{ImagePath}"' Platforms: [atari_2600] ImageExtensions: [7z, a26, bin, zip] ProfileFiles: ['cores\stella_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Stella 2014 StartupArguments: '-L ".\cores\stella2014_libretro.dll" "{ImagePath}"' Platforms: [atari_2600] ImageExtensions: [7z, a26, bin, zip] ProfileFiles: ['cores\stella2014_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Stella 2023 StartupArguments: '-L ".\cores\stella2023_libretro.dll" "{ImagePath}"' Platforms: [atari_2600] ImageExtensions: [7z, a26, bin, zip] ProfileFiles: ['cores\stella2023_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: SwanStation StartupArguments: '-L ".\cores\swanstation_libretro.dll" "{ImagePath}"' Platforms: [sony_playstation] ImageExtensions: [7z, bin, chd, cue, ecm, exe, img, iso, m3u, mds, pbp, psexe, psf, zip] ProfileFiles: ['cores\swanstation_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: TempGBA StartupArguments: '-L ".\cores\tempgba_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [7z, agb, bin, gba, gbz, zip] ProfileFiles: ['cores\tempgba_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: TGB Dual StartupArguments: '-L ".\cores\tgbdual_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor] ImageExtensions: [7z, cgb, dmg, gb, gbc, sgb, zip] ProfileFiles: ['cores\tgbdual_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: theodore StartupArguments: '-L ".\cores\theodore_libretro.dll" "{ImagePath}"' Platforms: [thomson_mo5, thomson_to7] ImageExtensions: [7z, fd, k7, m5, m7, rom, sap, zip] ProfileFiles: ['cores\theodore_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: UAE4ARM StartupArguments: '-L ".\cores\uae4arm_libretro.dll" "{ImagePath}"' Platforms: [commodore_amiga] ImageExtensions: [7z, adf, adz, ccd, cue, dms, hdf, ipf, iso, lha, uae, wrp, zip] ProfileFiles: ['cores\uae4arm_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VBA Next StartupArguments: '-L ".\cores\vba_next_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [7z, gba, zip] ProfileFiles: ['cores\vba_next_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VBA-M StartupArguments: '-L ".\cores\vbam_libretro.dll" "{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboyadvance, nintendo_gameboycolor] ImageExtensions: [7z, cgb, dmg, gb, gba, gbc, sgb, zip] ProfileFiles: ['cores\vbam_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: vecx StartupArguments: '-L ".\cores\vecx_libretro.dll" "{ImagePath}"' Platforms: [vectrex] ImageExtensions: [7z, bin, vec, zip] ProfileFiles: ['cores\vecx_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VICE SDL StartupArguments: '-L ".\cores\x64sdl_libretro.dll" "{ImagePath}"' Platforms: [commodore_64] ImageExtensions: [7z, bin, crt, d64, d6z, d71, d7z, d80, d81, d82, d8z, g41, g4z, g64, g6z, gz, p00, prg, t64, tap, x64, x6z, zip] ProfileFiles: ['cores\x64sdl_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VICE x128 StartupArguments: '-L ".\cores\vice_x128_libretro.dll" "{ImagePath}"' Platforms: [commodore_64] ImageExtensions: [7z, bin, cmd, crt, d2m, d4m, d64, d6z, d71, d7z, d80, d81, d82, d8z, g41, g4z, g64, g6z, gz, m3u, nbz, nib, p00, prg, t64, tap, vfl, vsf, x64, x6z, zip] ProfileFiles: ['cores\vice_x128_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VICE x64 StartupArguments: '-L ".\cores\vice_x64_libretro.dll" "{ImagePath}"' Platforms: [commodore_64] ImageExtensions: [7z, bin, cmd, crt, d2m, d4m, d64, d6z, d71, d7z, d80, d81, d82, d8z, g41, g4z, g64, g6z, gz, m3u, nbz, nib, p00, prg, t64, tap, vfl, vsf, x64, x6z, zip] ProfileFiles: ['cores\vice_x64_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VICE x64sc StartupArguments: '-L ".\cores\vice_x64sc_libretro.dll" "{ImagePath}"' Platforms: [commodore_64] ImageExtensions: [7z, bin, cmd, crt, d2m, d4m, d64, d6z, d71, d7z, d80, d81, d82, d8z, g41, g4z, g64, g6z, gz, m3u, nbz, nib, p00, prg, t64, tap, vfl, vsf, x64, x6z, zip] ProfileFiles: ['cores\vice_x64sc_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VICE xcbm2 StartupArguments: '-L ".\cores\vice_xcbm2_libretro.dll" "{ImagePath}"' Platforms: [commodore_cbm2] ImageExtensions: [7z, bin, cmd, crt, d2m, d4m, d64, d6z, d71, d7z, d80, d81, d82, d8z, g41, g4z, g64, g6z, gz, m3u, nbz, nib, p00, prg, t64, tap, vfl, vsf, x64, x6z, zip] ProfileFiles: ['cores\vice_xcbm2_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VICE xcbm5x0 StartupArguments: '-L ".\cores\vice_xcbm5x0_libretro.dll" "{ImagePath}"' Platforms: [commodore_cbm5x0] ImageExtensions: [7z, bin, cmd, crt, d2m, d4m, d64, d6z, d71, d7z, d80, d81, d82, d8z, g41, g4z, g64, g6z, gz, m3u, nbz, nib, p00, prg, t64, tap, vfl, vsf, x64, x6z, zip] ProfileFiles: ['cores\vice_xcbm5x0_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VICE xpet StartupArguments: '-L ".\cores\vice_xpet_libretro.dll" "{ImagePath}"' Platforms: [commodore_pet] ImageExtensions: [7z, bin, cmd, crt, d2m, d4m, d64, d6z, d71, d7z, d80, d81, d82, d8z, g41, g4z, g64, g6z, gz, m3u, nbz, nib, p00, prg, t64, tap, vfl, vsf, x64, x6z, zip] ProfileFiles: ['cores\vice_xpet_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VICE xscpu64 StartupArguments: '-L ".\cores\vice_xscpu64_libretro.dll" "{ImagePath}"' Platforms: [commodore_64] ImageExtensions: [7z, bin, cmd, crt, d2m, d4m, d64, d6z, d71, d7z, d80, d81, d82, d8z, g41, g4z, g64, g6z, gz, m3u, nbz, nib, p00, prg, t64, tap, vfl, vsf, x64, x6z, zip] ProfileFiles: ['cores\vice_xscpu64_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: VICE xvic StartupArguments: '-L ".\cores\vice_xvic_libretro.dll" "{ImagePath}"' Platforms: [commodore_vci20] ImageExtensions: [20, 40, 60, 7z, a0, b0, bin, cmd, crt, d2m, d4m, d64, d6z, d71, d7z, d80, d81, d82, d8z, g41, g4z, g64, g6z, gz, m3u, nbz, nib, p00, prg, rom, t64, tap, vfl, vsf, x64, x6z, zip] ProfileFiles: ['cores\vice_xvic_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Virtual Jaguar StartupArguments: '-L ".\cores\virtualjaguar_libretro.dll" "{ImagePath}"' Platforms: [atari_jaguar] ImageExtensions: [7z, abs, bin, cof, j64, jag, prg, rom, zip] ProfileFiles: ['cores\virtualjaguar_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: virtualxt StartupArguments: '-L ".\cores\virtualxt_libretro.dll" "{ImagePath}"' Platforms: [pc_dos] ImageExtensions: [7z, img, zip] ProfileFiles: ['cores\virtualxt_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: YabaSanshiro StartupArguments: '-L ".\cores\yabasanshiro_libretro.dll" "{ImagePath}"' Platforms: [sega_saturn] ImageExtensions: [7z, bin, ccd, chd, cue, iso, mds, zip] ProfileFiles: ['cores\yabasanshiro_libretro.dll'] StartupExecutable: ^retroarch\.exe$ - Name: Yabause StartupArguments: '-L ".\cores\yabause_libretro.dll" "{ImagePath}"' Platforms: [sega_saturn] ImageExtensions: [7z, bin, ccd, chd, cue, iso, m3u, mds, zip] ProfileFiles: ['cores\yabause_libretro.dll'] StartupExecutable: ^retroarch\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/RosaliesMupenGui/emulator.yaml ================================================ Id: rosaliesmupengui Name: Rosalie's Mupen GUI Website: 'https://github.com/Rosalie241/RMG' Profiles: - Name: Default StartupArguments: '--fullscreen --quit-after-emulation "{ImagePath}"' ImageExtensions: [n64, z64, v64, ndd, d64, zip] Platforms: [nintendo_64] StartupExecutable: ^RMG\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Ruffle/emulator.yaml ================================================ Id: ruffle Name: Ruffle Website: 'https://ruffle.rs/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [adobe_flash] ImageExtensions: [swf] StartupExecutable: ^ruffle\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Ryujinx/emulator.yaml ================================================ Id: ryujinx Name: Ryujinx Website: 'https://ryujinx.org/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_switch] ImageExtensions: [xci, nsp] StartupExecutable: ^ryujinx\.exe$ - Name: Avalonia StartupArguments: '"{ImagePath}"' Platforms: [nintendo_switch] ImageExtensions: [xci, nsp] StartupExecutable: ^ryujinx\.ava\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/SameBoy/emulator.yaml ================================================ Id: sameboy Name: SameBoy Website: 'https://sameboy.github.io/' Profiles: - Name: Nintendo - Game Boy StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [gb] StartupExecutable: ^sameboy\.exe$ - Name: Nintendo - Game Boy Color StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [gbc] StartupExecutable: ^sameboy\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/ScummVM/emulator.yaml ================================================ Id: scummvm Name: ScummVM Website: 'https://www.scummvm.org/' Profiles: - Name: Default StartupArguments: '-f {ImageName}' Platforms: [pc_dos, pc_windows] StartupExecutable: ^scummvm\.exe$ ScriptGameImport: true ================================================ FILE: source/Playnite/Emulation/Emulators/ScummVM/importGames.ps1 ================================================ param( $ImportArgs ) function Get-IniContent() { param( $filePath ) $ini = @{} switch -regex -file $FilePath { "^\[(.+)\]" # Section { $section = $matches[1] $ini[$section] = @{} $CommentCount = 0 } "^(;.*)$" # Comment { $value = $matches[1] $CommentCount = $CommentCount + 1 $name = "Comment" + $CommentCount $ini[$section][$name] = $value } "(.+?)\s*=(.*)" # Key { $name,$value = $matches[1..2] $ini[$section][$name] = $value } } return $ini } #switch the below $scummvmConfig lines to change the default and fallback scan directories. $scummvmConfig = Join-Path $ImportArgs.ScanDirectory "scummvm.ini" #Import Directory if (!(Test-Path $scummvmConfig)) { $scummvmConfig = Join-Path $env:APPDATA "ScummVM\scummvm.ini" #Default (appdata) directory if (!(Test-Path $scummvmConfig)) { $ImportArgs.PlayniteApi.Dialogs.ShowErrorMessage("Couldn't find ScummVM config file at $scummvmConfig", "") | Out-Null return } } $config = Get-IniContent $scummvmConfig foreach ($key in $config.Keys) { if ($config[$key].gameid) { $romPath = Join-Path $config[$key].path $key if (-not [System.IO.Path]::IsPathRooted($romPath)) # Check if it's a relative path { $romPath = Join-Path $ImportArgs.ScanDirectory $romPath } $anyFunc = [Func[string,bool]]{ param($a) [System.IO.Path]::GetFullPath($a) -ieq [System.IO.Path]::GetFullPath($romPath) } if ([System.Linq.Enumerable]::Any($ImportArgs.ImportedFiles, $anyFunc)) { continue } $scannedGame = New-Object "Playnite.Emulators.ScriptScannedGame" $scannedGame.Name = ($config[$key].description -replace "\(.*\)").Trim() $scannedGame.Path = $romPath $scannedGame } } ================================================ FILE: source/Playnite/Emulation/Emulators/Snes9X/emulator.yaml ================================================ Id: snes9x Name: Snes9X Website: 'http://www.snes9x.com/' Profiles: - Name: Default StartupArguments: '"{ImagePath}" -fullscreen' Platforms: [nintendo_super_nes] ImageExtensions: [zip, gz, jma, sfc, smc] StartupExecutable: ^snes9x.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Stella/emulator.yaml ================================================ Id: stella Name: Stella Website: 'https://stella-emu.github.io/' Profiles: - Name: Default StartupArguments: '-fullscreen 1 "{ImagePath}"' Platforms: [atari_2600] ImageExtensions: [bin, a26, zip] StartupExecutable: ^Stella\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/VirtualJaguar/emulator.yaml ================================================ Id: virtualjaguar Name: Virtual Jaguar Website: 'https://icculus.org/virtualjaguar/' Profiles: - Name: Default StartupArguments: '"{ImagePath}" --fullscreen' Platforms: [atari_jaguar] ImageExtensions: [abs, rom, jag, bin] StartupExecutable: ^virtualjaguar\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/VisualBoyAdvance/emulator.yaml ================================================ Id: visualboyadvance Name: VisualBoyAdvance Website: 'https://sourceforge.net/projects/vba/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor, nintendo_gameboyadvance] ImageExtensions: [zip, 7z, rar, bin, elf, mb, gba, agb, dmg, gb, gbc, cgb, sgb] StartupExecutable: ^visualboyadvance\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/VisualBoyAdvance-M/emulator.yaml ================================================ Id: visualboyadvance-m Name: VisualBoyAdvance-M Website: 'https://vba-m.com/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboy, nintendo_gameboycolor, nintendo_gameboyadvance] ImageExtensions: [zip, 7z, rar, bin, elf, mb, gba, agb, dmg, gb, gbc, cgb, sgb] StartupExecutable: ^visualboyadvance-m\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Vita3K/emulator.yaml ================================================ Id: vita3k Name: Vita3K Website: 'https://vita3k.org/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [sony_vita] ImageExtensions: [vpk] StartupExecutable: ^Vita3K.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/WinUAE/emulator.yaml ================================================ Id: winuae Name: WinUAE Website: 'http://www.winuae.net/' Profiles: - Name: Default StartupArguments: '-s use_gui=no -s gfx_fullscreen_amiga=true -0 "{ImagePath}"' Platforms: [commodore_amiga] ImageExtensions: [iso, ccd, cue, chd, mds, nrg, adf, adz, gz, dms, ipf, scp, fdi, lha] StartupExecutable: ^winuae.*\.exe$ - Name: Amiga CD32 StartupArguments: '-s use_gui=no -s gfx_fullscreen_amiga=true -s quickstart=cd32,0 -cdimage="{ImagePath}"' Platforms: [commodore_amiga_cd32] ImageExtensions: [cue, chd, iso, ccd, mds, nrg] StartupExecutable: ^winuae.*\.exe$ - Name: Amiga 500 StartupArguments: '-s use_gui=no -s gfx_fullscreen_amiga=true -s quickstart=a500,0 -0 "{ImagePath}"' Platforms: [commodore_amiga] ImageExtensions: [adf, adz, gz, dms, ipf, scp, fdi, lha] StartupExecutable: ^winuae.*\.exe$ - Name: Amiga 1200 StartupArguments: '-s use_gui=no -s gfx_fullscreen_amiga=true -s quickstart=a1200,0 -0 "{ImagePath}"' Platforms: [commodore_amiga] ImageExtensions: [iso, ccd, cue, chd, mds, nrg, adf, adz, gz, dms, ipf, scp, fdi, lha] StartupExecutable: ^winuae.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/WinVice/emulator.yaml ================================================ Id: winvice Name: WinVice Website: 'https://vice-emu.sourceforge.io/' Profiles: - Name: Commodore 128 StartupArguments: '"{ImagePath}"' Platforms: [commodore_64] ImageExtensions: [d64, d71, d80, d81, d82, g64, g41, x64, d1m, d2m, d4m, p64, t64, tap, prg, p00, crt, bin, zip, gz, d6z, d7z, d8z, g6z, g4z, x6z] StartupExecutable: ^x128\.exe$ - Name: Commodore 64 StartupArguments: '"{ImagePath}"' Platforms: [commodore_64] ImageExtensions: [d64, d71, d80, d81, d82, g64, g41, x64, d1m, d2m, d4m, p64, t64, tap, prg, p00, crt, bin, zip, gz, d6z, d7z, d8z, g6z, g4z, x6z] StartupExecutable: ^x64\.exe$ - Name: Commodore 64 (accurate) StartupArguments: '"{ImagePath}"' Platforms: [commodore_64] ImageExtensions: [d64, d71, d80, d81, d82, g64, g41, x64, d1m, d2m, d4m, p64, t64, tap, prg, p00, crt, bin, zip, gz, d6z, d7z, d8z, g6z, g4z, x6z] StartupExecutable: ^x64sc\.exe$ - Name: Commodore PLUS4 StartupArguments: '"{ImagePath}"' Platforms: [commodore_plus4] ImageExtensions: [d64, d71, d80, d81, d82, g64, g41, x64, d1m, d2m, d4m, p64, t64, tap, prg, p00, crt, bin, zip, gz, d6z, d7z, d8z, g6z, g4z, x6z] StartupExecutable: ^xplus4\.exe$ - Name: Commodore VIC20 StartupArguments: '"{ImagePath}"' Platforms: [commodore_vci20] ImageExtensions: [d64, d71, d80, d81, d82, g64, g41, x64, d1m, d2m, d4m, p64, t64, tap, prg, p00, crt, bin, zip, gz, d6z, d7z, d8z, g6z, g4z, x6z] StartupExecutable: ^xvic\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Xemu/emulator.yaml ================================================ Id: xemu Name: Xemu Website: 'https://xemu.app/' Profiles: - Name: Default StartupArguments: '-dvd_path "{ImagePath}" -full-screen' Platforms: [xbox] ImageExtensions: [iso, xiso] StartupExecutable: ^xemu\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Xenia/emulator.yaml ================================================ Id: xenia Name: Xenia Website: 'https://xenia.jp/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [xbox360] ImageExtensions: [iso, xex, cci, cxi, elf, zar, ] StartupExecutable: ^xenia\.exe$ - Name: Canary StartupArguments: '"{ImagePath}"' Platforms: [xbox360] ImageExtensions: [iso, xex, cci, cxi, elf, zar, ] StartupExecutable: ^xenia_canary\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Yabuse/emulator.yaml ================================================ Id: yabuse Name: Yabause Website: 'https://yabause.org/' Profiles: - Name: Default StartupArguments: '--iso="{ImagePath}"' Platforms: [sega_saturn] ImageExtensions: [iso, cue, bin, mds] StartupExecutable: ^yabause.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/Ymir/emulator.yaml ================================================ Id: ymir Name: Ymir Website: 'https://github.com/StrikerX3/Ymir' Profiles: - Name: Default Platforms: [sega_saturn] StartupArguments: '-d "{ImagePath}" -f' ImageExtensions: [chd, cue, ccd, mdf, iso] StartupExecutable: ^ymir-sdl3\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/ZSNES/emulator.yaml ================================================ Id: zsnes Name: ZSNES Website: 'https://www.zsnes.com/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [zip, smc, sfc, swc, fig, mgd, mgh, ufo, bin, gd3, gd7, dx2, usa, eur, jap, aus, st, bs, 048, 058, 078] StartupExecutable: ^zsnesw\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/ares/emulator.yaml ================================================ Id: ares Name: ares Website: 'https://ares-emu.net/' Profiles: - Name: Atari - Atari 2600 StartupArguments: '--system "Atari 2600" "{ImagePath}" --fullscreen' Platforms: [atari_2600] ImageExtensions: [zip, a26] StartupExecutable: ^ares\.exe$ - Name: Bandai - WonderSwan StartupArguments: '--system "WonderSwan" "{ImagePath}" --fullscreen' Platforms: [bandai_wonderswan] ImageExtensions: [zip, ws] StartupExecutable: ^ares\.exe$ - Name: Bandai - WonderSwan Color StartupArguments: '--system "WonderSwan Color" "{ImagePath}" --fullscreen' Platforms: [bandai_wonderswan_color] ImageExtensions: [zip, wsc] StartupExecutable: ^ares\.exe$ - Name: Coleco - ColecoVision StartupArguments: '--system "ColecoVision" "{ImagePath}" --fullscreen' Platforms: [coleco_vision] ImageExtensions: [zip, cv, col] StartupExecutable: ^ares\.exe$ - Name: Microsoft - MSX StartupArguments: '--system "MSX" "{ImagePath}" --fullscreen' Platforms: [microsoft_msx] ImageExtensions: [zip, msx] StartupExecutable: ^ares\.exe$ - Name: Microsoft - MSX2 StartupArguments: '--system "MSX2" "{ImagePath}" --fullscreen' Platforms: [microsoft_msx2] ImageExtensions: [zip, msx2] StartupExecutable: ^ares\.exe$ - Name: NEC - PC Engine StartupArguments: '--system "PC Engine" "{ImagePath}" --fullscreen' Platforms: [nec_turbografx_16] ImageExtensions: [zip, pce] StartupExecutable: ^ares\.exe$ - Name: NEC - PC Engine CD StartupArguments: '--system "PC Engine CD" "{ImagePath}" --fullscreen' Platforms: [nec_turbografx_cd] ImageExtensions: [cue, chd] StartupExecutable: ^ares\.exe$ - Name: NEC - SuperGrafx StartupArguments: '--system "SuperGrafx" "{ImagePath}" --fullscreen' Platforms: [nec_supergrafx] ImageExtensions: [zip, sgx] StartupExecutable: ^ares\.exe$ - Name: Nintendo - Nintendo 64 StartupArguments: '--system "Nintendo 64" "{ImagePath}" --fullscreen' Platforms: [nintendo_64] ImageExtensions: [zip, n64, v64, z64] StartupExecutable: ^ares\.exe$ - Name: Nintendo - Nintendo 64DD StartupArguments: '--system "Nintendo 64DD" "{ImagePath}" --fullscreen' Platforms: [nintendo_64] ImageExtensions: [zip, n64dd, ndd] StartupExecutable: ^ares\.exe$ - Name: Nintendo - Game Boy StartupArguments: '--system "Game Boy" "{ImagePath}" --fullscreen' Platforms: [nintendo_gameboy] ImageExtensions: [zip, gb] StartupExecutable: ^ares\.exe$ - Name: Nintendo - Game Boy Advance StartupArguments: '--system "Game Boy Advance" "{ImagePath}" --fullscreen' Platforms: [nintendo_gameboyadvance] ImageExtensions: [zip, gba] StartupExecutable: ^ares\.exe$ - Name: Nintendo - Game Boy Color StartupArguments: '--system "Game Boy Color" "{ImagePath}" --fullscreen' Platforms: [nintendo_gameboycolor] ImageExtensions: [zip, gbc] StartupExecutable: ^ares\.exe$ - Name: Nintendo - Famicom StartupArguments: '--system "Famicom" "{ImagePath}" --fullscreen' Platforms: [nintendo_nes] ImageExtensions: [zip, fc, nes, unf, unif] StartupExecutable: ^ares\.exe$ - Name: Nintendo - Famicom Disk System StartupArguments: '--system "Famicom Disk System" "{ImagePath}" --fullscreen' Platforms: [nintendo_nes] ImageExtensions: [zip, fds] StartupExecutable: ^ares\.exe$ - Name: Nintendo - Sufami Turbo StartupArguments: '--system "Sufami Turbo" "{ImagePath}" --fullscreen' Platforms: [nintendo_super_nes] ImageExtensions: [zip, st] StartupExecutable: ^ares\.exe$ - Name: Nintendo - Super Famicom StartupArguments: '--system "Super Famicom" "{ImagePath}" --fullscreen' Platforms: [nintendo_super_nes] ImageExtensions: [zip, sfc, smc, swc, fig] StartupExecutable: ^ares\.exe$ - Name: Sega - Game Gear StartupArguments: '--system "Game Gear" "{ImagePath}" --fullscreen' Platforms: [sega_gamegear] ImageExtensions: [zip, gg] StartupExecutable: ^ares\.exe$ - Name: Sega - Master System StartupArguments: '--system "Master System" "{ImagePath}" --fullscreen' Platforms: [sega_mastersystem] ImageExtensions: [zip, ms, sms] StartupExecutable: ^ares\.exe$ - Name: Sega - Mega 32X StartupArguments: '--system "Mega 32X" "{ImagePath}" --fullscreen' Platforms: [sega_32x] ImageExtensions: [zip, 32X] StartupExecutable: ^ares\.exe$ - Name: Sega - Mega CD StartupArguments: '--system "Mega CD" "{ImagePath}" --fullscreen' Platforms: [sega_cd] ImageExtensions: [cue, chd] StartupExecutable: ^ares\.exe$ - Name: Sega - Mega Drive StartupArguments: '--system "Mega Drive" "{ImagePath}" --fullscreen' Platforms: [sega_genesis] ImageExtensions: [zip, md, smd, gen, bin] StartupExecutable: ^ares\.exe$ - Name: Sega - Saturn StartupArguments: '--system "Saturn" "{ImagePath}" --fullscreen' Platforms: [sega_saturn] ImageExtensions: [cue, chd] StartupExecutable: ^ares\.exe$ - Name: Sega - SG-1000 StartupArguments: '--system "SG-1000" "{ImagePath}" --fullscreen' Platforms: [sega_sg1000] ImageExtensions: [sg1000, sg, zip] StartupExecutable: ^ares\.exe$ - Name: SNK - Neo Geo AES StartupArguments: '--system "Neo Geo" "{ImagePath}" --fullscreen' Platforms: [snk_neogeo_aes] ImageExtensions: [zip, ng] StartupExecutable: ^ares\.exe$ - Name: SNK - Neo Geo Pocket StartupArguments: '--system "Neo Geo Pocket" "{ImagePath}" --fullscreen' Platforms: [snk_neogeopocket] ImageExtensions: [zip, ngp] StartupExecutable: ^ares\.exe$ - Name: SNK - Neo Geo Pocket Color StartupArguments: '--system "Neo Geo Pocket Color" "{ImagePath}" --fullscreen' Platforms: [snk_neogeopocket_color] ImageExtensions: [zip, ngpc, ngc] StartupExecutable: ^ares\.exe$ - Name: Sony - PlayStation StartupArguments: '--system "PlayStation" "{ImagePath}" --fullscreen' Platforms: [sony_playstation] ImageExtensions: [cue, chd, exe] StartupExecutable: ^ares\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/blueMSX/emulator.yaml ================================================ Id: bluemsx Name: blueMSX Website: 'http://bluemsx.msxblue.com/index.htm' Profiles: - Name: ColecoVision StartupArguments: '/rom1 "{ImagePath}" /fullscreen /machine "COL - ColecoVision"' Platforms: [coleco_vision] ImageExtensions: [zip, col] StartupExecutable: ^blueMSX\.exe$ - Name: MSX StartupArguments: '/rom1 "{ImagePath}" /fullscreen /machine "MSX2+"' Platforms: [microsoft_msx] ImageExtensions: [zip, rom] StartupExecutable: ^blueMSX\.exe$ - Name: MSX2 StartupArguments: '/rom1 "{ImagePath}" /fullscreen /machine "MSX2+"' Platforms: [microsoft_msx2] ImageExtensions: [zip, rom] StartupExecutable: ^blueMSX\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/bsnes/emulator.yaml ================================================ Id: bsnes Name: bsnes Website: 'https://bsnes.dev/' Profiles: - Name: Default StartupArguments: '--fullscreen "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [sfc, smc, zip, 7z] StartupExecutable: ^bsnes\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/bsnes-hd/emulator.yaml ================================================ Id: bsnes-hd Name: bsnes-hd Website: 'https://github.com/DerKoun/bsnes-hd' Profiles: - Name: Default StartupArguments: '--fullscreen "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [sfc, smc, zip] StartupExecutable: ^bsnes_hd\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/bsnes-mt/emulator.yaml ================================================ Id: bsnes-mt Name: bsnes-mt Website: 'https://tanalin.com/en/projects/bsnes-mt/' Profiles: - Name: Default StartupArguments: '--fullscreen "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [sfc, smc, zip, 7z] StartupExecutable: ^bsnes-mt\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/decaf-emu/emulator.yaml ================================================ Id: decaf-emu Name: decaf-emu Website: 'https://github.com/decaf-emu/decaf-emu' Profiles: - Name: Default StartupArguments: 'play "{ImagePath}"' Platforms: [nintendo_wiiu] ImageExtensions: [wud, rpx] StartupExecutable: ^decaf-sdl\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/ePSXe/emulator.yaml ================================================ Id: epsxe Name: ePSXe Website: 'https://www.epsxe.com/' Profiles: - Name: Default Platforms: [sony_playstation] ImageExtensions: [bin, iso, img, pbp, zip, cue] InstallationFile: ^ePSXe\.exe$ StartupExecutable: ^ePSXe\.exe$ StartupArguments: '-nogui -slowboot -loadbin "{ImagePath}"' ================================================ FILE: source/Playnite/Emulation/Emulators/gopher64/emulator.yaml ================================================ Id: gopher64 Name: gopher64 Website: 'https://github.com/gopher64/gopher64' Profiles: - Name: Default StartupArguments: '"{ImagePath}" -f' Platforms: [nintendo_64] ImageExtensions: [zip,7z,z64,n64,v64] StartupExecutable: ^gopher64-windows-x86_64\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/higan/emulator.yaml ================================================ Id: higan Name: higan Website: 'https://higan.dev/' Profiles: - Name: Nintendo - NES StartupArguments: '"{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [zip, nes] StartupExecutable: ^higan.*\.exe$ - Name: Nintendo - SNES StartupArguments: '"{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [sfc, smc, bs, zip] StartupExecutable: ^higan.*\.exe$ - Name: Nintendo - Game Boy StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [zip, gb] StartupExecutable: ^higan.*\.exe$ - Name: Nintendo - Game Boy Color StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [zip, gbc] StartupExecutable: ^higan.*\.exe$ - Name: Nintendo - Game Boy Advance StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [zip, gba] StartupExecutable: ^higan.*\.exe$ - Name: Sega - Master System StartupArguments: '"{ImagePath}"' Platforms: [sega_mastersystem] ImageExtensions: [zip, sms] StartupExecutable: ^higan.*\.exe$ - Name: Sega - Mega Drive StartupArguments: '"{ImagePath}"' Platforms: [sega_genesis] ImageExtensions: [zip, bin, smd, md] StartupExecutable: ^higan.*\.exe$ - Name: Sega - Game Gear StartupArguments: '"{ImagePath}"' Platforms: [sega_gamegear] ImageExtensions: [zip, gg] StartupExecutable: ^higan.*\.exe$ - Name: NEC - PC Engine StartupArguments: '"{ImagePath}"' Platforms: [nec_turbografx_16, nec_turbografx_cd] ImageExtensions: [zip, pce, cue, ccd, chd] StartupExecutable: ^higan.*\.exe$ - Name: PC Engine SuperGrafx StartupArguments: '"{ImagePath}"' Platforms: [nec_supergrafx] ImageExtensions: [zip, pce] StartupExecutable: ^higan.*\.exe$ - Name: Bandai - WonderSwan StartupArguments: '"{ImagePath}"' Platforms: [bandai_wonderswan] ImageExtensions: [zip, ws] StartupExecutable: ^higan.*\.exe$ - Name: Bandai - WonderSwan Color StartupArguments: '"{ImagePath}"' Platforms: [bandai_wonderswan_color] ImageExtensions: [zip, wsc] StartupExecutable: ^higan.*\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/jgenesis/emulator.yaml ================================================ Id: jgenesis Name: jgenesis Website: 'https://github.com/jsgroth/jgenesis' Profiles: - Name: Master System StartupExecutable: ^jgenesis-cli\.exe$ StartupArguments: '--hardware MasterSystem --fullscreen -f "{ImagePath}"' Platforms: [sega_mastersystem] ImageExtensions: [sms, zip, 7z] - Name: Game Gear StartupExecutable: ^jgenesis-cli\.exe$ StartupArguments: '--hardware MasterSystem --fullscreen -f "{ImagePath}"' Platforms: [sega_gamegear] ImageExtensions: [gg, zip, 7z] - Name: Genesis StartupExecutable: ^jgenesis-cli\.exe$ StartupArguments: '--hardware Genesis --fullscreen -f "{ImagePath}"' Platforms: [sega_genesis] ImageExtensions: [gen, md, bin, smd, zip, 7z] - Name: Sega CD StartupExecutable: ^jgenesis-cli\.exe$ StartupArguments: '--hardware SegaCd --fullscreen -f "{ImagePath}"' Platforms: [sega_cd] ImageExtensions: [cue, chd, zip, 7z] - Name: Sega 32X StartupExecutable: ^jgenesis-cli\.exe$ StartupArguments: '--hardware Sega32X --fullscreen -f "{ImagePath}"' Platforms: [sega_32x] ImageExtensions: [32x, bin, zip, 7z] - Name: NES StartupExecutable: ^jgenesis-cli\.exe$ StartupArguments: '--hardware Nes --fullscreen -f "{ImagePath}"' Platforms: [nintendo_nes] ImageExtensions: [nes, zip, 7z] - Name: SNES StartupExecutable: ^jgenesis-cli\.exe$ StartupArguments: '--hardware Snes --fullscreen -f "{ImagePath}"' Platforms: [nintendo_super_nes] ImageExtensions: [sfc, smc, zip, 7z] - Name: Game Boy StartupExecutable: ^jgenesis-cli\.exe$ StartupArguments: '--hardware GameBoy --fullscreen -f "{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [gb, zip, 7z] - Name: Game Boy Color StartupExecutable: ^jgenesis-cli\.exe$ StartupArguments: '--hardware GameBoy --fullscreen -f "{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [gbc, zip, 7z] ================================================ FILE: source/Playnite/Emulation/Emulators/m64p/emulator.yaml ================================================ Id: m64p Name: m64p Website: 'https://m64p.github.io/' Profiles: - Name: Default StartupArguments: '--nogui "{ImagePath}"' ImageExtensions: [n64, v64, z64, zip, 7z] Platforms: [nintendo_64] StartupExecutable: ^mupen64plus-gui\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/mGBA/emulator.yaml ================================================ Id: mgba Name: mGBA Website: 'https://mgba.io/' Profiles: - Name: Nintendo Game Boy StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboy] ImageExtensions: [zip, 7z, rar, bin, gb, dmg] StartupExecutable: ^mGBA.exe$ - Name: Nintendo Game Boy Color StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboycolor] ImageExtensions: [zip, 7z, rar, bin, gbc, cgb, sgb] StartupExecutable: ^mGBA.exe$ - Name: Nintendo Game Boy Advance StartupArguments: '"{ImagePath}"' Platforms: [nintendo_gameboyadvance] ImageExtensions: [zip, 7z, rar, bin, elf, mb, gba, agb] StartupExecutable: ^mGBA.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/melonDS/emulator.yaml ================================================ Id: melonds Name: melonDS Website: 'http://melonds.kuribo64.net/' Profiles: - Name: Default StartupArguments: '"{ImagePath}" -f' Platforms: [nintendo_ds, nintendo_dsi] ImageExtensions: [nds, zip] StartupExecutable: ^melonDS\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/puNES/emulator.yaml ================================================ Id: punes Name: puNES Website: 'https://github.com/punesemu/puNES' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' ImageExtensions: [7zip, rar, nes, unf, unif, fds, nsf, nfse, fm2, zip] Platforms: [nintendo_nes] StartupExecutable: ^punes64\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/redream/emulator.yaml ================================================ Id: redream Name: redream Website: 'https://redream.io/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [sega_dreamcast] ImageExtensions: [cdi, gdi, chd, cue] ProfileFiles: [flash.bin] StartupExecutable: ^redream\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/shadPS4/emulator.yaml ================================================ Id: shadps4 Name: shadPS4 Website: 'https://shadps4.net//' Profiles: - Name: Default QT Platforms: [sony_playstation4] ProfileFiles: ['platforms\qwindows.dll'] StartupExecutable: ^shadPS4.*\.exe$ StartupArguments: '-g "{ImagePath}"' ScriptGameImport: true ================================================ FILE: source/Playnite/Emulation/Emulators/shadPS4/importGames.ps1 ================================================ param( $ImportArgs ) if (-not [System.IO.Directory]::Exists($ImportArgs.ScanDirectory)) { return } function Get-NullTerminatedString { param([Array]$bytes, [int]$offset) $strBytes = @() for ($j = $offset; $j -lt $bytes.Count; $j++) { $b = $bytes[$j] if($b -eq 0) { break } #strings end on null terminator $strBytes += $b } return [System.Text.Encoding]::UTF8.GetString($strBytes) } function Get-ParamSfoValue { param([string]$path, [string]$key) #thanks to https://psdevwiki.com/ps4/PARAM.SFO [byte[]]$bytes = Get-Content -LiteralPath $path -Encoding Byte -Raw $keyTableOffset = [System.BitConverter]::ToUInt32($bytes, 0x08) $dataTableOffset = [System.BitConverter]::ToUInt32($bytes, 0x0c) $indexTableOffset = 0x14 $indexRowLength = 0x10 #go through each index table row for ($i = $indexTableOffset; $i -lt $keyTableOffset; $i += $indexRowLength) { $relativeKeyOffset = [System.BitConverter]::ToUInt16($bytes, $i) $foundKey = Get-NullTerminatedString $bytes ($keyTableOffset + $relativeKeyOffset) if($foundKey -ne $key) { continue } $dataFormat = [System.BitConverter]::ToUInt16($bytes, $i + 0x02) $dataLength = [System.BitConverter]::ToUInt32($bytes, $i + 0x04) $relativeDataOffset = [System.BitConverter]::ToUInt32($bytes, $i + 0x0c) if ($dataFormat -eq 1028) #uint32 { $data = [System.BitConverter]::ToUInt32($bytes, $dataTableOffset + $relativeDataOffset) } else #string (usually null-terminated) { $data = [System.Text.Encoding]::UTF8.GetString($bytes, $dataTableOffset + $relativeDataOffset, $dataLength).Trim("`0") } return $data } return $null } [array]$games = Get-ChildItem -LiteralPath $ImportArgs.ScanDirectory -Recurse | Where-Object { $_.DirectoryName -match 'CUSA\d+$' -and ($_.Name -eq "ISO.BIN.EDAT" -or $_.Name -eq "EBOOT.BIN") } foreach ($game in $games) { $anyFunc = [Func[string,bool]]{ param($a) $a.Equals($game.FullName, 'OrdinalIgnoreCase') } if ([System.Linq.Enumerable]::Any($ImportArgs.ImportedFiles, $anyFunc)) { continue } $scannedGame = New-Object "Playnite.Emulators.ScriptScannedGame" $scannedGame.Path = $game.FullName $paramSfoPath = Join-Path $game.Directory "sce_sys\param.sfo" if (Test-Path -LiteralPath $paramSfoPath -PathType Leaf) { try { $scannedGame.Serial = Get-ParamSfoValue $paramSfoPath "TITLE_ID" if ($null -ne $scannedGame.Serial) { $scannedGame.Name = Get-ParamSfoValue $paramSfoPath "TITLE" $scannedGame } } catch { $__logger.Error($_.Exception, "Failed to scan PS4 PARAM.SFO file $paramSfoPath") $__logger.Error($_.ScriptStackTrace) } } } ================================================ FILE: source/Playnite/Emulation/Emulators/simple64/emulator.yaml ================================================ Id: simple64 Name: simple64 Website: 'https://simple64.github.io/' Profiles: - Name: Default StartupArguments: '--nogui "{ImagePath}"' ImageExtensions: [n64, v64, z64, rom, zip, 7z] Platforms: [nintendo_64] StartupExecutable: ^simple64-gui\.exe$ ================================================ FILE: source/Playnite/Emulation/Emulators/yuzu/emulator.yaml ================================================ Id: yuzu Name: yuzu Website: 'https://yuzu-emu.org/' Profiles: - Name: Default StartupArguments: '"{ImagePath}"' Platforms: [nintendo_switch] ImageExtensions: [nso, nro, nca, xci, nsp] StartupExecutable: ^yuzu\.exe$ ================================================ FILE: source/Playnite/Emulation/Platforms.yaml ================================================ - Name: 3DO Interactive Multiplayer Id: 3do IgdbId: 50 Databases: [The 3DO Company - 3DO] Emulators: [4do, retroarch] - Name: Adobe Flash Id: adobe_flash Emulators: [flashplayerprojector, ruffle] - Name: Amstrad CPC Id: amstrad_cpc IgdbId: 25 Databases: [Amstrad - CPC] Emulators: [retroarch] - Name: Apple II Id: apple_2 IgdbId: 75 Emulators: [bizhawk, mednafen] - Name: Atari 2600 Id: atari_2600 IgdbId: 59 Databases: [Atari - 2600] Emulators: [ares, bizhawk, retroarch, stella] - Name: Atari 5200 Id: atari_5200 IgdbId: 66 Databases: [Atari - 5200] Emulators: [altirra, atari800, retroarch] - Name: Atari 7800 Id: atari_7800 IgdbId: 60 Databases: [Atari - 7800] Emulators: [bizhawk, retroarch] - Name: Atari 8-bit Id: atari_8bit IgdbId: 65 Emulators: [altirra, atari800, retroarch] - Name: Atari Falcon030 Id: atari_falcon030 Emulators: [retroarch] - Name: Atari Jaguar Id: atari_jaguar IgdbId: 62 Databases: [Atari - Jaguar] Emulators: [bizhawk, retroarch, virtualjaguar] - Name: Atari Lynx Id: atari_lynx IgdbId: 61 Databases: [Atari - Lynx] Emulators: [bizhawk, mednafen, retroarch] - Name: Atari ST/STE Id: atari_st IgdbId: 63 Databases: [Atari - ST] Emulators: [retroarch] - Name: Bandai WonderSwan Color Id: bandai_wonderswan_color IgdbId: 123 Databases: [Bandai - WonderSwan Color] Emulators: [ares, bizhawk, higan, mednafen, retroarch] - Name: Bandai WonderSwan Id: bandai_wonderswan IgdbId: 57 Databases: [Bandai - WonderSwan] Emulators: [ares, bizhawk, higan, mednafen, retroarch] - Name: Coleco ColecoVision Id: coleco_vision IgdbId: 68 Databases: [Coleco - ColecoVision] Emulators: [ares, bizhawk, bluemsx, fbalpha, retroarch] - Name: Commodore 64 Id: commodore_64 IgdbId: 15 Databases: [Commodore - 64, Commodore - 64 (PP), Commodore - 64 (Tapes)] Emulators: [bizhawk, retroarch, winvice] - Name: Commodore Amiga CD32 Id: commodore_amiga_cd32 IgdbId: 114 Databases: [Commodore - Amiga] Emulators: [fs-uae, winuae] - Name: Commodore Amiga Id: commodore_amiga IgdbId: 16 Databases: [Commodore - Amiga] Emulators: [fs-uae, retroarch, winuae] - Name: Commodore CBM-5x0 Id: commodore_cbm5x0 Databases: [Commodore - CBM-5x0] Emulators: [retroarch] - Name: Commodore CBM-II Id: commodore_cbm2 Databases: [Commodore - CBM-II] Emulators: [retroarch] - Name: Commodore PET Id: commodore_pet IgdbId: 90 Emulators: [retroarch, winvice] - Name: Commodore Plus/4 Id: commodore_plus4 IgdbId: 94 Databases: [Commodore - Plus-4] Emulators: [winvice] - Name: Commodore VIC20 Id: commodore_vci20 IgdbId: 71 Databases: [Commodore - VIC-20] Emulators: [retroarch, winvice] - Name: GCE Vectrex Id: vectrex IgdbId: 67 Databases: [GCE - Vectrex] Emulators: [bizhawk, retroarch] - Name: Macintosh Id: macintosh IgdbId: 14 - Name: Magnavox Odyssey 2 Id: magnavox_odyssey_2 IgdbId: 133 Databases: [Magnavox - Odyssey2] Emulators: [bizhawk] - Name: Mattel Intellivision Id: mattel_intellivision IgdbId: 67 Databases: [Mattel - Intellivision] Emulators: [bizhawk, retroarch] - Name: Microsoft MSX Id: microsoft_msx IgdbId: 27 Databases: [Microsoft - MSX] Emulators: [ares, bluemsx, retroarch] - Name: Microsoft MSX2 Id: microsoft_msx2 IgdbId: 53 Databases: [Microsoft - MSX2] Emulators: [ares, bluemsx, retroarch] - Name: Microsoft Xbox 360 Id: xbox360 IgdbId: 12 Databases: [Microsoft - Xbox 360, Microsoft - XBOX 360 (Digital), Microsoft - XBOX 360 (Games on Demand)] Emulators: [xenia] - Name: Microsoft Xbox One Id: xbox_one IgdbId: 49 - Name: Microsoft Xbox Series Id: xbox_series IgdbId: 169 - Name: Microsoft Xbox Id: xbox IgdbId: 11 Databases: [Microsoft - Xbox] Emulators: [cxbx-reloaded, retroarch, xemu] - Name: NEC PC-98 Id: nec_pc98 IgdbId: 149 Databases: [NEC - PC-98] Emulators: [retroarch] - Name: NEC PC-FX Id: nec_pcfx IgdbId: 274 Databases: [NEC - PC-FX] Emulators: [mednafen, retroarch] - Name: NEC SuperGrafx Id: nec_supergrafx IgdbId: 128 Databases: [NEC - PC Engine SuperGrafx] Emulators: [ares, bizhawk, higan, mednafen, mesen, retroarch] - Name: NEC TurboGrafx 16 Id: nec_turbografx_16 IgdbId: 86 Databases: [NEC - PC Engine - TurboGrafx 16] Emulators: [ares, bizhawk, higan, mednafen, mesen, retroarch] - Name: NEC TurboGrafx-CD Id: nec_turbografx_cd IgdbId: 150 Databases: [NEC - PC Engine CD - TurboGrafx-CD] Emulators: [ares, bizhawk, higan, mednafen, mesen, retroarch] - Name: Nintendo 3DS Id: nintendo_3ds IgdbId: 37 Databases: [Nintendo - Nintendo 3DS, Nintendo - Nintendo 3DS (Digital)] Emulators: [citra, retroarch] - Name: Nintendo 64 Id: nintendo_64 IgdbId: 4 Databases: [Nintendo - Nintendo 64, Nintendo - Nintendo 64DD] Emulators: [ares, bizhawk, m64p, m64py, project64, retroarch, rosaliesmupengui, simple64, gopher64] - Name: Nintendo DS Id: nintendo_ds IgdbId: 20 Databases: [Nintendo - Nintendo DS, Nintendo - Nintendo DS (Download Play)] Emulators: [bizhawk, desmume, gbe+, melonds, retroarch] - Name: Nintendo DSi Id: nintendo_dsi IgdbId: 159 Databases: [Nintendo - Nintendo DSi, Nintendo - Nintendo DSi (Digital)] Emulators: [bizhawk, melonds] - Name: Nintendo Entertainment System Id: nintendo_nes IgdbId: 18 Databases: [Nintendo - Nintendo Entertainment System] Emulators: [ares, bizhawk, fceux, higan, jgenesis, mednafen, mesen, nestopia, punes, retroarch] - Name: Nintendo Family Computer Disk System Id: nintendo_famicom_disk IgdbId: 51 Databases: [Nintendo - Family Computer Disk System] Emulators: [fceux, retroarch] - Name: Nintendo Game Boy Advance Id: nintendo_gameboyadvance IgdbId: 24 Databases: [Nintendo - Game Boy Advance] Emulators: [ares, bizhawk, gbe+, higan, mednafen, mgba, nanoboyadvance, retroarch, visualboyadvance, visualboyadvance-m] - Name: Nintendo Game Boy Color Id: nintendo_gameboycolor IgdbId: 22 Databases: [Nintendo - Game Boy Color] Emulators: [ares, bgb, bizhawk, gambatte, gbe+, higan, jgenesis, mednafen, mesen, mgba, retroarch, sameboy, visualboyadvance, visualboyadvance-m] - Name: Nintendo Game Boy Id: nintendo_gameboy IgdbId: 33 Databases: [Nintendo - Game Boy] Emulators: [ares, bgb, bizhawk, gambatte, gbe+, higan, jgenesis, mednafen, mesen, mgba, retroarch, sameboy, visualboyadvance, visualboyadvance-m] - Name: Nintendo GameCube Id: nintendo_gamecube IgdbId: 21 Databases: [Nintendo - GameCube] Emulators: [dolphin, retroarch] - Name: Nintendo SNES Id: nintendo_super_nes IgdbId: 19 Databases: [Nintendo - Super Nintendo Entertainment System] Emulators: [ares, bizhawk, bsnes, bsnes-hd, bsnes-mt, higan, jgenesis, mednafen, mesen, mesen-s, retroarch, snes9x, zsnes] - Name: Nintendo Switch Id: nintendo_switch IgdbId: 130 - Name: Nintendo Switch 2 Id: nintendo_switch2 IgdbId: 508 - Name: Nintendo Virtual Boy Id: nintendo_virtualboy IgdbId: 87 Databases: [Nintendo - Virtual Boy] Emulators: [bizhawk, mednafen, retroarch] - Name: Nintendo Wii U Id: nintendo_wiiu IgdbId: 41 Databases: [Nintendo - Wii U, Nintendo - Wii U (Digital)] Emulators: [cemu, decaf-emu] - Name: Nintendo Wii Id: nintendo_wii IgdbId: 5 Databases: [Nintendo - Wii, Nintendo - Wii (Digital)] Emulators: [dolphin, retroarch] - Name: PC (DOS) Id: pc_dos IgdbId: 13 Databases: [DOS] Emulators: [dosbox, pcem, retroarch, scummvm] - Name: PC (Linux) Id: pc_linux IgdbId: 3 - Name: PC (Windows) Id: pc_windows IgdbId: 6 Emulators: [pcem, scummvm] - Name: Sega 32X Id: sega_32x IgdbId: 30 Databases: [Sega - 32X] Emulators: [ares, jgenesis, kegafusion, retroarch] - Name: Sega CD Id: sega_cd IgdbId: 78 Databases: [Sega - Mega-CD - Sega CD] Emulators: [ares, jgenesis, kegafusion, retroarch] - Name: Sega Dreamcast Id: sega_dreamcast IgdbId: 23 Databases: [Sega - Dreamcast] Emulators: [demul, flycast, nulldc, redream, reicast, retroarch] - Name: Sega Game Gear Id: sega_gamegear IgdbId: 35 Databases: [Sega - Game Gear] Emulators: [ares, bizhawk, higan, jgenesis, kegafusion, mednafen, retroarch] - Name: Sega Genesis Id: sega_genesis IgdbId: 29 Databases: [Sega - Mega Drive - Genesis] Emulators: [bizhawk, blastem, higan, jgenesis, kegafusion, mednafen, retroarch, ares] - Name: Sega Master System Id: sega_mastersystem IgdbId: 64 Databases: [Sega - Master System - Mark III] Emulators: [ares, bizhawk, higan, jgenesis, kegafusion, mednafen, retroarch] - Name: Sega Saturn Id: sega_saturn IgdbId: 32 Databases: [Sega - Saturn] Emulators: [ares, bizhawk, mednafen, retroarch, yabuse, ymir] - Name: Sega SG-1000 Id: sega_sg1000 IgdbId: 84 Databases: [Sega - SG-1000] Emulators: [ares, retroarch] - Name: Sharp X68000 Id: sharp_x68000 IgdbId: 121 Databases: [Sharp - X68000] Emulators: [retroarch] - Name: Sinclair ZX Spectrum +3 Id: sinclair_zxspectrum3 Databases: [Sinclair - ZX Spectrum +3] Emulators: [fuse, retroarch] - Name: Sinclair ZX Spectrum Id: sinclair_zxspectrum IgdbId: 26 Databases: [Sinclair - ZX Spectrum] Emulators: [bizhawk, fuse, retroarch] - Name: Sinclair ZX81 Id: sinclair_zx81 IgdbId: 373 Databases: [Sinclair - ZX 81] Emulators: [retroarch] - Name: SNK Neo Geo AES Id: snk_neogeo_aes IgdbId: 80 Emulators: [ares] - Name: SNK Neo Geo CD Id: snk_neogeo_cd IgdbId: 136 Databases: [SNK - Neo Geo CD] Emulators: [retroarch] - Name: SNK Neo Geo Pocket Color Id: snk_neogeopocket_color IgdbId: 120 Databases: [SNK - Neo Geo Pocket Color] Emulators: [ares, bizhawk, mednafen, retroarch] - Name: SNK Neo Geo Pocket Id: snk_neogeopocket IgdbId: 119 Databases: [SNK - Neo Geo Pocket] Emulators: [ares, bizhawk, mednafen, retroarch] - Name: Sony PlayStation 2 Id: sony_playstation2 IgdbId: 8 Databases: [Sony - PlayStation 2] Emulators: [pcsx2, retroarch] - Name: Sony PlayStation 3 Id: sony_playstation3 IgdbId: 9 Databases: [Sony - PlayStation 3, Sony - PlayStation 3 (PSN)] Emulators: [rpcs3] - Name: Sony PlayStation 4 Id: sony_playstation4 IgdbId: 48 Emulators: [shadps4] - Name: Sony PlayStation 5 Id: sony_playstation5 IgdbId: 167 - Name: Sony PlayStation Portable Id: sony_psp IgdbId: 38 Databases: [Sony - PlayStation Portable, Sony - PlayStation Portable (PSN), Sony - PlayStation Portable (PSX2PSP)] Emulators: [ppsspp, retroarch] - Name: Sony PlayStation Vita Id: sony_vita IgdbId: 46 Databases: [Sony - PlayStation Vita, Sony - PlayStation Vita (PSN)] Emulators: [vita3k] - Name: Sony PlayStation Id: sony_playstation IgdbId: 7 Databases: [Sony - PlayStation] Emulators: [ares, bizhawk, duckstation, epsxe, mednafen, pcsxr-pgxp, retroarch] - Name: Texas Instruments TI-83 Id: ti_83 Databases: [TIC-80] Emulators: [bizhawk] - Name: Thomson MO5 Id: thomson_mo5 IgdbId: 156 Databases: [Thomson - MOTO] Emulators: [retroarch] - Name: Thomson TO7 Id: thomson_to7 Databases: [Thomson - MOTO] Emulators: [retroarch] - Name: TIC-80 Id: tic_80 Emulators: [bizhawk] - Name: Uzebox Id: uzebox Databases: [Uzebox] Emulators: [bizhawk] - Name: Watara Supervision Id: watara_supervision Databases: [Watara - Supervision] Emulators: [retroarch] - Name: Philips CD-i Id: philips_cdi IgdbId: 117 - Name: Pokémon mini Id: pokemon_mini IgdbId: 166 ================================================ FILE: source/Playnite/Emulation/Regions.yaml ================================================ - Id: australia Name: Australia Codes: [A, AU, Australia] IgdbId: 3 - Id: asia Name: Asia Codes: [As, Asia] IgdbId: 7 - Id: brazil Name: Brazil Codes: [B, BR, Brazil] - Id: canada Name: Canada Codes: [C, CA, Canada] - Id: china Name: China Codes: [Ch, CN, China] IgdbId: 6 - Id: netherlands Name: Netherlands Codes: [D, Nl, Netherlands] - Id: europe Name: Europe Codes: [E, EU, Europe] DefaultImport: true IgdbId: 1 - Id: france Name: France Codes: [F, FR, France] - Id: germany Name: Germany Codes: [G, DE, Germany] - Id: greece Name: Greece Codes: [Gr, Greece] - Id: holland Name: Holland Codes: [H, Holland] - Id: hongKong Name: Hong Kong Codes: [HK, "Hong Kong"] - Id: italy Name: Italy Codes: [I, IT, Italy] - Id: japan Name: Japan Codes: [J, JP, Japan] DefaultImport: true IgdbId: 5 - Id: korea Name: Korea Codes: [K, KR, Korea] - Id: newZealand Name: New Zealand Codes: [NZ, "New Zealand"] IgdbId: 4 - Id: norway Name: Norway Codes: [No, Norway] - Id: russia Name: Russia Codes: [R, RU, Russia] - Id: spain Name: Spain Codes: [S, ES, Spain] - Id: sweden Name: Sweden Codes: [Sw, SE, Sweden] - Id: usa Name: USA Codes: [U, NA, USA, US] DefaultImport: true IgdbId: 2 - Id: unitedKingdom Name: United Kingdom Codes: [UK, GB, "United Kingdom"] - Id: world Name: World Codes: [W, World] DefaultImport: true IgdbId: 8 ================================================ FILE: source/Playnite/Emulators/DatModels.cs ================================================ using SqlNado; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using YamlDotNet.Serialization; namespace Playnite.Emulators { public class DatGame { [SQLiteColumn(IsPrimaryKey = true, AutoIncrements = true)] public int Id { get; set; } [DatProperty("name")] public string Name { get; set; } [DatProperty("region")] public string Region { get; set; } [DatProperty("releaseyear")] public string ReleaseYear { get; set; } [SQLiteIndex(nameof(Serial))] [DatProperty("serial")] public string Serial { get; set; } [SQLiteIndex(nameof(RomCrc))] [DatProperty("rom.crc")] public string RomCrc { get; set; } [DatProperty("rom.name")] public string RomName { get; set; } [YamlIgnore] [SQLiteColumn(Ignore = true)] [DatProperty("rom.serial")] public string RomSerial { get; set; } [YamlIgnore] [SQLiteColumn(Ignore = true)] [DatProperty("origin")] public string Origin { get; set; } [YamlIgnore] [SQLiteColumn(Ignore = true)] [DatProperty("comment")] public string Comment { get; set; } //[DatProperty("rom.size")] //public long RomSize { get; set; } //[DatProperty("rom.md5")] //public string RomMd5 { get; set; } //[DatProperty("rom.sha1")] //public string RomSha1 { get; set; } [YamlIgnore] [SQLiteColumn(Ignore = true)] public string SanitizedName => Name.IsNullOrEmpty() ? string.Empty : Emulators.RomName.SanitizeName(Name); public override string ToString() { return $"{Serial} {RomCrc} {Name}"; } public void CopyTo(DatGame target) { if (!Name.IsNullOrEmpty() && target.Name.IsNullOrEmpty()) { target.Name = Name; } if (!Origin.IsNullOrEmpty() && Region.IsNullOrEmpty() && target.Region.IsNullOrEmpty()) { target.Region = Origin; } if (!Region.IsNullOrEmpty() && target.Region.IsNullOrEmpty()) { target.Region = Region; } if (!ReleaseYear.IsNullOrEmpty() && target.ReleaseYear.IsNullOrEmpty()) { target.ReleaseYear = ReleaseYear; } if (!RomSerial.IsNullOrEmpty() && target.Serial.IsNullOrEmpty()) { target.Serial = RomSerial; } if (!Serial.IsNullOrEmpty() && target.Serial.IsNullOrEmpty()) { target.Serial = Serial; } } public void FixData() { if (Serial.IsNullOrEmpty() && !RomSerial.IsNullOrEmpty()) { Serial = RomSerial; } if (Region.IsNullOrEmpty() && !Origin.IsNullOrEmpty()) { Region = Origin; } if (Name.IsNullOrEmpty() && !Comment.IsNullOrEmpty()) { Name = Comment; } } } public class DatPropertyAttribute : Attribute { public string Name { get; } public DatPropertyAttribute(string name) { Name = name; } } } ================================================ FILE: source/Playnite/Emulators/Emulation.cs ================================================ using Playnite.Common; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Playnite.Emulators { public class Emulation : IEmulationAPI { private static readonly ILogger logger = LogManager.GetLogger(); private static string platformsFile => Path.Combine(PlaynitePaths.ProgramPath, "Emulation", "Platforms.yaml"); private static string regionsFile => Path.Combine(PlaynitePaths.ProgramPath, "Emulation", "Regions.yaml"); private static string emulatorDefDir => Path.Combine(PlaynitePaths.ProgramPath, "Emulation", "Emulators"); public const string StartupScriptFileName = "startGame.ps1"; public const string GameImportScriptFileName = "importGames.ps1"; #region IEmulationAPI IList IEmulationAPI.Platforms => Platforms.GetClone(); IList IEmulationAPI.Regions => Regions.GetClone(); IList IEmulationAPI.Emulators => Definitions.GetClone(); EmulatedPlatform IEmulationAPI.GetPlatform(string platformsId) { return GetPlatform(platformsId).GetClone(); } EmulatedRegion IEmulationAPI.GetRegion(string regionId) { return GetRegion(regionId).GetClone(); } EmulatorDefinition IEmulationAPI.GetEmulator(string emulatorDefinitionId) { return GetDefition(emulatorDefinitionId).GetClone(); } #endregion IEmulationAPI private static List platforms; public static IList Platforms { get { if (platforms != null) { return platforms; } if (File.Exists(platformsFile)) { try { platforms = Serialization.FromYamlFile>(platformsFile); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { platforms = new List(); logger.Error(e, $"Failed to emulated platforms list."); } } else { logger.Error("Emulation platforms file not found!"); platforms = new List(); } return platforms.AsReadOnly(); } } public static EmulatedPlatform GetPlatform(string platformsId) { return Platforms.FirstOrDefault(a => a.Id == platformsId); } public static EmulatedPlatform GetPlatformByDatabase(string databaseName) { return Platforms.FirstOrDefault(a => a.Databases?.ContainsString(databaseName, StringComparison.OrdinalIgnoreCase) == true); } private static List regions; public static IList Regions { get { if (regions != null) { return regions; } if (File.Exists(regionsFile)) { try { regions = Serialization.FromYamlFile>(regionsFile); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { regions = new List(); logger.Error(e, $"Failed to emulated regions list."); } } else { logger.Error("Emulation regions file not found!"); regions = new List(); } return regions.AsReadOnly(); } } public static EmulatedRegion GetRegion(string regionsId) { return Regions.FirstOrDefault(a => a.Id == regionsId); } public static EmulatedRegion GetRegionByCode(string code) { return Regions.FirstOrDefault(a => a.Codes.ContainsString(code, StringComparison.OrdinalIgnoreCase)); } private static List definitions; public static IList Definitions { get { if (definitions != null) { return definitions; } definitions = new List(); if (!Directory.Exists(emulatorDefDir)) { return definitions; } try { foreach (var dir in Directory.GetDirectories(emulatorDefDir)) { var manifest = Path.Combine(dir, "emulator.yaml"); try { var data = Serialization.FromYamlFile(manifest); data.DirectoryName = Path.GetFileName(dir); definitions.Add(data); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to load emulator definition file {manifest}"); } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { definitions = new List(); logger.Error(e, "Failed to load emulator definitions."); } return definitions.AsReadOnly(); } } public static EmulatorDefinition GetDefition(string emulatorId) { if (emulatorId.IsNullOrEmpty()) { return null; } return Definitions.FirstOrDefault(a => a.Id == emulatorId); } public static EmulatorDefinitionProfile GetProfile(string emulatorId, string profileName) { if (emulatorId.IsNullOrEmpty()) { return null; } return Definitions.FirstOrDefault(a => a.Id == emulatorId)?.Profiles?.FirstOrDefault(a => a.Name == profileName); } public static bool IsEmuProfileValid(SDK.Models.Emulator emulator, string profileId) { if (profileId.StartsWith(SDK.Models.CustomEmulatorProfile.ProfilePrefix)) { return emulator.CustomProfiles?.Any(a => a.Id == profileId) == true; } else { return GetProfile(emulator.BuiltInConfigId, profileId) != null; } } public static string GetExecutable(string directory, EmulatorDefinitionProfile profile, bool relative) { if (!Directory.Exists(directory)) { throw new Exception($"{directory} not found."); } var fileEnumerator = new SafeFileEnumerator(directory, "*.*", SearchOption.AllDirectories); foreach (var file in fileEnumerator) { if (file.Attributes.HasFlag(FileAttributes.Directory)) { continue; } var relativePath = file.FullName.Replace(Path.GetDirectoryName(file.FullName), "").Trim(Path.DirectorySeparatorChar); var regex = new Regex(profile.StartupExecutable, RegexOptions.IgnoreCase); if (regex.IsMatch(relativePath)) { if (relative) { return relativePath; } else { return file.FullName; } } } return null; } public static string GetStartupScriptPath(EmulatorDefinition emulator) { return Path.Combine(emulatorDefDir, emulator.DirectoryName, "startGame.ps1"); } public static string GetGameImportScriptPath(EmulatorDefinition emulator) { return Path.Combine(emulatorDefDir, emulator.DirectoryName, "importGames.ps1"); } } } ================================================ FILE: source/Playnite/Emulators/EmulationDatabase.cs ================================================ using Playnite.SDK; using SqlNado; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Emulators { public class EmulationDatabase { public interface IEmulationDatabaseReader : IDisposable { string DatabaseName { get; } IEnumerable GetByCrc(string checksum); IEnumerable GetBySerial(string serial); IEnumerable GetByRomName(string romName); IEnumerable GetByRomNamePartial(string romNamePart); void ClearStatementCache(); } public class EmulationDatabaseReader : IEmulationDatabaseReader { private readonly SQLiteDatabase db; public string DatabaseName { get; } public EmulationDatabaseReader(string dbPath) { DatabaseName = Path.GetFileNameWithoutExtension(dbPath); db = new SQLiteDatabase(dbPath, SQLiteOpenOptions.SQLITE_OPEN_READONLY); } public void ClearStatementCache() { db.ClearStatementsCache(); } public IEnumerable GetByCrc(string checksum) { if (db.TableExists("DatGame")) { return db.Load($"WHERE UPPER({nameof(DatGame.RomCrc)}) = '{checksum.ToUpper().Replace("'", "''")}'"); } else { return new List(); } } public IEnumerable GetBySerial(string serial) { if (db.TableExists("DatGame")) { return db.Load($"WHERE UPPER({nameof(DatGame.Serial)}) = '{serial.ToUpper().Replace("'", "''")}'"); } else { return new List(); } } public IEnumerable GetByRomName(string romName) { if (db.TableExists("DatGame")) { return db.Load($"WHERE UPPER({nameof(DatGame.RomName)}) = '{romName.ToUpper().Replace("'", "''")}'"); } else { return new List(); } } public IEnumerable GetByRomNamePartial(string romNamePart) { if (db.TableExists("DatGame")) { return db.Load($"WHERE INSTR(UPPER({nameof(DatGame.RomName)}), '{romNamePart.ToUpper().Replace("'", "''")}') > 0"); } else { return new List(); } } public void Dispose() { db.Dispose(); } } private static readonly ILogger logger = LogManager.GetLogger(); public static EmulationDatabaseReader GetDatabase(string databaseName, string databaseDir) { var dbFile = Path.Combine(databaseDir, $"{databaseName}.db"); if (File.Exists(dbFile)) { return new EmulationDatabaseReader(dbFile); } else { return null; } } } } ================================================ FILE: source/Playnite/Emulators/Scanner.cs ================================================ using Playnite.Common; using Playnite.Database; using Playnite.Native; using Playnite.Scripting.PowerShell; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace Playnite.Emulators { public class ScannedEmulator : ObservableObject { public class ScannedEmulatorProfile : ObservableObject { private bool import = true; public bool Import { get => import; set { import = value; OnPropertyChanged(); } } public string Name { get; set; } public string ProfileName { get; set; } } private bool import = true; public bool Import { get => import; set { import = value; OnPropertyChanged(); } } public string Name { get; set; } public string Id { get; set; } public string InstallDir { get; set; } public List Profiles { get; set; } } public class EmulatorScanner { private static readonly ILogger logger = LogManager.GetLogger(); public static List SearchForEmulators(string path, IList definitions, CancellationToken cancelToken) { logger.Info($"Looking for emulators in {path}, using {definitions.Count} definitions."); var imported = new Dictionary(); foreach (var file in new SafeFileEnumerator(path, "*", SearchOption.AllDirectories)) { if (cancelToken.IsCancellationRequested) { return new List(); } if (file.Attributes.HasFlag(FileAttributes.Directory)) { continue; } foreach (var definition in definitions) { var currentDir = Path.GetDirectoryName(file.FullName); var importId = definition.Id + currentDir; foreach (var defProfile in definition.Profiles) { var detectionStr = defProfile.InstallationFile; if (detectionStr.IsNullOrEmpty()) { detectionStr = defProfile.StartupExecutable; } if (detectionStr.IsNullOrEmpty() && !PlayniteEnvironment.ThrowAllErrors) { continue; } var reqMet = true; var regex = new Regex(detectionStr, RegexOptions.IgnoreCase); if (regex.IsMatch(file.Name)) { if (defProfile.ProfileFiles?.Any() == true) { foreach (var reqFile in defProfile.ProfileFiles) { if (!FileSystem.FileExists(Path.Combine(currentDir, reqFile))) { reqMet = false; break; } } } } else { reqMet = false; } if (reqMet) { imported.TryGetValue(importId, out var currentEmulator); if (currentEmulator == null) { currentEmulator = new ScannedEmulator() { Name = definition.Name, InstallDir = currentDir, Id = definition.Id, Profiles = new List() }; if (currentDir.StartsWith(PlaynitePaths.ProgramPath, StringComparison.OrdinalIgnoreCase)) { currentEmulator.InstallDir = currentDir.Replace(PlaynitePaths.ProgramPath, ExpandableVariables.PlayniteDirectory, StringComparison.OrdinalIgnoreCase); } imported.Add(importId, currentEmulator); } currentEmulator.Profiles.Add(new ScannedEmulator.ScannedEmulatorProfile { ProfileName = defProfile.Name, Name = defProfile.Name }); } } } } return imported.Values.ToList(); } } public class GameScanner { public class ScanExclusion { public string Path; public bool Absolute = true; public bool MatchByRegex = false; } private static readonly ILogger logger = LogManager.GetLogger(); private static readonly string[] supportedArchiveExt = new string[] { "rar", "7z", "zip", "tar", "bzip2", "gzip", "lzip" }; private readonly Dictionary isGoogleDriveCache = new Dictionary(); private readonly GameScannerConfig scanner; private List fileExclusions; private List directoryExclusions; private readonly IGameDatabaseMain database; internal HashSet importedFiles; private readonly Func, List> emuDbProvider; public GameScanner( GameScannerConfig scanner, IGameDatabaseMain database, Func, List> emuDbProvider = null) { this.scanner = scanner; this.database = database; if (emuDbProvider == null) { this.emuDbProvider = GetEmulationDbs; } else { this.emuDbProvider = emuDbProvider; } } public List Scan( CancellationToken cancelToken, out List newPlatforms, out List newRegions, Action fileScanCallback = null) { List games; newPlatforms = new List(); newRegions = new List(); var emulator = database.Emulators[scanner.EmulatorId]; if (emulator == null) { throw new Exception("Emulator not found."); } if (scanner.EmulatorProfileId.IsNullOrEmpty()) { throw new Exception("No emulator profile specified."); } importedFiles = database.GetImportedRomFiles(emulator.InstallDir); var globalScanConfig = database.GetGameScannersSettings(); var crcExclusions = string.Join(";", ListExtensions.Merge(globalScanConfig.CrcExcludeFileTypes, scanner.CrcExcludeFileTypes). Select(a => a.ToLower().Trim()).ToHashSet()); var dirToScan = PlaynitePaths.ExpandVariables(scanner.Directory, emulator.InstallDir, true); fileExclusions = ParseExclusions(dirToScan, scanner.ExcludedFiles); directoryExclusions = ParseExclusions(dirToScan, scanner.ExcludedDirectories); CustomEmulatorProfile customProfile = null; BuiltInEmulatorProfile builtinProfile = null; EmulatorDefinitionProfile builtinProfileDef = null; if (scanner.EmulatorProfileId.StartsWith(CustomEmulatorProfile.ProfilePrefix, StringComparison.Ordinal)) { customProfile = emulator.CustomProfiles?.FirstOrDefault(a => a.Id == scanner.EmulatorProfileId); if (customProfile == null) { throw new Exception("Assigned custom emulator profile not found."); } games = ScanDirectory( dirToScan, emulator, customProfile, cancelToken, crcExclusions, scanner.ScanSubfolders, scanner.ScanInsideArchives, scanner.MergeRelatedFiles, fileScanCallback); } else if (scanner.EmulatorProfileId.StartsWith(BuiltInEmulatorProfile.ProfilePrefix, StringComparison.Ordinal)) { builtinProfile = emulator.BuiltinProfiles?.FirstOrDefault(a => a.Id == scanner.EmulatorProfileId); if (builtinProfile == null) { throw new Exception("Assigned built-in emulator profile not found."); } builtinProfileDef = Emulation.GetProfile(emulator.BuiltInConfigId, builtinProfile.BuiltInProfileName); if (builtinProfileDef == null) { throw new Exception("Assigned built-in emulator profile definition not found."); } games = ScanDirectory( dirToScan, emulator, builtinProfile, cancelToken, crcExclusions, scanner.ScanSubfolders, scanner.ScanInsideArchives, scanner.MergeRelatedFiles, fileScanCallback); } else { throw new Exception("Emulator profile format not supported."); } foreach (var game in games) { game.SourceEmulator = emulator; game.SourceConfig = scanner; var assignedRegions = new List(); var assignedPlatforms = new List(); foreach (var rom in game.Roms) { // REGIONS if (rom.DbData?.Region.IsNullOrEmpty() == false) { var region = Emulation.GetRegionByCode(rom.DbData.Region); if (region != null) { assignedRegions.AddMissing(region); } } else if (rom.Name.Properties.HasItems()) { foreach (var prop in rom.Name.Properties) { var region = Emulation.GetRegionByCode(prop); if (region != null) { assignedRegions.AddMissing(region); break; } } } // PLATFORMS if (rom.DbData != null) { var platform = Emulation.GetPlatformByDatabase(rom.DbDataSource); if (platform != null) { assignedPlatforms.AddMissing(platform); } } else if (builtinProfileDef != null) { assignedPlatforms.AddMissing(Emulation.GetPlatform(builtinProfileDef.Platforms.First())); } rom.Path = Paths.TrimLongPathPrefix(rom.Path); } game.Regions = new List(); foreach (var asRegion in assignedRegions) { var dbRegion = database.Regions.FirstOrDefault(a => a.SpecificationId == asRegion.Id); if (dbRegion != null) { game.Regions.Add(dbRegion); } else { var generatedReg = newRegions.FirstOrDefault(a => a.SpecificationId == asRegion.Id); if (generatedReg == null) { var newReg = new Region(asRegion.Name) { SpecificationId = asRegion.Id }; newRegions.Add(newReg); game.Regions.Add(newReg); } else { game.Regions.Add(generatedReg); } } } game.Platforms = new List(); if (scanner.OverridePlatformId != Guid.Empty) { var dbPlatform = database.Platforms[scanner.OverridePlatformId]; if (dbPlatform != null) { game.Platforms.Add(dbPlatform); } } else { if (builtinProfile != null) { foreach (var asPlatform in assignedPlatforms) { var dbPlatform = database.Platforms.FirstOrDefault(a => a.SpecificationId == asPlatform.Id); if (dbPlatform != null) { game.Platforms.Add(dbPlatform); } else { var generatedPlat = newPlatforms.FirstOrDefault(a => a.SpecificationId == asPlatform.Id); if (generatedPlat == null) { var newPlat = new Platform(asPlatform.Name) { SpecificationId = asPlatform.Id }; newPlatforms.Add(newPlat); game.Platforms.Add(newPlat); } else { game.Platforms.Add(generatedPlat); } } } } else if (customProfile.Platforms.HasItems()) { foreach (var asPlatform in customProfile.Platforms) { var dbPlatform = database.Platforms[asPlatform]; if (dbPlatform != null) { game.Platforms.Add(dbPlatform); } } } } var releaseYear = game.Roms.FirstOrDefault(a => a.DbData?.ReleaseYear.IsNullOrEmpty() == false)?.DbData.ReleaseYear; if (!releaseYear.IsNullOrEmpty()) { if (ReleaseDate.TryDeserialize(releaseYear, out var releaseDate)) { game.ReleaseDate = releaseDate; } } } return games; } public static List ParseExclusions(string rootDir, List exclusions) { var result = new List(); if (!exclusions.HasItems()) { return result; } foreach (var excl in exclusions) { if (excl.IsNullOrWhiteSpace()) { continue; } var exclusion = new ScanExclusion(); for (int i = 0; i < excl.Length; i++) { if (excl[i] == '>') { exclusion.Absolute = false; } else if (excl[i] == '?') { exclusion.MatchByRegex = true; } else { break; } } if (exclusion.Absolute) { if (exclusion.MatchByRegex) { exclusion.Path = Regex.Escape(rootDir.EndWithDirSeparator()) + excl.TrimStart('>', '?').Trim(); } else { exclusion.Path = Path.Combine(rootDir, excl.TrimStart('>', '?').Trim()); } } else { exclusion.Path = excl.TrimStart('>', '?').Trim(); } result.Add(exclusion); } return result; } public static List GetFileExclusionMatches(List files, List exclusions) { var matches = new List(); foreach (var excFile in exclusions) { if (excFile.MatchByRegex) { // exclusion parser already appends root path if needed so same match can be done here // for any level match and absolute path match var match = files.Where(a => Regex.IsMatch(a, excFile.Path, RegexOptions.IgnoreCase)); matches.AddMissing(match); } else if (excFile.Absolute) { var match = files.FirstOrDefault(a => a.Equals(excFile.Path, StringComparison.OrdinalIgnoreCase)); if (match != null) { matches.AddMissing(match); } } else if (!excFile.Absolute) { var comp = excFile.Path.PrefixWithDirSeparator(); var match = files.Where(a => a.EndsWith(comp, StringComparison.OrdinalIgnoreCase)); matches.AddMissing(match); } else { throw new Exception("Uknown file exclusion configuration."); } } return matches; } public static List GetDirectoryExclusionMatches(List dirs, List exclusions) { var matches = new List(); foreach (var excDir in exclusions) { if (excDir.MatchByRegex) { // exclusion parser already appends root path if needed so same match can be done here // for any level match and absolute path match var match = dirs.Where(a => Regex.IsMatch(a.TrimEnd(Paths.DirectorySeparators), excDir.Path, RegexOptions.IgnoreCase)); matches.AddMissing(match); } else if (excDir.Absolute && !excDir.MatchByRegex) { var match = dirs.FirstOrDefault(a => a.TrimEnd(Paths.DirectorySeparators).Equals(excDir.Path.TrimEnd(Paths.DirectorySeparators), StringComparison.OrdinalIgnoreCase)); if (match != null) { matches.AddMissing(match); } } else if (!excDir.Absolute && !excDir.MatchByRegex) { var comp = excDir.Path.PrefixWithDirSeparator().TrimEnd(Paths.DirectorySeparators); var match = dirs.Where(a => a.TrimEnd(Paths.DirectorySeparators).EndsWith(comp, StringComparison.OrdinalIgnoreCase)); matches.AddMissing(match); } } return matches; } private List ScanDirectory( string directory, Emulator emulator, BuiltInEmulatorProfile profile, CancellationToken cancelToken, string crcExludePatterns, bool scanSubfolders, bool scanArchives, bool mergeRelatedFiles, Action fileScanCallback = null) { var emuProf = Emulation.GetProfile(emulator.BuiltInConfigId, profile.BuiltInProfileName); if (emuProf == null) { throw new Exception($"Emulator {emulator.BuiltInConfigId} and profile {profile.BuiltInProfileName} not found."); } if (emuProf.ScriptGameImport) { object scannedGames = null; Exception failExc = null; var importRuntime = new PowerShellRuntime("Emu game import"); var scriptTask = Task.Run(() => { try { scannedGames = importRuntime.ExecuteFile( Emulation.GetGameImportScriptPath(Emulation.GetDefition(emulator.BuiltInConfigId)), emulator.InstallDir, new Dictionary { { "CancelToken", cancelToken }, { "Emulator", emulator }, { "EmulatorProfile", emuProf }, { "ScanDirectory", directory }, { "PlayniteApi", SDK.API.Instance }, { "ImportedFiles", importedFiles } }); } catch (Exception e) { failExc = e; logger.Error(e, "Failed to scan directory using emulator."); } finally { importRuntime.Dispose(); } }); while (true) { Thread.Sleep(200); if (cancelToken.IsCancellationRequested) { scriptTask.Wait(5000); if (!importRuntime.IsDisposed) { importRuntime.Dispose(); } return new List(); } if (failExc != null) { throw failExc; } if (scriptTask.IsCompleted) { return ParseScriptScanResult( scannedGames, emuProf); } if (scriptTask.IsCanceled || scriptTask.IsFaulted) { return new List(); } } } else { return ScanDirectory( directory, emuProf.ImageExtensions?.Select(a => a.Trim()).ToList(), emuProf.Platforms, cancelToken, crcExludePatterns, scanSubfolders, scanArchives, mergeRelatedFiles, fileScanCallback); } } private List ScanDirectory( string directory, Emulator emulator, CustomEmulatorProfile profile, CancellationToken cancelToken, string crcExludePatterns, bool scanSubfolders, bool scanArchives, bool mergeRelatedFiles, Action fileScanCallback = null) { if (profile == null) { throw new Exception($"No profile provided."); } if (!profile.ImageExtensions.HasItems()) { return new List(); } var platforms = profile.Platforms?.Select(a => database.Platforms[a]?.SpecificationId).Where(a => !a.IsNullOrEmpty()).ToList(); return ScanDirectory( directory, profile.ImageExtensions.Select(a => a.Trim()).ToList(), platforms, cancelToken, crcExludePatterns, scanSubfolders, scanArchives, mergeRelatedFiles, fileScanCallback); } private List ScanDirectory( string directory, List supportedExtensions, List scanPlatforms, CancellationToken cancelToken, string crcExludePatterns, bool scanSubfolders, bool scanArchives, bool mergeRelatedFiles, Action fileScanCallback = null) { logger.Info($"Scanning emulated directory {directory}."); if (!FileSystem.DirectoryExists(directory)) { throw new Exception($"Can't scan emulation directory, {directory} doesn't exist."); } var emuDbs = emuDbProvider(scanPlatforms); var resultGames = new List(); try { ScanDirectoryBase( directory, supportedExtensions, emuDbs, resultGames, cancelToken, crcExludePatterns, scanSubfolders, scanArchives, mergeRelatedFiles, fileScanCallback); } finally { emuDbs.ForEach(a => a.Dispose()); } return resultGames; } internal void ScanDirectoryBase( string directory, List supportedExtensions, List databases, List resultGames, CancellationToken cancelToken, string crcExludePatterns, bool scanSubfolders, bool scanArchives, bool mergeRelatedFiles, Action fileScanCallback = null) { void addRom(ScannedRom rom) { if (mergeRelatedFiles) { var existing = resultGames.FirstOrDefault(a => a.Name == rom.Name.SanitizedName); if (existing != null) { existing.Roms.Add(rom); } else { resultGames.Add(new ScannedGame { Name = rom.Name.SanitizedName, Roms = new ObservableCollection { rom } }); } } else { resultGames.Add(new ScannedGame { Name = rom.Name.SanitizedName, Roms = new ObservableCollection { rom } }); } } List files; List dirs; try { directory = Paths.FixPathLength(directory); files = Directory.GetFiles(directory).ToList(); dirs = Directory.GetDirectories(directory).ToList(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to enumarete directory entires."); return; } fileScanCallback?.Invoke(directory); if (fileExclusions.HasItems()) { var matches = GetFileExclusionMatches(files, fileExclusions); if (matches.HasItems()) { matches.ForEach(a => files.Remove(a)); } } void processPlayListFile(string filePath, Func> playListParser) { var fileExt = Path.GetExtension(filePath).TrimStart('.'); files.Remove(filePath); try { var childFiles = playListParser(filePath); foreach (var child in childFiles ?? new List()) { var existingFile = files.FirstOrDefault(a => a.Equals(child, StringComparison.OrdinalIgnoreCase)); if (existingFile != null) { files.Remove(existingFile); } } if (importedFiles.ContainsString(Path.GetFullPath(filePath), StringComparison.OrdinalIgnoreCase)) { return; } if (!childFiles.HasItems()) { logger.Trace($"Detected playlist file with no referenced files: {filePath}"); addRom(new ScannedRom(filePath)); return; } Tuple romData = null; foreach (var childPath in childFiles) { if (cancelToken.IsCancellationRequested) { return; } if (!FileSystem.FileExists(childPath)) { continue; } fileScanCallback?.Invoke(childPath); var crcScan = true; if (databases.HasItems()) { if (scanner.ExcludeOnlineFiles && !IsFileDataAvailable(childPath)) { if (scanner.UseSimplifiedOnlineFileScan) { crcScan = false; } else { logger.Trace($"Skipping scan of {childPath} rom, scan of online files is disabled."); continue; } } if (crcScan && Paths.MathcesFilePattern(childPath, crcExludePatterns)) { logger.Trace($"Skipping crc check of {childPath}. Excluded by pattern settings."); crcScan = false; } } else { crcScan = false; } romData = LookupGameInDb( childPath, Path.GetExtension(childPath).TrimStart('.'), supportedExtensions, databases, crcScan, scanArchives); if (romData != null) { break; } } if (romData != null) { logger.Trace($"Detected rom with db info:{filePath}\n{romData.Item1}"); addRom(new ScannedRom(filePath, romData.Item1, romData.Item2)); } else { logger.Trace($"Detected rom: {filePath}"); addRom(new ScannedRom(filePath)); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to process {fileExt} playlist file {filePath}"); } fileScanCallback?.Invoke(directory); } // Cue files have priority since they will potentionaliy remove additional .bin files to match if (supportedExtensions.ContainsString("cue", StringComparison.OrdinalIgnoreCase)) { // ToList is needed here because we are potentionally modifing original files collection when playlist files are excluded foreach (var cueFile in files.Where(a => a.EndsWith(".cue", StringComparison.OrdinalIgnoreCase)).ToList()) { processPlayListFile(cueFile, (cFile) => CueSheet.GetFileEntries(cFile).Select(a => Path.Combine(directory, a.Path)).ToList()); } } // The same as with cue but for m3u playlist if (supportedExtensions.ContainsString("m3u", StringComparison.OrdinalIgnoreCase)) { // ToList is needed here because we are potentionally modifing original files collection when playlist files are excluded foreach (var m3uFile in files.ToList().Where(a => a.EndsWith(".m3u", StringComparison.OrdinalIgnoreCase)).ToList()) { processPlayListFile(m3uFile, (mFile) => M3U.GetEntries(mFile).Select(a => Path.Combine(directory, a.Path)).ToList()); } } // gdi files are basically cue files for Dreamcast dumps if (supportedExtensions.ContainsString("gdi", StringComparison.OrdinalIgnoreCase)) { // ToList is needed here because we are potentionally modifing original files collection when playlist files are excluded foreach (var gdiFile in files.ToList().Where(a => a.EndsWith(".gdi", StringComparison.OrdinalIgnoreCase)).ToList()) { processPlayListFile(gdiFile, (mFile) => GdiFile.GetEntries(mFile).Select(a => Path.Combine(directory, a.Path)).ToList()); } } foreach (var file in files) { if (cancelToken.IsCancellationRequested) { return; } string ext = null; foreach (var supportedExt in supportedExtensions) { // This is done this way to support nested extensions like PICO-8's .p8.png if (file.EndsWith("." + supportedExt, StringComparison.OrdinalIgnoreCase)) { ext = supportedExt; break; } else if (supportedExt == "" && Path.GetExtension(file).IsNullOrEmpty()) { ext = ""; break; } } if (ext == null) { continue; } if (!supportedExtensions.ContainsString(ext, StringComparison.OrdinalIgnoreCase)) { continue; } if (importedFiles.ContainsString(Path.GetFullPath(file), StringComparison.OrdinalIgnoreCase)) { continue; } try { var crcScan = true; if (databases.HasItems()) { if (scanner.ExcludeOnlineFiles && !IsFileDataAvailable(file)) { if (scanner.UseSimplifiedOnlineFileScan) { crcScan = false; } else { logger.Trace($"Skipping scan of {file} rom, scan of online files is disabled."); continue; } } if (crcScan && Paths.MathcesFilePattern(file, crcExludePatterns)) { logger.Trace($"Skipping crc check of {file}. Excluded by pattern settings."); crcScan = false; } } else { crcScan = false; } if (crcScan) { fileScanCallback?.Invoke(file); } var romData = LookupGameInDb( file, ext, supportedExtensions, databases, crcScan, scanArchives); if (romData != null) { logger.Trace($"Detected rom with db info:{file}\n{romData.Item1}"); addRom(new ScannedRom(file, romData.Item1, romData.Item2)); } else { logger.Trace($"Detected rom: {file}"); addRom(new ScannedRom(file, ext)); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed scan rom file {file}"); } fileScanCallback?.Invoke(directory); } if (scanSubfolders) { if (directoryExclusions.HasItems()) { var matches = GetDirectoryExclusionMatches(dirs, directoryExclusions); if (matches.HasItems()) { matches.ForEach(a => dirs.Remove(a)); } } foreach (var dir in dirs) { if (cancelToken.IsCancellationRequested) { break; } ScanDirectoryBase( dir, supportedExtensions, databases, resultGames, cancelToken, crcExludePatterns, scanSubfolders, scanArchives, mergeRelatedFiles, fileScanCallback); } } } private Tuple LookupGameInDb( string file, string fileExt, List supportedExtensions, List databases, bool scanCrc, bool scanArchives) { if (databases.HasItems()) { file = Paths.FixPathLength(file); DatGame datRec = null; string datRecSource = null; List crcs = new List(); if (scanCrc) { if (IsSupportedArchiveExtension(fileExt) && scanArchives) { var archFiles = Archive.GetArchiveFiles(file); var supportedFiles = archFiles.Where(a => supportedExtensions.ContainsString(Path.GetExtension(a).TrimStart('.'), StringComparison.OrdinalIgnoreCase)); foreach (var supportedFile in supportedFiles) { logger.Trace($"Getting rom crc from archive file '{supportedFile}'\r\n {file}"); var streams = Archive.GetEntryStream(file, supportedFile); if (streams != null) { using (streams.Item2) using (streams.Item1) { crcs.AddMissing(FileSystem.GetCRC32(streams.Item1)); } } } if (!crcs.HasItems()) { logger.Trace($"Failed to get crc info from archive: {file}"); crcs.Add(FileSystem.GetCRC32(file)); } } else { logger.Trace($"Getting rom crc from file: {file}"); crcs.Add(FileSystem.GetCRC32(file)); } } foreach (var db in databases) { // This is quick fix for high memory usage due to SQLNado's statement cache use. // This is mostly caused on our side because how ineffiently this whole ROM db lookup stuff is implemented // and it should be rewritten, but that's going to be done in P11. db.ClearStatementCache(); foreach (var crc in crcs) { datRec = db.GetByCrc(crc).FirstOrDefault(); if (datRec != null) { datRecSource = db.DatabaseName; break; } } if (datRecSource != null) { break; } var fileName = Path.GetFileName(file); datRec = db.GetByRomName(fileName).FirstOrDefault(); if (datRec != null) { datRecSource = db.DatabaseName; break; } // This is mainly for XBLA games that have those weird file names datRec = db.GetByRomNamePartial(fileName).FirstOrDefault(); if (datRec != null) { datRecSource = db.DatabaseName; break; } // For rare cases where ROM file name is the same as game's serial datRec = db.GetBySerial(Path.GetFileNameWithoutExtension(file)).FirstOrDefault(); if (datRec != null) { datRecSource = db.DatabaseName; break; } } if (datRec == null) { return null; } else { return new Tuple(datRec, datRecSource); } } else { return null; } } private List GetEmulationDbs(List platformIds) { var supportedPlatforms = Emulation.Platforms.Where(a => platformIds?.Contains(a.Id) == true); var supportedDatabases = supportedPlatforms.Where(a => a.Databases.HasItems()).SelectMany(a => a.Databases).Distinct(); var emuDbs = new List(); foreach (var supDb in supportedDatabases) { var db = EmulationDatabase.GetDatabase(supDb, PlaynitePaths.EmulationDatabasePath); if (db != null) { emuDbs.Add(db); } } return emuDbs; } private static ScannedGame ParseScripScannedGame( ScriptScannedGame scriptGame, List emuDbs) { var game = new ScannedGame(); game.Name = scriptGame.Name ?? scriptGame.Serial; game.Roms = new ObservableCollection(); game.ScriptSource = scriptGame; if (scriptGame.Serial.IsNullOrEmpty()) { game.Roms.Add(new ScannedRom(scriptGame.Path)); } else { DatGame datRec = null; string datRecSource = null; foreach (var db in emuDbs) { datRec = db.GetBySerial(scriptGame.Serial).FirstOrDefault(); if (datRec != null) { datRecSource = db.DatabaseName; break; } } if (datRec == null) { game.Roms.Add(new ScannedRom(scriptGame.Path)); } else { var romData = new ScannedRom(scriptGame.Path, datRec, datRecSource); game.Roms.Add(romData); game.Name = romData.Name.SanitizedName; } } return game; } private List ParseScriptScanResult( object scanResult, EmulatorDefinitionProfile emuProf) { if (scanResult == null) { return new List(); } if (!(scanResult is ScriptScannedGame) && !(scanResult is List)) { if (PlayniteEnvironment.ThrowAllErrors) { throw new Exception($"Scanning script returned unknown data type {scanResult.GetType()}"); } else { logger.Error($"Scanning script returned unknown data type {scanResult.GetType()}"); return new List(); } } var emuDbs = emuDbProvider(emuProf.Platforms); try { if (scanResult is ScriptScannedGame game) { return new List() { ParseScripScannedGame(game, emuDbs) }; } else if (scanResult is List games) { var result = new List(); foreach (ScriptScannedGame scannedGame in games) { result.Add(ParseScripScannedGame(scannedGame, emuDbs)); } return result; } } finally { emuDbs.ForEach(a => a.Dispose()); } return new List(); } private static bool IsSupportedArchiveExtension(string extension) { return supportedArchiveExt.ContainsString(extension, StringComparison.OrdinalIgnoreCase); } private bool IsFileDataAvailable(string path) { if (!Paths.IsFullPath(path)) { path = Path.GetFullPath(path); } if (!FileSystem.FileExists(path)) { return false; } var longPath = @"\\?\" + path; var att = Kernel32.GetFileAttributesW(longPath); if ((Winnt.FILE_ATTRIBUTE_OFFLINE & att) > 0) { return false; } // Used by OneDrive if ((Winnt.FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS & att) > 0) { return false; } if ((Winnt.FILE_ATTRIBUTE_SPARSE_FILE & att) > 0) { return false; } // GoogleDrive file check var driveLetter = Path.GetPathRoot(path); if (!isGoogleDriveCache.TryGetValue(driveLetter, out var isGoogleDrive)) { var drive = DriveInfo.GetDrives().First(a => string.Equals(a.Name, driveLetter, StringComparison.OrdinalIgnoreCase)); isGoogleDrive = drive.VolumeLabel?.Contains("Google") == true; isGoogleDriveCache.Add(driveLetter, isGoogleDrive); } if (isGoogleDrive) { // Based on undocumented file metadata // https://stackoverflow.com/questions/51439810/get-google-drive-files-links-using-drive-file-stream/52107704#52107704 longPath = longPath += ":user.drive.itemprotostr"; try { // Downloaded files (even partially) will have "content-entry" property var fileMetadata = File.ReadAllText(longPath); return fileMetadata.Contains("key: \"content-entry\""); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get file metadata from Google Drive file. {longPath}"); } } return true; } } public class ScriptScannedGame { public string Path { get; set; } public string Serial { get; set; } public string Name { get; set; } } public class ScannedGame : ObservableObject { #region backing fields private bool import = true; private ObservableCollection roms; private List platforms; private List regions; private string name; private GameScannerConfig sourceConfig; private ReleaseDate? releaseDate; private ScriptScannedGame scriptSource; private Emulator sourceEmulator; #endregion backing fields public bool Import { get => import; set => SetValue(ref import, value); } public ObservableCollection Roms { get => roms; set => SetValue(ref roms, value); } public List Platforms { get => platforms; set => SetValue(ref platforms, value); } public List Regions { get => regions; set => SetValue(ref regions, value); } public string Name { get => name; set => SetValue(ref name, value); } public GameScannerConfig SourceConfig { get => sourceConfig; set => SetValue(ref sourceConfig, value); } public ReleaseDate? ReleaseDate { get => releaseDate; set => SetValue(ref releaseDate, value); } public ScriptScannedGame ScriptSource { get => scriptSource; set => SetValue(ref scriptSource, value); } public Emulator SourceEmulator { get => sourceEmulator; set => SetValue(ref sourceEmulator, value); } public Game ToGame() { var game = new Game(Name) { IsInstalled = true, ReleaseDate = ReleaseDate }; if (Platforms.HasItems()) { game.PlatformIds = Platforms.Select(a => a.Id).ToList(); } if (Regions.HasItems()) { game.RegionIds = Regions.Select(a => a.Id).ToList(); } var playAction = new GameAction { Type = GameActionType.Emulator, EmulatorId = SourceConfig.EmulatorId, EmulatorProfileId = SourceConfig.EmulatorProfileId, IsPlayAction = true, Name = Name }; if (sourceConfig.PlayActionSettings == ScannerConfigPlayActionSettings.SelectProfiteOnStart) { playAction.EmulatorProfileId = null; } else if (sourceConfig.PlayActionSettings == ScannerConfigPlayActionSettings.SelectEmulatorOnStart) { playAction.EmulatorProfileId = null; playAction.EmulatorId = Guid.Empty; } game.GameActions = new ObservableCollection { playAction }; if (Roms.HasItems()) { var commonPath = Paths.GetCommonDirectory(Roms.Select(a => a.Path).ToArray()); game.Roms = new ObservableCollection(); var toReplace = string.Empty; var varToReplace = string.Empty; if (sourceConfig.ImportWithRelativePaths) { var emuDir = GameExtensions.ExpandVariables(new Game(), SourceEmulator.InstallDir, true) ?? string.Empty; if (commonPath.StartsWith(emuDir, StringComparison.OrdinalIgnoreCase)) { varToReplace = ExpandableVariables.EmulatorDirectory.EndWithDirSeparator(); toReplace = emuDir.EndWithDirSeparator(); } else if (commonPath.StartsWith(PlaynitePaths.ProgramPath, StringComparison.OrdinalIgnoreCase)) { varToReplace = ExpandableVariables.PlayniteDirectory.EndWithDirSeparator(); toReplace = PlaynitePaths.ProgramPath.EndWithDirSeparator(); } } if (sourceConfig.ImportWithRelativePaths && !toReplace.IsNullOrEmpty()) { game.InstallDirectory = commonPath.Replace(toReplace, varToReplace, StringComparison.OrdinalIgnoreCase); } else { game.InstallDirectory = commonPath; } foreach (var rom in Roms.Where(a => a.Import)) { var gameRom = new GameRom(); if (rom.Name.DiscName.IsNullOrEmpty()) { gameRom.Name = rom.Name.SanitizedName; } else { if (rom.Name.Properties.Count > 1) { gameRom.Name = rom.Name.DiscName + " - " + string.Join(" - ", rom.Name.Properties.Where(a => a != rom.Name.DiscName)); } else { gameRom.Name = rom.Name.DiscName; } } if (commonPath.IsNullOrEmpty()) { gameRom.Path = rom.Path; } else { gameRom.Path = rom.Path.Replace(commonPath, ExpandableVariables.InstallationDirectory.EndWithDirSeparator(), StringComparison.OrdinalIgnoreCase); } game.Roms.Add(gameRom); } } return game; } } public class ScannedRom : ObservableObject { #region backing fields private bool import = true; #endregion backing fields public bool Import { get => import; set => SetValue(ref import, value); } public DatGame DbData { get; set; } public RomName Name { get; set; } public string Path { get; set; } public string DbDataSource { get; set; } public ScannedRom(string path) { Path = path; Name = new RomName(System.IO.Path.GetFileNameWithoutExtension(path)); } public ScannedRom(string path, string scannedExtension) { Path = path; if (path.EndsWith("." + scannedExtension, StringComparison.OrdinalIgnoreCase)) { var fileName = System.IO.Path.GetFileName(path); if (scannedExtension.Equals("gz", StringComparison.OrdinalIgnoreCase)) { fileName = fileName.TrimEndString(".gz", StringComparison.OrdinalIgnoreCase); var trimIndex = fileName.LastIndexOf(".", StringComparison.OrdinalIgnoreCase); if (trimIndex < 0) { Name = new RomName(fileName); } else { Name = new RomName(fileName.Substring(0, trimIndex)); } } else { Name = new RomName(fileName.Substring(0, fileName.LastIndexOf("." + scannedExtension, StringComparison.OrdinalIgnoreCase))); } } else { Name = new RomName(System.IO.Path.GetFileNameWithoutExtension(path)); } } public ScannedRom(string path, DatGame dbData, string dbDataSource) { Path = path; DbData = dbData; DbDataSource = dbDataSource; if (!dbData.Name.IsNullOrEmpty()) { Name = new RomName(dbData.Name); } else if (!dbData.RomName.IsNullOrEmpty()) { Name = new RomName(dbData.RomName); } else { Name = new RomName(System.IO.Path.GetFileNameWithoutExtension(path)); } } public ScannedRom() { } } public class RomName { private static readonly Regex propsRegex = new Regex(@"\[(.*?)\]|\((.*?)\)", RegexOptions.Compiled); private static readonly char[] propertySplitter = new char[] { ',' }; public string Name { get; set; } public string SanitizedName { get; set; } public string DiscName { get; set; } public List Properties { get; set; } = new List(); public RomName() { } public RomName(string originalName) { if (originalName.IsNullOrEmpty()) { throw new ArgumentNullException(nameof(originalName)); } SanitizedName = SanitizeName(originalName); Name = originalName; var matches = propsRegex.Matches(originalName); if (matches.Count > 0) { foreach (Match match in matches) { for (int i = 1; i < match.Groups.Count; i++) { if (!match.Groups[i].Value.IsNullOrEmpty()) { Properties.AddRange(match.Groups[i].Value.Split(propertySplitter, StringSplitOptions.RemoveEmptyEntries).Select(a => a.Trim())); } } } } DiscName = Properties.FirstOrDefault(a => a.StartsWith("disc", StringComparison.InvariantCultureIgnoreCase) || a.StartsWith("disk", StringComparison.InvariantCultureIgnoreCase) || a.StartsWith("side", StringComparison.InvariantCultureIgnoreCase)); if (DiscName == null) { DiscName = originalName; } } public static string SanitizeName(string name) { var newName = propsRegex.Replace(name, string.Empty); return newName. Replace('’', '\''). RemoveTrademarks(). Replace("_", " "). Trim(); } public override string ToString() { return Name; } } } ================================================ FILE: source/Playnite/Exceptions.cs ================================================ using Playnite.API; using Playnite.Common; using Playnite.Plugins; using Playnite.SDK; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Management.Automation; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.CSharp.RuntimeBinder; namespace Playnite { public class NotSupportedInFullscreenException : Exception { public NotSupportedInFullscreenException() : base("Not supported in Fullscreen mode.") { } public NotSupportedInFullscreenException(string message) : base(message) { } } public class NotSupportedInDesktopException : Exception { public NotSupportedInDesktopException() : base("Not supported in Desktop mode.") { } public NotSupportedInDesktopException(string message) : base(message) { } } public class ExceptionInfo { public bool IsLiteDbCorruptionCrash; public bool IsExtensionCrash; public ExtensionManifest CrashExtension; public int PlayniteStackCalls; } public class Exceptions { private static readonly ILogger logger = LogManager.GetLogger(); public static ExceptionInfo GetExceptionInfo(Exception exception, ExtensionFactory extensions) { ExceptionInfo innerCrash = null; if (exception.InnerException != null) { innerCrash = GetExceptionInfoImpl(exception.InnerException, extensions); if (innerCrash.IsExtensionCrash || innerCrash.IsLiteDbCorruptionCrash) return innerCrash; } var crashInfo = GetExceptionInfoImpl(exception, extensions); // This usually happens if an exception occurs in XAML because of faulty custom theme. // The only stack entry would be Playnite's entry point or no entry at all. if ((innerCrash?.PlayniteStackCalls ?? 0 + crashInfo.PlayniteStackCalls) <= 1) crashInfo.IsExtensionCrash = true; return crashInfo; } private static ExceptionInfo GetExceptionInfoImpl(Exception exception, ExtensionFactory extensions) { var crashInfo = new ExceptionInfo(); try { if (// Seems to happen with extensions that use reflection that fails at runtime exception is RuntimeBinderException || // This happens with systems that use extensions/themes with integrated media player // but the actual system player used by media player is broken somehow. exception.StackTrace?.Contains("MediaPlayerState") == true || // Seems to happen with script extensions somehow calling PowerShell, or from PS, via blocking ProgressDialog exception is PSInvalidOperationException) { crashInfo.IsExtensionCrash = true; return crashInfo; } var stack = new StackTrace(exception); var crashModules = new List(); foreach (var frame in stack.GetFrames()) { var module = frame.GetMethod()?.Module; if (module == null) { continue; } if (module.Name.StartsWith("Playnite")) { crashInfo.PlayniteStackCalls++; } crashModules.AddMissing(module); } LoadedPlugin extDesc = null; foreach (var module in crashModules) { extDesc = extensions?.Plugins?.FirstOrDefault(a => module.Name == a.Value.Description.Module || Paths.AreEqual(a.Value.Description.DirectoryPath, Path.GetDirectoryName(module.Assembly.Location))).Value; if (extDesc != null) { break; } } var liteDbCrash = exception is LiteDB.LiteException || exception.Message.Contains("LiteDB."); crashInfo.IsLiteDbCorruptionCrash = liteDbCrash; if (extDesc != null) { crashInfo.IsExtensionCrash = true; crashInfo.CrashExtension = extDesc.Description; } return crashInfo; } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed check crash stack trace."); return crashInfo; } } } } ================================================ FILE: source/Playnite/Extensions/ControlExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; namespace System.Windows.Controls { public static class ControlExtensions { public static T GetTemplateItem(this Control control, string name) { var item = control.Template.FindName(name, control); return item is T t ? t : default; } } } ================================================ FILE: source/Playnite/Extensions/GameExtensions.cs ================================================ using Playnite.Common; using Playnite.Controllers; using Playnite.Database; using Playnite.Emulators; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using Playnite.Settings; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Playnite { public static class EmulatorProfileExtensions { public static CustomEmulatorProfile ExpandVariables(this CustomEmulatorProfile profile, Game game, string emulatorDir, string romPath) { var g = game.GetCopy(); g.Roms = new System.Collections.ObjectModel.ObservableCollection { new GameRom("", romPath) }; var expaded = profile.GetCopy(); expaded.Arguments = g.ExpandVariables(expaded.Arguments, false, emulatorDir); expaded.WorkingDirectory = g.ExpandVariables(expaded.WorkingDirectory, true, emulatorDir); expaded.Executable = g.ExpandVariables(expaded.Executable, true, emulatorDir); expaded.TrackingPath = g.ExpandVariables(expaded.TrackingPath, true, emulatorDir); return expaded; } } public static class GameActionExtensions { public static GameAction ExpandVariables(this GameAction action, Game game) { var expaded = action.GetCopy(); expaded.AdditionalArguments = game.ExpandVariables(expaded.AdditionalArguments); expaded.Arguments = game.ExpandVariables(expaded.Arguments); expaded.WorkingDir = game.ExpandVariables(expaded.WorkingDir, true); expaded.TrackingPath = game.ExpandVariables(expaded.TrackingPath, true); if (expaded.Type != GameActionType.URL) { expaded.Path = game.ExpandVariables(expaded.Path, true); } return expaded; } } public static class GameExtensions { private static ILogger logger = LogManager.GetLogger(); public static Game GetGameFromExecutable(string path) { if (!File.Exists(path)) { throw new FileNotFoundException($"Cannot create game from executable, {path} not found."); } var game = new Game(); if (string.Equals(Path.GetExtension(path), ".lnk", StringComparison.OrdinalIgnoreCase)) { var prog = Programs.GetLnkShortcutData(path); var fileInfo = new FileInfo(prog.Path); if (!fileInfo.Exists && prog.Path.Contains("Program Files (x86)")) { var newPath = prog.Path.Replace("Program Files (x86)", "Program Files"); if (File.Exists(newPath)) { fileInfo = new FileInfo(newPath); if (prog.WorkDir.Contains("Program Files (x86)")) { prog.WorkDir.Replace("Program Files (x86)", "Program Files"); } } } game.GameId = path.MD5(); game.Name = Path.GetFileNameWithoutExtension(path); game.InstallDirectory = prog.WorkDir.IsNullOrEmpty() ? fileInfo.Directory.FullName : prog.WorkDir; game.GameActions = new System.Collections.ObjectModel.ObservableCollection { new GameAction() { Type = GameActionType.File, WorkingDir = ExpandableVariables.InstallationDirectory, Path = fileInfo.FullName.Replace(game.InstallDirectory.EndWithDirSeparator(), ExpandableVariables.InstallationDirectory.EndWithDirSeparator()), Arguments = prog.Arguments, IsPlayAction = true, Name = game.Name } }; if (!prog.Icon.IsNullOrEmpty()) { var iconPath = Regex.Replace(prog.Icon, @",\d+$", ""); if (File.Exists(iconPath)) { game.Icon = iconPath; } else if (iconPath.Contains("Program Files (x86)")) { iconPath = iconPath.Replace("Program Files (x86)", "Program Files"); if (File.Exists(iconPath)) { game.Icon = iconPath; } } } } else if (string.Equals(Path.GetExtension(path), ".url", StringComparison.OrdinalIgnoreCase)) { var urlData = IniParser.Parse(File.ReadAllLines(path)); var shortcut = urlData["InternetShortcut"]; if (shortcut == null) { throw new Exception("URL file doesn't have shortcut definition section."); } game.Name = Path.GetFileNameWithoutExtension(path); game.Icon = shortcut["IconFile"]; game.GameActions = new System.Collections.ObjectModel.ObservableCollection { new GameAction() { Type = GameActionType.URL, Path = shortcut["URL"], IsPlayAction = true, Name = game.Name } }; } else { var file = new FileInfo(path); var versionInfo = FileVersionInfo.GetVersionInfo(path); var programName = !string.IsNullOrEmpty(versionInfo.ProductName?.Trim()) ? versionInfo.ProductName : new DirectoryInfo(file.DirectoryName).Name; game.Name = programName; game.InstallDirectory = file.DirectoryName; game.GameActions = new System.Collections.ObjectModel.ObservableCollection { new GameAction() { Type = GameActionType.File, WorkingDir = ExpandableVariables.InstallationDirectory, Path = file.FullName.Replace(game.InstallDirectory.EndWithDirSeparator(), ExpandableVariables.InstallationDirectory.EndWithDirSeparator()), IsPlayAction = true, Name = game.Name } }; }; game.IsInstalled = true; return game; } public static Game ExpandGame(this Game game, bool fixSeparators = false, string emulatorDir = null, string romPath = null) { var g = game.GetCopy(); g.InstallDirectory = g.StringExpand(g.InstallDirectory, fixSeparators, emulatorDir, romPath); g.Roms.ForEach(rom => rom.Path = g.StringExpand(rom.Path, fixSeparators, emulatorDir, romPath)); return g; } public static GameMetadata ExpandGame(this GameMetadata game) { var g = game.GetClone(); g.InstallDirectory = g.StringExpand(g.InstallDirectory); g.Roms.ForEach(rom => rom.Path = g.StringExpand(rom.Path)); return g; } public static string ExpandVariables(this Game game, string inputString, bool fixSeparators = false, string emulatorDir = null, string romPath = null) { var g = game.ExpandGame(fixSeparators, emulatorDir, romPath); return StringExpand(g, inputString, fixSeparators, emulatorDir, romPath); } // TODO rework this whole mess into something better and more maintainable :| private static string StringExpand(this Game game, string inputString, bool fixSeparators = false, string emulatorDir = null, string romPath = null) { if (string.IsNullOrWhiteSpace(inputString) || !inputString.Contains('{')) { return inputString; } var result = inputString; if (!game.InstallDirectory.IsNullOrWhiteSpace()) { result = result.Replace(ExpandableVariables.InstallationDirectory, game.InstallDirectory); result = result.Replace(ExpandableVariables.InstallationDirName, game.InstallDirectory.Split(Paths.DirectorySeparators, StringSplitOptions.RemoveEmptyEntries).LastOrDefault()); } if (romPath.IsNullOrWhiteSpace() && game.Roms.HasItems()) { var customPath = game.Roms[0].Path; if (!customPath.IsNullOrEmpty() && !Paths.ContainsInvalidPathChars(customPath)) { result = result.Replace(ExpandableVariables.ImagePath, customPath); result = result.Replace(ExpandableVariables.ImageNameNoExtension, Path.GetFileNameWithoutExtension(customPath)); result = result.Replace(ExpandableVariables.ImageName, Path.GetFileName(customPath)); } } else if (!romPath.IsNullOrWhiteSpace() && !Paths.ContainsInvalidPathChars(romPath)) { result = result.Replace(ExpandableVariables.ImagePath, romPath); result = result.Replace(ExpandableVariables.ImageNameNoExtension, Path.GetFileNameWithoutExtension(romPath)); result = result.Replace(ExpandableVariables.ImageName, Path.GetFileName(romPath)); } result = result.Replace(ExpandableVariables.PlayniteDirectory, PlaynitePaths.ProgramPath); result = result.Replace(ExpandableVariables.Name, game.Name); result = result.Replace(ExpandableVariables.PluginId, game.PluginId.ToString()); result = result.Replace(ExpandableVariables.GameId, game.GameId); result = result.Replace(ExpandableVariables.DatabaseId, game.Id.ToString()); result = result.Replace(ExpandableVariables.Version, game.Version); result = result.Replace(ExpandableVariables.EmulatorDirectory, emulatorDir ?? string.Empty); var plats = game.Platforms; if (plats.HasItems()) { result = result.Replace(ExpandableVariables.Platform, plats?[0].Name); } return fixSeparators ? Paths.FixSeparators(result) : result; } public static string ExpandVariables(this GameMetadata game, string inputString, bool fixSeparators = false) { var g = game.ExpandGame(); return StringExpand(g, inputString, fixSeparators); } private static string StringExpand(this GameMetadata game, string inputString, bool fixSeparators = false) { if (string.IsNullOrEmpty(inputString) || !inputString.Contains('{')) { return inputString; } var result = inputString; if (!game.InstallDirectory.IsNullOrWhiteSpace()) { result = result.Replace(ExpandableVariables.InstallationDirectory, game.InstallDirectory); result = result.Replace(ExpandableVariables.InstallationDirName, game.InstallDirectory.Split(Paths.DirectorySeparators, StringSplitOptions.RemoveEmptyEntries).LastOrDefault()); } if (game.Roms.HasItems()) { var romPath = game.Roms[0].Path; if (!romPath.IsNullOrEmpty()) { result = result.Replace(ExpandableVariables.ImagePath, romPath); result = result.Replace(ExpandableVariables.ImageNameNoExtension, Path.GetFileNameWithoutExtension(romPath)); result = result.Replace(ExpandableVariables.ImageName, Path.GetFileName(romPath)); } } result = result.Replace(ExpandableVariables.PlayniteDirectory, PlaynitePaths.ProgramPath); result = result.Replace(ExpandableVariables.Name, game.Name); result = result.Replace(ExpandableVariables.GameId, game.GameId); result = result.Replace(ExpandableVariables.Version, game.Version); if (game.Platforms.HasItems() && game.Platforms.First() is MetadataNameProperty prop) { result = result.Replace(ExpandableVariables.Platform, prop.Name); } return fixSeparators ? Paths.FixSeparators(result) : result; } public static string GetIdentifierInfo(this Game game) { return $"{game.Name}, {game.Id}, {game.GameId}, {game.PluginId}"; } public static string GetRawExecutablePath(this Game game) { try { var playAction = game.GameActions?.FirstOrDefault(a => a.IsPlayAction && a.Type == GameActionType.File); if (playAction == null) { return null; } playAction = playAction.ExpandVariables(game); if (playAction.Type == GameActionType.File) { if (string.IsNullOrEmpty(playAction.WorkingDir)) { if (Paths.IsValidFilePath(playAction.Path)) { return Path.GetFullPath(playAction.Path); } else { return null; } } else { if (Path.IsPathRooted(playAction.Path)) { return playAction.Path; } else { var combined = Path.Combine(playAction.WorkingDir, playAction.Path); return Path.GetFullPath(combined); } } } else if (playAction.Type == GameActionType.URL) { return playAction.Path; } else { return null; } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to get executable from game data."); return null; } } public static Dictionary> GetCompatibleEmulators(this Game game, GameDatabase database) { var emulators = new Dictionary>(); if (!game.Platforms.HasItems()) { return emulators; } foreach (var emulator in database.Emulators) { var profiles = game.GetCompatibleProfiles(emulator); if (profiles.HasItems()) { emulators.Add(emulator, new List(profiles)); } } return emulators; } public static List GetCompatibleProfiles(this Game game, Emulator emulator) { var profiles = new List(); if (!game.Platforms.HasItems()) { return profiles; } foreach (var profile in emulator.CustomProfiles ?? new ObservableCollection()) { if (profile.Platforms?.Intersect(game.PlatformIds).HasItems() == true) { profiles.Add(profile); } } foreach (var profile in emulator.BuiltinProfiles ?? new ObservableCollection()) { var profDef = Emulation.GetProfile(emulator.BuiltInConfigId, profile.BuiltInProfileName); if (profDef == null) { continue; } if (game.Platforms.Where(a => !a.SpecificationId.IsNullOrEmpty()).Any(a => profDef.Platforms.Contains(a.SpecificationId))) { profiles.Add(profile); } } return profiles; } } } ================================================ FILE: source/Playnite/Extensions/IProvideValueTarget.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Markup; namespace Playnite.Extensions { public static class IProvideValueTargetExtensions { public static Type GetTargetType(IProvideValueTarget provider) { if (provider.TargetProperty == null) { return null; } var type = provider.TargetProperty.GetType(); if (type == typeof(DependencyProperty)) { type = ((DependencyProperty)provider.TargetProperty).PropertyType; } else if (typeof(PropertyInfo).IsAssignableFrom(provider.TargetProperty.GetType())) { type = ((PropertyInfo)provider.TargetProperty).PropertyType; } return type; } } } ================================================ FILE: source/Playnite/Extensions/Markup/Api.cs ================================================ using Playnite.API.DesignData; using Playnite.Converters; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Extensions.Markup { public class Api : BindingExtension { public Api() : this(null) { } public Api(string path) : base(path) { if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) { Source = new DesignPlayniteAPI(); PathRoot = null; } else { Source = PlayniteApplication.Current; PathRoot = nameof(PlayniteApplication.PlayniteApiGlobal); } if (!path.IsNullOrEmpty()) { PathRoot += "."; } } } } ================================================ FILE: source/Playnite/Extensions/Markup/BindingExtension.cs ================================================ using Playnite.Converters; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Extensions.Markup { public class BindingExtension : MarkupExtension { internal Binding binding; public string PathRoot { get; set; } public object Source { get; set; } public string Path { get; set; } public object TargetNullValue { get; set; } public object FallbackValue { get; set; } public int Delay { get; set; } public BindingMode Mode { get; set; } = BindingMode.OneWay; public IValueConverter Converter { get; set; } public object ConverterParameter { get; set; } public string StringFormat { get; set; } public BindingExtension() { } public BindingExtension(string path) { Path = path; } public override object ProvideValue(IServiceProvider serviceProvider) { binding = new Binding() { Path = new PropertyPath(PathRoot + Path), Delay = Delay, Mode = Mode }; if (Source != null) { binding.Source = Source; } if (TargetNullValue != null) { binding.TargetNullValue = TargetNullValue; } if (FallbackValue != null) { binding.FallbackValue = FallbackValue; } if (Converter != null) { binding.Converter = Converter; } if (ConverterParameter != null) { binding.ConverterParameter = ConverterParameter; } if (!StringFormat.IsNullOrEmpty()) { binding.StringFormat = StringFormat; } var provider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; if (ServiceProvider.IsTargetTemplate(serviceProvider)) { return this; } else { var targetType = IProvideValueTargetExtensions.GetTargetType(provider); if (targetType == typeof(Visibility) && Converter == null) { binding.Converter = new BooleanToVisibilityConverter(); } if (provider.TargetProperty == null) { return binding; } else if (provider.TargetProperty.GetType() == typeof(DependencyProperty)) { return BindingOperations.SetBinding( provider.TargetObject as DependencyObject, provider.TargetProperty as DependencyProperty, binding); } else { if (targetType == typeof(BindingBase)) { return binding; } } return this; } } } } ================================================ FILE: source/Playnite/Extensions/Markup/MainViewModel.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; namespace Playnite.Extensions.Markup { public class MainViewModel : BindingExtension { public bool DirectValue { get; set; } = false; public MainViewModel() : this(null) { } public MainViewModel(string path) : base(path) { if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) { Source = typeof(TDesignViewModel).CrateInstance(); PathRoot = null; } else { Source = PlayniteApplication.Current; PathRoot = "MainModel"; } if (!path.IsNullOrEmpty()) { PathRoot += "."; } } public override object ProvideValue(IServiceProvider serviceProvider) { if (ServiceProvider.IsTargetTemplate(serviceProvider)) { return this; } if (DirectValue) { if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) { return typeof(TAppViewModel).GetProperty(Path).GetValue(Source, null); } else { var src = typeof(TApp).GetProperty("MainModel").GetValue(PlayniteApplication.Current, null); return typeof(TAppViewModel).GetProperty(Path).GetValue(src, null); } } else { return base.ProvideValue(serviceProvider); } } } } ================================================ FILE: source/Playnite/Extensions/Markup/PluginConverter.cs ================================================ using Playnite.Plugins; using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Extensions.Markup { public class PluginConverter : MarkupExtension { public string Converter { get; set; } public string Plugin { get; set; } public override object ProvideValue(IServiceProvider serviceProvider) { return new PluginConverterProvider(Plugin, Converter); } } // This intermediary class is needed because markup extensions are resolved when theme's xaml is loaded, // which happens before plugins are loaded. public class PluginConverterProvider : IValueConverter { private bool converterRequested = false; private IValueConverter actualConverter; private readonly string converterName; private readonly string pluginSource; public PluginConverterProvider(string plugin, string converter) { pluginSource = plugin; converterName = converter; } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return GetConverter()?.Convert(value, targetType, parameter, culture) ?? DependencyProperty.UnsetValue; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return GetConverter()?.ConvertBack(value, targetType, parameter, culture) ?? DependencyProperty.UnsetValue; } private IValueConverter GetConverter() { if (converterRequested) { return actualConverter; } var pSource = PlayniteApplication.Current?.Extensions?.ConvertersSupportList.FirstOrDefault(a => a.SourceName == pluginSource); actualConverter = null; if (pSource != null) { actualConverter = pSource.Converters?.FirstOrDefault(a => a.GetType().Name == converterName); } converterRequested = true; return actualConverter; } } } ================================================ FILE: source/Playnite/Extensions/Markup/PluginSettings.cs ================================================ using Playnite.Plugins; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Extensions.Markup { public class PluginSettings : BindingExtension { public string Plugin { get; set; } public PluginSettings() : this(null) { } public PluginSettings(string path) : base(path) { } public override object ProvideValue(IServiceProvider serviceProvider) { if (ServiceProvider.IsTargetTemplate(serviceProvider)) { return this; } if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) { Source = ""; PathRoot = ""; return base.ProvideValue(serviceProvider); } var pSource = PlayniteApplication.Current.Extensions?.SettingsSupportList.FirstOrDefault(a => a.SourceName == Plugin); if (pSource == null || pSource.SettingsRoot.IsNullOrEmpty()) { PlayniteApplication.Current.ExtensionsLoaded += Current_ExtensionsLoaded; Source = ""; PathRoot = ""; return base.ProvideValue(serviceProvider); } else { Source = pSource.Source; PathRoot = $"{pSource.SettingsRoot}."; return base.ProvideValue(serviceProvider); } } private void Current_ExtensionsLoaded(object sender, EventArgs e) { PlayniteApplication.Current.ExtensionsLoaded -= Current_ExtensionsLoaded; var pSource = PlayniteApplication.Current?.Extensions?.SettingsSupportList.FirstOrDefault(a => a.SourceName == Plugin); if (pSource != null) { PathRoot = $"{pSource.SettingsRoot}."; binding.Path = new PropertyPath(PathRoot + Path); binding.Source = pSource.Source; } } } } ================================================ FILE: source/Playnite/Extensions/Markup/PluginStatus.cs ================================================ using Playnite.Plugins; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Extensions.Markup { public class PluginStatus : BindingExtension { public string Status { get; set; } public string Plugin { get; set; } public PluginStatus() : this(null) { } public PluginStatus(string path) : base(path) { } public override object ProvideValue(IServiceProvider serviceProvider) { if (ServiceProvider.IsTargetTemplate(serviceProvider)) { return this; } Source = PlayniteApplication.Current; Path = $"{nameof(PlayniteApplication.ExtensionsStatusBinder)}[{Plugin}].{nameof(ExtensionsStatusBinder.Status.IsInstalled)}"; return base.ProvideValue(serviceProvider); } } } ================================================ FILE: source/Playnite/Extensions/Markup/Settings.cs ================================================ using Playnite.Converters; using Playnite.SDK; using Playnite.Settings; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Extensions.Markup { public class SettingsBinding : Binding { public SettingsBinding() : this(null) { } public SettingsBinding(string path) : base(path) { if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) { Source = new PlayniteSettings(); } else { Source = PlayniteApplication.Current.AppSettings; } } } public class Settings : BindingExtension { private static ILogger logger = LogManager.GetLogger(); private static readonly Dictionary directValuePropCache = new Dictionary(); public bool DirectValue { get; set; } = false; public Settings() : this(null) { } public Settings(string path) : base(path) { if (DesignerTools.IsInDesignMode) { Source = new PlayniteSettings(); PathRoot = null; } else { Source = PlayniteApplication.Current; PathRoot = nameof(PlayniteApplication.AppSettings); } if (!path.IsNullOrEmpty()) { PathRoot += "."; } } public override object ProvideValue(IServiceProvider serviceProvider) { if (ServiceProvider.IsTargetTemplate(serviceProvider)) { return this; } if (DirectValue) { // Doesn't support nested properties! var src = Source; if (!DesignerTools.IsInDesignMode) { src = PlayniteApplication.Current.AppSettings; } if (directValuePropCache.TryGetValue(Path, out var prop)) { return prop?.GetValue(src, null); } else { var newProp = typeof(PlayniteSettings).GetProperty(Path); directValuePropCache.Add(Path, newProp); if (newProp != null) { return newProp.GetValue(src, null); } else { logger.Error($"Failed to get value of \"{Path}\" path from app settings."); return null; } } } else { return base.ProvideValue(serviceProvider); } } } } ================================================ FILE: source/Playnite/Extensions/Markup/ThemeFile.cs ================================================ using Playnite.Common; using Playnite.Extensions; using Playnite.SDK; using Playnite.Settings; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Markup; using System.Windows.Media; using System.Xaml; using System.Diagnostics; using System.Text.RegularExpressions; using CommandLine; namespace Playnite.Extensions.Markup { public class ThemeFile : MarkupExtension { private static readonly ILogger logger = LogManager.GetLogger(); private static FileInfo lastUserTheme = null; private static bool? lastUserThemeFound = null; internal ThemeManifest CurrentTheme { get; set; } internal ThemeManifest DefaultTheme { get; set; } public string RelativePath { get; set; } public ThemeFile(ApplicationMode mode) { if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) { DefaultTheme = GetDesignTimeDefaultTheme(mode); } else { DefaultTheme = ThemeManager.DefaultTheme; CurrentTheme = ThemeManager.CurrentTheme; } } public ThemeFile(string path, ApplicationMode mode) : this(mode) { RelativePath = path; } public static ThemeManifest GetDesignTimeDefaultTheme(ApplicationMode mode) { if (lastUserThemeFound == null) { if (Process.GetCurrentProcess().TryGetParentId(out var parentId)) { var proc = Process.GetProcessById(parentId); var cmdline = proc.GetCommandLine(); var regEx = Regex.Match(cmdline, @"([^""]+\.sln?)"""); if (regEx.Success) { var spath = regEx.Groups[1].Value; if (spath.Contains($"Themes\\{mode.GetDescription()}")) { lastUserTheme = new FileInfo(spath); lastUserThemeFound = true; } } } } if (lastUserThemeFound == true) { return new ThemeManifest() { DirectoryName = lastUserTheme.DirectoryName, DirectoryPath = lastUserTheme.Directory.FullName, Name = "Default" }; } lastUserThemeFound = false; var defaultTheme = "Default"; var projectName = mode == ApplicationMode.Fullscreen ? "Playnite.FullscreenApp" : "Playnite.DesktopApp"; var slnPath = Path.Combine(Environment.GetEnvironmentVariable("PLAYNITE_SLN", EnvironmentVariableTarget.User), projectName); var themePath = Path.Combine(slnPath, "Themes", ThemeManager.GetThemeRootDir(mode), defaultTheme); return new ThemeManifest() { DirectoryName = defaultTheme, DirectoryPath = themePath, Name = defaultTheme }; } public static string GetFilePath(string relPath, bool checkExistance = true, bool matchByRegex = false) { return GetFilePath(relPath, ThemeManager.DefaultTheme, ThemeManager.CurrentTheme, checkExistance, matchByRegex); } public static string GetFilePath(string relPath, ThemeManifest defaultTheme, bool checkExistance = true, bool matchByRegex = false) { return GetFilePath(relPath, defaultTheme, ThemeManager.CurrentTheme, checkExistance, matchByRegex); } public static string GetFilePath(string relPath, ThemeManifest defaultTheme, ThemeManifest currentTheme, bool checkExistance = true, bool matchByRegex = false) { if (matchByRegex) { relPath = relPath.TrimStart(new char[] { Path.DirectorySeparatorChar }); string searchFile(string dir) { foreach (var file in Directory.EnumerateFiles(dir, "*.*", SearchOption.AllDirectories)) { if (Regex.IsMatch(file.Replace(dir, string.Empty, StringComparison.OrdinalIgnoreCase), relPath, RegexOptions.IgnoreCase)) { return file; } } return null; } if (currentTheme != null) { var match = searchFile(currentTheme.DirectoryPath.EndWithDirSeparator()); if (!match.IsNullOrEmpty()) { return match; } } if (defaultTheme != null) { var match = searchFile(defaultTheme.DirectoryPath.EndWithDirSeparator()); if (!match.IsNullOrEmpty()) { return match; } } return null; } else { relPath = Paths.FixSeparators(relPath).TrimStart(new char[] { Path.DirectorySeparatorChar }); string searchFile(string dir) { var themePath = Path.Combine(dir, relPath); if (File.Exists(themePath) && checkExistance) { return themePath; } else if (!checkExistance) { return themePath; } return null; } if (currentTheme != null) { var match = searchFile(currentTheme.DirectoryPath); if (!match.IsNullOrEmpty()) { return match; } } if (defaultTheme != null) { var match = searchFile(defaultTheme.DirectoryPath); if (!match.IsNullOrEmpty()) { return match; } } return null; } } public override object ProvideValue(IServiceProvider serviceProvider) { string path = GetFilePath(RelativePath, DefaultTheme, CurrentTheme); if (path.IsNullOrEmpty()) { return null; } var provider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; var type = IProvideValueTargetExtensions.GetTargetType(provider); var converter = TypeDescriptor.GetConverter(type); try { return converter.ConvertFrom(path); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to provide value for theme file {path}"); return null; } } } } ================================================ FILE: source/Playnite/Extensions/Markup/ThemeFileBinding.cs ================================================ using Playnite.API.DesignData; using Playnite.Converters; using Playnite.SDK; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace Playnite.Extensions.Markup { public class ThemeFileBinding : BindingExtension { public string PathFormat { get; set; } public ThemeFileBinding() : this(null) { } public ThemeFileBinding(string path) : base(path) { } public override object ProvideValue(IServiceProvider serviceProvider) { if (ServiceProvider.IsTargetTemplate(serviceProvider)) { return this; } if (!PathFormat.IsNullOrEmpty() && !(Converter is GenericTypeConverter)) { if (Converter != null) { Converter = new GenericTypeConverter { StringFormat = ThemeFile.GetFilePath(PathFormat, false), TestAsFilePath = true, CustomConverter = Converter }; } else { Converter = new GenericTypeConverter { StringFormat = ThemeFile.GetFilePath(PathFormat, false), TestAsFilePath = true }; } } return base.ProvideValue(serviceProvider); } } } ================================================ FILE: source/Playnite/Extensions/ServiceProvider.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Markup; namespace Playnite.Extensions { public class ServiceProvider { public static bool IsTargetTemplate(IServiceProvider serviceProvider) { var provider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; return provider.TargetObject.GetType().FullName == "System.Windows.SharedDp"; } } } ================================================ FILE: source/Playnite/FakePlayniteLibraryPlugin.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Controls; namespace Playnite { public class FakePlayniteLibraryPlugin : LibraryPlugin { public override string Name => "Playnite"; public override Guid Id => Guid.Empty; public FakePlayniteLibraryPlugin() : base(null) { } public override IEnumerable GetGames(LibraryGetGamesArgs args) { throw new NotImplementedException(); } } } ================================================ FILE: source/Playnite/GameTools.cs ================================================ using NLog; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class MultiEditGame : Game { public List DistinctGenreIds { get; set; } public List DistinctDeveloperIds { get; set; } public List DistinctPublisherIds { get; set; } public List DistinctCategoryIds { get; set; } public List DistinctTagIds { get; set; } public List DistinctFeatureIds { get; set; } public List DistinctPlatformIds { get; set; } public List DistinctRegionIds { get; set; } public List DistinctAgeRatingIds { get; set; } public List DistinctSeriesIds { get; set; } } public class GameTools { public static MultiEditGame GetMultiGameEditObject(IEnumerable games) { var dummyGame = new MultiEditGame(); if (games?.Any() != true) { return dummyGame; } var firstGame = games.First(); var firstName = firstGame.Name; if (games.All(a => a.Name == firstName) == true) { dummyGame.Name = firstName; } var firstSortingName = firstGame.SortingName; if (games.All(a => a.SortingName == firstSortingName) == true) { dummyGame.SortingName = firstSortingName; } dummyGame.GenreIds = ListExtensions.GetCommonItems(games.Select(a => a.GenreIds)).ToList(); dummyGame.DistinctGenreIds = ListExtensions.GetDistinctItems(games.Select(a => a.GenreIds)).ToList(); dummyGame.DeveloperIds = ListExtensions.GetCommonItems(games.Select(a => a.DeveloperIds)).ToList(); dummyGame.DistinctDeveloperIds = ListExtensions.GetDistinctItems(games.Select(a => a.DeveloperIds)).ToList(); dummyGame.PublisherIds = ListExtensions.GetCommonItems(games.Select(a => a.PublisherIds)).ToList(); dummyGame.DistinctPublisherIds = ListExtensions.GetDistinctItems(games.Select(a => a.PublisherIds)).ToList(); dummyGame.CategoryIds = ListExtensions.GetCommonItems(games.Select(a => a.CategoryIds)).ToList(); dummyGame.DistinctCategoryIds = ListExtensions.GetDistinctItems(games.Select(a => a.CategoryIds)).ToList(); dummyGame.TagIds = ListExtensions.GetCommonItems(games.Select(a => a.TagIds)).ToList(); dummyGame.DistinctTagIds = ListExtensions.GetDistinctItems(games.Select(a => a.TagIds)).ToList(); dummyGame.FeatureIds = ListExtensions.GetCommonItems(games.Select(a => a.FeatureIds)).ToList(); dummyGame.DistinctFeatureIds = ListExtensions.GetDistinctItems(games.Select(a => a.FeatureIds)).ToList(); dummyGame.PlatformIds = ListExtensions.GetCommonItems(games.Select(a => a.PlatformIds)).ToList(); dummyGame.DistinctPlatformIds = ListExtensions.GetDistinctItems(games.Select(a => a.PlatformIds)).ToList(); dummyGame.SeriesIds = ListExtensions.GetCommonItems(games.Select(a => a.SeriesIds)).ToList(); dummyGame.DistinctSeriesIds = ListExtensions.GetDistinctItems(games.Select(a => a.SeriesIds)).ToList(); dummyGame.AgeRatingIds = ListExtensions.GetCommonItems(games.Select(a => a.AgeRatingIds)).ToList(); dummyGame.DistinctAgeRatingIds = ListExtensions.GetDistinctItems(games.Select(a => a.AgeRatingIds)).ToList(); dummyGame.RegionIds = ListExtensions.GetCommonItems(games.Select(a => a.RegionIds)).ToList(); dummyGame.DistinctRegionIds = ListExtensions.GetDistinctItems(games.Select(a => a.RegionIds)).ToList(); var firstReleaseDate = firstGame.ReleaseDate; if (games.All(a => a.ReleaseDate == firstReleaseDate) == true) { dummyGame.ReleaseDate = firstReleaseDate; } var firstDescription = firstGame.Description; if (games.All(a => a.Description == firstDescription) == true) { dummyGame.Description = firstDescription; } var firstNotes = firstGame.Notes; if (games.All(a => a.Notes == firstNotes) == true) { dummyGame.Notes = firstNotes; } var firstManual = firstGame.Manual; if (games.All(a => a.Manual == firstManual) == true) { dummyGame.Manual = firstManual; } var firstLastActivity = firstGame.LastActivity; if (games.All(a => a.LastActivity == firstLastActivity) == true) { dummyGame.LastActivity = firstLastActivity; } var firstPlaytime = firstGame.Playtime; if (games.All(a => a.Playtime == firstPlaytime) == true) { dummyGame.Playtime = firstPlaytime; } var firstAdded = firstGame.Added; if (games.All(a => a.Added == firstAdded) == true) { dummyGame.Added = firstAdded; } var firstPlayCount = firstGame.PlayCount; if (games.All(a => a.PlayCount == firstPlayCount) == true) { dummyGame.PlayCount = firstPlayCount; } var firstInstallSize = firstGame.InstallSize; if (games.All(a => a.InstallSize == firstInstallSize) == true) { dummyGame.InstallSize = firstInstallSize; } var firstVersion = firstGame.Version; if (games.All(a => a.Version == firstVersion) == true) { dummyGame.Version = firstVersion; } var firstSource = firstGame.SourceId; if (games.All(a => a.SourceId == firstSource) == true) { dummyGame.SourceId = firstSource; } var firstCompletionStatus = firstGame.CompletionStatusId; if (games.All(a => a.CompletionStatusId == firstCompletionStatus) == true) { dummyGame.CompletionStatusId = firstCompletionStatus; } var firstUserScore = firstGame.UserScore; if (games.All(a => a.UserScore == firstUserScore) == true) { dummyGame.UserScore = firstUserScore; } var firstCriticScore = firstGame.CriticScore; if (games.All(a => a.CriticScore == firstCriticScore) == true) { dummyGame.CriticScore = firstCriticScore; } var firstCommunityScore = firstGame.CommunityScore; if (games.All(a => a.CommunityScore == firstCommunityScore) == true) { dummyGame.CommunityScore = firstCommunityScore; } var firstHidden = firstGame.Hidden; if (games.All(a => a.Hidden == firstHidden) == true) { dummyGame.Hidden = firstHidden; } var firstInstalled = firstGame.IsInstalled; if (games.All(a => a.IsInstalled == firstInstalled) == true) { dummyGame.IsInstalled = firstInstalled; } var firstInstallDir = firstGame.InstallDirectory; if (games.All(a => a.InstallDirectory == firstInstallDir) == true) { dummyGame.InstallDirectory = firstInstallDir; } var firstFavorite = firstGame.Favorite; if (games.All(a => a.Favorite == firstFavorite) == true) { dummyGame.Favorite = firstFavorite; } var firstPreScript = firstGame.PreScript; if (games.All(a => string.Equals(a.PreScript, firstPreScript, StringComparison.Ordinal))) { dummyGame.PreScript = firstPreScript; } var firstPostScript = firstGame.PostScript; if (games.All(a => string.Equals(a.PostScript, firstPostScript, StringComparison.Ordinal))) { dummyGame.PostScript = firstPostScript; } var firstGameStartedScript = firstGame.GameStartedScript; if (games.All(a => string.Equals(a.GameStartedScript, firstGameStartedScript, StringComparison.Ordinal))) { dummyGame.GameStartedScript = firstGameStartedScript; } var firstUseGlobalPreSrc = firstGame.UseGlobalPreScript; if (games.All(a => a.UseGlobalPreScript == firstUseGlobalPreSrc) == true) { dummyGame.UseGlobalPreScript = firstUseGlobalPreSrc; } var firstUseGlobalPostSrc = firstGame.UseGlobalPostScript; if (games.All(a => a.UseGlobalPostScript == firstUseGlobalPostSrc) == true) { dummyGame.UseGlobalPostScript = firstUseGlobalPostSrc; } var firstUseGlobalGameStartedSrc = firstGame.UseGlobalGameStartedScript; if (games.All(a => a.UseGlobalGameStartedScript == firstUseGlobalGameStartedSrc) == true) { dummyGame.UseGlobalGameStartedScript = firstUseGlobalGameStartedSrc; } var firstIncludeLibraryPluginAction = firstGame.IncludeLibraryPluginAction; if (games.All(a => a.IncludeLibraryPluginAction == firstIncludeLibraryPluginAction) == true) { dummyGame.IncludeLibraryPluginAction = firstIncludeLibraryPluginAction; } return dummyGame; } } } ================================================ FILE: source/Playnite/GamesCollectionView.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using Playnite.Database; using Playnite.SDK.Models; using Playnite.SDK; using Playnite.Settings; using Playnite.Plugins; using Playnite.SDK.Plugins; namespace Playnite { public abstract class BaseCollectionView : ObservableObject, IDisposable { public static ILogger Logger = LogManager.GetLogger(); private ExtensionFactory extensions; private FilterSettings filterSettings; private readonly PlayniteSettings settings; public IGameDatabaseMain Database { get; private set; } public RangeObservableCollection Items { get; private set; } public bool IgnoreViewConfigChanges { get; set; } = false; private ListCollectionView collectionView; public ListCollectionView CollectionView { get => collectionView; private set { collectionView = value; OnPropertyChanged(); } } public BaseCollectionView(IGameDatabaseMain database, ExtensionFactory extensions, FilterSettings filterSettings, PlayniteSettings settings) { Database = database; this.extensions = extensions; this.filterSettings = filterSettings; this.settings = settings; Items = new RangeObservableCollection(); filterSettings.FilterChanged += FilterSettings_FilterChanged; CollectionView = (ListCollectionView)CollectionViewSource.GetDefaultView(Items); CollectionView.Filter = Filter; } public virtual void Dispose() { filterSettings.FilterChanged -= FilterSettings_FilterChanged; Items = null; } private bool Filter(object item) { if (!(item is GamesCollectionViewEntry entry)) { return false; } return Database.GetGameMatchesFilter(entry.Game, filterSettings, settings.FuzzyMatchingInNameFilter); } private void FilterSettings_FilterChanged(object sender, FilterChangedEventArgs e) { if (IgnoreViewConfigChanges) { return; } Logger.Debug("Refreshing collection view filter."); CollectionView.Refresh(); } public LibraryPlugin GetLibraryPlugin(Game game) { if (game.PluginId != Guid.Empty && extensions.Plugins.TryGetValue(game.PluginId, out var plugin) && plugin.Plugin is LibraryPlugin libPlugin) { return libPlugin; } return null; } public abstract void RefreshView(); public void NotifyItemPropertyChanges(params string[] changedProperties) { if (!Items.HasItems()) { return; } Logger.Trace("NotifyItemPropertyChanges: "); changedProperties.ForEach(prop => Logger.Trace(prop)); foreach (var item in Items) { changedProperties.ForEach(prop => item.OnPropertyChanged(prop)); } } } } ================================================ FILE: source/Playnite/GamesCollectionViewEntry.cs ================================================ using Playnite.Converters; using Playnite.Database; using Playnite.Extensions.Markup; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Drawing.Imaging; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Media.Imaging; namespace Playnite { public class GamesCollectionViewEntry : INotifyPropertyChanged, IDisposable { private static readonly ILogger logger = LogManager.GetLogger(); private readonly PlayniteSettings settings; public static BitmapLoadProperties DetailsListIconProperties { get; private set; } public static BitmapLoadProperties GridViewCoverProperties { get; private set; } public static BitmapLoadProperties BackgroundImageProperties { get; private set; } public static BitmapLoadProperties FullscreenListCoverProperties { get; private set; } public LibraryPlugin LibraryPlugin { get; } public Guid Id => Game.Id; public Guid PluginId => Game.PluginId; public string GameId => Game.GameId; public ComparableDbItemList Tags => new ComparableDbItemList(Game.Tags); public ComparableDbItemList Features => new ComparableDbItemList(Game.Features); public ComparableDbItemList Genres => new ComparableDbItemList(Game.Genres); public ComparableDbItemList Developers => new ComparableDbItemList(Game.Developers); public ComparableDbItemList Publishers => new ComparableDbItemList(Game.Publishers); public ComparableDbItemList Categories => new ComparableDbItemList(Game.Categories); public ComparableDbItemList AgeRatings => new ComparableDbItemList(Game.AgeRatings); public ComparableDbItemList Series => new ComparableDbItemList(Game.Series); public ComparableDbItemList Regions => new ComparableDbItemList(Game.Regions); public ComparableDbItemList Platforms => new ComparableDbItemList(Game.Platforms); public ReleaseDate? ReleaseDate => Game.ReleaseDate; public int? ReleaseYear => Game.ReleaseYear; public DateTime? LastActivity => Game.LastActivity; public ObservableCollection Links => Game.Links; public string Icon => Game.Icon; public string CoverImage => Game.CoverImage; public string BackgroundImage => Game.BackgroundImage; public bool Hidden => Game.Hidden; public bool Favorite => Game.Favorite; public string InstallDirectory => Game.InstallDirectory; public ObservableCollection GameActions => Game.GameActions; public string DisplayName => Game.Name; public string Description => Game.Description; public string Notes => Game.Notes; public bool IsInstalled => Game.IsInstalled; public bool IsInstalling => Game.IsInstalling; public bool IsUninstalling => Game.IsUninstalling; public bool IsLaunching => Game.IsLaunching; public bool IsRunning => Game.IsRunning; public bool IsCustomGame => Game.IsCustomGame; public ulong Playtime => Game.Playtime; public DateTime? Added => Game.Added; public DateTime? Modified => Game.Modified; public ulong PlayCount => Game.PlayCount; public ulong? InstallSize => Game.InstallSize; public string Version => Game.Version; public int? UserScore => Game.UserScore; public int? CriticScore => Game.CriticScore; public int? CommunityScore => Game.CommunityScore; public ScoreGroup UserScoreGroup => Game.UserScoreGroup; public ScoreGroup CriticScoreGroup => Game.CriticScoreGroup; public ScoreGroup CommunityScoreGroup => Game.CommunityScoreGroup; public ScoreRating UserScoreRating => Game.UserScoreRating; public ScoreRating CriticScoreRating => Game.CriticScoreRating; public ScoreRating CommunityScoreRating => Game.CommunityScoreRating; public PastTimeSegment LastActivitySegment => Game.LastActivitySegment; public PastTimeSegment AddedSegment => Game.AddedSegment; public PastTimeSegment ModifiedSegment => Game.ModifiedSegment; public PastTimeSegment RecentActivitySegment => Game.RecentActivitySegment; public PlaytimeCategory PlaytimeCategory => Game.PlaytimeCategory; public InstallationStatus InstallationState => Game.InstallationStatus; public char NameGroup => Game.GetNameGroup(); public DateTime? RecentActivity => Game.RecentActivity; public string InstallDriveGroup => Game.GetInstallDriveGroup(); public InstallSizeGroup InstallSizeGroup => Game.GetInstallSizeGroup(); public bool OverrideInstallState => Game.OverrideInstallState; public List CategoryIds => Game.CategoryIds; public List GenreIds => Game.GenreIds; public List DeveloperIds => Game.DeveloperIds; public List PublisherIds => Game.PublisherIds; public List TagIds => Game.TagIds; public List SeriesIds => Game.SeriesIds; public List AgeRatingIds => Game.AgeRatingIds; public List RegionIds => Game.RegionIds; public Guid SourceId => Game.SourceId; public List PlatformIds => Game.PlatformIds; public List FeatureIds => Game.FeatureIds; public Guid CompletionStatusId => Game.CompletionStatusId; public ObservableCollection Roms => Game.Roms; public string RomList => Game.Roms.HasItems() ? string.Join(", ", Game.Roms.Select(a => a.Path)) : string.Empty; public object LibraryIcon => GetImageObject(LibraryPlugin?.LibraryIcon, true); public object IconObject => GetImageObject(Game.Icon, false); public object CoverImageObject => GetImageObject(Game.CoverImage, false); public object DefaultIconObject => GetDefaultIcon(false); public object DefaultCoverImageObject => GetDefaultCoverImage(false); public object IconObjectCached => GetImageObject(Game.Icon, true); public object CoverImageObjectCached => GetImageObject(Game.CoverImage, true); public object DefaultIconObjectCached => GetDefaultIcon(true); public object DefaultCoverImageObjectCached => GetDefaultCoverImage(true); public string DisplayBackgroundImage => GetBackgroundImage(); public object DisplayBackgroundImageObject => GetBackgroundImageObject(BackgroundImageProperties); public object DetailsListIconObjectCached => GetImageObject(Game.Icon, true, DetailsListIconProperties); public object GridViewCoverObjectCached => GetImageObject(Game.CoverImage, true, GridViewCoverProperties); public object DefaultDetailsListIconObjectCached => GetDefaultIcon(true, DetailsListIconProperties); public object DefaultGridViewCoverObjectCached => GetDefaultCoverImage(true, GridViewCoverProperties); public object FullscreenListItemCoverObject => GetImageObject( Game.CoverImage, settings.Fullscreen.ImageScalerMode != ImageLoadScaling.None, FullscreenListCoverProperties); public object DefaultFullscreenListItemCoverObject => GetDefaultCoverImage(true, FullscreenListCoverProperties); public Series Serie { get; private set; } = Playnite.SDK.Models.Series.Empty; public Platform Platform { get; private set; } = Platform.Empty; public Region Region { get; private set; } = Region.Empty; public GameSource Source { get => Game.Source ?? GameSource.Empty; } public CompletionStatus CompletionStatus { get => Game.CompletionStatus ?? CompletionStatus.Empty; } public AgeRating AgeRating { get; private set; } = AgeRating.Empty; public Category Category { get; private set; } = Category.Empty; public Genre Genre { get; private set; } = Genre.Empty; public Company Developer { get; private set; } = Company.Empty; public Company Publisher { get; private set; } = Company.Empty; public Tag Tag { get; private set; } = Tag.Empty; public GameFeature Feature { get; private set; } = GameFeature.Empty; public string Name { get { return string.IsNullOrEmpty(Game.SortingName) ? Game.Name : Game.SortingName; } } public Game Game { get; } public string Library { get; } public event PropertyChangedEventHandler PropertyChanged; public GamesCollectionViewEntry(Game game, LibraryPlugin plugin, PlayniteSettings settings, bool readOnly = false) { this.settings = settings; LibraryPlugin = plugin; Library = string.IsNullOrEmpty(plugin?.Name) ? "Playnite" : plugin.Name; Game = game; if (!readOnly) { Game.PropertyChanged += Game_PropertyChanged; } } public static void InitItemViewProperties(PlayniteApplication app, PlayniteSettings settings) { logger.Debug("Reloading collection item view properties."); // Use optimized rendering only for Desktop mode where we know pixel perfect data if (app.Mode == ApplicationMode.Desktop) { DetailsListIconProperties = new BitmapLoadProperties( 0, Convert.ToInt32(settings.DetailsViewListIconSize), app.DpiScale); GridViewCoverProperties = new BitmapLoadProperties( Convert.ToInt32(settings.GridItemWidth), 0, app.DpiScale, settings.ImageScalerMode); } else { FullscreenListCoverProperties = GetFullscreenItemRenderSettings(app, settings); } BackgroundImageProperties = new BitmapLoadProperties( app.CurrentScreen.WorkingArea.Width, 0, app.DpiScale, settings.ImageScalerMode); } private static BitmapLoadProperties GetFullscreenItemRenderSettings(PlayniteApplication app, PlayniteSettings settings) { if (app == null) { return null; } var dpi = app.DpiScale; var properties = new BitmapLoadProperties(0, 0, null, settings.Fullscreen.ImageScalerMode); if (settings.Fullscreen.HorizontalLayout) { properties.MaxDecodePixelWidth = app.CurrentScreen.Bounds.Width / (settings.Fullscreen.Columns == 0 ? 1 : settings.Fullscreen.Columns); properties.MaxDecodePixelWidth = (int)Math.Round(properties.MaxDecodePixelWidth / dpi.DpiScaleX); } else { properties.MaxDecodePixelHeight = app.CurrentScreen.Bounds.Height / (settings.Fullscreen.Rows == 0 ? 1 : settings.Fullscreen.Rows); properties.MaxDecodePixelHeight = (int)Math.Round(properties.MaxDecodePixelHeight / dpi.DpiScaleY); } return properties; } public static GamesCollectionViewEntry GetAdvancedGroupedEntry( Game game, LibraryPlugin plugin, Type colGroupType, Guid groupObjId, IGameDatabase database, PlayniteSettings settings) { if (colGroupType == typeof(Genre)) { var obj = database.Genres.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { Genre = obj }; } } else if (colGroupType == typeof(Developer)) { var obj = database.Companies.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { Developer = obj }; } } else if (colGroupType == typeof(Publisher)) { var obj = database.Companies.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { Publisher = obj }; } } else if (colGroupType == typeof(Tag)) { var obj = database.Tags.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { Tag = obj }; } } else if (colGroupType == typeof(GameFeature)) { var obj = database.Features.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { Feature = obj }; } } else if (colGroupType == typeof(Category)) { var obj = database.Categories.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { Category = obj }; } } else if (colGroupType == typeof(Platform)) { var obj = database.Platforms.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { Platform = obj }; } } else if (colGroupType == typeof(AgeRating)) { var obj = database.AgeRatings.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { AgeRating = obj }; } } else if (colGroupType == typeof(Series)) { var obj = database.Series.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { Serie = obj }; } } else if (colGroupType == typeof(Region)) { var obj = database.Regions.Get(groupObjId); if (obj != null) { return new GamesCollectionViewEntry(game, plugin, settings) { Region = obj }; } } return null; } public void Dispose() { Game.PropertyChanged -= Game_PropertyChanged; } private void Game_PropertyChanged(object sender, PropertyChangedEventArgs e) { OnPropertyChanged(e.PropertyName); } public void OnPropertyChanged(string propertyName) { if (propertyName == nameof(Game.SortingName) || propertyName == nameof(Game.Name)) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Game.Name))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayName))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(NameGroup))); } if (propertyName == nameof(Game.Icon)) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IconObject))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IconObjectCached))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DetailsListIconObjectCached))); } if (propertyName == nameof(Game.CoverImage)) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CoverImageObject))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CoverImageObjectCached))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(GridViewCoverObjectCached))); } if (propertyName == nameof(Game.BackgroundImage)) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayBackgroundImage))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DisplayBackgroundImageObject))); } PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private object GetImageObject(string data, bool cached, BitmapLoadProperties loadProperties = null) { return ImageSourceManager.GetImage(data, cached, loadProperties); } public object GetDefaultIcon(bool cached, BitmapLoadProperties loadProperties = null) { var icon = GetDefaultIconFile(Game, settings, GameDatabase.Instance, LibraryPlugin); if (icon.IsNullOrEmpty()) { return ImageSourceManager.GetResourceImage("DefaultGameIcon", cached, loadProperties); } else { return ImageSourceManager.GetImage(icon, cached); } } public static string GetDefaultIconFile(Game game, PlayniteSettings settings, IGameDatabaseMain database, LibraryPlugin plugin) { if (settings.DefaultIconSource == DefaultIconSourceOptions.None) { return null; } else if (settings.DefaultIconSource == DefaultIconSourceOptions.Library && plugin?.LibraryIcon.IsNullOrEmpty() == false) { return plugin.LibraryIcon; } else if (settings.DefaultIconSource == DefaultIconSourceOptions.Platform) { var plat = game.Platforms?.FirstOrDefault(a => !a.Icon.IsNullOrEmpty()); if (plat != null) { return database?.GetFullFilePath(plat.Icon); } } return null; } public object GetDefaultCoverImage(bool cached, BitmapLoadProperties loadProperties = null) { if (settings.DefaultCoverSource == DefaultCoverSourceOptions.None) { return null; } if (settings.DefaultCoverSource == DefaultCoverSourceOptions.Platform) { var plat = Game.Platforms?.FirstOrDefault(a => !a.Cover.IsNullOrEmpty()); if (plat != null) { return ImageSourceManager.GetImage(plat.Cover, cached); } } return ImageSourceManager.GetResourceImage("DefaultGameCover", cached, loadProperties); } public string GetBackgroundImage() { if (!Game.BackgroundImage.IsNullOrEmpty()) { return Game.BackgroundImage; } if (settings.DefaultBackgroundSource == DefaultBackgroundSourceOptions.None) { return null; } if (settings.DefaultBackgroundSource == DefaultBackgroundSourceOptions.Cover && !CoverImage.IsNullOrEmpty()) { return CoverImage; } if (settings.DefaultBackgroundSource == DefaultBackgroundSourceOptions.Platform) { var plat = Game.Platforms?.FirstOrDefault(a => !a.Background.IsNullOrEmpty()); if (plat != null) { return plat.Background; } } if (settings.DefaultBackgroundSource == DefaultBackgroundSourceOptions.Library && LibraryPlugin?.LibraryBackground.IsNullOrEmpty() == false) { return LibraryPlugin.LibraryBackground; } return null; } public object GetBackgroundImageObject(BitmapLoadProperties loadProperties = null) { var imagePath = GetBackgroundImage(); if (imagePath.IsNullOrEmpty()) { return null; } else { if (loadProperties == null) { return new BitmapLoadProperties(0, 0) { Source = imagePath }; } else { return new BitmapLoadProperties(loadProperties.MaxDecodePixelWidth, 0, loadProperties.DpiScale, loadProperties.Scaling) { Source = imagePath }; } } } public override string ToString() { return Name; } public static explicit operator Game(GamesCollectionViewEntry entry) { return entry.Game; } } } ================================================ FILE: source/Playnite/GamesEditor.cs ================================================ using Playnite; using Playnite.API; using Playnite.Common; using Playnite.Database; using Playnite.Plugins; using Playnite.Controllers; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.Settings; using Playnite.ViewModels; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Windows; using System.Windows.Shell; using Playnite.Scripting; using System.Threading; using System.Threading.Tasks; using System.Collections.Concurrent; using Playnite.SDK.Exceptions; using System.Drawing.Imaging; using Playnite.SDK.Plugins; using System.Collections.ObjectModel; using Playnite.Scripting.PowerShell; using Playnite.Windows; using System.Windows.Input; using System.Security.Cryptography; namespace Playnite { public enum GameScriptType { [Description(LOC.ScriptTypeStarting)] Starting, [Description(LOC.ScriptTypeStarted)] Started, [Description(LOC.ScriptTypeExit)] Exit, [Description("")] None } public interface IActionSelector { object SelectPlayAction(List controllers, List actions); InstallController SelectInstallAction(List pluginActions); UninstallController SelectUninstallAction(List pluginActions); } public class ClientShutdownJob { public Guid PluginId { get; set; } public CancellationTokenSource CancelToken { get; set; } public Task CancelTask { get; set; } } public class RunningGame { public Guid Id { get; set; } public bool LauchedFromUI { get; set; } public RunningGame(Guid id, bool lauchedFromUI) { Id = id; LauchedFromUI = lauchedFromUI; } } public class GamesEditor : ObservableObject, IDisposable { private static ILogger logger = LogManager.GetLogger(); private static bool showedPowerShellError = false; private IResourceProvider resources = new ResourceProvider(); private GameControllerFactory controllers; private readonly ConcurrentDictionary shutdownJobs = new ConcurrentDictionary(); private readonly ConcurrentDictionary gameStartups = new ConcurrentDictionary(); private readonly ConcurrentDictionary scriptRuntimes = new ConcurrentDictionary(); private readonly IActionSelector actionSelector; private bool wasHdrEnabled; public PlayniteApplication Application; public ExtensionFactory Extensions { get; private set; } public GameDatabase Database { get; private set; } public IDialogsFactory Dialogs { get; private set; } public PlayniteSettings AppSettings { get; private set; } public Dictionary RunningGames { get; } = new Dictionary(); public List QuickLaunchItems { get { if (AppSettings.QuickLaunchItems > 0) { return Database.Games. Where(a => a.LastActivity != null && a.IsInstalled && (!a.Hidden || (a.Hidden && AppSettings.ShowHiddenInQuickLaunch))). OrderByDescending(a => a.LastActivity). Take(AppSettings.QuickLaunchItems). ToList(); } else { return new List(); } } } public List FavoriteQuickLaunchItems { get { return Database.Games. Where(a => a.Favorite && a.IsInstalled && (!a.Hidden || (a.Hidden && AppSettings.ShowHiddenInQuickLaunch))). OrderBy(a => a.Name). ToList(); } } public GamesEditor( GameDatabase database, GameControllerFactory controllerFactory, PlayniteSettings appSettings, IDialogsFactory dialogs, ExtensionFactory extensions, PlayniteApplication app, IActionSelector actionSelector) { this.Dialogs = dialogs; this.Database = database; this.AppSettings = appSettings; this.Extensions = extensions; this.Application = app; this.actionSelector = actionSelector; controllers = controllerFactory; controllers.Installed += Controllers_Installed; controllers.InstallationCancelled += Controllers_InstallationCancelled; controllers.Uninstalled += Controllers_Uninstalled; controllers.Started += Controllers_Started; controllers.Stopped += Controllers_Stopped; AppSettings.PropertyChanged += AppSettings_PropertyChanged; } private void AppSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(PlayniteSettings.ShowHiddenInQuickLaunch) || e.PropertyName == nameof(PlayniteSettings.QuickLaunchItems)) { UpdateJumpList(); } } public void Dispose() { foreach (var controller in controllers.PlayControllers) { UpdateGameState(controller.Game.Id, null, false, false, false, false); } foreach (var controller in controllers.InstallControllers) { UpdateGameState(controller.Game.Id, null, false, false, false, false); } controllers.Installed -= Controllers_Installed; controllers.InstallationCancelled -= Controllers_InstallationCancelled; controllers.Uninstalled -= Controllers_Uninstalled; controllers.Started -= Controllers_Started; controllers.Stopped -= Controllers_Stopped; } public void StartContextAction(Game game) { if (game.IsInstalled) { PlayGame(game, true); } else { InstallGame(game); } } public void PlayGame(Game game, bool launchedFromUI) { if (!game.IsInstalled) { InstallGame(game); return; } logger.Info($"Starting {game.GetIdentifierInfo()}"); var dbGame = Database.Games.Get(game.Id); if (dbGame == null) { Dialogs.ShowMessage( string.Format(resources.GetString("LOCGameStartErrorNoGame"), game.Name), resources.GetString("LOCGameError"), MessageBoxButton.OK, MessageBoxImage.Error); UpdateJumpList(); return; } PlayController controller = null; try { if (game.IsRunning || game.IsLaunching) { logger.Warn("Failed to start the game, game is already running/launching."); return; } var gameActions = GetPlayActions(game); if (!gameActions.Item1.HasItems() && !gameActions.Item2.HasItems()) { Dialogs.ShowErrorMessage(LOC.ErrorNoPlayAction, LOC.GameError); return; } object playAction = null; if ((gameActions.Item1.Count + gameActions.Item2.Count) > 1) { playAction = actionSelector.SelectPlayAction(gameActions.Item1, gameActions.Item2); } else { if (gameActions.Item1.Count > 0) { playAction = gameActions.Item1[0]; } else { playAction = gameActions.Item2[0]; } } if (playAction == null) { return; } try { scriptRuntimes.TryAdd(game.Id, new PowerShellRuntime($"{game.Name} {game.Id} runtime")); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { // This should really only happen on Windows 7 without PS 5.1 installed, which is very small percentage of users. // It should not prevent game startup. logger.Error(e, "Failed to create PowerShell runtime."); if (!showedPowerShellError) { Dialogs.ShowErrorMessage(resources.GetString(LOC.PowerShellCreationError) + "\n\n" + e.Message, ""); showedPowerShellError = true; } scriptRuntimes.TryAdd(game.Id, new DummyPowerShellRuntime()); } if (playAction is AutomaticPlayController) { logger.Debug("Using automatic plugin controller to start a game."); controller = new GenericPlayController(Database, game, scriptRuntimes[game.Id], Application.PlayniteApiGlobal); } else if (playAction is PlayController plugAction) { logger.Debug("Using plugin to start a game."); controller = plugAction; } else if (playAction is EmulationPlayAction) { logger.Debug("Using generic controller to start emulated game."); controller = new GenericPlayController(Database, game, scriptRuntimes[game.Id], Application.PlayniteApiGlobal); } else if (playAction is GameAction gameAction) { logger.Debug("Using generic controller start a game."); controller = new GenericPlayController(Database, game, scriptRuntimes[game.Id], Application.PlayniteApiGlobal); } else { logger.Error($"Uknown controller found to start a game: {controller.GetType()}"); } foreach (var item in gameActions.Item1) { if (item != controller) { item.Dispose(); } } if (controller == null) { scriptRuntimes.TryRemove(game.Id, out _); Dialogs.ShowErrorMessage(LOC.ErrorStartupNoController, LOC.StartupError); return; } void cancelStartup(string message) { logger.Warn(message); controllers.InvokeOnGameStartupCancelled(this, game.GetCopy()); controllers.RemovePlayController(game.Id); UpdateGameState(game.Id, null, null, null, null, false); } controllers.RemovePlayController(game.Id); controllers.AddController(controller); RunningGames.Add(game.Id, new RunningGame(game.Id, launchedFromUI)); UpdateGameState(game.Id, null, null, null, null, true); var startingArgs = new SDK.Events.OnGameStartingEventArgs { Game = game.GetCopy(), SourceAction = (playAction as GameAction)?.GetClone(), SelectedRomFile = (playAction as EmulationPlayAction)?.SelectedRomPath }; controllers.InvokeOnStarting(this, startingArgs); if (startingArgs.CancelStartup) { cancelStartup("Game startup cancelled by an extension."); return; } if (!game.IsCustomGame && shutdownJobs.TryGetValue(game.PluginId, out var existingJob)) { logger.Debug($"Starting game with existing client shutdown job, canceling job {game.PluginId}."); existingJob.CancelToken.Cancel(); shutdownJobs.TryRemove(game.PluginId, out var _); } var scriptVars = new Dictionary { { "StartingArgs", startingArgs }, { "SourceAction", startingArgs.SourceAction }, { "SelectedRomFile", startingArgs.SelectedRomFile } }; //Get the current system HDR status only if this is the first game launched with HDR controlled by Playnite if (!controllers.PlayControllers.Any(c => c.Game.Id != game.Id && c.Game.EnableSystemHdr)) { wasHdrEnabled = HdrUtilities.IsHdrEnabled(); } if (game.EnableSystemHdr) { HdrUtilities.SetHdrEnabled(true); } if (!ExecuteScriptAction(scriptRuntimes[game.Id], AppSettings.PreScript, game, game.UseGlobalPreScript, true, GameScriptType.Starting, scriptVars)) { cancelStartup("Game startup cancelled because global game script failed."); return; } if (startingArgs.CancelStartup) { cancelStartup("Game startup cancelled by global game script."); return; } if (!ExecuteScriptAction(scriptRuntimes[game.Id], game.PreScript, game, true, false, GameScriptType.Starting, scriptVars)) { cancelStartup("Game startup cancelled because game script failed."); return; } if (startingArgs.CancelStartup) { cancelStartup("Game startup cancelled by game script."); return; } if (controller is GenericPlayController genCtrl) { if (playAction is EmulationPlayAction emuAct) { genCtrl.StartEmulator(emuAct, true, startingArgs); } else if (playAction is AutomaticPlayController autoAction) { genCtrl.Start(autoAction); } else if (playAction is GameAction act) { genCtrl.Start(act, true, startingArgs); } else { throw new NotSupportedException("Uknown play action type."); } } else { controller.Play(new PlayActionArgs()); } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Cannot start game: "); Dialogs.ShowMessage( resources.GetString(LOC.GameStartError).Format(exc.Message), LOC.GameError, MessageBoxButton.OK, MessageBoxImage.Error); if (controller != null) { controllers.RemoveController(controller); UpdateGameState(game.Id, null, null, null, null, false); } return; } UpdateJumpList(); } public void ActivateAction(Game game, GameAction action) { try { switch (action.Type) { case GameActionType.URL: ProcessStarter.StartUrl(action.Path); break; case GameActionType.File: case GameActionType.Emulator: case GameActionType.Script: using (var scriptRuntime = new PowerShellRuntime("Custom action runtime")) using (var controller = new GenericPlayController(Database, game, scriptRuntime, Application.PlayniteApiGlobal)) { if (action.Type == GameActionType.Emulator) { var emulator = Database.Emulators[action.EmulatorId]; if (emulator == null) { throw new Exception($"Emulator not found."); } var prof = emulator.AllProfiles.FirstOrDefault(a => a.Id == action.EmulatorProfileId); var newAction = action.GetClone(); newAction.SelectedEmulatorProfile = prof ?? throw new Exception("Specified emulator config does't exists."); newAction.SelectedRomPath = game.Roms.HasItems() ? game.Roms[0].Path : string.Empty; controller.StartEmulator(newAction, false, new SDK.Events.OnGameStartingEventArgs { Game = game, SelectedRomFile = newAction.SelectedRomPath, SourceAction = action }); } else { controller.Start(action, false, new SDK.Events.OnGameStartingEventArgs { Game = game, SourceAction = action }); } } break; default: throw new NotImplementedException(); } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Cannot activate action: "); Dialogs.ShowMessage( string.Format(resources.GetString("LOCGameStartActionError"), exc.Message), resources.GetString("LOCGameError"), MessageBoxButton.OK, MessageBoxImage.Error); } } public void OpenGameLocation(Game game) { if (string.IsNullOrEmpty(game.InstallDirectory)) { return; } try { var installDirectory = game.InstallDirectory; if (FileSystem.DirectoryExistsOnAnyDrive(installDirectory, out var newPath) && !string.Equals(newPath, installDirectory, StringComparison.OrdinalIgnoreCase)) { logger.Warn("Replaced missing game dir with new one:\n{0}\n{1}".Format(installDirectory, newPath)); installDirectory = newPath; } string emuDir = null; if (game.InstallDirectory.Contains(ExpandableVariables.EmulatorDirectory, StringComparison.OrdinalIgnoreCase)) { var action = game.GameActions.FirstOrDefault(a => a.IsPlayAction && a.Type == GameActionType.Emulator && a.EmulatorId != Guid.Empty); if (action != null) { var emu = Database.Emulators[action.EmulatorId]; if (emu != null) { emuDir = Paths.FixSeparators(emu.InstallDir.Replace(ExpandableVariables.PlayniteDirectory, PlaynitePaths.ProgramPath, StringComparison.OrdinalIgnoreCase)); } } } installDirectory = game.ExpandVariables(installDirectory, true, emuDir); Commands.GlobalCommands.NavigateDirectoryCommand.Execute(installDirectory); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Cannot open game location: "); Dialogs.ShowMessage( string.Format(resources.GetString("LOCGameOpenLocationError"), exc.Message), resources.GetString("LOCGameError"), MessageBoxButton.OK, MessageBoxImage.Error); } } public void UpdateGameSize(Game game, bool onlyIfDataMissing, bool updateGameOnLibrary, bool checkLastScanDate) { if (!game.IsInstalled) { return; } if (game.InstallSize != null && onlyIfDataMissing) { return; } ulong? scanSize; try { scanSize = CalculateGameSize(game, checkLastScanDate); } catch (Exception e) { logger.Error(e, $"Failed to get InstallSize from game {game.Name} with install dir {game.InstallDirectory}"); throw; } if (scanSize == null) { return; } game.LastSizeScanDate = DateTime.Now; if (game.InstallSize != scanSize) { game.InstallSize = scanSize; } if (updateGameOnLibrary) { Database.Games.Update(game); } } public ulong? CalculateGameSize(Game game, bool checkLastScanDate) { long size = 0; if (game.Roms.HasItems()) { var expandedGame = GetExpandedGameForRomsSizeScan(game); var romsFilesPaths = new List(); foreach (var rom in expandedGame.Roms) { if (rom.Path.IsNullOrWhiteSpace() || !FileSystem.FileExists(rom.Path)) { continue; } if (rom.Path.EndsWith(".cue", StringComparison.OrdinalIgnoreCase)) { AddPlaylistRomFilesPathsToList(rom, romsFilesPaths, CueSheet.GetFileEntries(rom.Path).Select(a => a.Path)); } else if (rom.Path.EndsWith(".m3u", StringComparison.OrdinalIgnoreCase)) { AddPlaylistRomFilesPathsToList(rom, romsFilesPaths, M3U.GetEntries(rom.Path).Select(a => a.Path)); } else if (rom.Path.EndsWith(".gdi", StringComparison.OrdinalIgnoreCase)) { AddPlaylistRomFilesPathsToList(rom, romsFilesPaths, GdiFile.GetEntries(rom.Path).Select(a => a.Path)); } else { romsFilesPaths.Add(rom.Path); } } if (!romsFilesPaths.HasItems()) { return null; } if (checkLastScanDate) { var addedAfterLastCheck = expandedGame.LastSizeScanDate == null || expandedGame.Added != null && expandedGame.Added > expandedGame.LastSizeScanDate; if (!addedAfterLastCheck && expandedGame.LastSizeScanDate != null && !romsFilesPaths.Any(x => FileSystem.FileGetLastWriteTime(x) > expandedGame.LastSizeScanDate)) { return null; } } foreach (var romFilePath in romsFilesPaths) { if (AppSettings.InstallSizeScanUseSizeOnDisk) { size += FileSystem.GetFileSizeOnDisk(romFilePath); } else { size += FileSystem.GetFileSize(romFilePath); } } } else { if (game.InstallDirectory.IsNullOrEmpty()) { return null; } var expandedGame = game.ExpandGame(); if (!FileSystem.DirectoryExists(expandedGame.InstallDirectory)) { return null; } if (checkLastScanDate) { var addedAfterLastCheck = expandedGame.LastSizeScanDate == null || expandedGame.Added != null && expandedGame.Added > expandedGame.LastSizeScanDate; if (!addedAfterLastCheck && expandedGame.LastSizeScanDate != null && FileSystem.DirectoryGetLastWriteTime(expandedGame.InstallDirectory) < expandedGame.LastSizeScanDate) { return null; } } size = FileSystem.GetDirectorySize(expandedGame.InstallDirectory, AppSettings.InstallSizeScanUseSizeOnDisk); } return (ulong)size; } private void AddPlaylistRomFilesPathsToList(GameRom rom, List pathsList, IEnumerable playlistChildren) { if (!playlistChildren.HasItems()) { return; } var rootDir = Path.GetDirectoryName(rom.Path); pathsList.AddRange ( playlistChildren .Select(a => Path.Combine(rootDir, a)) .Where(x => FileSystem.FileExists(x)) ); } public Game GetExpandedGameForRomsSizeScan(Game game) { if (game.GameActions == null) { return game.ExpandGame(); } var emulatorAction = game.GameActions.FirstOrDefault(x => x.Type == GameActionType.Emulator); if (emulatorAction == null) { return game.ExpandGame(); } var emulator = Database.Emulators[emulatorAction.EmulatorId]; if (emulator != null) { return game.ExpandGame(false, emulator.InstallDir); } return game.ExpandGame(); } public void UpdateGameSizeWithDialog(Game game, bool onlyIfDataMissing, bool updateGameOnLibrary) { var textTitle = string.Format(ResourceProvider.GetString("LOCCalculatingInstallSizeOfGameMessage"), game.Name); Dialogs.ActivateGlobalProgress((a) => { try { UpdateGameSize(game, onlyIfDataMissing, updateGameOnLibrary, false); } catch (Exception e) { Dialogs.ShowMessage( string.Format(resources.GetString("LOCCalculateGameSizeError"), e.Message), resources.GetString("LOCCalculateGameSizeErrorCaption"), MessageBoxButton.OK, MessageBoxImage.Error); } }, new GlobalProgressOptions(textTitle, false) { IsIndeterminate = true }); } public void UpdateGamesSizeWithDialog(List games, bool onlyIfDataMissing) { var textTitle = ResourceProvider.GetString("LOCCalculatingInstallSizeMessage"); var errorStrings = new List(); var errorsCount = 0; Dialogs.ActivateGlobalProgress((a) => { a.ProgressMaxValue = games.Count(); using (Database.BufferedUpdate()) { foreach (var game in games) { if (a.CancelToken.IsCancellationRequested) { break; } a.CurrentProgressValue++; a.Text = $"{textTitle}\n\n{game.Name}\n{a.CurrentProgressValue}/{a.ProgressMaxValue}"; try { UpdateGameSize(game, onlyIfDataMissing, true, false); } catch (Exception e) { errorsCount++; if (errorStrings.Count < 10) { errorStrings.Add($"{game.Name}: {e.Message}"); } } } } }, new GlobalProgressOptions(textTitle, true) { IsIndeterminate = false }); if (errorsCount > 0) { var errorMessage = ResourceProvider.GetString("LOCCalculateGamesSizeErrorMessage").Format(errorsCount) + $"\n\n" + string.Join("\n", errorStrings); if (errorsCount > 10) { errorMessage += "\n..."; } Dialogs.ShowMessage( errorMessage, resources.GetString("LOCCalculateGameSizeErrorCaption"), MessageBoxButton.OK, MessageBoxImage.Error); } } public void SetHideGame(Game game, bool state) { game.Hidden = state; Database.Games.Update(game); } public void SetHideGames(List games, bool state) { using (Database.BufferedUpdate()) { foreach (var game in games) { SetHideGame(game, state); } } } public void SetHdrSupport(Game game, bool state) { game.EnableSystemHdr = state; Database.Games.Update(game); } public void SetHdrSupport(List games, bool state) { using (Database.BufferedUpdate()) { foreach (var game in games) { SetHdrSupport(game, state); } } } public void ToggleHideGame(Game game) { game.Hidden = !game.Hidden; Database.Games.Update(game); } public void ToggleHideGames(List games) { using (Database.BufferedUpdate()) { foreach (var game in games) { ToggleHideGame(game); } } } public void SetFavoriteGame(Game game, bool state) { game.Favorite = state; Database.Games.Update(game); } public void SetFavoriteGames(List games, bool state) { using (Database.BufferedUpdate()) { foreach (var game in games) { SetFavoriteGame(game, state); } } } public void ToggleHdrGame(Game game) { game.EnableSystemHdr = !game.EnableSystemHdr; Database.Games.Update(game); } public void ToggleFavoriteGame(Game game) { game.Favorite = !game.Favorite; Database.Games.Update(game); } public void ToggleFavoriteGame(List games) { using (Database.BufferedUpdate()) { foreach (var game in games) { ToggleFavoriteGame(game); } } } public void SetCompletionStatus(Game game, CompletionStatus status) { if (game.CompletionStatusId != status.Id) { game.CompletionStatusId = status.Id; Database.Games.Update(game); } } public void SetCompletionStatus(List games, CompletionStatus status) { using (Database.BufferedUpdate()) { foreach (var game in games) { SetCompletionStatus(game, status); } } } public void RemoveGame(Game game) { if (game.IsInstalling || game.IsRunning || game.IsLaunching || game.IsUninstalling) { Dialogs.ShowMessage( "LOCGameRemoveRunningError", "LOCGameError", MessageBoxButton.OK, MessageBoxImage.Error); return; } var addToExclusionList = false; if (game.IsCustomGame) { if (Dialogs.ShowMessage( string.Format(resources.GetString("LOCGameRemoveAskMessage"), game.Name), "LOCGameRemoveAskTitle", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes) { return; } } else { var options = new List { new MessageBoxOption("LOCRemoveAskAddToExlusionListYesResponse"), new MessageBoxOption("LOCYesLabel", true), new MessageBoxOption("LOCNoLabel", false, true) }; var result = Dialogs.ShowMessage( string.Format(resources.GetString("LOCGameRemoveAskMessageIgnoreOption"), game.Name), "LOCGameRemoveAskTitle", MessageBoxImage.Question, options); if (result == options[0]) { addToExclusionList = true; } else if (result == null || result == options[2]) { return; } } if (Database.Games[game.Id] == null) { logger.Warn($"Failed to remove game {game.Name} {game.Id}, game doesn't exists anymore."); } else { Database.Games.Remove(game); if (addToExclusionList) { var exclusion = new ImportExclusionItem(game.GameId, game.Name, game.PluginId, Extensions.GetLibraryPlugin(game.PluginId)?.Name); if (Database.ImportExclusions[exclusion.Id] == null) { Database.ImportExclusions.Add(exclusion); } } } } public void RemoveGames(List games) { if (!games.HasItems()) { return; } if (games.Exists(a => a.IsInstalling || a.IsRunning || a.IsLaunching || a.IsUninstalling)) { Dialogs.ShowMessage( "LOCGameRemoveRunningError", "LOCGameError", MessageBoxButton.OK, MessageBoxImage.Error); return; } var addToExclusionList = false; if (games.All(a => a.IsCustomGame)) { if (Dialogs.ShowMessage( string.Format(resources.GetString("LOCGamesRemoveAskMessage"), games.Count()), "LOCGameRemoveAskTitle", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes) { return; } } else { var options = new List { new MessageBoxOption("LOCRemoveAskAddToExlusionListYesResponse"), new MessageBoxOption("LOCYesLabel", true), new MessageBoxOption("LOCNoLabel", false, true) }; var result = Dialogs.ShowMessage( string.Format(resources.GetString("LOCGamesRemoveAskMessageIgnoreOption"), games.Count()), "LOCGameRemoveAskTitle", MessageBoxImage.Question, options); if (result == options[0]) { addToExclusionList = true; } else if (result == options[2]) { return; } } foreach (var game in games.ToList()) { if (Database.Games[game.Id] == null) { logger.Warn($"Failed to remove game {game.Name} {game.Id}, game doesn't exists anymore."); games.Remove(game); } if (addToExclusionList && !game.IsCustomGame) { var exclusion = new ImportExclusionItem(game.GameId, game.Name, game.PluginId, Extensions.GetLibraryPlugin(game.PluginId)?.Name); if (Database.ImportExclusions[exclusion.Id] == null) { Database.ImportExclusions.Add(exclusion); } } } Database.Games.Remove(games); } public void CreateDesktopShortcut(List games) { foreach (var game in games) { CreateDesktopShortcut(game); } } public void CreateDesktopShortcut(Game game) { try { var path = Environment.ExpandEnvironmentVariables(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), Paths.GetSafePathName(game.Name) + ".url")); string icon = string.Empty; if (!game.Icon.IsNullOrEmpty()) { icon = Database.GetFullFilePath(game.Icon); } else { icon = GamesCollectionViewEntry.GetDefaultIconFile(game, AppSettings, Database, Extensions.GetLibraryPlugin(game.PluginId)); if (!File.Exists(icon)) { icon = string.Empty; } } if (File.Exists(icon)) { if (Path.GetExtension(icon) != ".ico") { FileSystem.CreateDirectory(PlaynitePaths.IconsCachePath); var targetIconPath = Path.Combine(PlaynitePaths.IconsCachePath, game.Id + ".ico"); BitmapExtensions.ConvertToIcon(icon, targetIconPath); if (File.Exists(targetIconPath)) { icon = targetIconPath; } } } else { icon = PlaynitePaths.DesktopExecutablePath; } var args = new CmdLineOptions() { Start = game.Id.ToString() }.ToString(); Programs.CreateUrlShortcut($"playnite://playnite/start/{game.Id}", icon, path); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to create shortcut: "); Dialogs.ShowMessage( string.Format(resources.GetString("LOCGameShortcutError"), exc.Message), resources.GetString("LOCGameError"), MessageBoxButton.OK, MessageBoxImage.Error); } } public void OpenManual(Game game) { if (game.Manual.IsNullOrEmpty()) { return; } try { var manualPath = game.ExpandVariables(game.Manual, fixSeparators: true); if (!manualPath.IsUri() && !File.Exists(manualPath)) { manualPath = Path.Combine(Database.GetFileStoragePath(game.Id), manualPath); } if (manualPath.IsUri()) { ProcessStarter.StartUrl(manualPath); } else { ProcessStarter.StartProcess(manualPath); } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to open manual."); Dialogs.ShowMessage( string.Format(resources.GetString("LOCManualOpenError"), exc.Message), resources.GetString("LOCGameError"), MessageBoxButton.OK, MessageBoxImage.Error); } } public void CreateDesktopShortcuts(List games) { foreach (var game in games) { CreateDesktopShortcut(game); } } public void InstallGame(Game game) { logger.Info($"Installing {game.GetIdentifierInfo()}"); InstallController controller = null; try { var installControllers = GetInstallActions(game); if (!installControllers.HasItems()) { Dialogs.ShowErrorMessage(LOC.ErrorNoInstallAction, LOC.GameError); return; } if (installControllers.Count > 1) { controller = actionSelector.SelectInstallAction(installControllers); } else { controller = installControllers[0]; } if (controller == null) { return; } controllers.RemoveInstallController(game.Id); controllers.AddController(controller); UpdateGameState(game.Id, null, null, true, null, null); controller.Install(new InstallActionArgs()); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Cannot install game: "); Dialogs.ShowMessage( resources.GetString(LOC.GameInstallError).Format(exc.Message), LOC.GameError, MessageBoxButton.OK, MessageBoxImage.Error); if (controller != null) { controllers.RemoveController(controller); UpdateGameState(game.Id, null, null, false, null, null); } } } public void UnInstallGame(Game game) { if (game.IsRunning || game.IsLaunching) { Dialogs.ShowMessage(LOC.GameUninstallRunningError, LOC.GameError, MessageBoxButton.OK, MessageBoxImage.Error); return; } logger.Info($"Uninstalling {game.GetIdentifierInfo()}"); UninstallController controller = null; try { var uninstallControllers = GetUninstallActions(game); if (!uninstallControllers.HasItems()) { Dialogs.ShowErrorMessage(LOC.ErrorNoInstallAction, LOC.GameError); return; } if (uninstallControllers.Count > 1) { controller = actionSelector.SelectUninstallAction(uninstallControllers); } else { controller = uninstallControllers[0]; } if (controller == null) { return; } controllers.RemoveInstallController(game.Id); controllers.AddController(controller); UpdateGameState(game.Id, null, null, null, true, null); controller.Uninstall(new UninstallActionArgs()); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Cannot un-install game: "); Dialogs.ShowMessage( resources.GetString(LOC.GameUninstallError).Format(exc.Message), LOC.GameError, MessageBoxButton.OK, MessageBoxImage.Error); if (controller != null) { controllers.RemoveController(controller); UpdateGameState(game.Id, null, null, null, false, null); } } } public void UpdateJumpList() { try { var jumpList = new JumpList(); jumpList.ShowFrequentCategory = false; jumpList.ShowRecentCategory = false; if (AppSettings.QuickLaunchItems > 0) { var catString = resources.GetString(LOC.QuickFilterRecentlyPlayed); foreach (var lastGame in QuickLaunchItems) { var args = new CmdLineOptions() { Start = lastGame.Id.ToString() }.ToString(); JumpTask task = new JumpTask { Title = lastGame.Name, Arguments = args, Description = string.Empty, CustomCategory = catString, ApplicationPath = PlaynitePaths.DesktopExecutablePath }; if (lastGame.Icon?.EndsWith(".ico", StringComparison.OrdinalIgnoreCase) == true) { task.IconResourcePath = Database.GetFullFilePath(lastGame.Icon); } else { task.IconResourcePath = lastGame.GetRawExecutablePath(); } jumpList.JumpItems.Add(task); } JumpTask fullscreen = new JumpTask { Title = resources.GetString(LOC.MenuOpenFullscreen), ApplicationPath = PlaynitePaths.FullscreenExecutablePath }; jumpList.JumpItems.Add(fullscreen); JumpList.SetJumpList(System.Windows.Application.Current, jumpList); } else { JumpList.SetJumpList(System.Windows.Application.Current, new JumpList()); } jumpList.Apply(); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to set jump list data."); } } public void CancelGameMonitoring(Game game) { var wasRunningOrLaunching = game.IsRunning || game.IsLaunching; controllers.RemoveInstallController(game.Id); controllers.RemoveUninstallController(game.Id); controllers.RemovePlayController(game.Id); var dbGame = Database.Games.Get(game.Id); dbGame.IsRunning = false; dbGame.IsLaunching = false; dbGame.IsInstalling = false; dbGame.IsUninstalling = false; ulong ellapsedTime = 0; if (gameStartups.TryRemove(game.Id, out var startupTime)) { // This shouldn't be a problem, but there was one crash report with startup type being more recent if (startupTime < DateTime.Now) { ellapsedTime = Convert.ToUInt64((DateTime.Now - startupTime).TotalSeconds); dbGame.Playtime += ellapsedTime; } } if (scriptRuntimes.TryRemove(game.Id, out var runtime)) { runtime.Dispose(); } Database.Games.Update(dbGame); if (wasRunningOrLaunching) { RunningGames.Remove(game.Id); Extensions.InvokeOnGameStopped(game, ellapsedTime, true); } if (AppSettings.DiscordPresenceEnabled) { Application.Discord?.ClearPresence(); } } private void UpdateGameState(Guid id, bool? installed, bool? running, bool? installing, bool? uninstalling, bool? launching) { if (running == true || launching == true) { RunningGames.AddMissing(id, new RunningGame(id, true)); } else if (running == false || launching == false) { RunningGames.Remove(id); } var game = Database.Games.Get(id); if (installed != null) { game.IsInstalled = installed.Value; } if (running != null) { game.IsRunning = running.Value; } if (installing != null) { game.IsInstalling = installing.Value; } if (uninstalling != null) { game.IsUninstalling = uninstalling.Value; } if (launching != null) { game.IsLaunching = launching.Value; } if (running == true) { game.LastActivity = DateTime.Now; game.PlayCount += 1; var comSettings = Database.GetCompletionStatusSettings(); if (comSettings.PlayedStatus == Constants.MaxGuidVal) { // Do nothing option } else if (game.CompletionStatusId == Guid.Empty || game.CompletionStatusId == comSettings.DefaultStatus) { game.CompletionStatusId = comSettings.PlayedStatus; } } Database.Games.Update(game); } private void Controllers_Started(object sender, GameStartedEventArgs args) { var game = args.Source.Game; logger.Info($"Started {game.Name} game."); UpdateGameState(game.Id, null, true, null, null, false); gameStartups.TryAdd(game.Id, DateTime.Now); var scriptVars = new Dictionary { { "SourceAction", (args.Source as GenericPlayController)?.StartingArgs?.SourceAction?.GetClone() }, { "SelectedRomFile", (args.Source as GenericPlayController)?.StartingArgs?.SelectedRomFile }, { "StartedProcessId", args.StartedProcessId } }; ExecuteScriptAction(scriptRuntimes[game.Id], game.GameStartedScript, game, true, false, GameScriptType.Started, scriptVars); ExecuteScriptAction(scriptRuntimes[game.Id], AppSettings.GameStartedScript, game, game.UseGlobalGameStartedScript, true, GameScriptType.Started, scriptVars); if (Application.Mode == ApplicationMode.Desktop) { if (AppSettings.AfterLaunch == AfterLaunchOptions.Close) { Application.Quit(); } else if (AppSettings.AfterLaunch == AfterLaunchOptions.Minimize) { Application.Minimize(); } } else { if (AppSettings.AfterLaunch == AfterLaunchOptions.Close) { Application.Quit(); return; } AppSettings.Fullscreen.IsMusicMuted = true; if (AppSettings.Fullscreen.MinimizeAfterGameStartup) { Application.Minimize(); } PlayniteApplication.Current.IsActive = false; } if (AppSettings.DiscordPresenceEnabled) { Application.Discord?.SetPresence(game.Name); } } private void Controllers_Stopped(object sender, GameStoppedEventArgs args) { var game = args.Source.Game; logger.Info($"Game {game.Name} stopped after {args.SessionLength} seconds."); // I have no idea under what conditions this could happen, but there are couple crash reports with this. if (!RunningGames.TryGetValue(game.Id, out var runningGame)) { logger.Error($"Got controller stopped event for a game that's not registered as running {game.Id}"); return; } RunningGames.Remove(game.Id); var dbGame = Database.Games.Get(game.Id); dbGame.IsRunning = false; dbGame.IsLaunching = false; dbGame.Playtime += args.SessionLength; Database.Games.Update(dbGame); controllers.RemoveController(args.Source); gameStartups.TryRemove(game.Id, out _); var restore = false; if (Application.Mode == ApplicationMode.Desktop && AppSettings.AfterGameClose == AfterGameCloseOptions.Restore) { restore = true; } if (Application.Mode == ApplicationMode.Desktop && AppSettings.AfterGameClose == AfterGameCloseOptions.RestoreOnlyFromUI && runningGame.LauchedFromUI) { restore = true; } else if (Application.Mode == ApplicationMode.Fullscreen) { restore = true; AppSettings.Fullscreen.IsMusicMuted = false; } if (restore) { // This delay apparently fixes issues with Playnite not restoring properly after game exits. // The window will restore, but application will not regain active state. // This was mainly reported to happen with some emulators, like RPCS3, no idea why. Thread.Sleep(1000); Application.Restore(); } if (AppSettings.DiscordPresenceEnabled) { Application.Discord?.ClearPresence(); } // Reset HDR if there are no more active games with HDR controlled by Playnite // and the game being closed has HDR controlled by Playnite if (!controllers.PlayControllers.Any(c => c.Game.EnableSystemHdr) && game.EnableSystemHdr) { HdrUtilities.SetHdrEnabled(wasHdrEnabled); } var scriptVars = new Dictionary(); if (args.Source is GenericPlayController genCtrl) { scriptVars["SourceAction"] = genCtrl.StartingArgs?.SourceAction?.GetClone(); scriptVars["SelectedRomFile"] = genCtrl.StartingArgs?.SelectedRomFile; } ExecuteScriptAction(scriptRuntimes[game.Id], game.PostScript, game, true, false, GameScriptType.Exit, scriptVars); ExecuteScriptAction(scriptRuntimes[game.Id], AppSettings.PostScript, game, game.UseGlobalPostScript, true, GameScriptType.Exit, scriptVars); if (scriptRuntimes.TryRemove(game.Id, out var runtime)) { runtime.Dispose(); } Extensions.InvokeOnGameStopped(game, args.SessionLength, false); if (Application.Mode == ApplicationMode.Desktop && AppSettings.AfterGameClose == AfterGameCloseOptions.Exit) { Application.Quit(); } if (AppSettings.ClientAutoShutdown.ShutdownClients && !game.IsCustomGame) { if (args.SessionLength <= AppSettings.ClientAutoShutdown.MinimalSessionTime) { logger.Debug("Game session was too short for client to be shutdown."); } else if (Database.Games.Any(x => (x.IsRunning || x.IsInstalling || x.IsUninstalling) && x.PluginId == game.PluginId)) { logger.Debug("Shutdown process canceled because another game from library was detected as having game action active."); } else { var plugin = Extensions.GetLibraryPlugin(game.PluginId); if (plugin?.Properties?.CanShutdownClient == true && AppSettings.ClientAutoShutdown.ShutdownPlugins.Contains(plugin.Id)) { if (shutdownJobs.TryGetValue(game.PluginId, out var existingJob)) { existingJob.CancelToken.Cancel(); shutdownJobs.TryRemove(game.PluginId, out var _); } var newJob = new ClientShutdownJob { PluginId = plugin.Id, CancelToken = new CancellationTokenSource() }; var task = new Task(async () => { var ct = newJob.CancelToken; var libPlugin = plugin; var timeout = AppSettings.ClientAutoShutdown.GraceTimeout; var curTime = 0; logger.Info($"Scheduled {libPlugin.Name} to be closed after {timeout} seconds."); while (curTime < timeout) { if (ct.IsCancellationRequested) { logger.Debug($"Client {libPlugin.Name} shutdown canceled."); return; } await Task.Delay(1000); curTime++; } if (curTime >= timeout) { try { shutdownJobs.TryRemove(libPlugin.Id, out var _); libPlugin.Client.Shutdown(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to shutdown {libPlugin.Name} client."); } } }); newJob.CancelTask = task; shutdownJobs.TryAdd(plugin.Id, newJob); newJob.CancelTask.Start(); } } } } private void Controllers_Installed(object sender, GameInstalledEventArgs args) { var game = args.Source.Game; logger.Info($"Game {game.Name} installed."); var dbGame = Database.Games.Get(game.Id); dbGame.IsInstalling = false; dbGame.IsInstalled = true; if (args.InstalledInfo != null) { dbGame.InstallDirectory = args.InstalledInfo.InstallDirectory; if (args.InstalledInfo.Roms.HasItems()) { dbGame.Roms = args.InstalledInfo.Roms.ToObservable(); } } try { UpdateGameSize(dbGame, false, false, false); } catch { // Already logged } Database.Games.Update(dbGame); controllers.RemoveController(args.Source); } private void Controllers_InstallationCancelled(object sender, GameInstallationCancelledEventArgs args) { var game = args.Source.Game; logger.Info($"Game {game.Name} installation cancelled."); var dbGame = Database.Games.Get(game.Id); dbGame.IsInstalling = false; Database.Games.Update(dbGame); controllers.RemoveController(args.Source); } private void Controllers_Uninstalled(object sender, GameUninstalledEventArgs args) { var game = args.Source.Game; logger.Info($"Game {game.Name} uninstalled."); var dbGame = Database.Games.Get(game.Id); dbGame.IsUninstalling = false; dbGame.IsInstalled = false; dbGame.InstallDirectory = string.Empty; Database.Games.Update(dbGame); controllers.RemoveController(args.Source); } public bool ExecuteScriptAction( IPowerShellRuntime runtime, string script, Game game, bool execute, bool global, GameScriptType type, Dictionary vars = null) { if (!execute || script.IsNullOrWhiteSpace()) { return true; } try { if (runtime == null) { throw new Exception("Cannot execute script, no runtime given."); } if (!PowerShellRuntime.IsInstalled) { throw new Exception(resources.GetString("LOCErrorPowerShellNotInstalled")); } var scriptVars = new Dictionary { { "PlayniteApi", Application.PlayniteApiGlobal }, { "Game", game.GetCopy() } }; vars?.ForEach(a => scriptVars.AddOrUpdate(a.Key, a.Value)); var expandedScript = game.ExpandVariables(script); var dir = game.ExpandVariables(game.InstallDirectory, true); if (!dir.IsNullOrEmpty() && Directory.Exists(dir)) { runtime.Execute(expandedScript, dir, scriptVars); } else { runtime.Execute(expandedScript, variables: scriptVars); } return true; } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, global ? "Failed to execute global script." : "Failed to execute game script."); logger.Debug(script); var message = type.GetDescription() + Environment.NewLine + exc.Message; if (exc is ScriptRuntimeException scriptExc) { message = message + Environment.NewLine + Environment.NewLine + scriptExc.ScriptStackTrace; } Dialogs.ShowMessage( message, resources.GetString(global ? LOC.ErrorGlobalScriptAction : LOC.ErrorGameScriptAction), MessageBoxButton.OK, MessageBoxImage.Error); return false; } } public Tuple, List> GetPlayActions(Game game) { var controllers = new List(); var actions = new List(); foreach (var plugin in Extensions.Plugins.Values) { if (!game.IncludeLibraryPluginAction && plugin.Plugin.Id == game.PluginId) { continue; } try { var ctrls = plugin.Plugin.GetPlayActions(new GetPlayActionsArgs { Game = game })?.ToList(); if (ctrls.HasItems()) { controllers.AddRange(ctrls); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get play actions from {plugin.Description.Name}"); continue; } } if (game.GameActions.HasItems()) { var selectEmuAdded = false; var multipleRoms = game.Roms?.Count > 1; var romList = game.Roms.HasItems() ? game.Roms : new ObservableCollection { new GameRom() }; void addAction(string name, EmulatorProfile profile, EmulationPlayAction baseData) { foreach (var rom in romList) { var newAction = baseData.GetClone(); newAction.Name = multipleRoms ? $"{name}: {rom.Name}" : name; newAction.SelectedEmulatorProfile = profile; newAction.SelectedRomPath = rom.Path; actions.Add(newAction); } } foreach (var action in game.GameActions.Where(a => a.IsPlayAction)) { if (action.Type == GameActionType.Emulator) { if (action.EmulatorId == Guid.Empty) { if (selectEmuAdded) { continue; } var supportedEmus = game.GetCompatibleEmulators(Database); if (!supportedEmus.HasItems()) { continue; } selectEmuAdded = true; foreach (var supEmu in supportedEmus.OrderBy(a => a.Key.Name)) { var emu = supEmu.Key; var profiles = supEmu.Value; var profCount = profiles.Count; foreach (var profile in profiles.OrderBy(a => a.Name)) { addAction(profCount == 1 ? emu.Name : $"{emu.Name}: {profile.Name}", profile, new EmulationPlayAction { EmulatorId = emu.Id, EmulatorProfileId = profile.Id }); } } } else { var emu = Database.Emulators[action.EmulatorId]; if (emu == null) { continue; } if (action.EmulatorProfileId == null) { var profiles = game.GetCompatibleProfiles(emu); profiles.ForEach(profile => addAction($"{action.Name}: {profile.Name}", profile, action.GetClone())); } else { var prof = emu.AllProfiles.FirstOrDefault(a => a.Id == action.EmulatorProfileId); if (prof == null) { logger.Error($"Specified emulator config does't exists {action.EmulatorProfileId}"); } else { addAction(action.Name, prof, action.GetClone()); } } } } else { actions.Add(action); } } } return new Tuple, List>(controllers, actions); } public List GetInstallActions(Game game) { var allActions = new List(); foreach (var plugin in Extensions.Plugins.Values) { try { var actions = plugin.Plugin.GetInstallActions(new GetInstallActionsArgs { Game = game })?.ToList(); if (actions.HasItems()) { allActions.AddRange(actions); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get install actions from {plugin.Description.Name}"); } } return allActions; } public List GetUninstallActions(Game game) { var allActions = new List(); foreach (var plugin in Extensions.Plugins.Values) { try { var actions = plugin.Plugin.GetUninstallActions(new GetUninstallActionsArgs { Game = game })?.ToList(); if (actions.HasItems()) { allActions.AddRange(actions); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get install actions from {plugin.Description.Name}"); } } return allActions; } } } ================================================ FILE: source/Playnite/GlobalTaskHandler.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite { public static class GlobalTaskHandler { private static CancellationTokenSource cancelToken = new CancellationTokenSource(); public static CancellationTokenSource CancelToken { get => cancelToken; set { cancelToken?.Dispose(); cancelToken = value; } } public static Task ProgressTask; public static bool IsActive { get => ProgressTask?.Status == TaskStatus.Running || ProgressTask?.Status == TaskStatus.WaitingForActivation; } public static void CancelAndWait() { if (IsActive) { CancelToken?.Cancel(); ProgressTask?.Wait(); } } public static bool? CancelAndWait(int millisecondsTimeout) { if (IsActive) { CancelToken?.Cancel(); return ProgressTask?.Wait(millisecondsTimeout); } return null; } public async static Task CancelAndWaitAsync() { if (IsActive) { CancelToken?.Cancel(); await ProgressTask; } } } } ================================================ FILE: source/Playnite/GoogleImageDownloader.cs ================================================ using AngleSharp.Parser.Html; using Flurl; using Newtonsoft.Json; using Playnite.Common; using Playnite.SDK; using Playnite.WebView; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace Playnite { public class GoogleImage { [JsonProperty("ow")] public uint Width { get; set; } [JsonProperty("oh")] public uint Height { get; set; } [JsonProperty("ou")] public string ImageUrl { get; set; } [JsonProperty("tu")] public string ThumbUrl { get; set; } public string Size => $"{Width}x{Height}"; } public class GoogleImageDownloader : IDisposable { private static ILogger logger = LogManager.GetLogger(); private readonly OffscreenWebView webView; public GoogleImageDownloader() { webView = new OffscreenWebView(); } public void Dispose() { webView.Dispose(); } public async Task> GetImages(string searchTerm, SafeSearchSettings safeSearch, bool transparent = false) { var images = new List(); var parser = new HtmlParser(); var url = new Url(@"https://www.google.com/search"); url.SetQueryParam("tbm", "isch"); url.SetQueryParam("client", "firefox-b-d"); url.SetQueryParam("source", "lnt"); url.SetQueryParam("q", searchTerm); if (safeSearch == SafeSearchSettings.On) { url.SetQueryParam("safe", "on"); } else if (safeSearch == SafeSearchSettings.Off) { url.SetQueryParam("safe", "off"); } if (transparent) { url.SetQueryParam("tbs", "ic:trans"); } webView.NavigateAndWait(url.ToString()); if (webView.GetCurrentAddress().StartsWith(@"https://consent.google.com", StringComparison.OrdinalIgnoreCase)) { // This rejects Google's consent form for cookies await webView.EvaluateScriptAsync(@"document.getElementsByTagName('form')[0].submit();"); await Task.Delay(3000); webView.NavigateAndWait(url.ToString()); } var googleContent = await webView.GetPageSourceAsync(); if (googleContent.Contains(".rg_meta", StringComparison.Ordinal)) { var document = parser.Parse(googleContent); foreach (var imageElem in document.QuerySelectorAll(".rg_meta")) { images.Add(Serialization.FromJson(imageElem.InnerHtml)); } } else { var formatted = Regex.Replace(googleContent, @"\r\n?|\n", string.Empty); var matches = Regex.Matches(formatted, @"\[""(https:\/\/encrypted-[^,]+?)"",\d+,\d+\],\[""(http.+?)"",(\d+),(\d+)\]"); foreach (Match match in matches) { var data = Serialization.FromJson>>($"[{match.Value}]"); var imageUrl = data[1][0].ToString(); if (images.Any(a => a.ImageUrl.Equals(imageUrl, StringComparison.OrdinalIgnoreCase))) continue; images.Add(new GoogleImage { ThumbUrl = data[0][0].ToString(), ImageUrl = imageUrl, Height = uint.Parse(data[1][1].ToString()), Width = uint.Parse(data[1][2].ToString()) }); } } if (!images.HasItems()) { logger.Error("Failed to parse any Google image results."); logger.Debug(googleContent); } return images; } } } ================================================ FILE: source/Playnite/HdrUtilities.cs ================================================ using Playnite.Common; using Playnite.Native; using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite { public class HdrUtilities { private static readonly ILogger logger = LogManager.GetLogger(); /// /// Determines if HDR is supported on the primary display /// /// True if HDR is supported on the primary display, false if unknown or not supported public static bool IsHdrSupported() { if (Computer.WindowsVersion < WindowsVersion.Win10) { return false; } try { DISPLAYCONFIG_PATH_TARGET_INFO? targetInfo = GetPrimaryDisplayTargetInfo(); if (!targetInfo.HasValue) { logger.Error("Failed to retrieve primary display target info"); return false; } (bool isHdrSupported, _) = GetHdrStatus(targetInfo.Value); return isHdrSupported; } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to check if HDR is supported"); return false; } } /// /// Determines if HDR is enabled on the primary display /// /// True if HDR is enabled on the primary display, false if unknown or not supported public static bool IsHdrEnabled() { if (Computer.WindowsVersion < WindowsVersion.Win10) { return false; } try { DISPLAYCONFIG_PATH_TARGET_INFO? targetInfo = GetPrimaryDisplayTargetInfo(); if (!targetInfo.HasValue) { logger.Error("Failed to retrieve primary display target info"); return false; } (_, bool isHdrEnabled) = GetHdrStatus(targetInfo.Value); return isHdrEnabled; } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to check if HDR is enabled"); return false; } } /// /// Enables/disables HDR on the primary display /// /// True if enabling HDR, false if disabling HDR public static void SetHdrEnabled(bool enable) { if (Computer.WindowsVersion < WindowsVersion.Win10) { return; } try { DISPLAYCONFIG_PATH_TARGET_INFO? targetInfo = GetPrimaryDisplayTargetInfo(); if (!targetInfo.HasValue) { logger.Error("Failed to retrieve primary display target info"); return; } if (IsHdrSupported()) { //Enable or Disable HDR var newColorInfo = new DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE { header = { type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE, adapterId = targetInfo.Value.adapterId, id = targetInfo.Value.id, size = (uint)Marshal.SizeOf() }, enableAdvancedColor = enable }; User32.DisplayConfigSetDeviceInfo(ref newColorInfo.header); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to set HDR state"); } } private static bool IsPrimaryDisplayMode(DISPLAYCONFIG_MODE_INFO mode) { return mode.infoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE && mode.modeInfo.sourceMode.position.x == 0 && mode.modeInfo.sourceMode.position.y == 0; } private static DISPLAYCONFIG_PATH_TARGET_INFO? GetPrimaryDisplayTargetInfo() { var result = User32.GetDisplayConfigBufferSizes(QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, out uint pathCount, out uint modeCount); if (result != WinError.ERROR_SUCCESS) { logger.Error("Failed to retrieve display config buffer sizes"); return null; } var paths = new DISPLAYCONFIG_PATH_INFO[pathCount]; var modes = new DISPLAYCONFIG_MODE_INFO[modeCount]; result = User32.QueryDisplayConfig(QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero); if (result != WinError.ERROR_SUCCESS) { logger.Error("Failed to retrieve display config info"); return null; } var primaryDisplayMode = modes .FirstOrDefault(IsPrimaryDisplayMode); var targetInfo = paths.FirstOrDefault(p => p.sourceInfo.id == primaryDisplayMode.id) .targetInfo; return targetInfo; } private static (bool, bool) GetHdrStatus(DISPLAYCONFIG_PATH_TARGET_INFO targetInfo) { //Retrieve current HDR state var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO { header = { type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO, adapterId = targetInfo.adapterId, id = targetInfo.id, size = (uint)Marshal.SizeOf() } }; var result = User32.DisplayConfigGetDeviceInfo(ref colorInfo); if (result != WinError.ERROR_SUCCESS) { logger.Error("Failed to retrieve advanced color info"); return (false, false); } return (colorInfo.advancedColorSupported, colorInfo.advancedColorEnabled); } } } ================================================ FILE: source/Playnite/HotKey.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace Playnite { public class HotKey { public Key Key { get; } public ModifierKeys Modifiers { get; } public HotKey(Key key, ModifierKeys modifiers) { Key = key; Modifiers = modifiers; } public override string ToString() { var str = ""; if (Modifiers.HasFlag(ModifierKeys.Control)) { str += "Ctrl + "; } if (Modifiers.HasFlag(ModifierKeys.Shift)) { str += "Shift + "; } if (Modifiers.HasFlag(ModifierKeys.Alt)) { str += "Alt + "; } if (Modifiers.HasFlag(ModifierKeys.Windows)) { str += "Win + "; } return str += Key.ToString(); } } } ================================================ FILE: source/Playnite/HttpFileCache.cs ================================================ using Playnite.Common; using Playnite.Common.Web; using Playnite.SDK; using Playnite.Settings; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; namespace Playnite { public class HttpFileCache { private static ILogger logger = LogManager.GetLogger(); private static readonly object cacheLock = new object(); public static string CacheDirectory { get; set; } = PlaynitePaths.ImagesCachePath; private static string GetFileNameFromUrl(string url) { var uri = new Uri(url); var extension = Path.GetExtension(uri.Segments[uri.Segments.Length - 1]); var md5 = url.MD5(); return md5 + extension; } public static string GetWebFile(string url) { if (string.IsNullOrEmpty(url)) { return string.Empty; } var cacheFile = Path.Combine(CacheDirectory, GetFileNameFromUrl(url)); lock (cacheLock) { if (File.Exists(cacheFile) && (new FileInfo(cacheFile)).Length != 0) { logger.Debug($"Returning {url} from file cache {cacheFile}."); return cacheFile; } else { FileSystem.CreateDirectory(CacheDirectory); try { HttpDownloader.DownloadFile(url, cacheFile); return cacheFile; } catch (WebException e) { if (e.Response == null) { throw; } var response = (HttpWebResponse)e.Response; if (response.StatusCode != HttpStatusCode.NotFound) { throw; } else { return string.Empty; } } } } } public static void ClearCache(string url) { if (string.IsNullOrEmpty(url)) { return; } lock (cacheLock) { var cacheFile = Path.Combine(CacheDirectory, GetFileNameFromUrl(url)); if (File.Exists(cacheFile)) { logger.Debug($"Removing {url} from file cache: {cacheFile}"); try { FileSystem.DeleteFileSafe(cacheFile); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to remove {url} from cache."); } } } } } } ================================================ FILE: source/Playnite/ImageSourceManager.cs ================================================ using Playnite.Common; using Playnite.Database; using Playnite.SDK; using System; using System.Collections.Generic; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media.Imaging; namespace Playnite { public class ImageSourceManager { private static ILogger logger = LogManager.GetLogger(); private static GameDatabase database; public static MemoryCache Cache = new MemoryCache(Units.MegaBytesToBytes(100)); private const string btmpPropsFld = "bitmappros"; public static void SetDatabase(GameDatabase db) { if (database != null) { database.DatabaseFileChanged -= Database_DatabaseFileChanged; } database = db; database.DatabaseFileChanged += Database_DatabaseFileChanged; } private static void Database_DatabaseFileChanged(object sender, DatabaseFileEventArgs args) { if (args.EventType == FileEvent.Removed) { Cache.TryRemove(args.FileId, out var file); } } public static string GetImagePath(string source) { if (source.IsNullOrEmpty()) { return null; } if (source.StartsWith("resources:") || source.StartsWith("pack://")) { try { var imagePath = source; if (source.StartsWith("resources:")) { imagePath = source.Replace("resources:", "pack://application:,,,"); } return imagePath; } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to create bitmap from resources " + source); return null; } } if (StringExtensions.IsHttpUrl(source)) { try { var cachedFile = HttpFileCache.GetWebFile(source); if (string.IsNullOrEmpty(cachedFile)) { logger.Warn("Web file not found: " + source); return null; } return cachedFile; } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, $"Failed to create bitmap from {source} file."); return null; } } if (File.Exists(source)) { return source; } if (database == null) { logger.Error("Cannot load database image, database not found."); return null; } try { return database.GetFullFilePath(source); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, $"Failed to get bitmap from {source} database file."); return null; } } public static BitmapSource GetResourceImage(string resourceKey, bool cached, BitmapLoadProperties loadProperties = null) { if (cached && Cache.TryGet(resourceKey, out var image)) { BitmapLoadProperties existingMetadata = null; if (image.Metadata.TryGetValue(btmpPropsFld, out object metaValue)) { existingMetadata = (BitmapLoadProperties)metaValue; } if (existingMetadata?.MaxDecodePixelWidth == loadProperties?.MaxDecodePixelWidth) { return image.CacheObject as BitmapSource; } else { Cache.TryRemove(resourceKey); } } var resource = ResourceProvider.GetResource(resourceKey) as BitmapSource; if (loadProperties?.MaxDecodePixelWidth > 0 && resource?.PixelWidth > loadProperties?.MaxDecodePixelWidth) { resource = resource.GetClone(loadProperties); } if (cached && resource != null) { long imageSize = 0; try { imageSize = resource.GetSizeInMemory(); } catch (Exception e) { logger.Error(e, $"Failed to get image memory size: {resourceKey}"); } if (imageSize > 0) { Cache.TryAdd(resourceKey, resource, imageSize, new Dictionary { { btmpPropsFld, loadProperties } }); } } return resource; } public static BitmapSource GetImage(string source, bool cached, BitmapLoadProperties loadProperties = null) { if (DesignerTools.IsInDesignMode) { cached = false; } if (source.IsNullOrEmpty()) { return null; } if (cached && Cache.TryGet(source, out var image)) { BitmapLoadProperties existingMetadata = null; if (image.Metadata.TryGetValue(btmpPropsFld, out object metaValue)) { existingMetadata = (BitmapLoadProperties)metaValue; } if (existingMetadata == loadProperties) { return image.CacheObject as BitmapSource; } else { Cache.TryRemove(source); } } if (source.StartsWith("resources:") || source.StartsWith("pack://")) { try { var imagePath = source; if (source.StartsWith("resources:")) { imagePath = source.Replace("resources:", "pack://application:,,,"); } var streamInfo = Application.GetResourceStream(new Uri(imagePath)); using (var stream = streamInfo.Stream) { var imageData = BitmapExtensions.BitmapFromStream(stream, loadProperties); if (imageData != null) { if (cached) { Cache.TryAdd(source, imageData, imageData.GetSizeInMemory(), new Dictionary { { btmpPropsFld, loadProperties } }); } return imageData; } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to create bitmap from resources " + source); return null; } } if (StringExtensions.IsHttpUrl(source)) { try { var cachedFile = HttpFileCache.GetWebFile(source); if (string.IsNullOrEmpty(cachedFile)) { logger.Warn("Web file not found: " + source); return null; } return BitmapExtensions.BitmapFromFile(cachedFile, loadProperties); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, $"Failed to create bitmap from {source} file."); return null; } } if (File.Exists(source)) { try { var imageData = BitmapExtensions.BitmapFromFile(source, loadProperties); if (imageData != null) { if (cached) { Cache.TryAdd(source, imageData, imageData.GetSizeInMemory(), new Dictionary { { btmpPropsFld, loadProperties } }); } return imageData; } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to create bitmap from " + source); return null; } } try { if (database == null) { logger.Error("Cannot load database image, database not found."); return null; } try { var imageData = database.GetFileAsImage(source, loadProperties); if (imageData == null) { logger.Warn("Image not found in database: " + source); return null; } else { if (cached) { Cache.TryAdd(source, imageData, imageData.GetSizeInMemory(), new Dictionary { { btmpPropsFld, loadProperties } }); } return imageData; } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, $"Failed to get bitmap from {source} database file."); return null; } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to load image from database."); return null; } } } } ================================================ FILE: source/Playnite/Input/GameController.cs ================================================ using Playnite.Native; using Playnite.SDK; using Playnite.SDK.Events; using Playnite.Windows; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Interop; using static SDL2.SDL; namespace Playnite.Input { public class GameControllerGesture : InputGesture { public static event EventHandler ConfirmationBindingChanged; public static event EventHandler CancellationBindingChanged; private static ControllerInput confirmationBinding = ControllerInput.A; public static ControllerInput ConfirmationBinding { get => confirmationBinding; set { confirmationBinding = value; ConfirmationBindingChanged?.Invoke(null, EventArgs.Empty); } } private static ControllerInput cancellationBinding = ControllerInput.B; public static ControllerInput CancellationBinding { get => cancellationBinding; set { cancellationBinding = value; CancellationBindingChanged?.Invoke(null, EventArgs.Empty); } } private readonly ControllerInput button; public GameControllerGesture(ControllerInput button) { this.button = button; } public override bool Matches(object targetElement, InputEventArgs inputEventArgs) { if (inputEventArgs is GameControllerInputEventArgs args) { return args.ButtonState == ControllerInputState.Pressed && args.Button == button; } else { return false; } } } [System.Runtime.InteropServices.Guid("36CB2F69-F227-4165-8CEE-6C10BC575524")] public class GameControllerManager : IDisposable { public class InputState { public Stopwatch Watch { get; set; } = new Stopwatch(); public bool IsReSending { get; set; } = false; } private static readonly ILogger logger = LogManager.GetLogger(); public bool SimulateNavigationKeys { get; set; } = true; public bool SimulateAllKeys { get; set; } = false; public bool StandardProcessingEnabled { get; set; } = true; public event EventHandler ButtonChanged; public event EventHandler ControllerConnected; public event EventHandler ControllerDisconnected; public event EventHandler ControllersChanged; private readonly OnControllerButtonStateChangedArgs controllerButtonStateChangedArgs = new OnControllerButtonStateChangedArgs(); private readonly OnControllerConnectedArgs controllerConnectedArgs = new OnControllerConnectedArgs(); private readonly OnControllerDisconnectedArgs controllerDisconnectedArgs = new OnControllerDisconnectedArgs(); private readonly int resendDelay = 700; private readonly int resendRate = 80; private readonly Dictionary keyboardMap = new Dictionary() { { ControllerInput.A, Winuser.VK_RETURN }, { ControllerInput.B, 0 }, { ControllerInput.Back, 0 }, { ControllerInput.DPadDown, Winuser.VK_DOWN }, { ControllerInput.DPadLeft, Winuser.VK_LEFT }, { ControllerInput.DPadRight, Winuser.VK_RIGHT }, { ControllerInput.DPadUp, Winuser.VK_UP }, { ControllerInput.Guide, 0 }, { ControllerInput.LeftShoulder, 0 }, { ControllerInput.LeftStick, 0 }, { ControllerInput.LeftStickDown, Winuser.VK_DOWN }, { ControllerInput.LeftStickLeft, Winuser.VK_LEFT }, { ControllerInput.LeftStickRight, Winuser.VK_RIGHT }, { ControllerInput.LeftStickUp, Winuser.VK_UP }, { ControllerInput.RightShoulder, 0 }, { ControllerInput.RightStick, 0 }, { ControllerInput.RightStickDown, 0 }, { ControllerInput.RightStickLeft, 0 }, { ControllerInput.RightStickRight, 0 }, { ControllerInput.RightStickUp, 0 }, { ControllerInput.Start, 0 }, { ControllerInput.TriggerLeft, Winuser.VK_PRIOR }, { ControllerInput.TriggerRight, Winuser.VK_NEXT }, { ControllerInput.X, 0 }, { ControllerInput.Y, 0 } }; private readonly Dictionary keyWatches = new Dictionary() { { ControllerInput.A, new InputState() }, { ControllerInput.B, new InputState() }, { ControllerInput.Back, new InputState() }, { ControllerInput.DPadDown, new InputState() }, { ControllerInput.DPadLeft, new InputState() }, { ControllerInput.DPadRight, new InputState() }, { ControllerInput.DPadUp, new InputState() }, { ControllerInput.Guide, new InputState() }, { ControllerInput.LeftShoulder, new InputState() }, { ControllerInput.LeftStick, new InputState() }, { ControllerInput.LeftStickDown, new InputState() }, { ControllerInput.LeftStickLeft, new InputState() }, { ControllerInput.LeftStickRight, new InputState() }, { ControllerInput.LeftStickUp, new InputState() }, { ControllerInput.RightShoulder, new InputState() }, { ControllerInput.RightStick, new InputState() }, { ControllerInput.RightStickDown, new InputState() }, { ControllerInput.RightStickLeft, new InputState() }, { ControllerInput.RightStickRight, new InputState() }, { ControllerInput.RightStickUp, new InputState() }, { ControllerInput.Start, new InputState() }, { ControllerInput.TriggerLeft, new InputState() }, { ControllerInput.TriggerRight, new InputState() }, { ControllerInput.X, new InputState() }, { ControllerInput.Y, new InputState() } }; private readonly SynchronizationContext context; private readonly InputManager inputManager; private bool isDisposed = false; public class LoadedGameController : GamepadController { public IntPtr Controller { get; } public IntPtr Joystic { get; } public new bool Enabled { get; set; } public readonly Dictionary LastInputState = new Dictionary() { { ControllerInput.A, ControllerInputState.Released }, { ControllerInput.B, ControllerInputState.Released }, { ControllerInput.Back, ControllerInputState.Released }, { ControllerInput.DPadDown, ControllerInputState.Released }, { ControllerInput.DPadLeft, ControllerInputState.Released }, { ControllerInput.DPadRight, ControllerInputState.Released }, { ControllerInput.DPadUp, ControllerInputState.Released }, { ControllerInput.Guide, ControllerInputState.Released }, { ControllerInput.LeftShoulder, ControllerInputState.Released }, { ControllerInput.LeftStick, ControllerInputState.Released }, { ControllerInput.LeftStickDown, ControllerInputState.Released }, { ControllerInput.LeftStickLeft, ControllerInputState.Released }, { ControllerInput.LeftStickRight, ControllerInputState.Released }, { ControllerInput.LeftStickUp, ControllerInputState.Released }, { ControllerInput.RightShoulder, ControllerInputState.Released }, { ControllerInput.RightStick, ControllerInputState.Released }, { ControllerInput.RightStickDown, ControllerInputState.Released }, { ControllerInput.RightStickLeft, ControllerInputState.Released }, { ControllerInput.RightStickRight, ControllerInputState.Released }, { ControllerInput.RightStickUp, ControllerInputState.Released }, { ControllerInput.Start, ControllerInputState.Released }, { ControllerInput.TriggerLeft, ControllerInputState.Released }, { ControllerInput.TriggerRight, ControllerInputState.Released }, { ControllerInput.X, ControllerInputState.Released }, { ControllerInput.Y, ControllerInputState.Released } }; public LoadedGameController(IntPtr controller, IntPtr joystic, int instanceId, string path, string name) { Controller = controller; Joystic = joystic; InstanceId = instanceId; Path = path; Name = name; } } public readonly List Controllers = new List(); public GameControllerManager(InputManager input, List disabledList) { inputManager = input; context = SynchronizationContext.Current; for (int i = 0; i < SDL_NumJoysticks(); i++) { if (SDL_IsGameController(i) == SDL_bool.SDL_TRUE) { AddController(i, disabledList); } } } public void ProcessInputs() { if (isDisposed) { return; } SDL_GameControllerUpdate(); foreach (var controller in Controllers) { if (controller.Enabled) { ProcessState(controller); } } } public void AddController(int joyIndex, List disabledList) { if (isDisposed) { return; } var controller = SDL_GameControllerOpen(joyIndex); var joystick = SDL_GameControllerGetJoystick(controller); var con = new LoadedGameController(controller, joystick, SDL_JoystickInstanceID(joystick), SDL_JoystickPath(joystick), SDL_JoystickName(joystick)); con.Enabled = !disabledList.Contains(con.Path); Controllers.Add(con); logger.Info($"Added controller {con.InstanceId}, {con.Name}, {con.Path}"); controllerConnectedArgs.Controller = con; context.Send((a) => { ControllersChanged?.Invoke(this, EventArgs.Empty); ControllerConnected?.Invoke(this, controllerConnectedArgs); }, null); } public void RemoveController(int instanceId) { if (isDisposed) { return; } var controller = Controllers.FirstOrDefault(a => a.InstanceId == instanceId); if (controller == null) { return; } SDL_GameControllerClose(controller.Controller); Controllers.Remove(controller); logger.Info($"Removed controller {instanceId}, {controller.Name}, {controller.Path}"); controllerDisconnectedArgs.Controller = controller; context.Send((a) => { ControllersChanged?.Invoke(this, EventArgs.Empty); ControllerDisconnected?.Invoke(this, controllerDisconnectedArgs); }, null); } public void Dispose() { isDisposed = true; foreach (var controller in Controllers) { SDL_GameControllerClose(controller.Controller); } } private uint MapPadToKeyboard(ControllerInput input) { if (input == GameControllerGesture.ConfirmationBinding) { return Winuser.VK_RETURN; } else if (input == GameControllerGesture.CancellationBinding) { // I don't remember anymore why we don't map B to ESC, but it's probably because of some WPF FS mode hack BS return 0; } else { return keyboardMap[input]; } } private void ProcessState(LoadedGameController controller) { ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A), ControllerInput.A, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B), ControllerInput.B, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_BACK), ControllerInput.Back, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_GUIDE), ControllerInput.Guide, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER), ControllerInput.LeftShoulder, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK), ControllerInput.LeftStick, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER), ControllerInput.RightShoulder, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSTICK), ControllerInput.RightStick, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START), ControllerInput.Start, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X), ControllerInput.X, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y), ControllerInput.Y, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_DOWN), ControllerInput.DPadDown, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_LEFT), ControllerInput.DPadLeft, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_RIGHT), ControllerInput.DPadRight, controller); ProcessButtonState(SDL_GameControllerGetButton(controller.Controller, SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_DPAD_UP), ControllerInput.DPadUp, controller); ProcessAxisState(SDL_GameControllerGetAxis(controller.Controller, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT), ControllerInput.TriggerLeft, true, controller); ProcessAxisState(SDL_GameControllerGetAxis(controller.Controller, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT), ControllerInput.TriggerRight, true, controller); var state = SDL_GameControllerGetAxis(controller.Controller, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX); ProcessAxisState(state, ControllerInput.LeftStickLeft, false, controller); ProcessAxisState(state, ControllerInput.LeftStickRight, true, controller); state = SDL_GameControllerGetAxis(controller.Controller, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY); ProcessAxisState(state, ControllerInput.LeftStickUp, false, controller); ProcessAxisState(state, ControllerInput.LeftStickDown, true, controller); state = SDL_GameControllerGetAxis(controller.Controller, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX); ProcessAxisState(state, ControllerInput.RightStickLeft, false, controller); ProcessAxisState(state, ControllerInput.RightStickRight, true, controller); state = SDL_GameControllerGetAxis(controller.Controller, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY); ProcessAxisState(state, ControllerInput.RightStickUp, false, controller); ProcessAxisState(state, ControllerInput.RightStickDown, true, controller); } private bool IsButtonNotNavigation(ControllerInput button) { return button == ControllerInput.A || button == ControllerInput.B || button == ControllerInput.Back || button == ControllerInput.Guide || button == ControllerInput.LeftShoulder || button == ControllerInput.LeftStick || button == ControllerInput.None || button == ControllerInput.RightShoulder || button == ControllerInput.RightStick || button == ControllerInput.Start || button == ControllerInput.X || button == ControllerInput.Y; } private bool ShouldResendKey(ControllerInput button) { var state = keyWatches[button]; var sendInput = false; var elapsed = state.Watch.ElapsedMilliseconds; if (!state.Watch.IsRunning) { sendInput = true; state.Watch.Start(); } else { if (IsButtonNotNavigation(button)) { sendInput = false; } else { if (!state.IsReSending && elapsed < resendDelay) { sendInput = false; } else if (!state.IsReSending && elapsed > resendDelay) { state.IsReSending = true; state.Watch.Restart(); sendInput = true; } else { if (state.IsReSending && elapsed > resendRate) { state.Watch.Restart(); sendInput = true; } } } } return sendInput; } private void ResetButtonResend(ControllerInput button) { var state = keyWatches[button]; state.Watch.Reset(); state.IsReSending = false; } private void ProcessButtonState(byte currentState, ControllerInput button, LoadedGameController controller) { var pressed = false; if (currentState == 1) { pressed = true; } else if (currentState < 0) { logger.Error($"Failed to get controller button state: {SDL_GetError()}"); } if (IsButtonNotNavigation(button)) { var lastState = controller.LastInputState[button]; if (pressed && lastState == ControllerInputState.Released) { SendControllerInput(button, true, controller); SimulateKeyInput(MapPadToKeyboard(button), true); } else if (!pressed && lastState == ControllerInputState.Pressed) { SendControllerInput(button, false, controller); SimulateKeyInput(MapPadToKeyboard(button), false); } controller.LastInputState[button] = pressed ? ControllerInputState.Pressed : ControllerInputState.Released; } else { if (pressed && ShouldResendKey(button)) { SendControllerInput(button, true, controller); controller.LastInputState[button] = ControllerInputState.Pressed; SimulateKeyInput(MapPadToKeyboard(button), true); } else if (!pressed && controller.LastInputState[button] == ControllerInputState.Pressed) { ResetButtonResend(button); SendControllerInput(button, false, controller); controller.LastInputState[button] = ControllerInputState.Released; SimulateKeyInput(MapPadToKeyboard(button), false); } } } private void ProcessAxisState(short currentState, ControllerInput button, bool positive, LoadedGameController controller) { var pressed = false; // SDL2 docs: // The state is a value ranging from -32768 to 32767. Triggers, however, range from 0 to 32767 (they never return a negative value). if (button == ControllerInput.TriggerLeft || button == ControllerInput.TriggerRight) { pressed = currentState > 16383; } else { if (positive) { pressed = currentState > 16383; } else { pressed = currentState < -16383; } } if (pressed && ShouldResendKey(button)) { SendControllerInput(button, true, controller); controller.LastInputState[button] = ControllerInputState.Pressed; SimulateKeyInput(MapPadToKeyboard(button), true); } else if (!pressed && controller.LastInputState[button] == ControllerInputState.Pressed) { ResetButtonResend(button); SendControllerInput(button, false, controller); controller.LastInputState[button] = ControllerInputState.Released; SimulateKeyInput(MapPadToKeyboard(button), false); } } private void SendControllerInput(ControllerInput button, bool pressed, LoadedGameController controller) { context.Post((a) => { if (StandardProcessingEnabled) { if (InputManager.Current.PrimaryKeyboardDevice?.ActiveSource == null) { return; } var args = new GameControllerInputEventArgs(Key.None, pressed ? ControllerInputState.Pressed : ControllerInputState.Released, button); inputManager.ProcessInput(args); } controllerButtonStateChangedArgs.Controller = controller; controllerButtonStateChangedArgs.Button = button; controllerButtonStateChangedArgs.State = pressed ? ControllerInputState.Pressed : ControllerInputState.Released; ButtonChanged?.Invoke(null, controllerButtonStateChangedArgs); }, null); } private void SendKeyInput(uint key, bool pressed) { if (!StandardProcessingEnabled) { return; } if (key == 0) { return; } var windowHandle = IntPtr.Zero; context.Send((_) => { var window = WindowManager.CurrentWindow; if (window == null) { windowHandle = IntPtr.Zero; } else { var helper = new WindowInteropHelper(window); windowHandle = helper.Handle; } }, null); if (windowHandle == IntPtr.Zero) { return; } if (pressed) { User32.SendMessage(windowHandle, Winuser.WM_KEYDOWN, key, IntPtr.Zero); } else { User32.SendMessage(windowHandle, Winuser.WM_KEYUP, key, IntPtr.Zero); } } private void SimulateKeyInput(uint key, bool pressed) { if (!StandardProcessingEnabled) { return; } if (SimulateAllKeys || (SimulateNavigationKeys && IsKeysDirectionKey(key))) { SendKeyInput(key, pressed); } } private bool IsKeysDirectionKey(uint key) { switch (key) { case Winuser.VK_UP: case Winuser.VK_DOWN: case Winuser.VK_LEFT: case Winuser.VK_RIGHT: case Winuser.VK_PRIOR: case Winuser.VK_NEXT: return true; default: return false; } } } public class GameControllerInputEventArgs : KeyEventArgs { public ControllerInputState ButtonState { get; set; } public ControllerInput Button { get; set; } public GameControllerInputEventArgs(Key key, ControllerInputState state, ControllerInput button) : base(InputManager.Current.PrimaryKeyboardDevice, InputManager.Current.PrimaryKeyboardDevice.ActiveSource, Environment.TickCount, key) { ButtonState = state; Button = button; RoutedEvent = state == ControllerInputState.Pressed ? Keyboard.KeyDownEvent : Keyboard.KeyUpEvent; } } public class GameControllerInputBinding : InputBinding { public static readonly DependencyProperty ButtonProperty = DependencyProperty.Register( nameof(Button), typeof(ControllerInput), typeof(GameControllerInputBinding), new UIPropertyMetadata(ControllerInput.None, new PropertyChangedCallback(OnButtonPropertyChanged))); public ControllerInput Button { get { return (ControllerInput)GetValue(ButtonProperty); } set { SetValue(ButtonProperty, value); } } public override InputGesture Gesture { get { return base.Gesture as GameControllerGesture; } set { var gesture = value as GameControllerGesture; base.Gesture = gesture; } } public GameControllerInputBinding() { } public GameControllerInputBinding(ICommand command, ControllerInput button) { Command = command; Button = button; } private static void OnButtonPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var binding = (GameControllerInputBinding)d; binding.Gesture = new GameControllerGesture((ControllerInput)e.NewValue); } } } ================================================ FILE: source/Playnite/Input/MouseWheelGesture.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; namespace Playnite.Input { // Courtesy of https://social.msdn.microsoft.com/Forums/vstudio/en-US/b39b5d98-d039-4e83-8c65-ca434786d6af/mouse-wheel-input-binding?forum=wpf public class MouseWheelGesture : MouseGesture { public WheelDirection Direction { get; set; } public static MouseWheelGesture Up { get { return new MouseWheelGesture { Direction = WheelDirection.Up }; } } public static MouseWheelGesture Down { get { return new MouseWheelGesture { Direction = WheelDirection.Down }; } } public static MouseWheelGesture CtrlUp { get { return new MouseWheelGesture(ModifierKeys.Control) { Direction = WheelDirection.Up }; } } public static MouseWheelGesture CtrlDown { get { return new MouseWheelGesture(ModifierKeys.Control) { Direction = WheelDirection.Down }; } } public MouseWheelGesture() : base(MouseAction.WheelClick) { } public MouseWheelGesture(ModifierKeys modifiers) : base(MouseAction.WheelClick, modifiers) { } public override bool Matches(object targetElement, InputEventArgs inputEventArgs) { if (!base.Matches(targetElement, inputEventArgs)) { return false; } if (!(inputEventArgs is MouseWheelEventArgs)) { return false; } var args = (MouseWheelEventArgs)inputEventArgs; switch (Direction) { case WheelDirection.None: return args.Delta == 0; case WheelDirection.Up: return args.Delta > 0; case WheelDirection.Down: return args.Delta < 0; default: return false; } } public enum WheelDirection { None, Up, Down, } } } ================================================ FILE: source/Playnite/ItemSelector.cs ================================================ using Playnite.ViewModels; using Playnite.Windows; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public static class ItemSelector { public static bool SelectSingle(string header, string message, List> items, out TItem selected) { var result = new SingleItemSelectionViewModel( new SingleItemSelectionWindowFactory(), header, message). SelectItem(items, out TItem selectedItem); selected = selectedItem; return result; } public static bool SelectMultiple(string header, string message, List> items, out List selected) { var result = new MultiItemSelectionViewModel( new MultiItemSelectionWindowFactory(), header, message). SelectItem(items, out List selectedItems); selected = selectedItems; return result; } } } ================================================ FILE: source/Playnite/Localization/LocSource.xaml ================================================  English Playnite language Exit Filter Active Filter Disabled Additional filters Filters Filter Invalid Data Save Changes? Homepage at www.playnite.link Source Code at GitHub Create diag. package Send diag. information About Playnite Made by Josef Němec Assign Category Set Categories Add Category Checked - Assign category Unchecked - Remove category Indeterminate - No changes (when editing multiple games) No Category No Platform Whoops! Something went wrong… An unrecoverable error has occurred. If you would like to help us fix this issue, please briefly describe the actions taken before the crash, and then send diagnostic information. If you are online, the package will be uploaded to the Playnite server for analysis. Alternatively, you can click on the 'Report Crash' button to create a new GitHub issue and report the crash manually. Thank you for your help. Extension "{0}" caused an unrecoverable error. We recommend saving the log file and reporting the issue to extension's developer. If the issue keeps reoccurring, disable the extension. Extension "{0}" caused an unrecoverable error. We recommend reporting the issue to extension's developer. If the issue keeps reoccurring, disable the extension. Unknown extension or a theme caused an unrecoverable error. We recommend disabling 3rd party add-ons, isolating the problematic one and reporting the issue to add-on's developer. Unrecoverable error occurred. If you want to help us fix this issue, please send diagnostic information. Thank you. Disable extension Save log file Send diag. info Report Crash Restart Playnite Restart in Safe Mode Disabling all 3rd party extensions and using default theme. Exit Playnite Actions taken before the crash (in English): Library Manager Remove Game(s)? Cannot remove - Game or installer is running. Cannot uninstall - Game is running. Are you sure you want to remove {0}? Are you sure you want to remove {0} games? Are you sure you want to remove {0}? Selecting "add to exclusion list" option will prevent game from being imported again next time library is updated. Are you sure you want to remove {0} games? Selecting "add to exclusion list" option will prevent games from being imported again next time library is updated. Are you sure you want to remove {0} entries that are currently not in use? No unused fields found. Yes (add to exclusion list) There are unsaved changes in this section Updating game library format… Database update failed. Cannot update game library. {0} MBs of free space is required. GameError Cannot start game. '{0}' was not found in database. Cannot start game: {0} Cannot start action: {0} Cannot open game location: {0} Could not detect game install size: {0} Install size scan error There were {0} errors during install size scan Failed to create shortcut: {0} Failed to open manual: {0} Cannot install game: {0} Cannot un-install game: {0} No valid game startup actions found. When using emulator actions, make sure platform definitions match between the game and emulator configuration. Installation implementation is not available. The library plugin responsible for this game is disabled or not installed. Official metadata download is not available. No game is selected. Game's script execution failed. Application script execution failed. Global script execution failed. Emulator script execution failed. Play script action execution failed. PowerShell 3.0 or newer is not installed. Couldn't determine how to start the game. Enabled Disabled Remove Remove unused Rename Copy Add Default Icon Default Cover Image Default Background Image Finish Next Back DONE BACK CLEAR Clear Dismiss Dismiss All Import Name Author Module Series Version Last Played Most Played Play Count Install Size Folder Notes Added Date Added Modified Date Modified Website Path OK Save Close Cancel Confirm Reset Yes No Welcome Local User General Media Links Installation Actions Downloading… Downloading media… Loading… Type Profile Profiles Remove Download Search Resolution: Any Zoom List View Covers Grid View Details View Custom URL Special thanks License Contributors Exiting Playnite… Today Yesterday Monday Tuesday Wednesday Thursday Friday Saturday Sunday Past Week Past Month Past Year More than a year ago 0 to 100MB 100MB to 1GB 1GB to 5GB 5GB to 10GB 10GB to 20GB 20GB to 40GB 40GB to 100GB 100GB or more Import completed successfully. All Games Game Id Database Id Presets Column Columns Row Rows Couldn't get icon from Play action. There's no action of File type present. Only download missing metadata Enabling this option will skip downloading metadata for data fields that already contain information. Games selection Please select which games should to be updated with new metadata: All games from database All currently filtered games Selected games only No metadata fields selected No metadata fields are selected for download. Please select at least one, and enable at least one metadata provider for it. Official Store IGDB Please select which fields should be automatically populated by Playnite and which sources should be used to obtain the data from. Please consider clicking on the logo above and contribute updates to igdb.com database in order to improve data Playnite uses. Downloading metadata… Importing installed games… Importing {0} games… Importing emulated games from {0}… Downloading library updates… Scanning size of games in library… Scanning size of imported games… Library update finished Releasing resources… Configuration Settings… Platforms and Emulators Configure Emulators… Library Manager… Tools Download Metadata… Software Tools… Configure Integrations… Open 3rd Party Client 3rd Party Clients Update Game Library Cancel Library Update Update Emulated Folders Add Game Manually… Scan Automatically… Emulated Game… Microsoft Store application… About Playnite Send Feedback Switch to Fullscreen Mode Links Help Support on Patreon Support on Ko-fi User manual SDK Documentation Restart System Turn Off System Suspend System Hibernate System Lock System Log Out User Pick a Random Game Game fields to be displayed on details panel: Item spacing Draw grid item background Grid item border width Missing game icon source Missing game cover source Missing game background source Vertical spacing to game details Grid view details position Details view game list position Draw separator between panels Game cover image height Game list icon height Application font Monospaced font Filter panel position Explorer panel position Cover art rendering Target aspect ratio Following options also affect tile rendering in Fullscreen mode! Stretch mode DVD Box Epic Games Store GOG Galaxy 2.0 IGDB Square Steam Banner Steam vertical cover Twitch * Requires restart to apply Settings General Top panel Appearance Game Details Layout Advanced Fullscreen Input Performance Metadata Updating Search Backup Backup Library Data Restore Data Backup Import changes in library automatically Invalid database file location, proper file path must be set. Account name cannot be empty. Download metadata after importing games Launch Playnite minimized Launch Playnite when you start your computer Start closed to tray Failed to register Playnite to launch when computer starts. Launch in Fullscreen Mode Asynchronous image loading Can improve scrolling smoothness of game lists in exchange for slower image load times. Show game name if cover art is missing Show game names on Grid view Darken not installed games Show game icons on Details view list Show item count on group descriptions Show only assigned fields on filter and explorer panels Disable hardware acceleration Use when experiencing stuttering or similar UI issues Show hidden games in quick launch lists Affects Jump List and tray menu lists. Number of quick launch items Use game background image as window background Blur background High Quality Darken background Show on Grid view Theme Theme Profile Fullscreen Theme Fullscreen Theme Profile Database Location Login status: Playnite Settings Clear web cache May solve issues encountered while linking accounts. Show system tray icon Minimize Playnite to system tray Minimize Playnite to system tray when the application window is closed When game starts: After game closes: Format time played to indicate the number of days played Dates formats: This will log you out of all linked services. Application restart is required, do you want to proceed? Clear Cache? Playnite restart is required to apply new theme Get more themes Create new theme Get more extensions Create new extension Help us translate Playnite Playnite needs to be restarted in order to apply new settings. Restart now? Restarting will cancel any active tasks (downloads) currently in progress. Restart Playnite? Playnite cannot move your library files automatically. You must manually move/copy the files before changing the location. If there is no library in the target location, a new one will be created. The new database location will not be used until Playnite is restarted. Play time will not be recorded if "Close" action is set. Number of rows Number of columns Number of detail view rows Show Background Image on Main Screen Doesn't apply retrospectively to existing games without re-downloading metadata. Import playtime of games in library: Configures when should Playnite import the playtime reported by library plugins for games in the Playnite database. Support by the library plugins in charge of handling the game(s) is needed to be able to use this feature. Always: Imports playtime for new imported and existing games in Playnite database. Only for newly imported games: Imports playtime only for new imported games. Never: Never imports playtime under any circumstance. Always Only for newly imported games Never Enable controller support in Desktop Mode Guide button opens Fullscreen mode Automatic Metadata download settings for newly imported games. Target display Always use primary display Show Game Titles Show Battery Status Show Battery Percentage Show Clock Hide Mouse Cursor Installed Only in Quick Filters Button Prompts Layout Horizontal Scrolling Select one of the subsections No settings available Failed to load settings These scripts are executed for every game in the library. Individual scripts can be assigned to each game separately while editing game details. Animate background image transitions Font sizes Auto Aliased Grayscale ClearType Ideal Display Text formatting mode Text rendering mode Text rendering and formatting methods are currently not used for game descriptions. Preload background images If enabled, Playnite will download background artwork while downloading metadata, using more disk space and making artwork available when offline. If disabled, background artwork is downloaded only when first needed, using less space, but may result in a delay before artwork is displayed and some images might not be available when offline. Automatically close third party client after game exits Client shutdown delay (in seconds) Don't close after game sessions shorter than (in seconds) Automatically close following clients: Auto Close Clients Import Exclusion List Display warning when assigning too large game media Folder open command Preferred age rating organization Update install size of games on library update Scans and updates the install size of games if it is detected that their files have been modified since the last scan None Fill Uniform Uniform to fill Left Right Top Bottom Import Error Authentification required Authentification failed Alternative web view rendering mode Use when experiencing issues with web views, for example integration authentication dialogs. Partial loading of large game descriptions Large descriptions can cause noticeable lag when selecting games. When enabled, only part of description text will be initially loaded with an option to load the rest on demand. Metadata Import Download Metadata Set selected configuration to be used for any future metadata downloads. Can also be changed in application settings. Emulation Import Wizard This wizard will guide you through the process of downloading and importing console emulators and importing emulated games. Keep in mind that you can always add additional emulators and/or games later via main menu (under "Library" menu for Emulator settings and "Add Games" menu for emulated games). Below is a list of emulators that Playnite can recognize and configure automatically. You can download emulator installers from their websites. Once you have the emulators installed (manually), you can import them on emulator configuration dialog. You can import any emulators that are installed on your PC by clicking the 'Autodetect From Folder…' button. Playnite will search the selected folder for any known emulators and provide the option to import them. You can use this button multiple times to import emulators from different folders. Emulators will be added to the bottom of the current list. You can import games by clicking the 'Scan Folder Using Emulator' button. Selecting the appropriate emulator will tell Playnite which file types should be scanned and imported. You can use this button multiple times to import games from different folders. Games will be added to the bottom of the current list. There are no emulators selected for import. You won't be able to automatically import any emulated games without configuring emulators first. Are you sure you want to continue and exit import process? There are no emulators configured in Playnite. You cannot import games without first configuring the emulator and selecting the appropriate file types. Do you want to add some emulators now? Scan folder using Emulator Select files Autodetect From Folder… Configure Emulators… Scanning… Scanning {0}… First Time Configuration This process will guide you through an automatic import and configuration of external game libraries. Playnite can automatically import games from multiple game services, such as Steam or GOG. Keep in mind that you can also manually add any custom or emulated game for any platform later from main menu. Library Integration Following is the list of some curated library integrations Playnite supports. Please select ones you want to install. More integrations can be installed later from "Add-ons" menu. Configuration Finished The initial setup has been completed. Remember that you can change all settings later as well as add additional integrations from main menu. Failed to download one or more extensions. You can try to re-download integrations from add-ons menu after first run wizard finishes. Downloading {0} integration… Downloading list of recommended integrations… Failed to download list of recommended integrations. You can try and re-download integrations later via the Addons menu. Configure Platforms and Emulators Configure Emulators Platforms Platform Emulators Emulator Add Platform Select Icon Select Cover Select Image Select Item Select Background Select File Select URL Add Emulator Supported Platform(s) Do you want to save platform changes? Do you want to save emulator changes? Executable Arguments Working Directory Supported File Types Import Emulators… Download Emulators… Load arguments preset from known emulator profile Are you sure you want to remove {0} emulator? It's currently being used by {1} game(s). Are you sure you want to remove {0} platform? It's currently being used by {1} game(s) and {2} emulator(s). Settings help Sort By Sort Direction Group By Ascending Descending Don't group Group by Library Group by Category Group by Platform View Type View Explorer Panel Filter Panel Icon Library Icon Cover Image Background Image Sorting Name Library Manual Name Install Drive Account Name Platform Category Genre Release Date Release Year Developer Tag Publisher Installation Status Match all filters If enabled, only games that use all the items in all the filters will be included in the view. If disabled, games that use any item in any filter will be included in the view. Installed Installed Not installed Hidden Favorite Enable HDR Support If enabled, HDR will be enabled on the primary display before starting the game. Note that HDR is not supported on your primary display. Last Played Category Description Installation Folder Cover Image Links Image, ROM or ISO Path Genre Genres Company Companies Developer Developers Publisher Publishers Category Categories Tag Tags Feature Features Age Rating Age Ratings Region Regions Source Sources Recent Activity Database Error Failed to open library database. Database is not opened. Cannot access library database. File "{0}" is being used by another process or it's in inaccessible location. Failed to create diagnostics package. Failed to automatically upload diagnostics package. Diagnostics information was sent successfully. The diagnostics package has been created and submitted successfully. Please attach the following ID to your issue report: Failed to import games from {0}. Failed to import emulated games from {0}. Cannot search for games by selected emulator profile. Profile doesn't contain any file extensions or platforms. Playnite failed to start. Please close all other instances and try again. Failed to apply theme "{0}", color profile "{1}" {2} Cannot open link, URL is not in valid format. Failed to start the application. Failed to initialize web view component. Playnite cannot continue with startup process. More information at https://playnite.link/cefstartup Cannot import emulators due to missing or corrupted definition file. Failed to execute menu action. Edit Game Details Image URL Add Link Add ROM Save Changes Apply field changes to game(s) being edited. Add Action Remove Remove Play Action Add Games Scan Folder… Detect Installed Browse… Open Playnite Profile Settings Game name cannot be empty. Game action tracking directory cannot be empty. Game name cannot be empty before searching metadata. Invalid game data Enter valid web URL starting with http:// or https:// Select URL Failed to download metadata: {0} Download Error Clear Filters Private Account Public Account API Key Startup Error Theme Error Clear All Installing Uninstalling Launching Running Invalid URL Do nothing Minimize Restore window Restore window only when launched from UI Close Change Advanced Never Completion Status Completion Statuses User Score Critic Score Community Score Game scripts Application scripts Scripts Plugins Metadata Sources Extensions Extension ID Reload Scripts Interactive SDK PowerShell All scripts reloaded successfully. No games found for specified search/filter criteria No items found Switch to Desktop Mode Exit Playnite Libraries Update All Created By: Version: Updated: Module: Library Statistics All None Notifications Width Height Size Small Normal Large Larger Largest Default Select Select All Deselect All First Random User select Load more Transparent Collapse Expand Collapse All Expand All Other Themes Emulator Arguments Built-in Arguments Custom Arguments Additional Emulator Arguments Override Emulator Arguments Play action Select metadata to import Select Games to Import Metadata search Update Available Changes since last update Download and Install Update Check for Updates Update Error Failed to check for new version. No new version found, you are up to date. Failed to download and install update. Some background task is currently in progress. Do you want to cancel it and proceed with the update? Some background task is currently in progress. Do you want to cancel it and exit Playnite? Some background task is currently in progress. Switching modes will cancel the task, do you want to switch anyways? An update for Playnite is available Reload theme list Apply selected theme Watch file changes Automatically apply theme when the source file changes Script runtime Execute before starting a game Execute after exiting a game Execute after a game is started Execute on application start Execute on application shutdown Game starting script Game started script Game stopped script Execute global script Global Filtered Current New Test script Show only selected items. Save as default Add to Favorites Remove from Favorites Hide this game Remove from Hidden Enable HDR Support Disable HDR Support Edit… Calculate install size Calculate install size (All games) Calculate install size (Only missing data) Install size Set Category… Set Completion Status Remove Play Install Game Options Details Uninstall Open Installation Location Create Desktop Shortcut Open Manual More Managed by the library plugin The game starting process will be managed by the library plugin responsible for this game. No relevant information about the '{0}' game has been found on the specified page. Tip: You can use more advanced metadata download process while editing single game via "Edit" menu option. Not available when some action is in progress. Description text is HTML syntax sensitive Game time is recorded in seconds. Install size is indicated in bytes. Release date must be set in 'year-month-day' format. Month and Day values can be omitted. Values from 0 to 100 or empty for no score. Playnite's development is supported by these patrons and Ko-fi members: Code, localization and other contributors in no particular order: Cancel game monitoring? Installation monitoring is currently running. Do you want to cancel the process and return the game to the previous state? Game execution monitoring is currently running. Do you want to cancel the process and return the game to the previous state? Time Played Last Played {0}d {1}h {2}m {0}h {1}m {0} minutes {0} seconds Not Played Opening Desktop mode… Opening Fullscreen mode… Loading game library… Calculating install size… Calculating install size of {0}… Failed to install script file. Script installed successfully. Install Script Script error Failed to execute extension function. Open metadata folder Calculate Automatically calculates the install size using the ROMs if the game has any or the installation directory if it has been set {0} client is not installed. {0} client will now open. Please sign in and then close this message. Waiting for user to sign in, please close this when you're done… Game's installation folder not found. Invalid game action configuration. Troubleshooting account sync issues Troubleshooting issues Rename item Add new item Enter name Enter new name Less than an hour 1 to 10 hours 10 to 100 hours 100 to 500 hours 500 to 1000 hours Over 1000 hours Playnite must be restarted to complete the installation. Do you want to restart now? Extension is not packaged properly. Theme is not packaged properly. Extension "{0}" failed to load properly. Can't load "{0}" extension, current Playnite version is not supported. Theme "{0}" failed to load properly. Can't load "{0}" theme, current Playnite version is not supported. Extension failed to load properly. Theme failed to load properly. Theme/Extension is using unsupported API version. Installation was successful. Install add-on? Generic Failed to install "{0}" add-on. Failed to install extension. {0} Do you want to install a new extension? {0} By {1} Version {2} Do you want to update "{0}" extension? Current version: {1} New version: {2} Failed to install theme. {0} Do you want to install a new theme? {0} By {1} Version {2} Do you want to update "{0}" theme? Current version: {1} New version: {2} You are about to leave Playnite and navigate to the following web page using your default web browser. Do you want to continue? {0} The selected image(s) might be too large for optimal performance. Using very large images can result in worse UI responsiveness and increased memory usage. Maximum recommended resolutions: Icons: {0} mega pixels Covers: {1} mega pixels Backgrounds: {2} mega pixels Performance Warning Don't Show Again File with extension {0} is not compatible. Incompatible file extension Selected image file might be too large for optimal performance. Are you sure you want to uninstall selected theme? Uninstallation will be queued to next application start. Built-in themes can't be uninstalled. This theme doesn't support this version of Playnite. Are you sure you want to uninstall selected extension? Uninstallation will be queued to next application start. Built-in extensions can't be uninstalled. This extension doesn't support this version of Playnite. Installation folder Data folder Generating diagnostics package… Uploading diagnostics package… Import file… What is this? Are you sure you want to do this? Total play time Average play time Top play time Total install size Overview Sidebar Show on Sidebar Reset settings All application settings will be reset to default values, excluding: - Database location - Import exclusion list - Extension settings, including library integrations Application restart is requred to finish the process. Do you want to reset settings? For developers External extensions Enter full folder path. Achievements Forum News Store Page The initial setup is not complete. Playnite will now restart to Desktop Mode to finish the procedure. Recently Played Favorites Most Played All There are filters applied. There are additional filters applied. Showing search results for: An item with the same name already exists. Limit selection to current filter Pick another Add-ons… Installed Extensions settings Browse Updates Updates ({0}) Management of installed extensions and themes, including their settings, has been moved to a new "Add-ons" menu. All currently installed library integration extensions can be configured here. If you want to install or uninstall additional integrations, use "Add-ons" option from main menu. Themes Desktop Themes Fullscreen Searching… Add-on is not compatible with this version of Playnite. Failed to download add-on installation package. Failed to download add-on installation manifest. Application restart is required to apply pending changes. This add-on is scheduled for installation. Install Re-install Uninstall Already installed No new add-on updates found. Update add-ons Changelog is not available Scheduled for installation Download failed License rejected Downloading {0}… Looking for add-on updates… Looking for program updates… One or more add-on updates are available. Select items to update Extension development instance {0} license agreement Accept Decline Include library integration play actions Select action Tracking Mode Tracking Path Initial tracking delay Tracking frequency Link File Emulator Script Default Process Folder Original process Process name Log trace messages Following changes overwrite data for all currently selected games! None Uniform Items only Start and end only Scrolling sensitivity Smooth scrolling Animation speed Remove item? Are you sure you want to remove this item? Show buttons on top panel: General view settings Grouping settings Sorting settings Filter presets Plugin items position Section separator width Move main menu button to the sidebar Explorer panel Random game picker Views random game selector Select random game from the view Save grouping and sorting settings Show as quick filter in Fullscreen mode In past 7 days In past 31 days In past 365 days More than 365 days ago Configure Save preset Minimize after starting game Minimize Playnite after a game is started. Disabling this can lead to issues with games not getting input focus on startup! Font Size Font Size Small Enable game controller API support Game controller support If disabled, Playnite won't accept any game controller inputs. Disable if you use tools that translates game controller inputs to mouse/keyboards inputs and you are getting double inputs in Playnite. Show items on main menu: Inverted X/A main view button binding Swaps button bindings for starting a game and showing game details page on main view. Swap confirmation/cancelation button binding Inverts A/B button bindings for confirmation and cancellation. Primary controller only Only accept inputs from primary controller when enabled. Guide button focuses Playnite Interface volume Background volume Mute when in background Failed to initialize audio interface. Output API API used for audio output. Change if you are experiencing issues with sound. General Visuals Audio Layout Menus Input {0} is starting… {0} is running… Caps Space Image rendering scaler Alternative Balanced Quality Quality: Best image quality, slow, high memory usage. Balanced: Good quality, fast, low memory usage. Alternative: Better quality, medium speed, low memory usage. Select file… Select folder… Startup script Please note that both extensions and themes can greatly affect Playnite's performance, stability and security. If you start experiencing some issues after installing a theme or an extension, try disabling/uninstalling them first to see if they are root of the issue. Choose on startup Choose on startup Built-in profiles Built-in profile Custom profiles Custom profile Handled by a built-in script Emulator specification Platform specification Region specification Execute before starting emulator Execute after emulator is started Execute after exiting emulator Emulator executable not found. Emulator specification not found. Emulator startup script not found. Split as separate games Merge into one game Set platform Set region Scan folder Scan configurations Exclude patterns from checksum scan Files matching specified pattern(s) won't be scanned for checksum and will be matched by file name. See emulator help page for more information. Scan with emulator Name has to be set when saving new configuration. Emulator or emulator profile is not set. Directory to scan is not specified or it doesn't exist. Scan configuration is not set properly. Include in bulk scan auto-scan Failed to scan folder for emulators. Failed to scan folder(s) for emulated games. Hide imported Profiles to import: Auto-scan configurations Save as auto-scan configuration Saves configuration for later use during library update. Saved configurations can be managed via "Configure Emulators" menu. Import using relative paths If possible import game files using paths relative to Playnite's installation folder or emulator's installation folder. Scan subfolders Scan inside archives Merge related files Merge related game files, like individual game discs, under one game entry. Add scanner Add saved scanner Start scan Add scan configuration(s) with emulators to scan specific folders. Make sure that emulators are properly configured prior to importing games (via Library -> Configure Emulators menu). Default status assigned to newly added games Status assigned to games played for the first time Failed to initialize PowerShell script runtime. If you are Windows 7 user, try (re)installing PowerShell 5.1 to fix the issue. Filter preset with specified name already exists. Update preset with new settings? Automatically fill missing sorting names for batch-added or edited games When you edit a game, add games via a library update, an emulator folder scan, or a normal folder scan, automatically fill the "Sorting Name" field with a better sortable representation of the game's name. For example "The Witcher 3" will get a Sorting Name of "Witcher 03". This will never set a sorting name that doesn't differ from the game name, and it will only automatically update sorting names that are empty. These words will be removed from the start of the automatically filled Sorting Name value: Use this for ignoring words at the start of a string for sorting purposes. The default is "The", "An", and "A". Fill Sorting Name for games without one Sorting Filling Sorting Name values… Nahimic service has been detected to be running on your system. This service is known to cause rendering issues to Playnite (and other apps). If you encounter any graphics corruption or other rendering issues in Playnite, we recommend disabling or completely uninstalling Nahimic service. More information at https://playnite.link/nahimicsucks Playnite is running with elevated privileges (as an administrator). This is not recommended since it gives elevated privileges to all installed extensions and all games/apps started from Playnite! More information at https://playnite.link/adminfaq Show warning if Playnite is running with elevated privileges Get the real size on drive when calculating the size of games If enabled, scans will be slower and will get the real size that files use in the drive. If disabled, scans will be faster and will use the size of the files themselves. Following add-on(s) have been reported as potentially problematic, either due to high stability/performance impact or security issues. We strongly recommend that you uninstall them: {0} Exclude online files from scan Files stored on cloud storage won't be scanned and imported if not available locally. Supported only for: Google Drive, DropBox, OneDrive Scan but using simplified method without file content Files will be imported but using less accurate method that doesn't require file content to be downloaded and present locally. Apply to all Override installation state When set, Playnite will ignore installation state (including installation directory) set by the integration plugin that imports this game. This option may not fully work with plugins that use specific game import method unless they also take this override option into account. Only manually Once a day Once a week On every startup Check for program updates Check for add-on updates Update libraries Scan emulation folders Include hidden games Edit fields Select / Deselect all Open Activate Assign Start typing to search for games… [F1] for help Starting with # brings up a list of available commands. Starting with / brings up a list of available search providers/plugins. Typing search keyword and ending with SPACE switches immediately to that search. TAB: switch action ENTER: activate selected action SHIFT-ENTER: open item menu Include uninstalled games Include hidden games Uninstalled games included Uninstalled games excluded Hidden games included Hidden games excluded Play or Install Go to details Game menu Edit game Open search Search box Search button Primary game action Secondary game action CTRL-F opens global search instead of focusing search box Save game filter settings between search sessions Search providers Default keyword Custom keyword System wide shortcut Playnite search Extension Settings Exclusions Excluded files relative to scan folder Excluded folders relative to scan folder Add file to exclusion list Add folder to exclusion list Exclusions can be only added to saved scanner configurations. Exclusions have been added to "{0}" scanner. Override platform When set, scanner will assign this platform to all games, overwriting any automatically detected platforms. Include commands in default search When disabled, commands won't be included in default search until # prefix is used. Use fuzzy matching in name filter When enabled, name filter will match game names the same way as global search. Strict matching can be enforced on an individual case by prefixing filter with ! character. Fields to be displayed for game results: Hidden Status Data backup was cancelled. Data backup failed. Data backup error Data backup in progress… Restoring data from backup… Failed to restore data from backup. Settings Game library Game library media Installed extensions Extensions data Installed themes Select data to be restored from specified backup file. Playnite will automatically restart to start backup restore process. Select items to be included with data backup. Application settings and game library data are included by default. Playnite will automatically restart to start backup process. Automatic data backup Auto backup frequency Backup folder Rotating backups Include additional data: Backup folder needs to be set if auto backup is enabled. Show notifications for patch releases only When enabled, only updates available for currently installed major release will result in update notification. New major releases will not result in update notification. Use relative dates for the past week Use relative dates in "Today", "Yesterday" etc. format if the date is less than a week old. The specified date format will be used for all other dates. Web image search Icon image search string Cover image search string Background image search string Getting add-on information… No metadata source is available Play action settings Use scanner settings Select profile on startup Select emulator on startup Automatic Always on Always off Accessibility (screen reader) support Application menu Game menu Program folder User data directory Library file corruption has been detected, Playnite will now shutdown. Open new issue on Playnite's GitHub page with a request to fix corruption in your files. Do you want to save changes you made? Portable installation No controllers detected ================================================ FILE: source/Playnite/Localization/LocalizationKeys.cs ================================================ /// /// DO NOT MODIFY! Automatically generated via buildLocConstants.ps1 script. /// namespace Playnite { public static class LOC { /// /// English /// public const string LanguageName = "LanguageName"; /// /// Playnite language /// public const string LanguageSettingsLabel = "LOCLanguageSettingsLabel"; /// /// Exit /// public const string ExitAppLabel = "LOCExitAppLabel"; /// /// Filter Active /// public const string FilterActiveLabel = "LOCFilterActiveLabel"; /// /// Filter Disabled /// public const string FilterInactiveLabel = "LOCFilterInactiveLabel"; /// /// Additional filters /// public const string AditionalFilters = "LOCAditionalFilters"; /// /// Filters /// public const string Filters = "LOCFilters"; /// /// Filter /// public const string Filter = "LOCFilter"; /// /// Invalid Data /// public const string InvalidDataTitle = "LOCInvalidDataTitle"; /// /// Save Changes? /// public const string SaveChangesAskTitle = "LOCSaveChangesAskTitle"; /// /// Homepage at www.playnite.link /// public const string AboutHomePageLink = "LOCAboutHomePageLink"; /// /// Source Code at GitHub /// public const string AboutSourceLink = "LOCAboutSourceLink"; /// /// Create diag. package /// public const string AboutCreateDiagButton = "LOCAboutCreateDiagButton"; /// /// Send diag. information /// public const string AboutSendDiagButton = "LOCAboutSendDiagButton"; /// /// About Playnite /// public const string AboutWindowTitle = "LOCAboutWindowTitle"; /// /// Made by Josef Němec /// public const string AboutAuthor = "LOCAboutAuthor"; /// /// Assign Category /// public const string CategoryWindowTitle = "LOCCategoryWindowTitle"; /// /// Set Categories /// public const string CategorySetButton = "LOCCategorySetButton"; /// /// Add Category /// public const string CategoryAddCatButton = "LOCCategoryAddCatButton"; /// /// Checked - Assign category /// public const string CategoryTooltip = "LOCCategoryTooltip"; /// /// No Category /// public const string NoCategory = "LOCNoCategory"; /// /// No Platform /// public const string NoPlatform = "LOCNoPlatform"; /// /// Whoops! Something went wrong… /// public const string CrashWindowTitle = "LOCCrashWindowTitle"; /// /// An unrecoverable error has occurred. /// public const string CrashDescription = "LOCCrashDescription"; /// /// Extension "{0}" caused an unrecoverable error. /// public const string ExtCrashDescription = "LOCExtCrashDescription"; /// /// Extension "{0}" caused an unrecoverable error. /// public const string ExtCrashDescriptionFS = "LOCExtCrashDescriptionFS"; /// /// Unknown extension or a theme caused an unrecoverable error. /// public const string ExtCrashDescriptionUknown = "LOCExtCrashDescriptionUknown"; /// /// Unrecoverable error occurred. /// public const string CrashDescriptionFullscreen = "LOCCrashDescriptionFullscreen"; /// /// Disable extension /// public const string CrashDisableExtension = "LOCCrashDisableExtension"; /// /// Save log file /// public const string CrashSaveLog = "LOCCrashSaveLog"; /// /// Send diag. info /// public const string CrashSendDiag = "LOCCrashSendDiag"; /// /// Report Crash /// public const string CrashReportIssue = "LOCCrashReportIssue"; /// /// Restart Playnite /// public const string CrashRestartPlaynite = "LOCCrashRestartPlaynite"; /// /// Restart in Safe Mode /// public const string CrashRestartSafe = "LOCCrashRestartSafe"; /// /// Disabling all 3rd party extensions and using default theme. /// public const string CrashRestartSafeTooltip = "LOCCrashRestartSafeTooltip"; /// /// Exit Playnite /// public const string CrashClosePlaynite = "LOCCrashClosePlaynite"; /// /// Actions taken before the crash (in English): /// public const string CrashUserActionsDescription = "LOCCrashUserActionsDescription"; /// /// Library Manager /// public const string LibraryManager = "LOCLibraryManager"; /// /// Remove Game(s)? /// public const string GameRemoveAskTitle = "LOCGameRemoveAskTitle"; /// /// Cannot remove - Game or installer is running. /// public const string GameRemoveRunningError = "LOCGameRemoveRunningError"; /// /// Cannot uninstall - Game is running. /// public const string GameUninstallRunningError = "LOCGameUninstallRunningError"; /// /// Are you sure you want to remove {0}? /// public const string GameRemoveAskMessage = "LOCGameRemoveAskMessage"; /// /// Are you sure you want to remove {0} games? /// public const string GamesRemoveAskMessage = "LOCGamesRemoveAskMessage"; /// /// Are you sure you want to remove {0}? /// public const string GameRemoveAskMessageIgnoreOption = "LOCGameRemoveAskMessageIgnoreOption"; /// /// Are you sure you want to remove {0} games? /// public const string GamesRemoveAskMessageIgnoreOption = "LOCGamesRemoveAskMessageIgnoreOption"; /// /// Are you sure you want to remove {0} entries that are currently not in use? /// public const string RemoveUnusedFieldsAskMessage = "LOCRemoveUnusedFieldsAskMessage"; /// /// No unused fields found. /// public const string RemoveUnusedFieldsNoUnusedMessage = "LOCRemoveUnusedFieldsNoUnusedMessage"; /// /// Yes (add to exclusion list) /// public const string RemoveAskAddToExlusionListYesResponse = "LOCRemoveAskAddToExlusionListYesResponse"; /// /// There are unsaved changes in this section /// public const string GameEditChangeNotif = "LOCGameEditChangeNotif"; /// /// Updating game library format… /// public const string DBUpgradeProgress = "LOCDBUpgradeProgress"; /// /// Database update failed. /// public const string DBUpgradeFail = "LOCDBUpgradeFail"; /// /// Cannot update game library. {0} MBs of free space is required. /// public const string DBUpgradeEmptySpaceFail = "LOCDBUpgradeEmptySpaceFail"; /// /// GameError /// public const string GameError = "LOCGameError"; /// /// Cannot start game. '{0}' was not found in database. /// public const string GameStartErrorNoGame = "LOCGameStartErrorNoGame"; /// /// Cannot start game: {0} /// public const string GameStartError = "LOCGameStartError"; /// /// Cannot start action: {0} /// public const string GameStartActionError = "LOCGameStartActionError"; /// /// Cannot open game location: {0} /// public const string GameOpenLocationError = "LOCGameOpenLocationError"; /// /// Could not detect game install size: {0} /// public const string CalculateGameSizeError = "LOCCalculateGameSizeError"; /// /// Install size scan error /// public const string CalculateGameSizeErrorCaption = "LOCCalculateGameSizeErrorCaption"; /// /// There were {0} errors during install size scan /// public const string CalculateGamesSizeErrorMessage = "LOCCalculateGamesSizeErrorMessage"; /// /// Failed to create shortcut: {0} /// public const string GameShortcutError = "LOCGameShortcutError"; /// /// Failed to open manual: {0} /// public const string ManualOpenError = "LOCManualOpenError"; /// /// Cannot install game: {0} /// public const string GameInstallError = "LOCGameInstallError"; /// /// Cannot un-install game: {0} /// public const string GameUninstallError = "LOCGameUninstallError"; /// /// No valid game startup actions found. When using emulator actions, make sure platform definitions match between the game and emulator configuration. /// public const string ErrorNoPlayAction = "LOCErrorNoPlayAction"; /// /// Installation implementation is not available. /// public const string ErrorNoInstallAction = "LOCErrorNoInstallAction"; /// /// The library plugin responsible for this game is disabled or not installed. /// public const string ErrorLibraryPluginNotFound = "LOCErrorLibraryPluginNotFound"; /// /// Official metadata download is not available. /// public const string ErrorNoMetadataDownloader = "LOCErrorNoMetadataDownloader"; /// /// No game is selected. /// public const string ErrorNoGameSelected = "LOCErrorNoGameSelected"; /// /// Game's script execution failed. /// public const string ErrorGameScriptAction = "LOCErrorGameScriptAction"; /// /// Application script execution failed. /// public const string ErrorApplicationScript = "LOCErrorApplicationScript"; /// /// Global script execution failed. /// public const string ErrorGlobalScriptAction = "LOCErrorGlobalScriptAction"; /// /// Emulator script execution failed. /// public const string ErrorEmulatorScriptAction = "LOCErrorEmulatorScriptAction"; /// /// Play script action execution failed. /// public const string ErrorPlayScriptAction = "LOCErrorPlayScriptAction"; /// /// PowerShell 3.0 or newer is not installed. /// public const string ErrorPowerShellNotInstalled = "LOCErrorPowerShellNotInstalled"; /// /// Couldn't determine how to start the game. /// public const string ErrorStartupNoController = "LOCErrorStartupNoController"; /// /// Enabled /// public const string EnabledTitle = "LOCEnabledTitle"; /// /// Disabled /// public const string DisabledTitle = "LOCDisabledTitle"; /// /// Remove /// public const string RemoveTitle = "LOCRemoveTitle"; /// /// Remove unused /// public const string RemoveUnusedTitle = "LOCRemoveUnusedTitle"; /// /// Rename /// public const string RenameTitle = "LOCRenameTitle"; /// /// Copy /// public const string CopyTitle = "LOCCopyTitle"; /// /// Add /// public const string AddTitle = "LOCAddTitle"; /// /// Default Icon /// public const string DefaultIconTitle = "LOCDefaultIconTitle"; /// /// Default Cover Image /// public const string DefaultCoverTitle = "LOCDefaultCoverTitle"; /// /// Default Background Image /// public const string DefaultBackgroundTitle = "LOCDefaultBackgroundTitle"; /// /// Finish /// public const string FinishLabel = "LOCFinishLabel"; /// /// Next /// public const string NextLabel = "LOCNextLabel"; /// /// Back /// public const string BackLabel = "LOCBackLabel"; /// /// DONE /// public const string DoneCapLabel = "LOCDoneCapLabel"; /// /// BACK /// public const string BackCapLabel = "LOCBackCapLabel"; /// /// CLEAR /// public const string ClearCapLabel = "LOCClearCapLabel"; /// /// Clear /// public const string ClearLabel = "LOCClearLabel"; /// /// Dismiss /// public const string Dismiss = "LOCDismiss"; /// /// Dismiss All /// public const string DismissAll = "LOCDismissAll"; /// /// Import /// public const string ImportLabel = "LOCImportLabel"; /// /// Name /// public const string NameLabel = "LOCNameLabel"; /// /// Author /// public const string AuthorLabel = "LOCAuthorLabel"; /// /// Module /// public const string ModuleLabel = "LOCModuleLabel"; /// /// Series /// public const string SeriesLabel = "LOCSeriesLabel"; /// /// Version /// public const string VersionLabel = "LOCVersionLabel"; /// /// Last Played /// public const string LastPlayedLabel = "LOCLastPlayedLabel"; /// /// Most Played /// public const string MostPlayedLabel = "LOCMostPlayedLabel"; /// /// Play Count /// public const string PlayCountLabel = "LOCPlayCountLabel"; /// /// Install Size /// public const string InstallSizeLabel = "LOCInstallSizeLabel"; /// /// Folder /// public const string FolderLabel = "LOCFolderLabel"; /// /// Notes /// public const string NotesLabel = "LOCNotesLabel"; /// /// Added /// public const string AddedLabel = "LOCAddedLabel"; /// /// Date Added /// public const string DateAddedLabel = "LOCDateAddedLabel"; /// /// Modified /// public const string ModifiedLabel = "LOCModifiedLabel"; /// /// Date Modified /// public const string DateModifiedLabel = "LOCDateModifiedLabel"; /// /// Website /// public const string WebsiteLabel = "LOCWebsiteLabel"; /// /// Path /// public const string PathLabel = "LOCPathLabel"; /// /// OK /// public const string OKLabel = "LOCOKLabel"; /// /// Save /// public const string SaveLabel = "LOCSaveLabel"; /// /// Close /// public const string CloseLabel = "LOCCloseLabel"; /// /// Cancel /// public const string CancelLabel = "LOCCancelLabel"; /// /// Confirm /// public const string ConfirmLabel = "LOCConfirmLabel"; /// /// Reset /// public const string ResetLabel = "LOCResetLabel"; /// /// Yes /// public const string YesLabel = "LOCYesLabel"; /// /// No /// public const string NoLabel = "LOCNoLabel"; /// /// Welcome /// public const string WelcomeLabel = "LOCWelcomeLabel"; /// /// Local User /// public const string LocalUserLabel = "LOCLocalUserLabel"; /// /// General /// public const string GeneralLabel = "LOCGeneralLabel"; /// /// Media /// public const string MediaLabel = "LOCMediaLabel"; /// /// Links /// public const string LinksLabel = "LOCLinksLabel"; /// /// Installation /// public const string InstallationLabel = "LOCInstallationLabel"; /// /// Actions /// public const string ActionsLabel = "LOCActionsLabel"; /// /// Downloading… /// public const string DownloadingLabel = "LOCDownloadingLabel"; /// /// Downloading media… /// public const string DownloadingMediaLabel = "LOCDownloadingMediaLabel"; /// /// Loading… /// public const string LoadingLabel = "LOCLoadingLabel"; /// /// Type /// public const string TypeLabel = "LOCTypeLabel"; /// /// Profile /// public const string ProfileLabel = "LOCProfileLabel"; /// /// Profiles /// public const string ProfilesLabel = "LOCProfilesLabel"; /// /// Remove /// public const string RemoveLabel = "LOCRemoveLabel"; /// /// Download /// public const string DownloadLabel = "LOCDownloadLabel"; /// /// Search /// public const string SearchLabel = "LOCSearchLabel"; /// /// Resolution: /// public const string SearchResolutionLabel = "LOCSearchResolutionLabel"; /// /// Any /// public const string SearchResolutionAnyLabel = "LOCSearchResolutionAnyLabel"; /// /// Zoom /// public const string ZoomLabel = "LOCZoomLabel"; /// /// List View /// public const string ListViewLabel = "LOCListViewLabel"; /// /// Covers /// public const string CoversLabel = "LOCCoversLabel"; /// /// Grid View /// public const string GridViewLabel = "LOCGridViewLabel"; /// /// Details View /// public const string DetailsViewLabel = "LOCDetailsViewLabel"; /// /// Custom /// public const string CustomLabel = "LOCCustomLabel"; /// /// URL /// public const string URLLabel = "LOCURLLabel"; /// /// Special thanks /// public const string SpecialThanks = "LOCSpecialThanks"; /// /// License /// public const string LicenseLabel = "LOCLicenseLabel"; /// /// Contributors /// public const string ContributorsLabel = "LOCContributorsLabel"; /// /// Exiting Playnite… /// public const string ClosingPlaynite = "LOCClosingPlaynite"; /// /// Today /// public const string Today = "LOCToday"; /// /// Yesterday /// public const string Yesterday = "LOCYesterday"; /// /// Monday /// public const string Monday = "LOCMonday"; /// /// Tuesday /// public const string Tuesday = "LOCTuesday"; /// /// Wednesday /// public const string Wednesday = "LOCWednesday"; /// /// Thursday /// public const string Thursday = "LOCThursday"; /// /// Friday /// public const string Friday = "LOCFriday"; /// /// Saturday /// public const string Saturday = "LOCSaturday"; /// /// Sunday /// public const string Sunday = "LOCSunday"; /// /// Past Week /// public const string PastWeek = "LOCPastWeek"; /// /// Past Month /// public const string PastMonth = "LOCPastMonth"; /// /// Past Year /// public const string PastYear = "LOCPastYear"; /// /// More than a year ago /// public const string MoreThenYear = "LOCMoreThenYear"; /// /// 0 to 100MB /// public const string SizeZeroTo100Mb = "LOCSizeZeroTo100Mb"; /// /// 100MB to 1GB /// public const string Size100MbTo1Gb = "LOCSize100MbTo1Gb"; /// /// 1GB to 5GB /// public const string Size1GbTo5Gb = "LOCSize1GbTo5Gb"; /// /// 5GB to 10GB /// public const string Size5GbTo10Gb = "LOCSize5GbTo10Gb"; /// /// 10GB to 20GB /// public const string Size10GbTo20Gb = "LOCSize10GbTo20Gb"; /// /// 20GB to 40GB /// public const string Size20GbTo40Gb = "LOCSize20GbTo40Gb"; /// /// 40GB to 100GB /// public const string Size40GbTo100Gb = "LOCSize40GbTo100Gb"; /// /// 100GB or more /// public const string SizeMoreThan100Gb = "LOCSizeMoreThan100Gb"; /// /// Import completed successfully. /// public const string ImportCompleted = "LOCImportCompleted"; /// /// All Games /// public const string AllGames = "LOCAllGames"; /// /// Game Id /// public const string GameId = "LOCGameId"; /// /// Database Id /// public const string DatabaseId = "LOCDatabaseId"; /// /// Presets /// public const string Presets = "LOCPresets"; /// /// Column /// public const string Column = "LOCColumn"; /// /// Columns /// public const string Columns = "LOCColumns"; /// /// Row /// public const string Row = "LOCRow"; /// /// Rows /// public const string Rows = "LOCRows"; /// /// Couldn't get icon from Play action. There's no action of File type present. /// public const string ExecIconMissingPlayAction = "LOCExecIconMissingPlayAction"; /// /// Only download missing metadata /// public const string MetaSkipNonEmpty = "LOCMetaSkipNonEmpty"; /// /// Enabling this option will skip downloading metadata for data fields that already contain information. /// public const string MetaSkipNonEmptyTooltip = "LOCMetaSkipNonEmptyTooltip"; /// /// Games selection /// public const string MetaGamesSourceIntro = "LOCMetaGamesSourceIntro"; /// /// Please select which games should to be updated with new metadata: /// public const string MetaGamesSourceDescription = "LOCMetaGamesSourceDescription"; /// /// All games from database /// public const string MetaGameSourceAll = "LOCMetaGameSourceAll"; /// /// All currently filtered games /// public const string MetaGameSourceFiltered = "LOCMetaGameSourceFiltered"; /// /// Selected games only /// public const string MetaGameSourceSelected = "LOCMetaGameSourceSelected"; /// /// No metadata fields selected /// public const string MetaNoFieldsSelectedErrorCaption = "LOCMetaNoFieldsSelectedErrorCaption"; /// /// No metadata fields are selected for download. Please select at least one, and enable at least one metadata provider for it. /// public const string MetaNoFieldsSelectedErrorMessage = "LOCMetaNoFieldsSelectedErrorMessage"; /// /// Official Store /// public const string MetaSourceStore = "LOCMetaSourceStore"; /// /// IGDB /// public const string MetaSourceIGDB = "LOCMetaSourceIGDB"; /// /// Please select which fields should be automatically populated by Playnite and which sources should be used to obtain the data from. /// public const string MetaDescriptionFields = "LOCMetaDescriptionFields"; /// /// Please consider clicking on the logo above and contribute updates to igdb.com database in order to improve data Playnite uses. /// public const string MetaIgdbContribNotif = "LOCMetaIgdbContribNotif"; /// /// Downloading metadata… /// public const string ProgressMetadata = "LOCProgressMetadata"; /// /// Importing installed games… /// public const string ProgressInstalledGames = "LOCProgressInstalledGames"; /// /// Importing {0} games… /// public const string ProgressImportinGames = "LOCProgressImportinGames"; /// /// Importing emulated games from {0}… /// public const string ProgressImportinEmulatedGames = "LOCProgressImportinEmulatedGames"; /// /// Downloading library updates… /// public const string ProgressLibraryGames = "LOCProgressLibraryGames"; /// /// Scanning size of games in library… /// public const string ProgressScanningGamesInstallSize = "LOCProgressScanningGamesInstallSize"; /// /// Scanning size of imported games… /// public const string ProgressScanningImportedGamesInstallSize = "LOCProgressScanningImportedGamesInstallSize"; /// /// Library update finished /// public const string ProgressLibImportFinish = "LOCProgressLibImportFinish"; /// /// Releasing resources… /// public const string ProgressReleasingResources = "LOCProgressReleasingResources"; /// /// Configuration /// public const string MenuConfigurationTitle = "LOCMenuConfigurationTitle"; /// /// Settings… /// public const string MenuPlayniteSettingsTitle = "LOCMenuPlayniteSettingsTitle"; /// /// Platforms and Emulators /// public const string MenuPlatformEmulatorSettingsTitle = "LOCMenuPlatformEmulatorSettingsTitle"; /// /// Configure Emulators… /// public const string MenuConfigureEmulatorsMenuTitle = "LOCMenuConfigureEmulatorsMenuTitle"; /// /// Library Manager… /// public const string MenuLibraryManagerTitle = "LOCMenuLibraryManagerTitle"; /// /// Tools /// public const string MenuTools = "LOCMenuTools"; /// /// Download Metadata… /// public const string MenuDownloadMetadata = "LOCMenuDownloadMetadata"; /// /// Software Tools… /// public const string MenuSoftwareTools = "LOCMenuSoftwareTools"; /// /// Configure Integrations… /// public const string MenuConfigureIntegrations = "LOCMenuConfigureIntegrations"; /// /// Open 3rd Party Client /// public const string MenuOpenClient = "LOCMenuOpenClient"; /// /// 3rd Party Clients /// public const string MenuClients = "LOCMenuClients"; /// /// Update Game Library /// public const string MenuReloadLibrary = "LOCMenuReloadLibrary"; /// /// Cancel Library Update /// public const string MenuCancelLibraryUpdate = "LOCMenuCancelLibraryUpdate"; /// /// Update Emulated Folders /// public const string MenuUpdateEmulatedDirs = "LOCMenuUpdateEmulatedDirs"; /// /// Add Game /// public const string MenuAddGame = "LOCMenuAddGame"; /// /// Manually… /// public const string MenuAddGameManual = "LOCMenuAddGameManual"; /// /// Scan Automatically… /// public const string MenuAddGameInstalled = "LOCMenuAddGameInstalled"; /// /// Emulated Game… /// public const string MenuAddGameEmulated = "LOCMenuAddGameEmulated"; /// /// Microsoft Store application… /// public const string MenuAddWindowsStore = "LOCMenuAddWindowsStore"; /// /// About Playnite /// public const string MenuAbout = "LOCMenuAbout"; /// /// Send Feedback /// public const string MenuIssues = "LOCMenuIssues"; /// /// Switch to Fullscreen Mode /// public const string MenuOpenFullscreen = "LOCMenuOpenFullscreen"; /// /// Links /// public const string MenuLinksTitle = "LOCMenuLinksTitle"; /// /// Help /// public const string MenuHelpTitle = "LOCMenuHelpTitle"; /// /// Support on Patreon /// public const string MenuPatreonSupport = "LOCMenuPatreonSupport"; /// /// Support on Ko-fi /// public const string MenuKofiSupport = "LOCMenuKofiSupport"; /// /// User manual /// public const string UserManual = "LOCUserManual"; /// /// SDK Documentation /// public const string SDKDocumentation = "LOCSDKDocumentation"; /// /// Restart System /// public const string MenuRestartSystem = "LOCMenuRestartSystem"; /// /// Turn Off System /// public const string MenuShutdownSystem = "LOCMenuShutdownSystem"; /// /// Suspend System /// public const string MenuSuspendSystem = "LOCMenuSuspendSystem"; /// /// Hibernate System /// public const string MenuHibernateSystem = "LOCMenuHibernateSystem"; /// /// Lock System /// public const string MenuLockSystem = "LOCMenuLockSystem"; /// /// Log Out User /// public const string MenuLogoutUser = "LOCMenuLogoutUser"; /// /// Pick a Random Game /// public const string MenuSelectRandomGame = "LOCMenuSelectRandomGame"; /// /// Game fields to be displayed on details panel: /// public const string SettingsDetailsPanelItems = "LOCSettingsDetailsPanelItems"; /// /// Item spacing /// public const string SettingsGridItemSpacing = "LOCSettingsGridItemSpacing"; /// /// Draw grid item background /// public const string SettingsGridItemDrawBackground = "LOCSettingsGridItemDrawBackground"; /// /// Grid item border width /// public const string SettingsGridItemCoverMargin = "LOCSettingsGridItemCoverMargin"; /// /// Missing game icon source /// public const string SettingsDefaulIconSource = "LOCSettingsDefaulIconSource"; /// /// Missing game cover source /// public const string SettingsDefaulCoverSource = "LOCSettingsDefaulCoverSource"; /// /// Missing game background source /// public const string SettingsDefaulBackgroundSource = "LOCSettingsDefaulBackgroundSource"; /// /// Vertical spacing to game details /// public const string SettingsIndentGameDetails = "LOCSettingsIndentGameDetails"; /// /// Grid view details position /// public const string SettingsGridViewDetailsPosition = "LOCSettingsGridViewDetailsPosition"; /// /// Details view game list position /// public const string SettingsDetailsGameListPosition = "LOCSettingsDetailsGameListPosition"; /// /// Draw separator between panels /// public const string SettingsDrawPanelSeparators = "LOCSettingsDrawPanelSeparators"; /// /// Game cover image height /// public const string SettingsGameDetailsCoverHeight = "LOCSettingsGameDetailsCoverHeight"; /// /// Game list icon height /// public const string SettingsGameDetailsListIconSize = "LOCSettingsGameDetailsListIconSize"; /// /// Application font /// public const string SettingsInterfaceFont = "LOCSettingsInterfaceFont"; /// /// Monospaced font /// public const string SettingsInterfaceMonoFont = "LOCSettingsInterfaceMonoFont"; /// /// Filter panel position /// public const string SettingsFilterPanelPosition = "LOCSettingsFilterPanelPosition"; /// /// Explorer panel position /// public const string SettingsExplorerPanelPosition = "LOCSettingsExplorerPanelPosition"; /// /// Cover art rendering /// public const string SettingsCoverArtRenderingLabel = "LOCSettingsCoverArtRenderingLabel"; /// /// Target aspect ratio /// public const string SettingsTargetAspectRatioLabel = "LOCSettingsTargetAspectRatioLabel"; /// /// Following options also affect tile rendering in Fullscreen mode! /// public const string SettingsGridTileLayoutFSNote = "LOCSettingsGridTileLayoutFSNote"; /// /// Stretch mode /// public const string SettingsStrechModeLabel = "LOCSettingsStrechModeLabel"; /// /// DVD Box /// public const string SettingsCovertAspectDVD = "LOCSettingsCovertAspectDVD"; /// /// Epic Games Store /// public const string SettingsCovertAspectEpicGamesStore = "LOCSettingsCovertAspectEpicGamesStore"; /// /// GOG Galaxy 2.0 /// public const string SettingsCovertAspectGogGalaxy2 = "LOCSettingsCovertAspectGogGalaxy2"; /// /// IGDB /// public const string SettingsCovertAspectIgdb = "LOCSettingsCovertAspectIgdb"; /// /// Square /// public const string SettingsCovertAspectSquare = "LOCSettingsCovertAspectSquare"; /// /// Steam Banner /// public const string SettingsCovertAspectSteam = "LOCSettingsCovertAspectSteam"; /// /// Steam vertical cover /// public const string SettingsCovertAspectSteamVertical = "LOCSettingsCovertAspectSteamVertical"; /// /// Twitch /// public const string SettingsCovertAspectTwitch = "LOCSettingsCovertAspectTwitch"; /// /// * Requires restart to apply /// public const string SettingsRestartNotification = "LOCSettingsRestartNotification"; /// /// Settings /// public const string SettingsLabel = "LOCSettingsLabel"; /// /// General /// public const string SettingsGeneralLabel = "LOCSettingsGeneralLabel"; /// /// Top panel /// public const string SettingsTopPanelLabel = "LOCSettingsTopPanelLabel"; /// /// Appearance /// public const string SettingsAppearanceLabel = "LOCSettingsAppearanceLabel"; /// /// Game Details /// public const string SettingsGameDetailsLabel = "LOCSettingsGameDetailsLabel"; /// /// Layout /// public const string SettingsLayoutLabel = "LOCSettingsLayoutLabel"; /// /// Advanced /// public const string SettingsAdvancedLabel = "LOCSettingsAdvancedLabel"; /// /// Fullscreen /// public const string SettingsFullscreenLabel = "LOCSettingsFullscreenLabel"; /// /// Input /// public const string SettingsInputLabel = "LOCSettingsInputLabel"; /// /// Performance /// public const string SettingsPerformanceLabel = "LOCSettingsPerformanceLabel"; /// /// Metadata /// public const string SettingsMetadataLabel = "LOCSettingsMetadataLabel"; /// /// Updating /// public const string SettingsUpdating = "LOCSettingsUpdating"; /// /// Search /// public const string SettingsSearch = "LOCSettingsSearch"; /// /// Backup /// public const string SettingsBackup = "LOCSettingsBackup"; /// /// Backup Library Data /// public const string MenuBackupData = "LOCMenuBackupData"; /// /// Restore Data Backup /// public const string MenuRestoreBackup = "LOCMenuRestoreBackup"; /// /// Import changes in library automatically /// public const string SettingsImportLabel = "LOCSettingsImportLabel"; /// /// Invalid database file location, proper file path must be set. /// public const string SettingsInvalidDBLocation = "LOCSettingsInvalidDBLocation"; /// /// Account name cannot be empty. /// public const string SettingsInvalidAccountName = "LOCSettingsInvalidAccountName"; /// /// Download metadata after importing games /// public const string SettingsDownloadMetadataOnImport = "LOCSettingsDownloadMetadataOnImport"; /// /// Launch Playnite minimized /// public const string SettingsStartMinimized = "LOCSettingsStartMinimized"; /// /// Launch Playnite when you start your computer /// public const string SettingsStartOnBoot = "LOCSettingsStartOnBoot"; /// /// Start closed to tray /// public const string SettingsStartOnBootClosedToTray = "LOCSettingsStartOnBootClosedToTray"; /// /// Failed to register Playnite to launch when computer starts. /// public const string SettingsStartOnBootRegistrationError = "LOCSettingsStartOnBootRegistrationError"; /// /// Launch in Fullscreen Mode /// public const string SettingsStartInFullscreen = "LOCSettingsStartInFullscreen"; /// /// Asynchronous image loading /// public const string SettingsAsyncImageLoading = "LOCSettingsAsyncImageLoading"; /// /// Can improve scrolling smoothness of game lists in exchange for slower image load times. /// public const string SettingsAsyncImageLoadingTooltip = "LOCSettingsAsyncImageLoadingTooltip"; /// /// Show game name if cover art is missing /// public const string SettingsShowNameEmptyCover = "LOCSettingsShowNameEmptyCover"; /// /// Show game names on Grid view /// public const string SettingsShowNamesUnderCover = "LOCSettingsShowNamesUnderCover"; /// /// Darken not installed games /// public const string SettingsDarkenUninstalledGridCovers = "LOCSettingsDarkenUninstalledGridCovers"; /// /// Show game icons on Details view list /// public const string SettingsShowIconList = "LOCSettingsShowIconList"; /// /// Show item count on group descriptions /// public const string SettingsShowGroupCount = "LOCSettingsShowGroupCount"; /// /// Show only assigned fields on filter and explorer panels /// public const string SettingsUsedFieldsOnlyOnFilterLists = "LOCSettingsUsedFieldsOnlyOnFilterLists"; /// /// Disable hardware acceleration /// public const string SettingsDisableAcceleration = "LOCSettingsDisableAcceleration"; /// /// Use when experiencing stuttering or similar UI issues /// public const string SettingsDisableAccelerationTooltip = "LOCSettingsDisableAccelerationTooltip"; /// /// Show hidden games in quick launch lists /// public const string SettingsHiddenInQuickLaunch = "LOCSettingsHiddenInQuickLaunch"; /// /// Affects Jump List and tray menu lists. /// public const string SettingsHiddenInQuickLaunchTooltip = "LOCSettingsHiddenInQuickLaunchTooltip"; /// /// Number of quick launch items /// public const string SettingsQuicLaunchItems = "LOCSettingsQuicLaunchItems"; /// /// Use game background image as window background /// public const string SettingsShowBackgroundWindowImage = "LOCSettingsShowBackgroundWindowImage"; /// /// Blur background /// public const string SettingsBlurWindowBackgrounImage = "LOCSettingsBlurWindowBackgrounImage"; /// /// High Quality /// public const string SettingsBlurHighQuality = "LOCSettingsBlurHighQuality"; /// /// Darken background /// public const string SettingsDarkenWindowBackghrounImage = "LOCSettingsDarkenWindowBackghrounImage"; /// /// Show on Grid view /// public const string SettingsShowBackImageOnGridView = "LOCSettingsShowBackImageOnGridView"; /// /// Theme /// public const string SettingsSkin = "LOCSettingsSkin"; /// /// Theme Profile /// public const string SettingsSkinColor = "LOCSettingsSkinColor"; /// /// Fullscreen Theme /// public const string SettingsSkinFullscreen = "LOCSettingsSkinFullscreen"; /// /// Fullscreen Theme Profile /// public const string SettingsSkinColorFullscreen = "LOCSettingsSkinColorFullscreen"; /// /// Database Location /// public const string SettingsDBLocation = "LOCSettingsDBLocation"; /// /// Login status: /// public const string SettingsLoginStatus = "LOCSettingsLoginStatus"; /// /// Playnite Settings /// public const string SettingsWindowTitle = "LOCSettingsWindowTitle"; /// /// Clear web cache /// public const string SettingsClearWebCache = "LOCSettingsClearWebCache"; /// /// May solve issues encountered while linking accounts. /// public const string SettingsClearWebCacheTooltip = "LOCSettingsClearWebCacheTooltip"; /// /// Show system tray icon /// public const string SettingsShowTray = "LOCSettingsShowTray"; /// /// Minimize Playnite to system tray /// public const string SettingsMinimizeToTray = "LOCSettingsMinimizeToTray"; /// /// Minimize Playnite to system tray when the application window is closed /// public const string SettingsCloseToTray = "LOCSettingsCloseToTray"; /// /// When game starts: /// public const string SettingsAfterGameStart = "LOCSettingsAfterGameStart"; /// /// After game closes: /// public const string SettingsAfterGameClose = "LOCSettingsAfterGameClose"; /// /// Format time played to indicate the number of days played /// public const string SettingsPlaytimeUseDaysFormatLabel = "LOCSettingsPlaytimeUseDaysFormatLabel"; /// /// Dates formats: /// public const string SettingsDatesFormatsLabel = "LOCSettingsDatesFormatsLabel"; /// /// This will log you out of all linked services. Application restart is required, do you want to proceed? /// public const string SettingsClearCacheWarn = "LOCSettingsClearCacheWarn"; /// /// Clear Cache? /// public const string SettingsClearCacheTitle = "LOCSettingsClearCacheTitle"; /// /// Playnite restart is required to apply new theme /// public const string SettingsSkinChangeRestart = "LOCSettingsSkinChangeRestart"; /// /// Get more themes /// public const string SettingsGetThemes = "LOCSettingsGetThemes"; /// /// Create new theme /// public const string SettingsCreateThemes = "LOCSettingsCreateThemes"; /// /// Get more extensions /// public const string SettingsGetExtensions = "LOCSettingsGetExtensions"; /// /// Create new extension /// public const string SettingsCreateExtensions = "LOCSettingsCreateExtensions"; /// /// Help us translate Playnite /// public const string SettingsCreateLocalization = "LOCSettingsCreateLocalization"; /// /// Playnite needs to be restarted in order to apply new settings. Restart now? /// public const string SettingsRestartAskMessage = "LOCSettingsRestartAskMessage"; /// /// Restart Playnite? /// public const string SettingsRestartTitle = "LOCSettingsRestartTitle"; /// /// Playnite cannot move your library files automatically. You must manually move/copy the files before changing the location. If there is no library in the target location, a new one will be created. /// public const string SettingsDBPathNotification = "LOCSettingsDBPathNotification"; /// /// Play time will not be recorded if "Close" action is set. /// public const string SettingsClosePlaytimeNotif = "LOCSettingsClosePlaytimeNotif"; /// /// Number of rows /// public const string SettingsFullscreenRows = "LOCSettingsFullscreenRows"; /// /// Number of columns /// public const string SettingsFullscreenColumns = "LOCSettingsFullscreenColumns"; /// /// Number of detail view rows /// public const string SettingsFullscreenRowDetails = "LOCSettingsFullscreenRowDetails"; /// /// Show Background Image on Main Screen /// public const string SettingsFullscreenBackgroundOnMainScreen = "LOCSettingsFullscreenBackgroundOnMainScreen"; /// /// Doesn't apply retrospectively to existing games without re-downloading metadata. /// public const string BackgroundImageScreenOptionTooltip = "LOCBackgroundImageScreenOptionTooltip"; /// /// Import playtime of games in library: /// public const string SettingsPlaytimeImportMode = "LOCSettingsPlaytimeImportMode"; /// /// Configures when should Playnite import the playtime reported by library plugins for games in the Playnite database. Support by the library plugins in charge of handling the game(s) is needed to be able to use this feature. /// public const string SettingsPlaytimeImportModeTooltip = "LOCSettingsPlaytimeImportModeTooltip"; /// /// Always /// public const string SettingsPlaytimeImportModeAlways = "LOCSettingsPlaytimeImportModeAlways"; /// /// Only for newly imported games /// public const string SettingsPlaytimeImportModeNewImportsOnly = "LOCSettingsPlaytimeImportModeNewImportsOnly"; /// /// Never /// public const string SettingsPlaytimeImportModeNever = "LOCSettingsPlaytimeImportModeNever"; /// /// Enable controller support in Desktop Mode /// public const string SettingsXInputInDesktopMode = "LOCSettingsXInputInDesktopMode"; /// /// Guide button opens Fullscreen mode /// public const string SettingsXInputGuideOpensFullscreen = "LOCSettingsXInputGuideOpensFullscreen"; /// /// Automatic Metadata download settings for newly imported games. /// public const string SettingsDefaultMetadataDescription = "LOCSettingsDefaultMetadataDescription"; /// /// Target display /// public const string SettingsTargetDisplay = "LOCSettingsTargetDisplay"; /// /// Always use primary display /// public const string SettingsAlwaysUsePrimaryDisplay = "LOCSettingsAlwaysUsePrimaryDisplay"; /// /// Show Game Titles /// public const string SettingsFullscreenShowGameTitles = "LOCSettingsFullscreenShowGameTitles"; /// /// Show Battery Status /// public const string SettingsShowBatteryStatus = "LOCSettingsShowBatteryStatus"; /// /// Show Battery Percentage /// public const string SettingsShowBatteryPercentage = "LOCSettingsShowBatteryPercentage"; /// /// Show Clock /// public const string SettingsShowClock = "LOCSettingsShowClock"; /// /// Hide Mouse Cursor /// public const string SettingsHideMouseCursor = "LOCSettingsHideMouseCursor"; /// /// Installed Only in Quick Filters /// public const string SettingsFullscreenQuickFilterInstalled = "LOCSettingsFullscreenQuickFilterInstalled"; /// /// Button Prompts /// public const string SettingsFullscreenButtonPrompts = "LOCSettingsFullscreenButtonPrompts"; /// /// Layout /// public const string SettingsFullscreenLayout = "LOCSettingsFullscreenLayout"; /// /// Horizontal Scrolling /// public const string SettingsFullscreenHorizontalScrolling = "LOCSettingsFullscreenHorizontalScrolling"; /// /// Select one of the subsections /// public const string SettingsSelectChildSection = "LOCSettingsSelectChildSection"; /// /// No settings available /// public const string SettingsNoSettingsAvailable = "LOCSettingsNoSettingsAvailable"; /// /// Failed to load settings /// public const string SettingsErrorLoadingSettings = "LOCSettingsErrorLoadingSettings"; /// /// These scripts are executed for every game in the library. Individual scripts can be assigned to each game separately while editing game details. /// public const string SettingsScriptingNotice = "LOCSettingsScriptingNotice"; /// /// Animate background image transitions /// public const string SettingsBackgroundImageAnimation = "LOCSettingsBackgroundImageAnimation"; /// /// Font sizes /// public const string SettingsFontSizes = "LOCSettingsFontSizes"; /// /// Auto /// public const string SettingsTextRenderingModeOptionAuto = "LOCSettingsTextRenderingModeOptionAuto"; /// /// Aliased /// public const string SettingsTextRenderingModeOptionAliased = "LOCSettingsTextRenderingModeOptionAliased"; /// /// Grayscale /// public const string SettingsTextRenderingModeOptionGrayscale = "LOCSettingsTextRenderingModeOptionGrayscale"; /// /// ClearType /// public const string SettingsTextRenderingModeOptionClearType = "LOCSettingsTextRenderingModeOptionClearType"; /// /// Ideal /// public const string SettingsTextFormattingModeOptionIdeal = "LOCSettingsTextFormattingModeOptionIdeal"; /// /// Display /// public const string SettingsTextFormattingModeOptionDisplay = "LOCSettingsTextFormattingModeOptionDisplay"; /// /// Text formatting mode /// public const string SettingsTextFormattingMode = "LOCSettingsTextFormattingMode"; /// /// Text rendering mode /// public const string SettingsTextRenderingMode = "LOCSettingsTextRenderingMode"; /// /// Text rendering and formatting methods are currently not used for game descriptions. /// public const string SettingsTextRenderingNotice = "LOCSettingsTextRenderingNotice"; /// /// Preload background images /// public const string SettingsImmediateBackgroundDownload = "LOCSettingsImmediateBackgroundDownload"; /// /// If enabled, Playnite will download background artwork while downloading metadata, using more disk space and making artwork available when offline. /// public const string SettingsImmediateBackgroundDownloadTooltip = "LOCSettingsImmediateBackgroundDownloadTooltip"; /// /// Automatically close third party client after game exits /// public const string SettingsAutoCloseLauncherOption = "LOCSettingsAutoCloseLauncherOption"; /// /// Client shutdown delay (in seconds) /// public const string SettingsAutoCloseGracePeriod = "LOCSettingsAutoCloseGracePeriod"; /// /// Don't close after game sessions shorter than (in seconds) /// public const string SettingsAutoCloseMinimalSessionTime = "LOCSettingsAutoCloseMinimalSessionTime"; /// /// Automatically close following clients: /// public const string SettingsAutoCloseSpecificClients = "LOCSettingsAutoCloseSpecificClients"; /// /// Auto Close Clients /// public const string SettingsAutoCloseSection = "LOCSettingsAutoCloseSection"; /// /// Import Exclusion List /// public const string SettingsImportExclusionList = "LOCSettingsImportExclusionList"; /// /// Display warning when assigning too large game media /// public const string SettingsShowMediaSizeWarning = "LOCSettingsShowMediaSizeWarning"; /// /// Folder open command /// public const string OpenDirectoryCommand = "LOCOpenDirectoryCommand"; /// /// Preferred age rating organization /// public const string SettingsPreferredAgeRatingOrg = "LOCSettingsPreferredAgeRatingOrg"; /// /// Update install size of games on library update /// public const string SettingsScanLibInstallSizeOnLibUpdate = "LOCSettingsScanLibInstallSizeOnLibUpdate"; /// /// Scans and updates the install size of games if it is detected that their files have been modified since the last scan /// public const string SettingsScanLibInstallSizeOnLibUpdateTooltip = "LOCSettingsScanLibInstallSizeOnLibUpdateTooltip"; /// /// None /// public const string StrechNone = "LOCStrechNone"; /// /// Fill /// public const string StrechFill = "LOCStrechFill"; /// /// Uniform /// public const string StrechUniform = "LOCStrechUniform"; /// /// Uniform to fill /// public const string StrechUniformToFill = "LOCStrechUniformToFill"; /// /// Left /// public const string DockLeft = "LOCDockLeft"; /// /// Right /// public const string DockRight = "LOCDockRight"; /// /// Top /// public const string DockTop = "LOCDockTop"; /// /// Bottom /// public const string DockBottom = "LOCDockBottom"; /// /// Import Error /// public const string ImportError = "LOCImportError"; /// /// Authentification required /// public const string LoginRequired = "LOCLoginRequired"; /// /// Authentification failed /// public const string LoginFailed = "LOCLoginFailed"; /// /// Alternative web view rendering mode /// public const string SettingsAltWebViewRendering = "LOCSettingsAltWebViewRendering"; /// /// Use when experiencing issues with web views, for example integration authentication dialogs. /// public const string SettingsAltWebViewRenderingTooltip = "LOCSettingsAltWebViewRenderingTooltip"; /// /// Partial loading of large game descriptions /// public const string SettingsPartialDescriptionLoading = "LOCSettingsPartialDescriptionLoading"; /// /// Large descriptions can cause noticeable lag when selecting games. /// public const string SettingsPartialDescriptionLoadingTooltip = "LOCSettingsPartialDescriptionLoadingTooltip"; /// /// Metadata Import /// public const string MetaImportWindowTitle = "LOCMetaImportWindowTitle"; /// /// Download Metadata /// public const string DownloadMetaButton = "LOCDownloadMetaButton"; /// /// Set selected configuration to be used for any future metadata downloads. /// public const string SaveDefaultTooltip = "LOCSaveDefaultTooltip"; /// /// Emulation Import Wizard /// public const string EmuWizardWindowTitle = "LOCEmuWizardWindowTitle"; /// /// This wizard will guide you through the process of downloading and importing console emulators and importing emulated games. /// public const string EmuWizardIntro = "LOCEmuWizardIntro"; /// /// Keep in mind that you can always add additional emulators and/or games later via main menu (under "Library" menu for Emulator settings and "Add Games" menu for emulated games). /// public const string EmuWizardNotice = "LOCEmuWizardNotice"; /// /// Below is a list of emulators that Playnite can recognize and configure automatically. You can download emulator installers from their websites. Once you have the emulators installed (manually), you can import them on emulator configuration dialog. /// public const string EmuDownloadDescription = "LOCEmuDownloadDescription"; /// /// You can import any emulators that are installed on your PC by clicking the 'Autodetect From Folder…' button. Playnite will search the selected folder for any known emulators and provide the option to import them. You can use this button multiple times to import emulators from different folders. Emulators will be added to the bottom of the current list. /// public const string EmuWizardEmuImportIntro = "LOCEmuWizardEmuImportIntro"; /// /// You can import games by clicking the 'Scan Folder Using Emulator' button. Selecting the appropriate emulator will tell Playnite which file types should be scanned and imported. You can use this button multiple times to import games from different folders. Games will be added to the bottom of the current list. /// public const string EmuWizardGameImportIntro = "LOCEmuWizardGameImportIntro"; /// /// There are no emulators selected for import. You won't be able to automatically import any emulated games without configuring emulators first. Are you sure you want to continue and exit import process? /// public const string EmuWizardNoEmulatorWarning = "LOCEmuWizardNoEmulatorWarning"; /// /// There are no emulators configured in Playnite. You cannot import games without first configuring the emulator and selecting the appropriate file types. Do you want to add some emulators now? /// public const string EmuWizardNoEmulatorForGamesWarning = "LOCEmuWizardNoEmulatorForGamesWarning"; /// /// Scan folder using Emulator /// public const string EmuWizardButtonScanGames = "LOCEmuWizardButtonScanGames"; /// /// Select files /// public const string EmuWizardButtonSelectFiles = "LOCEmuWizardButtonSelectFiles"; /// /// Autodetect From Folder… /// public const string EmuWizardButtonScanEmulator = "LOCEmuWizardButtonScanEmulator"; /// /// Configure Emulators… /// public const string EmuWizardButtonConfigureEmulator = "LOCEmuWizardButtonConfigureEmulator"; /// /// Scanning… /// public const string EmuWizardScanning = "LOCEmuWizardScanning"; /// /// Scanning {0}… /// public const string EmuWizardScanningSpecific = "LOCEmuWizardScanningSpecific"; /// /// First Time Configuration /// public const string FirstWindowTitle = "LOCFirstWindowTitle"; /// /// This process will guide you through an automatic import and configuration of external game libraries. Playnite can automatically import games from multiple game services, such as Steam or GOG. /// public const string FirstIntro = "LOCFirstIntro"; /// /// Library Integration /// public const string FirstExternalTitle = "LOCFirstExternalTitle"; /// /// Following is the list of some curated library integrations Playnite supports. Please select ones you want to install. /// public const string FirstImportIntroduction = "LOCFirstImportIntroduction"; /// /// Configuration Finished /// public const string FirstConfigFinishedTitle = "LOCFirstConfigFinishedTitle"; /// /// The initial setup has been completed. Remember that you can change all settings later as well as add additional integrations from main menu. /// public const string FirstConfigFinishedText = "LOCFirstConfigFinishedText"; /// /// Failed to download one or more extensions. /// public const string FirstPluginDownloadError = "LOCFirstPluginDownloadError"; /// /// Downloading {0} integration… /// public const string FirstDownloadingAddon = "LOCFirstDownloadingAddon"; /// /// Downloading list of recommended integrations… /// public const string DefaultAddonListDownload = "LOCDefaultAddonListDownload"; /// /// Failed to download list of recommended integrations. You can try and re-download integrations later via the Addons menu. /// public const string DefaultAddonListDownloadError = "LOCDefaultAddonListDownloadError"; /// /// Configure Platforms and Emulators /// public const string PlatformsWindowTitle = "LOCPlatformsWindowTitle"; /// /// Configure Emulators /// public const string EmulatorsWindowTitle = "LOCEmulatorsWindowTitle"; /// /// Platforms /// public const string PlatformsTitle = "LOCPlatformsTitle"; /// /// Platform /// public const string PlatformTitle = "LOCPlatformTitle"; /// /// Emulators /// public const string EmulatorsTitle = "LOCEmulatorsTitle"; /// /// Emulator /// public const string EmulatorTitle = "LOCEmulatorTitle"; /// /// Add Platform /// public const string AddPlatformTitle = "LOCAddPlatformTitle"; /// /// Select Icon /// public const string SelectIconTitle = "LOCSelectIconTitle"; /// /// Select Cover /// public const string SelectCoverTitle = "LOCSelectCoverTitle"; /// /// Select Image /// public const string SelectImageTitle = "LOCSelectImageTitle"; /// /// Select Item /// public const string SelectItemTitle = "LOCSelectItemTitle"; /// /// Select Background /// public const string SelectBackgroundTitle = "LOCSelectBackgroundTitle"; /// /// Select File /// public const string SelectFileTitle = "LOCSelectFileTitle"; /// /// Select URL /// public const string SelectUrlTitle = "LOCSelectUrlTitle"; /// /// Add Emulator /// public const string AddEmulatorTitle = "LOCAddEmulatorTitle"; /// /// Supported Platform(s) /// public const string SupportedPlatformsTitle = "LOCSupportedPlatformsTitle"; /// /// Do you want to save platform changes? /// public const string ConfirmUnsavedPlatformsTitle = "LOCConfirmUnsavedPlatformsTitle"; /// /// Do you want to save emulator changes? /// public const string ConfirmUnsavedEmulatorsTitle = "LOCConfirmUnsavedEmulatorsTitle"; /// /// Executable /// public const string ExecutableTitle = "LOCExecutableTitle"; /// /// Arguments /// public const string ArgumentsTitle = "LOCArgumentsTitle"; /// /// Working Directory /// public const string WorkingDirTitle = "LOCWorkingDirTitle"; /// /// Supported File Types /// public const string SupportedFilesTitle = "LOCSupportedFilesTitle"; /// /// Import Emulators… /// public const string ImportEmulatorsButton = "LOCImportEmulatorsButton"; /// /// Download Emulators… /// public const string DownloadEmulatorsButton = "LOCDownloadEmulatorsButton"; /// /// Load arguments preset from known emulator profile /// public const string EmuLoadArgsPresetTooltip = "LOCEmuLoadArgsPresetTooltip"; /// /// Are you sure you want to remove {0} emulator? /// public const string EmuRemovalConfirmation = "LOCEmuRemovalConfirmation"; /// /// Are you sure you want to remove {0} platform? /// public const string PlatformRemovalConfirmation = "LOCPlatformRemovalConfirmation"; /// /// Settings help /// public const string EmulatorSettingsHelp = "LOCEmulatorSettingsHelp"; /// /// Sort By /// public const string MenuSortByTitle = "LOCMenuSortByTitle"; /// /// Sort Direction /// public const string MenuSortByDirection = "LOCMenuSortByDirection"; /// /// Group By /// public const string MenuGroupByTitle = "LOCMenuGroupByTitle"; /// /// Ascending /// public const string MenuSortAscending = "LOCMenuSortAscending"; /// /// Descending /// public const string MenuSortDescending = "LOCMenuSortDescending"; /// /// Don't group /// public const string MenuGroupDont = "LOCMenuGroupDont"; /// /// Group by Library /// public const string MenuGroupProvider = "LOCMenuGroupProvider"; /// /// Group by Category /// public const string MenuGroupCategory = "LOCMenuGroupCategory"; /// /// Group by Platform /// public const string MenuGroupPlatform = "LOCMenuGroupPlatform"; /// /// View Type /// public const string ViewType = "LOCViewType"; /// /// View /// public const string MenuView = "LOCMenuView"; /// /// Explorer Panel /// public const string MenuViewExplorerPanel = "LOCMenuViewExplorerPanel"; /// /// Filter Panel /// public const string MenuViewFilterPanel = "LOCMenuViewFilterPanel"; /// /// Icon /// public const string GameIconTitle = "LOCGameIconTitle"; /// /// Library Icon /// public const string LibraryIconTitle = "LOCLibraryIconTitle"; /// /// Cover Image /// public const string GameCoverImageTitle = "LOCGameCoverImageTitle"; /// /// Background Image /// public const string GameBackgroundTitle = "LOCGameBackgroundTitle"; /// /// Sorting Name /// public const string GameSortingNameTitle = "LOCGameSortingNameTitle"; /// /// Library /// public const string GameProviderTitle = "LOCGameProviderTitle"; /// /// Manual /// public const string GameManualTitle = "LOCGameManualTitle"; /// /// Name /// public const string GameNameTitle = "LOCGameNameTitle"; /// /// Install Drive /// public const string InstallDriveTitle = "LOCInstallDriveTitle"; /// /// Account Name /// public const string GameAccountNameTitle = "LOCGameAccountNameTitle"; /// /// Platform /// public const string GamePlatformTitle = "LOCGamePlatformTitle"; /// /// Category /// public const string GameCategoriesTitle = "LOCGameCategoriesTitle"; /// /// Genre /// public const string GameGenresTitle = "LOCGameGenresTitle"; /// /// Release Date /// public const string GameReleaseDateTitle = "LOCGameReleaseDateTitle"; /// /// Release Year /// public const string GameReleaseYearTitle = "LOCGameReleaseYearTitle"; /// /// Developer /// public const string GameDevelopersTitle = "LOCGameDevelopersTitle"; /// /// Tag /// public const string GameTagsTitle = "LOCGameTagsTitle"; /// /// Publisher /// public const string GamePublishersTitle = "LOCGamePublishersTitle"; /// /// Installation Status /// public const string GameInstallationStatus = "LOCGameInstallationStatus"; /// /// Match all filters /// public const string UseFilterStyleAndTitle = "LOCUseFilterStyleAndTitle"; /// /// If enabled, only games that use all the items in all the filters will be included in the view. /// public const string UseFilterStyleAndTooltip = "LOCUseFilterStyleAndTooltip"; /// /// Installed /// public const string GameIsInstalledTitle = "LOCGameIsInstalledTitle"; /// /// Installed /// public const string GameIsGameInstalledTitle = "LOCGameIsGameInstalledTitle"; /// /// Not installed /// public const string GameIsUnInstalledTitle = "LOCGameIsUnInstalledTitle"; /// /// Hidden /// public const string GameHiddenTitle = "LOCGameHiddenTitle"; /// /// Favorite /// public const string GameFavoriteTitle = "LOCGameFavoriteTitle"; /// /// Enable HDR Support /// public const string GameHdrTitle = "LOCGameHdrTitle"; /// /// If enabled, HDR will be enabled on the primary display before starting the game. /// public const string GameHdrTooltip = "LOCGameHdrTooltip"; /// /// Note that HDR is not supported on your primary display. /// public const string GameHdrNotSupportedTooltip = "LOCGameHdrNotSupportedTooltip"; /// /// Last Played /// public const string GameLastActivityTitle = "LOCGameLastActivityTitle"; /// /// Category /// public const string GameCategoryTitle = "LOCGameCategoryTitle"; /// /// Description /// public const string GameDescriptionTitle = "LOCGameDescriptionTitle"; /// /// Installation Folder /// public const string GameInstallDirTitle = "LOCGameInstallDirTitle"; /// /// Cover Image /// public const string GameCoverTitle = "LOCGameCoverTitle"; /// /// Links /// public const string GameLinksTitle = "LOCGameLinksTitle"; /// /// Image, ROM or ISO Path /// public const string GameROMTitle = "LOCGameROMTitle"; /// /// Genre /// public const string GenreLabel = "LOCGenreLabel"; /// /// Genres /// public const string GenresLabel = "LOCGenresLabel"; /// /// Company /// public const string CompanyLabel = "LOCCompanyLabel"; /// /// Companies /// public const string CompaniesLabel = "LOCCompaniesLabel"; /// /// Developer /// public const string DeveloperLabel = "LOCDeveloperLabel"; /// /// Developers /// public const string DevelopersLabel = "LOCDevelopersLabel"; /// /// Publisher /// public const string PublisherLabel = "LOCPublisherLabel"; /// /// Publishers /// public const string PublishersLabel = "LOCPublishersLabel"; /// /// Category /// public const string CategoryLabel = "LOCCategoryLabel"; /// /// Categories /// public const string CategoriesLabel = "LOCCategoriesLabel"; /// /// Tag /// public const string TagLabel = "LOCTagLabel"; /// /// Tags /// public const string TagsLabel = "LOCTagsLabel"; /// /// Feature /// public const string FeatureLabel = "LOCFeatureLabel"; /// /// Features /// public const string FeaturesLabel = "LOCFeaturesLabel"; /// /// Age Rating /// public const string AgeRatingLabel = "LOCAgeRatingLabel"; /// /// Age Ratings /// public const string AgeRatingsLabel = "LOCAgeRatingsLabel"; /// /// Region /// public const string RegionLabel = "LOCRegionLabel"; /// /// Regions /// public const string RegionsLabel = "LOCRegionsLabel"; /// /// Source /// public const string SourceLabel = "LOCSourceLabel"; /// /// Sources /// public const string SourcesLabel = "LOCSourcesLabel"; /// /// Recent Activity /// public const string RecentActivityLabel = "LOCRecentActivityLabel"; /// /// Database Error /// public const string DatabaseErroTitle = "LOCDatabaseErroTitle"; /// /// Failed to open library database. /// public const string DatabaseOpenError = "LOCDatabaseOpenError"; /// /// Database is not opened. /// public const string DatabaseNotOpenedError = "LOCDatabaseNotOpenedError"; /// /// Cannot access library database. File "{0}" is being used by another process or it's in inaccessible location. /// public const string DatabaseOpenAccessError = "LOCDatabaseOpenAccessError"; /// /// Failed to create diagnostics package. /// public const string DiagPackageCreationError = "LOCDiagPackageCreationError"; /// /// Failed to automatically upload diagnostics package. /// public const string DiagPackageUploadError = "LOCDiagPackageUploadError"; /// /// Diagnostics information was sent successfully. /// public const string DiagPackageSentSuccess = "LOCDiagPackageSentSuccess"; /// /// The diagnostics package has been created and submitted successfully. /// public const string DiagPackageCreationSuccess = "LOCDiagPackageCreationSuccess"; /// /// Failed to import games from {0}. /// public const string LibraryImportError = "LOCLibraryImportError"; /// /// Failed to import emulated games from {0}. /// public const string LibraryImportEmulatedError = "LOCLibraryImportEmulatedError"; /// /// Cannot search for games by selected emulator profile. Profile doesn't contain any file extensions or platforms. /// public const string ScanEmulatorGamesEmptyProfileError = "LOCScanEmulatorGamesEmptyProfileError"; /// /// Playnite failed to start. Please close all other instances and try again. /// public const string StartGenericError = "LOCStartGenericError"; /// /// Failed to apply theme "{0}", color profile "{1}" /// public const string SkinApplyError = "LOCSkinApplyError"; /// /// Cannot open link, URL is not in valid format. /// public const string URLFormatError = "LOCURLFormatError"; /// /// Failed to start the application. /// public const string AppStartupError = "LOCAppStartupError"; /// /// Failed to initialize web view component. Playnite cannot continue with startup process. /// public const string CefSharpInitError = "LOCCefSharpInitError"; /// /// Cannot import emulators due to missing or corrupted definition file. /// public const string EmulatorImportNoDefinitionsError = "LOCEmulatorImportNoDefinitionsError"; /// /// Failed to execute menu action. /// public const string MenuActionExecError = "LOCMenuActionExecError"; /// /// Edit Game Details /// public const string GameEditWindowTitle = "LOCGameEditWindowTitle"; /// /// Image URL /// public const string ImageURL = "LOCImageURL"; /// /// Add Link /// public const string AddLinkButton = "LOCAddLinkButton"; /// /// Add ROM /// public const string AddRom = "LOCAddRom"; /// /// Save Changes /// public const string SaveChanges = "LOCSaveChanges"; /// /// Apply field changes to game(s) being edited. /// public const string GameEditChangeSaveTooltip = "LOCGameEditChangeSaveTooltip"; /// /// Add Action /// public const string AddAction = "LOCAddAction"; /// /// Remove /// public const string DeleteAction = "LOCDeleteAction"; /// /// Remove Play Action /// public const string RemovePlayAction = "LOCRemovePlayAction"; /// /// Add Games /// public const string AddGames = "LOCAddGames"; /// /// Scan Folder… /// public const string ScanFolder = "LOCScanFolder"; /// /// Detect Installed /// public const string DetectInstalled = "LOCDetectInstalled"; /// /// Browse… /// public const string Browse = "LOCBrowse"; /// /// Open Playnite /// public const string OpenPlaynite = "LOCOpenPlaynite"; /// /// Profile Settings /// public const string ProfileSettings = "LOCProfileSettings"; /// /// Game name cannot be empty. /// public const string EmptyGameNameError = "LOCEmptyGameNameError"; /// /// Game action tracking directory cannot be empty. /// public const string EmptyTrackingFolderError = "LOCEmptyTrackingFolderError"; /// /// Game name cannot be empty before searching metadata. /// public const string EmptyGameNameMetaSearchError = "LOCEmptyGameNameMetaSearchError"; /// /// Invalid game data /// public const string InvalidGameData = "LOCInvalidGameData"; /// /// Enter valid web URL starting with http:// or https:// /// public const string URLInputInfo = "LOCURLInputInfo"; /// /// Select URL /// public const string URLInputInfoTitile = "LOCURLInputInfoTitile"; /// /// Failed to download metadata: {0} /// public const string MetadataDownloadError = "LOCMetadataDownloadError"; /// /// Download Error /// public const string DownloadError = "LOCDownloadError"; /// /// Clear Filters /// public const string ClearFilters = "LOCClearFilters"; /// /// Private Account /// public const string PrivateAccount = "LOCPrivateAccount"; /// /// Public Account /// public const string PublicAccount = "LOCPublicAccount"; /// /// API Key /// public const string APIKey = "LOCAPIKey"; /// /// Startup Error /// public const string StartupError = "LOCStartupError"; /// /// Theme Error /// public const string SkinError = "LOCSkinError"; /// /// Clear All /// public const string ClearAll = "LOCClearAll"; /// /// Installing /// public const string SetupRunning = "LOCSetupRunning"; /// /// Uninstalling /// public const string Uninstalling = "LOCUninstalling"; /// /// Launching /// public const string GameLaunching = "LOCGameLaunching"; /// /// Running /// public const string GameRunning = "LOCGameRunning"; /// /// Invalid URL /// public const string InvalidURL = "LOCInvalidURL"; /// /// Do nothing /// public const string DoNothing = "LOCDoNothing"; /// /// Minimize /// public const string Minimize = "LOCMinimize"; /// /// Restore window /// public const string RestoreWindow = "LOCRestoreWindow"; /// /// Restore window only when launched from UI /// public const string RestoreWindowOnlyFromUI = "LOCRestoreWindowOnlyFromUI"; /// /// Close /// public const string Close = "LOCClose"; /// /// Change /// public const string Change = "LOCChange"; /// /// Advanced /// public const string Advanced = "LOCAdvanced"; /// /// Never /// public const string Never = "LOCNever"; /// /// Completion Status /// public const string CompletionStatus = "LOCCompletionStatus"; /// /// Completion Statuses /// public const string CompletionStatuses = "LOCCompletionStatuses"; /// /// User Score /// public const string UserScore = "LOCUserScore"; /// /// Critic Score /// public const string CriticScore = "LOCCriticScore"; /// /// Community Score /// public const string CommunityScore = "LOCCommunityScore"; /// /// Game scripts /// public const string GameScripts = "LOCGameScripts"; /// /// Application scripts /// public const string ApplicationScripts = "LOCApplicationScripts"; /// /// Scripts /// public const string Scripts = "LOCScripts"; /// /// Plugins /// public const string Plugins = "LOCPlugins"; /// /// Metadata Sources /// public const string MetadataProviders = "LOCMetadataProviders"; /// /// Extensions /// public const string Extensions = "LOCExtensions"; /// /// Extension ID /// public const string ExtensionID = "LOCExtensionID"; /// /// Reload Scripts /// public const string ReloadScripts = "LOCReloadScripts"; /// /// Interactive SDK PowerShell /// public const string StartInteractivePowerShell = "LOCStartInteractivePowerShell"; /// /// All scripts reloaded successfully. /// public const string ReloadScriptsSuccess = "LOCReloadScriptsSuccess"; /// /// No games found for specified search/filter criteria /// public const string NoGamesFound = "LOCNoGamesFound"; /// /// No items found /// public const string NoItemsFound = "LOCNoItemsFound"; /// /// Switch to Desktop Mode /// public const string BackToDesktopMode = "LOCBackToDesktopMode"; /// /// Exit Playnite /// public const string ExitPlaynite = "LOCExitPlaynite"; /// /// Libraries /// public const string Libraries = "LOCLibraries"; /// /// Update All /// public const string UpdateAll = "LOCUpdateAll"; /// /// Created By: /// public const string ExtensionCreatedBy = "LOCExtensionCreatedBy"; /// /// Version: /// public const string ExtensionVersion = "LOCExtensionVersion"; /// /// Updated: /// public const string ExtensionUpdated = "LOCExtensionUpdated"; /// /// Module: /// public const string ExtensionModule = "LOCExtensionModule"; /// /// Library /// public const string Library = "LOCLibrary"; /// /// Statistics /// public const string Statistics = "LOCStatistics"; /// /// All /// public const string All = "LOCAll"; /// /// None /// public const string None = "LOCNone"; /// /// Notifications /// public const string Notifications = "LOCNotifications"; /// /// Width /// public const string Width = "LOCWidth"; /// /// Height /// public const string Height = "LOCHeight"; /// /// Size /// public const string Size = "LOCSize"; /// /// Small /// public const string FontSmall = "LOCFontSmall"; /// /// Normal /// public const string FontNormal = "LOCFontNormal"; /// /// Large /// public const string FontLarge = "LOCFontLarge"; /// /// Larger /// public const string FontLarger = "LOCFontLarger"; /// /// Largest /// public const string FontLargest = "LOCFontLargest"; /// /// Default /// public const string Default = "LOCDefault"; /// /// Select /// public const string Select = "LOCSelect"; /// /// Select All /// public const string SelectAll = "LOCSelectAll"; /// /// Deselect All /// public const string DeselectAll = "LOCDeselectAll"; /// /// First /// public const string First = "LOCFirst"; /// /// Random /// public const string Random = "LOCRandom"; /// /// User select /// public const string UserSelect = "LOCUserSelect"; /// /// Load more /// public const string LoadMore = "LOCLoadMore"; /// /// Transparent /// public const string Transparent = "LOCTransparent"; /// /// Collapse /// public const string Collapse = "LOCCollapse"; /// /// Expand /// public const string Expand = "LOCExpand"; /// /// Collapse All /// public const string CollapseAll = "LOCCollapseAll"; /// /// Expand All /// public const string ExpandAll = "LOCExpandAll"; /// /// Other /// public const string Other = "LOCOther"; /// /// Themes /// public const string Themes = "LOCThemes"; /// /// Emulator Arguments /// public const string EmulatorArguments = "LOCEmulatorArguments"; /// /// Built-in Arguments /// public const string BuiltinArguments = "LOCBuiltinArguments"; /// /// Custom Arguments /// public const string CustomArguments = "LOCCustomArguments"; /// /// Additional Emulator Arguments /// public const string AdditionalEmulatorArguments = "LOCAdditionalEmulatorArguments"; /// /// Override Emulator Arguments /// public const string OverrideEmulatorArguments = "LOCOverrideEmulatorArguments"; /// /// Play action /// public const string IsPlayAction = "LOCIsPlayAction"; /// /// Select metadata to import /// public const string MedataDiffWindowTitle = "LOCMedataDiffWindowTitle"; /// /// Select Games to Import /// public const string GameImportWindowTitle = "LOCGameImportWindowTitle"; /// /// Metadata search /// public const string MetaLookupWindowTitle = "LOCMetaLookupWindowTitle"; /// /// Update Available /// public const string UpdaterWindowTitle = "LOCUpdaterWindowTitle"; /// /// Changes since last update /// public const string UpdaterChangesInfo = "LOCUpdaterChangesInfo"; /// /// Download and Install Update /// public const string UpdaterInstallUpdate = "LOCUpdaterInstallUpdate"; /// /// Check for Updates /// public const string CheckForUpdates = "LOCCheckForUpdates"; /// /// Update Error /// public const string UpdateError = "LOCUpdateError"; /// /// Failed to check for new version. /// public const string UpdateCheckFailMessage = "LOCUpdateCheckFailMessage"; /// /// No new version found, you are up to date. /// public const string UpdateNoNewUpdateMessage = "LOCUpdateNoNewUpdateMessage"; /// /// Failed to download and install update. /// public const string GeneralUpdateFailMessage = "LOCGeneralUpdateFailMessage"; /// /// Some background task is currently in progress. Do you want to cancel it and proceed with the update? /// public const string UpdateProgressCancelAsk = "LOCUpdateProgressCancelAsk"; /// /// Some background task is currently in progress. Do you want to cancel it and exit Playnite? /// public const string BackgroundProgressCancelAskExit = "LOCBackgroundProgressCancelAskExit"; /// /// Some background task is currently in progress. Switching modes will cancel the task, do you want to switch anyways? /// public const string BackgroundProgressCancelAskSwitchMode = "LOCBackgroundProgressCancelAskSwitchMode"; /// /// An update for Playnite is available /// public const string UpdateIsAvailableNotificationBody = "LOCUpdateIsAvailableNotificationBody"; /// /// Reload theme list /// public const string ThemeTestReloadList = "LOCThemeTestReloadList"; /// /// Apply selected theme /// public const string ThemeTestApplySkin = "LOCThemeTestApplySkin"; /// /// Watch file changes /// public const string ThemeTestWatchChanges = "LOCThemeTestWatchChanges"; /// /// Automatically apply theme when the source file changes /// public const string ThemeTestWatchChangesTooltip = "LOCThemeTestWatchChangesTooltip"; /// /// Script runtime /// public const string ScriptRuntime = "LOCScriptRuntime"; /// /// Execute before starting a game /// public const string PreScriptDescription = "LOCPreScriptDescription"; /// /// Execute after exiting a game /// public const string PostScriptDescription = "LOCPostScriptDescription"; /// /// Execute after a game is started /// public const string GameStartedScriptDescription = "LOCGameStartedScriptDescription"; /// /// Execute on application start /// public const string AppScriptStartupDescription = "LOCAppScriptStartupDescription"; /// /// Execute on application shutdown /// public const string AppScriptShutdownDescription = "LOCAppScriptShutdownDescription"; /// /// Game starting script /// public const string ScriptTypeStarting = "LOCScriptTypeStarting"; /// /// Game started script /// public const string ScriptTypeStarted = "LOCScriptTypeStarted"; /// /// Game stopped script /// public const string ScriptTypeExit = "LOCScriptTypeExit"; /// /// Execute global script /// public const string ExecuteGlobalScript = "LOCExecuteGlobalScript"; /// /// Global /// public const string StatsGlobal = "LOCStatsGlobal"; /// /// Filtered /// public const string StatsFiltered = "LOCStatsFiltered"; /// /// Current /// public const string MetadataDiffCurrent = "LOCMetadataDiffCurrent"; /// /// New /// public const string MetadataDiffNew = "LOCMetadataDiffNew"; /// /// Test script /// public const string TestScript = "LOCTestScript"; /// /// Show only selected items. /// public const string OnlyItemsSelectedTooltip = "LOCOnlyItemsSelectedTooltip"; /// /// Save as default /// public const string SaveAsDefault = "LOCSaveAsDefault"; /// /// Add to Favorites /// public const string FavoriteGame = "LOCFavoriteGame"; /// /// Remove from Favorites /// public const string RemoveFavoriteGame = "LOCRemoveFavoriteGame"; /// /// Hide this game /// public const string HideGame = "LOCHideGame"; /// /// Remove from Hidden /// public const string UnHideGame = "LOCUnHideGame"; /// /// Enable HDR Support /// public const string EnableHdr = "LOCEnableHdr"; /// /// Disable HDR Support /// public const string DisableHdr = "LOCDisableHdr"; /// /// Edit… /// public const string EditGame = "LOCEditGame"; /// /// Calculate install size /// public const string CalculateInstallSize = "LOCCalculateInstallSize"; /// /// Calculate install size (All games) /// public const string CalculateGamesAllInstallSize = "LOCCalculateGamesAllInstallSize"; /// /// Calculate install size (Only missing data) /// public const string CalculateGamesMissingInstallSize = "LOCCalculateGamesMissingInstallSize"; /// /// Install size /// public const string InstallSizeMenuLabel = "LOCInstallSizeMenuLabel"; /// /// Set Category… /// public const string SetGameCategory = "LOCSetGameCategory"; /// /// Set Completion Status /// public const string SetCompletionStatus = "LOCSetCompletionStatus"; /// /// Remove /// public const string RemoveGame = "LOCRemoveGame"; /// /// Play /// public const string PlayGame = "LOCPlayGame"; /// /// Install /// public const string InstallGame = "LOCInstallGame"; /// /// Game Options /// public const string GameOptions = "LOCGameOptions"; /// /// Details /// public const string GameDetails = "LOCGameDetails"; /// /// Uninstall /// public const string UninstallGame = "LOCUninstallGame"; /// /// Open Installation Location /// public const string OpenGameLocation = "LOCOpenGameLocation"; /// /// Create Desktop Shortcut /// public const string CreateDesktopShortcut = "LOCCreateDesktopShortcut"; /// /// Open Manual /// public const string OpenGameManual = "LOCOpenGameManual"; /// /// More /// public const string MoreAction = "LOCMoreAction"; /// /// Managed by the library plugin /// public const string PlayActionUsePlugin = "LOCPlayActionUsePlugin"; /// /// The game starting process will be managed by the library plugin responsible for this game. /// public const string PlayActionUsePluginTooltip = "LOCPlayActionUsePluginTooltip"; /// /// No relevant information about the '{0}' game has been found on the specified page. /// public const string MetadownloadNoResultsMessage = "LOCMetadownloadNoResultsMessage"; /// /// Tip: You can use more advanced metadata download process while editing single game via "Edit" menu option. /// public const string MetadownloadSingleGameTip = "LOCMetadownloadSingleGameTip"; /// /// Not available when some action is in progress. /// public const string ProgreessAvailabilityMessage = "LOCProgreessAvailabilityMessage"; /// /// Description text is HTML syntax sensitive /// public const string DescriptionHTMLSupportTooltip = "LOCDescriptionHTMLSupportTooltip"; /// /// Game time is recorded in seconds. /// public const string DescriptionPlaytimeSeconds = "LOCDescriptionPlaytimeSeconds"; /// /// Install size is indicated in bytes. /// public const string DescriptionSizeBytes = "LOCDescriptionSizeBytes"; /// /// Release date must be set in 'year-month-day' format. Month and Day values can be omitted. /// public const string ReleaseDateTooltip = "LOCReleaseDateTooltip"; /// /// Values from 0 to 100 or empty for no score. /// public const string DescriptionScoreValues = "LOCDescriptionScoreValues"; /// /// Playnite's development is supported by these patrons and Ko-fi members: /// public const string PatreonDevelopMessage = "LOCPatreonDevelopMessage"; /// /// Code, localization and other contributors in no particular order: /// public const string AboutContributorsMessage = "LOCAboutContributorsMessage"; /// /// Cancel game monitoring? /// public const string CancelMonitoringAskTitle = "LOCCancelMonitoringAskTitle"; /// /// Installation monitoring is currently running. Do you want to cancel the process and return the game to the previous state? /// public const string CancelMonitoringSetupAsk = "LOCCancelMonitoringSetupAsk"; /// /// Game execution monitoring is currently running. Do you want to cancel the process and return the game to the previous state? /// public const string CancelMonitoringExecutionAsk = "LOCCancelMonitoringExecutionAsk"; /// /// Time Played /// public const string TimePlayed = "LOCTimePlayed"; /// /// Last Played /// public const string LastPlayed = "LOCLastPlayed"; /// /// {0}d {1}h {2}m /// public const string PlayedDays = "LOCPlayedDays"; /// /// {0}h {1}m /// public const string PlayedHours = "LOCPlayedHours"; /// /// {0} minutes /// public const string PlayedMinutes = "LOCPlayedMinutes"; /// /// {0} seconds /// public const string PlayedSeconds = "LOCPlayedSeconds"; /// /// Not Played /// public const string PlayedNone = "LOCPlayedNone"; /// /// Opening Desktop mode… /// public const string OpeningDesktopModeMessage = "LOCOpeningDesktopModeMessage"; /// /// Opening Fullscreen mode… /// public const string OpeningFullscreenModeMessage = "LOCOpeningFullscreenModeMessage"; /// /// Loading game library… /// public const string OpeningDatabase = "LOCOpeningDatabase"; /// /// Calculating install size… /// public const string CalculatingInstallSizeMessage = "LOCCalculatingInstallSizeMessage"; /// /// Calculating install size of {0}… /// public const string CalculatingInstallSizeOfGameMessage = "LOCCalculatingInstallSizeOfGameMessage"; /// /// Failed to install script file. /// public const string ScriptInstallFail = "LOCScriptInstallFail"; /// /// Script installed successfully. /// public const string ScriptInstallSuccess = "LOCScriptInstallSuccess"; /// /// Install Script /// public const string InstallScript = "LOCInstallScript"; /// /// Script error /// public const string ScriptError = "LOCScriptError"; /// /// Failed to execute extension function. /// public const string ScriptExecutionError = "LOCScriptExecutionError"; /// /// Open metadata folder /// public const string OpenMetadataFolder = "LOCOpenMetadataFolder"; /// /// Calculate /// public const string InstallSizeCalculate = "LOCInstallSizeCalculate"; /// /// Automatically calculates the install size using the ROMs if the game has any or the installation directory if it has been set /// public const string InstallSizeCalculateEditButtonTooltip = "LOCInstallSizeCalculateEditButtonTooltip"; /// /// {0} client is not installed. /// public const string ClientNotInstalledError = "LOCClientNotInstalledError"; /// /// {0} client will now open. Please sign in and then close this message. /// public const string SignInExternalNotif = "LOCSignInExternalNotif"; /// /// Waiting for user to sign in, please close this when you're done… /// public const string SignInExternalWaitMessage = "LOCSignInExternalWaitMessage"; /// /// Game's installation folder not found. /// public const string InstallDirNotFoundError = "LOCInstallDirNotFoundError"; /// /// Invalid game action configuration. /// public const string InvalidGameActionSettings = "LOCInvalidGameActionSettings"; /// /// Troubleshooting account sync issues /// public const string TroubleShootingAccountLink = "LOCTroubleShootingAccountLink"; /// /// Troubleshooting issues /// public const string TroubleShootingIssues = "LOCTroubleShootingIssues"; /// /// Rename item /// public const string RenameItem = "LOCRenameItem"; /// /// Add new item /// public const string AddNewItem = "LOCAddNewItem"; /// /// Enter name /// public const string EnterName = "LOCEnterName"; /// /// Enter new name /// public const string EnterNewName = "LOCEnterNewName"; /// /// Less than an hour /// public const string PLaytimeLessThenAnHour = "LOCPLaytimeLessThenAnHour"; /// /// 1 to 10 hours /// public const string PLaytime1to10 = "LOCPLaytime1to10"; /// /// 10 to 100 hours /// public const string PLaytime10to100 = "LOCPLaytime10to100"; /// /// 100 to 500 hours /// public const string PLaytime100to500 = "LOCPLaytime100to500"; /// /// 500 to 1000 hours /// public const string PLaytime500to1000 = "LOCPLaytime500to1000"; /// /// Over 1000 hours /// public const string PLaytime1000plus = "LOCPLaytime1000plus"; /// /// Playnite must be restarted to complete the installation. Do you want to restart now? /// public const string ExtInstallationRestartNotif = "LOCExtInstallationRestartNotif"; /// /// Extension is not packaged properly. /// public const string GeneralExtensionPackageError = "LOCGeneralExtensionPackageError"; /// /// Theme is not packaged properly. /// public const string GeneralThemePackageError = "LOCGeneralThemePackageError"; /// /// Extension "{0}" failed to load properly. /// public const string SpecificExtensionLoadError = "LOCSpecificExtensionLoadError"; /// /// Can't load "{0}" extension, current Playnite version is not supported. /// public const string SpecificExtensionLoadSDKError = "LOCSpecificExtensionLoadSDKError"; /// /// Theme "{0}" failed to load properly. /// public const string SpecificThemeLoadError = "LOCSpecificThemeLoadError"; /// /// Can't load "{0}" theme, current Playnite version is not supported. /// public const string SpecificThemeLoadSDKError = "LOCSpecificThemeLoadSDKError"; /// /// Extension failed to load properly. /// public const string GeneralExtensionLoadError = "LOCGeneralExtensionLoadError"; /// /// Theme failed to load properly. /// public const string GeneralThemeLoadError = "LOCGeneralThemeLoadError"; /// /// Theme/Extension is using unsupported API version. /// public const string GeneralExtensionInstallApiVersionFails = "LOCGeneralExtensionInstallApiVersionFails"; /// /// Installation was successful. /// public const string GeneralExtensionInstallSuccess = "LOCGeneralExtensionInstallSuccess"; /// /// Install add-on? /// public const string GeneralExtensionInstallTitle = "LOCGeneralExtensionInstallTitle"; /// /// Generic /// public const string ExtensionGeneric = "LOCExtensionGeneric"; /// /// Failed to install "{0}" add-on. /// public const string AddonInstallFaild = "LOCAddonInstallFaild"; /// /// Failed to install extension. /// public const string ExtensionInstallFail = "LOCExtensionInstallFail"; /// /// Do you want to install a new extension? /// public const string ExtensionInstallPrompt = "LOCExtensionInstallPrompt"; /// /// Do you want to update "{0}" extension? /// public const string ExtensionUpdatePrompt = "LOCExtensionUpdatePrompt"; /// /// Failed to install theme. /// public const string ThemeInstallFail = "LOCThemeInstallFail"; /// /// Do you want to install a new theme? /// public const string ThemeInstallPrompt = "LOCThemeInstallPrompt"; /// /// Do you want to update "{0}" theme? /// public const string ThemeUpdatePrompt = "LOCThemeUpdatePrompt"; /// /// You are about to leave Playnite and navigate to the following web page using your default web browser. Do you want to continue? /// public const string UrlNavigationMessage = "LOCUrlNavigationMessage"; /// /// The selected image(s) might be too large for optimal performance. Using very large images can result in worse UI responsiveness and increased memory usage. /// public const string GameImageSizeWarning = "LOCGameImageSizeWarning"; /// /// Performance Warning /// public const string PerformanceWarningTitle = "LOCPerformanceWarningTitle"; /// /// Don't Show Again /// public const string DontShowAgainTitle = "LOCDontShowAgainTitle"; /// /// File with extension {0} is not compatible. /// public const string IncompatibleDragAndDropExtensionError = "LOCIncompatibleDragAndDropExtensionError"; /// /// Incompatible file extension /// public const string IncompatibleDragAndDropExtensionErrorTitle = "LOCIncompatibleDragAndDropExtensionErrorTitle"; /// /// Selected image file might be too large for optimal performance. /// public const string LargeMediaWarningTooltip = "LOCLargeMediaWarningTooltip"; /// /// Are you sure you want to uninstall selected theme? Uninstallation will be queued to next application start. /// public const string ThemeUninstallQuestion = "LOCThemeUninstallQuestion"; /// /// Built-in themes can't be uninstalled. /// public const string ThemeBuiltInUninstallHint = "LOCThemeBuiltInUninstallHint"; /// /// This theme doesn't support this version of Playnite. /// public const string ThemeUnsupported = "LOCThemeUnsupported"; /// /// Are you sure you want to uninstall selected extension? Uninstallation will be queued to next application start. /// public const string ExtensionUninstallQuestion = "LOCExtensionUninstallQuestion"; /// /// Built-in extensions can't be uninstalled. /// public const string ExtensionBuiltInUninstallHint = "LOCExtensionBuiltInUninstallHint"; /// /// This extension doesn't support this version of Playnite. /// public const string ExtensionUnsupported = "LOCExtensionUnsupported"; /// /// Installation folder /// public const string ExtensionInstallDir = "LOCExtensionInstallDir"; /// /// Data folder /// public const string ExtensionDataDir = "LOCExtensionDataDir"; /// /// Generating diagnostics package… /// public const string DiagGenerating = "LOCDiagGenerating"; /// /// Uploading diagnostics package… /// public const string DiagUploading = "LOCDiagUploading"; /// /// Import file… /// public const string AddFromExe = "LOCAddFromExe"; /// /// What is this? /// public const string WhatIsThis = "LOCWhatIsThis"; /// /// Are you sure you want to do this? /// public const string ConfirumationAskGeneric = "LOCConfirumationAskGeneric"; /// /// Total play time /// public const string StatsTotalPlayTime = "LOCStatsTotalPlayTime"; /// /// Average play time /// public const string StatsAvaragePlayTime = "LOCStatsAvaragePlayTime"; /// /// Top play time /// public const string StatsTopPlayTime = "LOCStatsTopPlayTime"; /// /// Total install size /// public const string StatsTotalInstallSize = "LOCStatsTotalInstallSize"; /// /// Overview /// public const string OverviewLabel = "LOCOverviewLabel"; /// /// Sidebar /// public const string Sidebar = "LOCSidebar"; /// /// Show on Sidebar /// public const string ToolsShowOnSidebar = "LOCToolsShowOnSidebar"; /// /// Reset settings /// public const string SettingsReset = "LOCSettingsReset"; /// /// All application settings will be reset to default values, excluding: /// public const string SettingsDefaultResetDesc = "LOCSettingsDefaultResetDesc"; /// /// For developers /// public const string SettingsForDevelopers = "LOCSettingsForDevelopers"; /// /// External extensions /// public const string SettingsExternalExtensions = "LOCSettingsExternalExtensions"; /// /// Enter full folder path. /// public const string SettingsNewExternalExtensionBox = "LOCSettingsNewExternalExtensionBox"; /// /// Achievements /// public const string CommonLinksAchievements = "LOCCommonLinksAchievements"; /// /// Forum /// public const string CommonLinksForum = "LOCCommonLinksForum"; /// /// News /// public const string CommonLinksNews = "LOCCommonLinksNews"; /// /// Store Page /// public const string CommonLinksStorePage = "LOCCommonLinksStorePage"; /// /// The initial setup is not complete. Playnite will now restart to Desktop Mode to finish the procedure. /// public const string FullscreenFirstTimeError = "LOCFullscreenFirstTimeError"; /// /// Recently Played /// public const string QuickFilterRecentlyPlayed = "LOCQuickFilterRecentlyPlayed"; /// /// Favorites /// public const string QuickFilterFavorites = "LOCQuickFilterFavorites"; /// /// Most Played /// public const string QuickFilterMostPlayed = "LOCQuickFilterMostPlayed"; /// /// All /// public const string QuickFilterAllGames = "LOCQuickFilterAllGames"; /// /// There are filters applied. /// public const string GameListFiltered = "LOCGameListFiltered"; /// /// There are additional filters applied. /// public const string GameListExtraFiltered = "LOCGameListExtraFiltered"; /// /// Showing search results for: /// public const string GameListSearchResults = "LOCGameListSearchResults"; /// /// An item with the same name already exists. /// public const string ItemAlreadyExists = "LOCItemAlreadyExists"; /// /// Limit selection to current filter /// public const string RandomGameLimistToFilter = "LOCRandomGameLimistToFilter"; /// /// Pick another /// public const string RandomGamePickAnother = "LOCRandomGamePickAnother"; /// /// Add-ons… /// public const string MenuAddons = "LOCMenuAddons"; /// /// Installed /// public const string ExtensionsInstalled = "LOCExtensionsInstalled"; /// /// Extensions settings /// public const string ExtensionsSettings = "LOCExtensionsSettings"; /// /// Browse /// public const string ExtensionsBrowse = "LOCExtensionsBrowse"; /// /// Updates /// public const string ExtensionsUpdates = "LOCExtensionsUpdates"; /// /// Updates ({0}) /// public const string ExtensionsUpdatesCount = "LOCExtensionsUpdatesCount"; /// /// Management of installed extensions and themes, including their settings, has been moved to a new "Add-ons" menu. /// public const string AddonsConfigMoveInfo = "LOCAddonsConfigMoveInfo"; /// /// All currently installed library integration extensions can be configured here. /// public const string LibrariesConfigWindowDescription = "LOCLibrariesConfigWindowDescription"; /// /// Themes Desktop /// public const string AddonsThemesDesktop = "LOCAddonsThemesDesktop"; /// /// Themes Fullscreen /// public const string AddonsThemesFullscren = "LOCAddonsThemesFullscren"; /// /// Searching… /// public const string AddonsSearching = "LOCAddonsSearching"; /// /// Add-on is not compatible with this version of Playnite. /// public const string AddonErrorNotCompatible = "LOCAddonErrorNotCompatible"; /// /// Failed to download add-on installation package. /// public const string AddonErrorDownloadFailed = "LOCAddonErrorDownloadFailed"; /// /// Failed to download add-on installation manifest. /// public const string AddonErrorManifestDownloadError = "LOCAddonErrorManifestDownloadError"; /// /// Application restart is required to apply pending changes. /// public const string AddonChangesRestart = "LOCAddonChangesRestart"; /// /// This add-on is scheduled for installation. /// public const string AddonQueuedForInstall = "LOCAddonQueuedForInstall"; /// /// Install /// public const string AddonInstall = "LOCAddonInstall"; /// /// Re-install /// public const string AddonReinstall = "LOCAddonReinstall"; /// /// Uninstall /// public const string AddonUninstall = "LOCAddonUninstall"; /// /// Already installed /// public const string AddonAlreadyInstalled = "LOCAddonAlreadyInstalled"; /// /// No new add-on updates found. /// public const string AddonNoAddonsAvailable = "LOCAddonNoAddonsAvailable"; /// /// Update add-ons /// public const string AddonUpdateAddons = "LOCAddonUpdateAddons"; /// /// Changelog is not available /// public const string AddonChangelogNotAvailavble = "LOCAddonChangelogNotAvailavble"; /// /// Scheduled for installation /// public const string AddonUpdateStatusDownloaded = "LOCAddonUpdateStatusDownloaded"; /// /// Download failed /// public const string AddonUpdateStatusFailed = "LOCAddonUpdateStatusFailed"; /// /// License rejected /// public const string AddonUpdateStatusLicenseRejected = "LOCAddonUpdateStatusLicenseRejected"; /// /// Downloading {0}… /// public const string AddonDownloadingAddon = "LOCAddonDownloadingAddon"; /// /// Looking for add-on updates… /// public const string AddonLookingForUpdates = "LOCAddonLookingForUpdates"; /// /// Looking for program updates… /// public const string AppLookingForUpdates = "LOCAppLookingForUpdates"; /// /// One or more add-on updates are available. /// public const string AddonUpdatesAvailable = "LOCAddonUpdatesAvailable"; /// /// Select items to update /// public const string AddonSelectToUpdate = "LOCAddonSelectToUpdate"; /// /// Extension development instance /// public const string AddonDevReferenceLoaded = "LOCAddonDevReferenceLoaded"; /// /// {0} license agreement /// public const string AddonLicenseWindowTitle = "LOCAddonLicenseWindowTitle"; /// /// Accept /// public const string LicenseAccept = "LOCLicenseAccept"; /// /// Decline /// public const string LicenseDecline = "LOCLicenseDecline"; /// /// Include library integration play actions /// public const string IncludePluginGameActions = "LOCIncludePluginGameActions"; /// /// Select action /// public const string SelectActionTitle = "LOCSelectActionTitle"; /// /// Tracking Mode /// public const string ActionTrackingMode = "LOCActionTrackingMode"; /// /// Tracking Path /// public const string ActionTrackingPath = "LOCActionTrackingPath"; /// /// Initial tracking delay /// public const string ActionTrackingTrackingDelay = "LOCActionTrackingTrackingDelay"; /// /// Tracking frequency /// public const string ActionTrackingFrequency = "LOCActionTrackingFrequency"; /// /// Link /// public const string GameActionTypeLink = "LOCGameActionTypeLink"; /// /// File /// public const string GameActionTypeFile = "LOCGameActionTypeFile"; /// /// Emulator /// public const string GameActionTypeEmulator = "LOCGameActionTypeEmulator"; /// /// Script /// public const string GameActionTypeScript = "LOCGameActionTypeScript"; /// /// Default /// public const string ActionTrackingModeDefault = "LOCActionTrackingModeDefault"; /// /// Process /// public const string ActionTrackingModeProcess = "LOCActionTrackingModeProcess"; /// /// Folder /// public const string ActionTrackingModeDirectory = "LOCActionTrackingModeDirectory"; /// /// Original process /// public const string ActionTrackingOriginalProcess = "LOCActionTrackingOriginalProcess"; /// /// Process name /// public const string ActionTrackingProcessName = "LOCActionTrackingProcessName"; /// /// Log trace messages /// public const string DevelTraceLogEnable = "LOCDevelTraceLogEnable"; /// /// Following changes overwrite data for all currently selected games! /// public const string MultiEditOverwriteWarning = "LOCMultiEditOverwriteWarning"; /// /// None /// public const string GridViewSpacingModeNone = "LOCGridViewSpacingModeNone"; /// /// Uniform /// public const string GridViewSpacingModeUniform = "LOCGridViewSpacingModeUniform"; /// /// Items only /// public const string GridViewSpacingModeBetweenItemsOnly = "LOCGridViewSpacingModeBetweenItemsOnly"; /// /// Start and end only /// public const string GridViewSpacingModeStartAndEndOnly = "LOCGridViewSpacingModeStartAndEndOnly"; /// /// Scrolling sensitivity /// public const string ScrollingSensitivity = "LOCScrollingSensitivity"; /// /// Smooth scrolling /// public const string SmoothScrolling = "LOCSmoothScrolling"; /// /// Animation speed /// public const string SmoothScrollingSpeed = "LOCSmoothScrollingSpeed"; /// /// Remove item? /// public const string AskRemoveItemTitle = "LOCAskRemoveItemTitle"; /// /// Are you sure you want to remove this item? /// public const string AskRemoveItemMessage = "LOCAskRemoveItemMessage"; /// /// Show buttons on top panel: /// public const string SettingsTopPanelItems = "LOCSettingsTopPanelItems"; /// /// General view settings /// public const string SettingsTopPanelGeneralViewItem = "LOCSettingsTopPanelGeneralViewItem"; /// /// Grouping settings /// public const string SettingsTopPanelGroupingItem = "LOCSettingsTopPanelGroupingItem"; /// /// Sorting settings /// public const string SettingsTopPanelSortingItem = "LOCSettingsTopPanelSortingItem"; /// /// Filter presets /// public const string SettingsTopPanelFilterPresetsItem = "LOCSettingsTopPanelFilterPresetsItem"; /// /// Plugin items position /// public const string TopPanelPluginPanelPosition = "LOCTopPanelPluginPanelPosition"; /// /// Section separator width /// public const string TopPanelSeparatorWidth = "LOCTopPanelSeparatorWidth"; /// /// Move main menu button to the sidebar /// public const string TopPanelMainButtonMove = "LOCTopPanelMainButtonMove"; /// /// Explorer panel /// public const string TopPanelExplorerSwitch = "LOCTopPanelExplorerSwitch"; /// /// Random game picker /// public const string TopPanelSelectRandomGameButton = "LOCTopPanelSelectRandomGameButton"; /// /// Views random game selector /// public const string TopPanelViewSelectRandomGameButton = "LOCTopPanelViewSelectRandomGameButton"; /// /// Select random game from the view /// public const string TopPanelViewSelectRandomGameButtonTooltip = "LOCTopPanelViewSelectRandomGameButtonTooltip"; /// /// Save grouping and sorting settings /// public const string FilterPresetSaveViewOptions = "LOCFilterPresetSaveViewOptions"; /// /// Show as quick filter in Fullscreen mode /// public const string FilterPresetShowOnFSTopPanel = "LOCFilterPresetShowOnFSTopPanel"; /// /// In past 7 days /// public const string InPast7Days = "LOCInPast7Days"; /// /// In past 31 days /// public const string InPast31Days = "LOCInPast31Days"; /// /// In past 365 days /// public const string InPast365Days = "LOCInPast365Days"; /// /// More than 365 days ago /// public const string MoreThan365DaysAgo = "LOCMoreThan365DaysAgo"; /// /// Configure /// public const string Configure = "LOCConfigure"; /// /// Save preset /// public const string FilterPresetSave = "LOCFilterPresetSave"; /// /// Minimize after starting game /// public const string SettingsMinimizeAfterStartingGame = "LOCSettingsMinimizeAfterStartingGame"; /// /// Minimize Playnite after a game is started. /// public const string SettingsMinimizeAfterStartingGameDescription = "LOCSettingsMinimizeAfterStartingGameDescription"; /// /// Font Size /// public const string SettingsFontSize = "LOCSettingsFontSize"; /// /// Font Size Small /// public const string SettingsFontSizeSmall = "LOCSettingsFontSizeSmall"; /// /// Enable game controller API support /// public const string SettingsControllerApi = "LOCSettingsControllerApi"; /// /// Game controller support /// public const string SettingsXInputProcessing = "LOCSettingsXInputProcessing"; /// /// If disabled, Playnite won't accept any game controller inputs. /// public const string SettingsXInputProcessingDescription = "LOCSettingsXInputProcessingDescription"; /// /// Show items on main menu: /// public const string SettingsShowItemsMainMenuHeader = "LOCSettingsShowItemsMainMenuHeader"; /// /// Inverted X/A main view button binding /// public const string SettingsSwapMainViewXAButtons = "LOCSettingsSwapMainViewXAButtons"; /// /// Swaps button bindings for starting a game and showing game details page on main view. /// public const string SettingsSwapMainViewXAButtonsDescriptions = "LOCSettingsSwapMainViewXAButtonsDescriptions"; /// /// Swap confirmation/cancelation button binding /// public const string SettingsSwapConfirmCancelButtons = "LOCSettingsSwapConfirmCancelButtons"; /// /// Inverts A/B button bindings for confirmation and cancellation. /// public const string SettingsSwapConfirmCancelButtonsDescriptions = "LOCSettingsSwapConfirmCancelButtonsDescriptions"; /// /// Primary controller only /// public const string SettingsPrimaryControllerOnly = "LOCSettingsPrimaryControllerOnly"; /// /// Only accept inputs from primary controller when enabled. /// public const string SettingsPrimaryControllerOnlyDescription = "LOCSettingsPrimaryControllerOnlyDescription"; /// /// Guide button focuses Playnite /// public const string SettingsRefocusOnGuidButton = "LOCSettingsRefocusOnGuidButton"; /// /// Interface volume /// public const string SettingsInterfaceVolume = "LOCSettingsInterfaceVolume"; /// /// Background volume /// public const string SettingsMusiVolume = "LOCSettingsMusiVolume"; /// /// Mute when in background /// public const string SettingsMuteBackground = "LOCSettingsMuteBackground"; /// /// Failed to initialize audio interface. /// public const string ErrorAudioInterfaceInit = "LOCErrorAudioInterfaceInit"; /// /// Output API /// public const string SettingsAudioOutputApi = "LOCSettingsAudioOutputApi"; /// /// API used for audio output. Change if you are experiencing issues with sound. /// public const string SettingsAudioOutputApiDescription = "LOCSettingsAudioOutputApiDescription"; /// /// General /// public const string SettingsFSSectionGeneral = "LOCSettingsFSSectionGeneral"; /// /// Visuals /// public const string SettingsFSSectionVisuals = "LOCSettingsFSSectionVisuals"; /// /// Audio /// public const string SettingsFSSectionAudio = "LOCSettingsFSSectionAudio"; /// /// Layout /// public const string SettingsFSSectionLayout = "LOCSettingsFSSectionLayout"; /// /// Menus /// public const string SettingsFSSectionMenus = "LOCSettingsFSSectionMenus"; /// /// Input /// public const string SettingsFSSection = "LOCSettingsFSSection"; /// /// {0} is starting… /// public const string GameIsStarting = "LOCGameIsStarting"; /// /// {0} is running… /// public const string GameIsRunning = "LOCGameIsRunning"; /// /// Caps /// public const string TextInputCapitalize = "LOCTextInputCapitalize"; /// /// Space /// public const string TextInputSpace = "LOCTextInputSpace"; /// /// Image rendering scaler /// public const string SettingsImageScalerMode = "LOCSettingsImageScalerMode"; /// /// Alternative /// public const string SettingsImageScalingAlternative = "LOCSettingsImageScalingAlternative"; /// /// Balanced /// public const string SettingsImageScalingBalanced = "LOCSettingsImageScalingBalanced"; /// /// Quality /// public const string SettingsImageScalingQuality = "LOCSettingsImageScalingQuality"; /// /// Quality: /// public const string SettingsImageScalerModeTooltip = "LOCSettingsImageScalerModeTooltip"; /// /// Select file… /// public const string SelectFileTooltip = "LOCSelectFileTooltip"; /// /// Select folder… /// public const string SelectDirectoryTooltip = "LOCSelectDirectoryTooltip"; /// /// Startup script /// public const string StartupScript = "LOCStartupScript"; /// /// Please note that both extensions and themes can greatly affect Playnite's performance, stability and security. /// public const string AddonPerfNotice = "LOCAddonPerfNotice"; /// /// Choose on startup /// public const string GameActionSelectOnStart = "LOCGameActionSelectOnStart"; /// /// Choose on startup /// public const string EmulatorSelectOnStart = "LOCEmulatorSelectOnStart"; /// /// Built-in profiles /// public const string EmulatorBuiltInProfiles = "LOCEmulatorBuiltInProfiles"; /// /// Built-in profile /// public const string EmulatorBuiltInProfile = "LOCEmulatorBuiltInProfile"; /// /// Custom profiles /// public const string EmulatorCustomProfiles = "LOCEmulatorCustomProfiles"; /// /// Custom profile /// public const string EmulatorCustomProfile = "LOCEmulatorCustomProfile"; /// /// Handled by a built-in script /// public const string EmulatorFunctionHandledByScript = "LOCEmulatorFunctionHandledByScript"; /// /// Emulator specification /// public const string EmulatorSpec = "LOCEmulatorSpec"; /// /// Platform specification /// public const string PlatformSpec = "LOCPlatformSpec"; /// /// Region specification /// public const string RegionSpec = "LOCRegionSpec"; /// /// Execute before starting emulator /// public const string EmulatorPreScriptDescription = "LOCEmulatorPreScriptDescription"; /// /// Execute after emulator is started /// public const string EmulatorPostScriptDescription = "LOCEmulatorPostScriptDescription"; /// /// Execute after exiting emulator /// public const string EmulatorStartedScriptDescription = "LOCEmulatorStartedScriptDescription"; /// /// Emulator executable not found. /// public const string ErrorEmulatorExecutableNotFound = "LOCErrorEmulatorExecutableNotFound"; /// /// Emulator specification not found. /// public const string ErrorEmulatorSpecificationNotFound = "LOCErrorEmulatorSpecificationNotFound"; /// /// Emulator startup script not found. /// public const string ErrorEmulatorStartupScriptNotFound = "LOCErrorEmulatorStartupScriptNotFound"; /// /// Split as separate games /// public const string SplitEmuImportSplitGames = "LOCSplitEmuImportSplitGames"; /// /// Merge into one game /// public const string SplitEmuImportMergeGames = "LOCSplitEmuImportMergeGames"; /// /// Set platform /// public const string EmuImportAssignPlatform = "LOCEmuImportAssignPlatform"; /// /// Set region /// public const string EmuImportAssignRegion = "LOCEmuImportAssignRegion"; /// /// Scan folder /// public const string EmuScanDirectory = "LOCEmuScanDirectory"; /// /// Scan configurations /// public const string EmuScanConfigurations = "LOCEmuScanConfigurations"; /// /// Exclude patterns from checksum scan /// public const string EmuCrcExcludeFileTypes = "LOCEmuCrcExcludeFileTypes"; /// /// Files matching specified pattern(s) won't be scanned for checksum and will be matched by file name. See emulator help page for more information. /// public const string EmuCrcExcludeFileTypesTooltip = "LOCEmuCrcExcludeFileTypesTooltip"; /// /// Scan with emulator /// public const string EmuScanEmulator = "LOCEmuScanEmulator"; /// /// Name has to be set when saving new configuration. /// public const string ScanConfigNameError = "LOCScanConfigNameError"; /// /// Emulator or emulator profile is not set. /// public const string ScanConfigNoEmulatorError = "LOCScanConfigNoEmulatorError"; /// /// Directory to scan is not specified or it doesn't exist. /// public const string ScanConfigDirectoryError = "LOCScanConfigDirectoryError"; /// /// Scan configuration is not set properly. /// public const string ScanConfigError = "LOCScanConfigError"; /// /// Include in bulk scan auto-scan /// public const string EmuScanIncludeGlobalUpdate = "LOCEmuScanIncludeGlobalUpdate"; /// /// Failed to scan folder for emulators. /// public const string EmulatorScanFailed = "LOCEmulatorScanFailed"; /// /// Failed to scan folder(s) for emulated games. /// public const string EmulatedGameScanFailed = "LOCEmulatedGameScanFailed"; /// /// Hide imported /// public const string EmuScanHideImported = "LOCEmuScanHideImported"; /// /// Profiles to import: /// public const string EmuImportProfilesToImportHeader = "LOCEmuImportProfilesToImportHeader"; /// /// Auto-scan configurations /// public const string EmuAutoScanConfigurations = "LOCEmuAutoScanConfigurations"; /// /// Save as auto-scan configuration /// public const string EmuSaveScanConfig = "LOCEmuSaveScanConfig"; /// /// Saves configuration for later use during library update. Saved configurations can be managed via "Configure Emulators" menu. /// public const string EmuSaveScanConfigTooltip = "LOCEmuSaveScanConfigTooltip"; /// /// Import using relative paths /// public const string EmuImportWithRelativePaths = "LOCEmuImportWithRelativePaths"; /// /// If possible import game files using paths relative to Playnite's installation folder or emulator's installation folder. /// public const string EmuImportWithRelativePathsTooltip = "LOCEmuImportWithRelativePathsTooltip"; /// /// Scan subfolders /// public const string EmuImportScanSubfolders = "LOCEmuImportScanSubfolders"; /// /// Scan inside archives /// public const string EmuImportScanInsideArchives = "LOCEmuImportScanInsideArchives"; /// /// Merge related files /// public const string EmuMergeRelatedFiles = "LOCEmuMergeRelatedFiles"; /// /// Merge related game files, like individual game discs, under one game entry. /// public const string EmuMergeRelatedFilesTooltip = "LOCEmuMergeRelatedFilesTooltip"; /// /// Add scanner /// public const string EmuAddScanner = "LOCEmuAddScanner"; /// /// Add saved scanner /// public const string EmuAddSavedScanner = "LOCEmuAddSavedScanner"; /// /// Start scan /// public const string StartScan = "LOCStartScan"; /// /// Add scan configuration(s) with emulators to scan specific folders. Make sure that emulators are properly configured prior to importing games (via Library -> Configure Emulators menu). /// public const string EmuImportDirectoryConfigDesc = "LOCEmuImportDirectoryConfigDesc"; /// /// Default status assigned to newly added games /// public const string CompletionStatusDefaultStatusDesc = "LOCCompletionStatusDefaultStatusDesc"; /// /// Status assigned to games played for the first time /// public const string CompletionStatusPlayedStatusDesc = "LOCCompletionStatusPlayedStatusDesc"; /// /// Failed to initialize PowerShell script runtime. If you are Windows 7 user, try (re)installing PowerShell 5.1 to fix the issue. /// public const string PowerShellCreationError = "LOCPowerShellCreationError"; /// /// Filter preset with specified name already exists. Update preset with new settings? /// public const string FilterPresetNameConflict = "LOCFilterPresetNameConflict"; /// /// Automatically fill missing sorting names for batch-added or edited games /// public const string SortingNameAutofill = "LOCSortingNameAutofill"; /// /// When you edit a game, add games via a library update, an emulator folder scan, or a normal folder scan, automatically fill the "Sorting Name" field with a better sortable representation of the game's name. For example "The Witcher 3" will get a Sorting Name of "Witcher 03". This will never set a sorting name that doesn't differ from the game name, and it will only automatically update sorting names that are empty. /// public const string SortingNameAutofillTooltip = "LOCSortingNameAutofillTooltip"; /// /// These words will be removed from the start of the automatically filled Sorting Name value: /// public const string SortingNameRemovedArticles = "LOCSortingNameRemovedArticles"; /// /// Use this for ignoring words at the start of a string for sorting purposes. The default is "The", "An", and "A". /// public const string SortingNameRemovedArticlesTooltip = "LOCSortingNameRemovedArticlesTooltip"; /// /// Fill Sorting Name for games without one /// public const string SortingNameAutofillButton = "LOCSortingNameAutofillButton"; /// /// Sorting /// public const string SettingsSortingLabel = "LOCSettingsSortingLabel"; /// /// Filling Sorting Name values… /// public const string SortingNameAutofillProgress = "LOCSortingNameAutofillProgress"; /// /// Nahimic service has been detected to be running on your system. This service is known to cause rendering issues to Playnite (and other apps). /// public const string NahimicServiceWarning = "LOCNahimicServiceWarning"; /// /// Playnite is running with elevated privileges (as an administrator). This is not recommended since it gives elevated privileges to all installed extensions and all games/apps started from Playnite! /// public const string ElevatedProcessWarning = "LOCElevatedProcessWarning"; /// /// Show warning if Playnite is running with elevated privileges /// public const string ElevatedProcessWarningShowOption = "LOCElevatedProcessWarningShowOption"; /// /// Get the real size on drive when calculating the size of games /// public const string InstallSizeScanUseSizeOnDiskOption = "LOCInstallSizeScanUseSizeOnDiskOption"; /// /// If enabled, scans will be slower and will get the real size that files use in the drive. /// public const string InstallSizeScanUseSizeOnDiskOptionTooltip = "LOCInstallSizeScanUseSizeOnDiskOptionTooltip"; /// /// Following add-on(s) have been reported as potentially problematic, either due to high stability/performance impact or security issues. We strongly recommend that you uninstall them: /// public const string WarningBlacklistedExtensions = "LOCWarningBlacklistedExtensions"; /// /// Exclude online files from scan /// public const string EmuExcludeOnlineFiles = "LOCEmuExcludeOnlineFiles"; /// /// Files stored on cloud storage won't be scanned and imported if not available locally. /// public const string EmuExcludeOnlineFilesTooltip = "LOCEmuExcludeOnlineFilesTooltip"; /// /// Scan but using simplified method without file content /// public const string EmuUseSimplifiedOnlineFileScan = "LOCEmuUseSimplifiedOnlineFileScan"; /// /// Files will be imported but using less accurate method that doesn't require file content to be downloaded and present locally. /// public const string EmuUseSimplifiedOnlineFileScanTooltip = "LOCEmuUseSimplifiedOnlineFileScanTooltip"; /// /// Apply to all /// public const string MetadatSetAllFieldsToValue = "LOCMetadatSetAllFieldsToValue"; /// /// Override installation state /// public const string OverrideInstallState = "LOCOverrideInstallState"; /// /// When set, Playnite will ignore installation state (including installation directory) set by the integration plugin that imports this game. /// public const string OverrideInstallStateTooltip = "LOCOverrideInstallStateTooltip"; /// /// Only manually /// public const string OptionOnlyManually = "LOCOptionOnlyManually"; /// /// Once a day /// public const string OptionOnceADay = "LOCOptionOnceADay"; /// /// Once a week /// public const string OptionOnceAWeek = "LOCOptionOnceAWeek"; /// /// On every startup /// public const string OptionOnEveryStartup = "LOCOptionOnEveryStartup"; /// /// Check for program updates /// public const string CheckProgramUpdates = "LOCCheckProgramUpdates"; /// /// Check for add-on updates /// public const string CheckAddonUpdates = "LOCCheckAddonUpdates"; /// /// Update libraries /// public const string CheckLibraryUpdates = "LOCCheckLibraryUpdates"; /// /// Scan emulation folders /// public const string CheckEmulatedLibraryUpdates = "LOCCheckEmulatedLibraryUpdates"; /// /// Include hidden games /// public const string StatsIncludeHidden = "LOCStatsIncludeHidden"; /// /// Edit fields /// public const string MenuSetFields = "LOCMenuSetFields"; /// /// Select / Deselect all /// public const string ItemSelectionSelectDeselectAll = "LOCItemSelectionSelectDeselectAll"; /// /// Open /// public const string Open = "LOCOpen"; /// /// Activate /// public const string Activate = "LOCActivate"; /// /// Assign /// public const string Assign = "LOCAssign"; /// /// Start typing to search for games… [F1] for help /// public const string DefaultSearchDescription = "LOCDefaultSearchDescription"; /// /// Starting with # brings up a list of available commands. /// public const string DefaultSearchHint = "LOCDefaultSearchHint"; /// /// Include uninstalled games /// public const string SearchFilterUninstalled = "LOCSearchFilterUninstalled"; /// /// Include hidden games /// public const string SearchFilterHidden = "LOCSearchFilterHidden"; /// /// Uninstalled games included /// public const string SearchFilterUninstalledIncluded = "LOCSearchFilterUninstalledIncluded"; /// /// Uninstalled games excluded /// public const string SearchFilterUninstalledExcluded = "LOCSearchFilterUninstalledExcluded"; /// /// Hidden games included /// public const string SearchFilterHiddenIncluded = "LOCSearchFilterHiddenIncluded"; /// /// Hidden games excluded /// public const string SearchFilterHiddenalledExcluded = "LOCSearchFilterHiddenalledExcluded"; /// /// Play or Install /// public const string GameSearchItemActionPlay = "LOCGameSearchItemActionPlay"; /// /// Go to details /// public const string GameSearchItemActionSwitchTo = "LOCGameSearchItemActionSwitchTo"; /// /// Game menu /// public const string GameSearchItemActionOpenMenu = "LOCGameSearchItemActionOpenMenu"; /// /// Edit game /// public const string GameSearchItemActionEdit = "LOCGameSearchItemActionEdit"; /// /// Open search /// public const string OpenSearch = "LOCOpenSearch"; /// /// Search box /// public const string TopPanelSearchBox = "LOCTopPanelSearchBox"; /// /// Search button /// public const string TopPanelSearchButton = "LOCTopPanelSearchButton"; /// /// Primary game action /// public const string SearchSettingsPrimaryAction = "LOCSearchSettingsPrimaryAction"; /// /// Secondary game action /// public const string SearchSettingsSecondaryAction = "LOCSearchSettingsSecondaryAction"; /// /// CTRL-F opens global search instead of focusing search box /// public const string SearchSettingsKeyboardOpenSearch = "LOCSearchSettingsKeyboardOpenSearch"; /// /// Save game filter settings between search sessions /// public const string SearchSettingsSaveFilter = "LOCSearchSettingsSaveFilter"; /// /// Search providers /// public const string SearchSettingsSearchProviders = "LOCSearchSettingsSearchProviders"; /// /// Default keyword /// public const string SearchSettingsDefaultKeyword = "LOCSearchSettingsDefaultKeyword"; /// /// Custom keyword /// public const string SearchSettingsCustomKeyword = "LOCSearchSettingsCustomKeyword"; /// /// System wide shortcut /// public const string SearchSettingsSystemWideShortcut = "LOCSearchSettingsSystemWideShortcut"; /// /// Playnite search /// public const string SearchTitle = "LOCSearchTitle"; /// /// Extension Settings /// public const string ExtensionSettingsMenu = "LOCExtensionSettingsMenu"; /// /// Exclusions /// public const string Exclusions = "LOCExclusions"; /// /// Excluded files relative to scan folder /// public const string EmuScannerExcludedFiles = "LOCEmuScannerExcludedFiles"; /// /// Excluded folders relative to scan folder /// public const string EmuScannerExcludedFolders = "LOCEmuScannerExcludedFolders"; /// /// Add file to exclusion list /// public const string EmuImportAddROMExclusionList = "LOCEmuImportAddROMExclusionList"; /// /// Add folder to exclusion list /// public const string EmuImportAddFolderExclusionList = "LOCEmuImportAddFolderExclusionList"; /// /// Exclusions can be only added to saved scanner configurations. /// public const string EmuExclusionNoConfigError = "LOCEmuExclusionNoConfigError"; /// /// Exclusions have been added to "{0}" scanner. /// public const string EmuExclusionAddedMessage = "LOCEmuExclusionAddedMessage"; /// /// Override platform /// public const string EmuOverridePlatform = "LOCEmuOverridePlatform"; /// /// When set, scanner will assign this platform to all games, overwriting any automatically detected platforms. /// public const string EmuOverridePlatformTooltip = "LOCEmuOverridePlatformTooltip"; /// /// Include commands in default search /// public const string SearchIncludeCommandsInDefault = "LOCSearchIncludeCommandsInDefault"; /// /// When disabled, commands won't be included in default search until # prefix is used. /// public const string SearchIncludeCommandsInDefaultTooltip = "LOCSearchIncludeCommandsInDefaultTooltip"; /// /// Use fuzzy matching in name filter /// public const string NameFilterUseFuzzyMatching = "NameFilterUseFuzzyMatching"; /// /// When enabled, name filter will match game names the same way as global search. /// public const string NameFilterUseFuzzyMatchingTooltip = "NameFilterUseFuzzyMatchingTooltip"; /// /// Fields to be displayed for game results: /// public const string SearchViewGameFieldOptions = "LOCSearchViewGameFieldOptions"; /// /// Hidden Status /// public const string HiddenStatus = "LOCHiddenStatus"; /// /// Data backup was cancelled. /// public const string BackupCancelled = "LOCBackupCancelled"; /// /// Data backup failed. /// public const string BackupFailed = "LOCBackupFailed"; /// /// Data backup error /// public const string BackupErrorTitle = "LOCBackupErrorTitle"; /// /// Data backup in progress… /// public const string BackupProgress = "LOCBackupProgress"; /// /// Restoring data from backup… /// public const string BackupRestoreProgress = "LOCBackupRestoreProgress"; /// /// Failed to restore data from backup. /// public const string BackupRestoreFailed = "LOCBackupRestoreFailed"; /// /// Settings /// public const string BackupOptionSettings = "LOCBackupOptionSettings"; /// /// Game library /// public const string BackupOptionLibrary = "LOCBackupOptionLibrary"; /// /// Game library media /// public const string BackupOptionGameMedia = "LOCBackupOptionGameMedia"; /// /// Installed extensions /// public const string BackupOptionExtensions = "LOCBackupOptionExtensions"; /// /// Extensions data /// public const string BackupOptionExtensionsData = "LOCBackupOptionExtensionsData"; /// /// Installed themes /// public const string BackupOptionThemes = "LOCBackupOptionThemes"; /// /// Select data to be restored from specified backup file. /// public const string BackupRestoreMessage = "LOCBackupRestoreMessage"; /// /// Select items to be included with data backup. Application settings and game library data are included by default. /// public const string BackupDataBackupMessage = "LOCBackupDataBackupMessage"; /// /// Automatic data backup /// public const string SettingsEnableAutomaticBackup = "LOCSettingsEnableAutomaticBackup"; /// /// Auto backup frequency /// public const string SettingsAutoBackupFrequency = "LOCSettingsAutoBackupFrequency"; /// /// Backup folder /// public const string SettingsBackupFolder = "LOCSettingsBackupFolder"; /// /// Rotating backups /// public const string SettingsRotatingBackups = "LOCSettingsRotatingBackups"; /// /// Include additional data: /// public const string SettingsAutoBackupIncludeItems = "LOCSettingsAutoBackupIncludeItems"; /// /// Backup folder needs to be set if auto backup is enabled. /// public const string SettingsNoBackupDirSpecifiedError = "LOCSettingsNoBackupDirSpecifiedError"; /// /// Show notifications for patch releases only /// public const string UpdateNotifyOnlyPatches = "LOCUpdateNotifyOnlyPatches"; /// /// When enabled, only updates available for currently installed major release will result in update notification. /// public const string UpdateNotifyOnlyPatchesToolip = "LOCUpdateNotifyOnlyPatchesToolip"; /// /// Use relative dates for the past week /// public const string SettingsPastWeekRelativeFormat = "LOCSettingsPastWeekRelativeFormat"; /// /// Use relative dates in "Today", "Yesterday" etc. format if the date is less than a week old. /// public const string SettingsPastWeekRelativeFormatTooltip = "LOCSettingsPastWeekRelativeFormatTooltip"; /// /// Web image search /// public const string SettingsWebImageSearch = "LOCSettingsWebImageSearch"; /// /// Icon image search string /// public const string SettingsWebImageSearchIconTerm = "LOCSettingsWebImageSearchIconTerm"; /// /// Cover image search string /// public const string SettingsWebImageSearchCoverTerm = "LOCSettingsWebImageSearchCoverTerm"; /// /// Background image search string /// public const string SettingsWebImageSearchBackground = "LOCSettingsWebImageSearchBackground"; /// /// Getting add-on information… /// public const string GettingsAddonInformation = "LOCGettingsAddonInformation"; /// /// No metadata source is available /// public const string NoMetadataSource = "LOCNoMetadataSource"; /// /// Play action settings /// public const string ScannerConfigPlayActionSettings = "LOCScannerConfigPlayActionSettings"; /// /// Use scanner settings /// public const string ScannerConfigPlayActionSettingsScanner = "LOCScannerConfigPlayActionSettingsScanner"; /// /// Select profile on startup /// public const string ScannerConfigPlayActionSettingsSelectProfile = "LOCScannerConfigPlayActionSettingsSelectProfile"; /// /// Select emulator on startup /// public const string ScannerConfigPlayActionSettingsSelectEmulator = "LOCScannerConfigPlayActionSettingsSelectEmulator"; /// /// Automatic /// public const string Automatic = "LOCAutomatic"; /// /// Always on /// public const string AlwaysOn = "LOCAlwaysOn"; /// /// Always off /// public const string AlwaysOff = "LOCAlwaysOff"; /// /// Accessibility (screen reader) support /// public const string SettingsAccessibilityInterface = "LOCSettingsAccessibilityInterface"; /// /// Application menu /// public const string ApplicationMenu = "LOCApplicationMenu"; /// /// Game menu /// public const string GameMenu = "LOCGameMenu"; /// /// Program folder /// public const string ProgramFolder = "LOCProgramFolder"; /// /// User data directory /// public const string UserDataFolder = "LOCUserDataFolder"; /// /// Library file corruption has been detected, Playnite will now shutdown. /// public const string DBCorruptionCrashMessage = "LOCDBCorruptionCrashMessage"; /// /// Do you want to save changes you made? /// public const string UnsavedChangesAskMessage = "LOCUnsavedChangesAskMessage"; /// /// Portable installation /// public const string PortableInstallation = "LOCPortableInstallation"; /// /// No controllers detected /// public const string NoControllersDetected = "LOCNoControllersDetected"; } } ================================================ FILE: source/Playnite/Localization/af_ZA.xaml ================================================  Engels Playnite taal Gaan Uit Filter Aktief Filter onaktief Additionele filters Filters Filter Ongeldige Data Stoor Veranderinge? Tuisblad by www.playnite.link Bronkode by GitHub Maak diag. pakket Stuur diag. inligting Oor Playnite Gemaak deur Josef Němec Ken Kategorie Toe Stel Kategorie Voeg Kategorie By Gemerk - Ken kategorie toe Ongemerk - Verwyder kategorie Onbepaald - Geen veranderinge (wanneer vele speletjies gewysig word) Geen Kategorie Geen Platform Oeps! Iets het verkeerd gegaan 'n Onherwinbare fout het voorgekom. As jy ons wil help om hierdie kwessie op te los, beskryf asseblief die aksies wat geneem was voor die crash, en stuur dan diagnostiese inligting. As jy aanlyn is sal die pakket na die Playnite bediener opgelaai word vir ontleding. Alternatiewelik, kan jy op die 'Meld crash aan' knoppie klik om 'n nuwe GitHub kwessie te maak en die crash per hand aan te meld. Dankie vir jou hulp. Meld Crash Aan Begin Playnite Oor Begin oor in Veilige Modus Verlaat Playnite Biblioteekbestuurder Verwyder Speletjie(s)? Steam Vriende Verwyder Verwyder ongebruikte ================================================ FILE: source/Playnite/Localization/ar_SA.xaml ================================================  العربية لغة Playnite خروج التصفية نشطة التصفية معطلة تصفيات اضافية تصفيات تصفية بيانات غير صحيحة حفظ التغييرات؟ الصفحة الرسمية على www.playnite.link الكود المصدر على Github انشاء حزمة diag. إرسال معلومات التشخيص حول Playnite صنع بواسطة Josef Němec تحديد فئة تعيين الفئات إضافة فئة محدد - تحديد الفئة غير محدد - إزالة فئة غير معروف - لا تغيرات (عند تحرير ألعاب متعددة) بلا فئة بلا منصة عفواً! حصل خطأ ما. حدث خطأ غير قابل للاسترداد. إذا كنت تودّ مساعدتنا في حل هذه المشكلة، فالمرجو وصف الإجراءات التي تم اتخاذها قبل العطل الذي حصل ثم بعد ذلك قم بإنشاء حزمة تشخيص. إذا كنت متصلا بالأنترنت فسيتم رفع الحزمة على سيرفر Playnite ليتم تحليلها. بدلًا من ذلك، يمكنك استخدام زر خيار "تبليغ الخلل" لإنشاء خطأ جديد على GitHub والإبلاغ عن الأعطال يدويًا. شكرا لك على مساعدتك. تسبب الملحق "{0}" في حدوث خطأ لا يمكن إصلاحه. نوصي بحفظ ملف السجل والإبلاغ عن المشكلة لمطوّر الامتداد أو الملحق. إذا أستمرت المشكلة فقم بتعطيله. تسبب الملحق "{0}" في حدوث خطأ لا يمكن إصلاحه. نوصي بالإبلاغ عن المشكلة لمطوّر الامتداد أو الملحق ، إذا أستمرت المشكلة مرة أخرى فقم بتعطيله إضافة غير معروفة أو سمة جمالية "ثيم" قد تسببت بعطل لا يمكن اصلاحه. ننصحك بإقاف إضافات الطرف الثالث, وعزل الاضافات التي قد تسبب مشاكل, وإبلاغ مطور الإضافة بالمشكلة. حدث خطأ غير قابل للاسترداد. إذا أردت مساعدتنا في حل هذه المشكلة، فرجاءً قم بإرسال معلومات التّشخيص. شكرا لك. تعطيل التمديد حفظ ملف السجل إرسال معلومات تبليغ الانهيار إعادة تشغيل Playnite اعادة التشغيل بالطور الامن تعطيل كافة ملحقات الطرف الثالث واستخدام المظهر الافتراضي. إغلاق Playnite الإجراءات التي تم اتخاذها قبل العطل (الوصف يجب أن يكون بالانجليزية): مدير المكتبة إزالة اللعبة/الألعاب؟ اللعبة قيد التشغيل - لا يمكنك الإزالة لا يمكنك إزالة تثبيت اللعبة وهي قيد التشغيل. هل أنت متأكد من أنك تريد إزالة "{0}" ؟ هل أنت متأكد من إزالة {0} الألعاب؟ هل أنت متأكد من أنك تريد إزالة "{0}" ؟ سيؤدي تحديد خيار "إضافة إلى قائمة الاستبعاد" إلى منع استيراد الألعاب مرة أخرى في المرة التالية التي يتم فيها تحديث المكتبة. هل أنت متأكد من إزالة {0} الألعاب؟ سيؤدي تحديد خيار "إضافة إلى قائمة الاستبعاد" إلى منع استيراد الألعاب مرة أخرى في المرة التالية التي يتم فيها تحديث المكتبة. هل أنت متأكد أنك تريد حذف {0} إدخالًا والتي ليست قيد الاستخدام حاليًا؟ لم يتم العثور على أماكن غير مستخدمة. نعم (إضافة إلى قائمة الاستبعاد) هناك تغييرات غير محفوظة في هذا القسم جاري تحديث تنسيق مكتبة الألعاب… فشل تحديث قاعدة البيانات. تعذّر تحديث مكنبة الألعاب. مطلوب {0} ميغابايت من مساحة التخزين. خطأ في اللعبة تعذّر بدأ اللعبة. لم يتم العثور على {0} في قاعدة البيانات. تعذّر بدأ اللعبة:{0} تعذّر بدأ الإجراء:{0} تعذّر فقح موقع اللعبة:{0} تعذر الكشف عن حجم تثبيت اللعبة: {0} خطأ في فحص حجم التثبيت حدث {0} خطأ في أثناء فحص حجم التثبيت فشل إنشاء إختصار:{0} فشل فتح الدليل: {0} تعذّر تثبيت اللعبة:{0} تعذّر إلغاء تثبيت اللعبة:{0} لم يتم العثور على إجراءات بدء تشغيل اللعبة صالحة. عند استخدام إجراءات المحاكاة، تأكد من تطابق تعريفات المنصة بين تكوين اللعبة والمحاكي. المنفذ للتثبيت غير متوفر. البرنامج المساعد للمكتبة المسؤول عن هذه اللعبة معطل أو غير مثبت. تحميل بيانات التعريف الرسمية غير متاح. لم يتم تحديد أي لعبة. فشل تنفيذ إجراء السكريبت للعبة. فشل تنفيذ البرنامج النصي للتطبيق. فشل تنفيذ إجراء السكريبت العام. فشل تنفيذ إجراء نص المحاكي. فشل تنفيذ إجراء نص اللعب. لم يتم تثبيت PowerShell 3.0 أو الإصدار الأحدث منه. لم نستطع في كيفية تشغيل اللعبة. مفعل معطل حذف إزالة الغير مستخدمة إعادة تسمية نسخ إضافة الأيقونة الافتراضية صورة الغلاف الافتراضية الخلفية الإفتراضية انهاء التالي السابق تم السابق محو محو استبعاد استبعاد الكل استيراد الاسم المؤلف الوحدة السلسلة النسخة اخر تشغيل الأكثر لعبًا مرات اللعب حجم التثبيت مجلد الملاحظات أضيف تاريخ الإضافة تم تعديله تاريخ التعديل الموقع الالكتروني السار موافق حفظ إغلاق إلغاء تأكيد إعادة ضبط نعم لا أهلا وسهلا بكم مستخدم محلي عام وسائط الإعلام روابط التثبيت الاجراءت جاري التحميل… جارٍ تنزيل الوسائط ... جاري التحميل… نوع الملف الشخصي ملفات التعريف حذف تنزيل بحث دقة الوضوح اي واحدة تكبير العرض كقائمة أغلفة العرض كشبكة عرض التفاصيل مخصص الرابط شكر خاص الترخيص المساهمون جاري الخروج من Playnite… اليوم أمس الإثنين الثلاثاء الأربعاء الخميس الجمعة السبت الأحد الأسبوع الماضي الشهر الماضي السنة الماضية منذ أكثر من سنة 0 إلى 100 ميغابايت 100MB إلى 1GB 1GB إلى 5GB 5GB إلى 10GB 10GB إلى 20GB 20GB إلى 40GB 40GB إلى 100GB 100GB أو أكثر تم الاستيراد بنجاح. جميع الألعاب معرف اللعبة (ID) معرف قاعدة البيانات (ID) الإعدادات المسبقة عمود أعمدة صف صفوف لم نستطع الحصول على ملف مشغل اللعبة. لا يوجد اي ملف مشغل للعبة في الوقت الراهن. تحميل بيانات التعريف المفقودة فقط تمكين هذا الخيار سيؤدي إلى تخطي تحميل بيانات التعريف لبيانات الخانات التي تحتوي مسبقًا على معلومات. اختيار الألعاب الرجاء تحديد الألعاب التي يجب تحديثها باستخدام البيانات التعريفية الجديدة: كل الألعاب الموجودة في قاعدة البيانات كل الالعاب المصفية حاليا الالعاب المحددة فقط لا توجد حقول لبيانات التعريف محددة لم يتم تحديد حقول بيانات التعريف للتحميل. الرجاء تحديد واحد على الأقل، وتمكين موفر بيانات التعريف واحد على الأقل. المتجر الرسمي IGDB المرجو تحديد الأماكن التي يجب ملؤها تلقائيًا بواسطة Playnite والمصادر التي يجب استخدامها للحصول على البيانات منها. الرجاء التفكير في النقر على الشعار أعلاه والمساهمة في تحديثات قاعدة بيانات igdb.com من أجل تحسبن البيانات التي بستخدمها Playnite. تنزيل بيانات التعريف… جاري استيراد الألعاب المثبتة… جاري استيراد {0} لعبة… إستيراد ألعاب المحاكيات من {0} جاري تحميل تحديثات المكتبة… يتم فحص حجم الألعاب في المكتبة… جارٍ فحص حجم الألعاب المستوردة ... تم الانتهاء من تحديث المكتبة جاري تحرير الموارد… الإعدادات إعدادات… منصات و محاكيات إعداد المحاكيات… مدير المكتبات… أدوات تنزيل بيانات التعريف… أدوات البرمجيات… تهيئة التكامل… فتح عميل الطرف الثالث عملاء الطرف الثالث تحديث مكتبة الألعاب الغاء تحديث مكتبة الالعاب تحديث ملفات مخصصة إضافة لعبة يدوي… الفحص تلقائيًا… لعبة محاكاة… تطبيق متجر Microsoft… حول Playnite إرسال ملاحظات التبديل إلى وضع ملء الشاشة روابط مساعدة الدعم في Patreon لدعم على Ko-fi دليل المستخدم مستندات SDK إعادة تشغيل النظام إيقاف تشغيل النظام تعليق النظام نظام السُبات قفل النظام تسجيل الخروج اختر لعبة عشوائية مكان اللعبة لعرضها على لوحة التفاصيل: تباعد العناصر رسم خلفية العنصر الشبكي عرض حدود العرض الشبكي مصدر رمز اللعبة المفقود مصدر غلاف اللعبة المفقود مصدر خلفية اللعبة المفقود التباعد العمودي لتفاصيل الألعاب موضع تفاصيل العرض الشبكي موضع عرض تفاصيل قائمة الألعاب رسم فاصل بين اللوحات ارتفاع صورة غلاف اللعبة ارتفاع رمز قائمة الألعاب خط التطبيق خط رمزي موضع لوحة الفلتر موضع لوحة الاستطلاع استحضار صورة الغلاف نسبة العرض إلى الارتفاع المستهدفة تؤثر الخيارات التالية أيضًا على عرض التجانب في وضع ملء الشاشة! وضع التمدد DVD Box متجر ألعاب Epic GOG Galaxy 2.0 IGDB مربع شعار Steam الغلاف العمودي لمكتبة Steam التويتش * يتطلب إعادة التشغيل من أجل التطبيق إعدادات عامة اللوحة العلوية المظهر تفاصيل اللعبة الواجهة متقدم شاشة كاملة الإدخال الأداء بيانات التعريف جاري التحديث... بحث نسخ احتياطي نسخ احتياطي لبيانات المكتبة استعادة النسخة الاحتياطية استيراد التغييرات في المكتبة تلقائيًا موقع ملف قاعدة البيانات غير صحيح، يجب تعيين مسار الملف الصحيح. لا يمكن أن يكون اسم الحساب فارغًا. تنزيل بيانات التعريف بعد استيراد الألعاب تشغيل Playnite مصغر قم بتشغيل Playnite عند بدء تشغيل جهاز الكمبيوتر الخاص بك البدأ مغلقًا في خانة الإشعارات فشل تسجيل Playnite لبدء تشغيله عند بدء تشغيل الحاسوب. التشغيل في وضع ملء الشاشة تحميل صورة غير متزامنة يعمل على تحسين سلاسة التمرير في لقوائم الألعاب مقابل أوقات تحميل صور أبطئ. إظهار اسم اللعبة إذا كانت صورة الغلاف مفقودة إظهار أسماء الألعاب في العرض الشبكي تعتيم الألعاب الغير المثبتة إظهار أيقونات الألعاب في قائمة عرض التفاصيل إظهار عدد العناصر في وصف المجموعة إظهار الحقول المخصصة فقط في لوحات التصفية والمستكشف تعطيل تسريع الأجهزة استخدمه عند مواجهة تشنج أو مشاكل في واجهة المستخدم المشابهة إظهار الألعاب المخفية في قوائم التشغيل السريع يؤثر على قوائم قائمة الانتقال السريع وقوائم الأدراج. عدد عناصر التشغيل السريع استخدم صورة خلفية اللعبة كخلفية للويندوز خلفية ضبابية جودة عالية تعتيم الخلفية الإظهار في العرض الشبكي السِمة سِمة الملف الشخصي سِمة ملء الشاشة سِمة الملف الشخصي ملء الشاشة موقع قاعدة البيانات حالة تسجيل الدخول: اعدادات Playnite مسح ذاكرة التخزين الموقت على الويب قد يقوم بحل المشاكل التي تمت مواجهتها أثناء ربط الحسابات. اظهار أيقونه النظام التصغير إلى جانب شريط المهام تصغير Playnite عند إغلاق نافذة التطبيق عند تشغيل اللعبة: عند إغلاق اللعبة: قم بتنسيق الوقت الملعوب ليُظهر عدد الأيام الملعوبة صيغة التاريخ: سيؤذي هذا إلى تسجيل خروجك من جميع الخدمات المرتبطة. يتطلب إعادة تشغيل التطبيق، هل تود المتابعة؟ مسح ذاكرة التخزين المؤقت؟ مطلوب إعادة تشغيل Playnite لتطبيق السِمة الجديدة المزيد من المظاهر إنشاء مظهر جديد المزيد من الإضافات انشىء إضافة جديدة ساعدنا في ترجمة Playnite يحتاج Playnite إلى إعادة التشغيل من أجل تطبيق الإعدادات الجديدة. اعد البدء الان؟ ستؤدي إعادة التشغيل إلى إلغاء أي مهام نشطة (تنزيلات) قيد التقدم حاليًا إعادة تشغيل Playnite؟ لا يمكن لـ Playnite نقل ملفات مكتبتك تلقائيًا. يجب عليك نقل / نسخ الملفات يدويًا قبل تغيير الموقع. إذا لم تكن هناك مكتبة في الموقع المستهدف ، فسيتم إنشاء مكتبة جديدة. لن يتم استخدام موقع قاعدة البيانات الجديد حتى يتم إعادة تشغيل Playnite. لن يتم تسجيل وقت اللعبة إذا تم تعيين إجراء "الإغلاق". عدد الصفوف عدد الأعمدة عدد صفوف عرض التفاصيل عرض صورة الخلفية في الشاشة الرئيسية لا يتم تطبيقه بأثر رجعي على الألعاب الحالية دون إعادة تحميل بيانات التعريف. استيراد وقت لعب الألعاب في المكتبة: يحدد متى يجب على Playnite استيراد وقت التشغيل الذي أبلغتْ عنه إضافات المكتبة للألعاب في قاعدة بيانات Playnite. يحتاج الدعم من المكتبة الإضافية المسؤولة عن التعامل مع اللعبة (اللعب) إلى أن تكون قادرة على استخدام هذه الميزة. دائما: يستورد وقت التشغيل لألعاب جديدة مستوردة وموجودة في قاعدة بيانات Playnite. فقط للألعاب المستوردة حديثاً: استيراد وقت اللعب فقط للألعاب المستوردة الجديدة. لا: لا تستورد أبدا وقت اللعب تحت أي ظرف من الظروف. دائماً فقط للألعاب المستوردة حديثا ابداً تمكين دعم وحدة التحكم في وضع سطح المكتب يفتح زر الدليل وضع ملء الشاشة إعدادات تنزيل بيانات التعريف التلقائية للألعاب التي تم استيرادها حديثًا. عرض الهدف استخدم الشاشة الاساسية دائماً إظهار عناوين الألعاب اظهار حالة البطارية إظهار النسبة المئوية للبطارية إظهار الساعة إخفاء مؤشر الفأرة مثبتة فقط في الفلاتر السريعة زر الدفع الواجهة التمرير الأفقي حدد أحد الأقسام الفرعية لا توجد إعدادات متاحة فشل تحميل الإعدادات يتم تنفيذ هذه السكريبتات لكل لعبة في المكتبة، يمكن تعيين السكريبتات الفردية لكل لعبة على حدة أثناء تعديل تفاصيل اللعبة. تنقل صورة الخلفية بشكل متحرك حجم الخط تلقائي ذو ظلال رمادي نوع واضح مثالي العرض وصع تنسيق النص وضع عرض النص لا يتم استخدام أساليب عرض النص وتنسيقه حاليًا لوصف اللعبة. تحميل سابق لصور الخلفية إذا تم تفعيله، سيقوم Playnite بتحميل خلفية العمل الفني أثناء تحميل بيانات التعريف، وهذا باستحدام مساحة أكبر من القرص وإتاحة العمل الفني عندما تكون غير متصل بالأنترنيت. في حالة عدم التفعيل، لا يتم تحميل خلفية العمل الفني إلا عند الحاجة لتحميله، وباستخدام مساحة أقل، لكن قد يؤذي إلى تأخيرٍ قبل عرض العمل الفني وبعض الصور قد لا تكون متاحة عندما تكون غير متصل بالأنترنيت. غلق عميل الطرف الثالث تلقائيًا بعد الخروج من اللعبة تأخير إيقاف تشغيل العميل (بالثواني) لا تغلق بعد جلسات اللعب التي تكون أقصر من (بالثواني) إغلاق العملاء التاليين تلقائيًا: غلق العملاء تلقائيًا استيراد قائمة استبعاد عرض تحذير عند تعيين وسائط ألعاب كبيرة جدًا أمر فتح الدليل التصنيف العمري تحديث حجم الألعاب على تحديث المكتبة القيام بمسح وتحديث حجم الألعاب المثبتة إذا تم الاكتشاف أن ملفاتهم قد تم تعديلها منذ آخر فحص لا شيء ملء متحد متحد لملء اليسار اليمين الأعلى الأسفل خطأ في الاستيراد يتطلب المصادقة فشلت المصادقة وضع عرض الويب البديل تستخدم عند مواجة مشاكل مع عرض الويب,على سبيل المثال المصادقة على دمج الحوارات. تحميل جزئي لوصف الألعاب الكبيرة الوصف الكبير يمكن أن يسبب تباطؤا ملحوظا عند اختيار الألعاب. عند التمكين، سيتم تحميل جزء فقط من نص الوصف في البداية مع خيار تحميل الباقي عند الطلب. استيراد بيانات التعريف تحميل بيانات التعريف تعيين التكوين المحدد لاستخدامه في أي تحميلات للبيانات التعريف في المستقبل. يمكن كذلك تغييره في إعدادات التطبيق. استيراد محاكاة المعالج هذا المعالج سيرشدك خلال عملية تنزيل واستيراد الألعاب التي تمت محاكتها ضع في عين الاعنبار أنه يمكنك دائمًا إضافة محاكيات/ أو ألعاب إضافية لاحقًا عبر القائمة الرئيسية (ضمن قائمة "المكتبة" لإعدادات المحاكي وقائمة "إضافة الألعاب" لألعاب المحاكاة). يوجد أدناه قائمة بالمحاكيات التي يمكن لـ Playnite التعرف عليها وتكوينها تلقائيًا. يمكنك تنزيل أدوات التثبيت من مواقع الويب الخاصة بهم. بمجرد تثبيت المحاكيات ، انتقل إلى الشاشة التالية لاستيرادها إلى Playnite.يمكنك أيضًا تكوين واستيراد أي محاكي مخصص من قائمة التكوين لاحقًا. يمكنك استيراد أي برامج محاكاة مثبتة على جهاز الكمبيوتر الخاص بك بالنقر فوق الزر "الكشف التلقائي من المجلد ...". سيبحث Playnite في المجلد المحدد عن أي محاكيات معروفة ويوفر خيار استيرادها. يمكنك استخدام هذا الزر عدة مرات لاستيراد برامج محاكاة من مجلدات مختلفة. ستتم إضافة المحاكيات إلى أسفل القائمة الحالية. يمكنك استيراد الألعاب بالنقر فوق الزر "مسح المجلد باستخدام المحاكي". سيؤدي تحديد المحاكي المناسب إلى إخبار Playnite بأنواع الملفات التي يجب مسحها ضوئيًا واستيرادها. يمكنك استخدام هذا الزر عدة مرات لاستيراد الألعاب من مجلدات مختلفة. ستتم إضافة الألعاب إلى أسفل القائمة الحالية. لم يتم تحديد محاكيات للاستيراد. لن تتمكن من استيراد أي ألعاب تمت محاكاتها تلقائيًا دون تكوين المحاكيات أولًا. هل انت متأكد أنك تريد المتابعة والخروج من عملية الاستيراد؟ لا توجد محاكيات تم تكوينها في Playnite. لا يمكنك استيراد الألعاب دون تكوين المحاكي أولاً وتحديد أنواع الملفات المناسبة. هل تريد إضافة بعض المحاكيات الآن؟ فحص المجلد باستخدام المحاكي حدد ملفات الكشف تلقائيًا من المجلد… تهيئة المحاكيات… جاري الفحص… يتم المسح {0} تهيئة لأول مرة هذا المعالج سوف يرشدك خلال عملية الاستيراد التلقائي والتكوين لمكتبات الألعاب الخارجية. يمكن لـ Playnite استيراد الألعاب تلقائيًا من خدمات ألعاب متعددة، مثل Steam أو GOG، كما يمكنك أيضًا تحديث مكتبتك عن طريق تحديثها تلقائيًا عند بدء تشغيل التطبيق. ضع في اعتبارك أنه يمكنك إضافة أي لعبة مخصصة لأي نظام أساسي يدويًا لاحقًا من خلال النقر على زر "Playnite" في القائمة الرئيسية. دمج المكتبة استيراد الألعاب تلقائيًا من الخدمات المدرجة أدناه. سيتم تحديث أي تغييرات لاحقة في اللعبة (حالة التثبيت) تلقائيًا عند بدء تشغيل Playnite أو عند تشغيلها يدويًا. ستؤثر الإعدادات المحددة على عمليات الاستيراد الأولية وكل عمليات الاستيراد اللاحقة. انتهى التكوين تم الانتهاء من الإعداد الأولي. نذكر أنه يمكنك تغيير جميع الإعدادات لاحقًا في قائمة "الإعدادات". يمكنك كذلك إضافة أي لعبة أخرى لاحقًا بالنقر فوق قائمة شعار Playnite فشل عملية تحميل واحدة أو أكثر من الإضافات. يمكنك إعادة تحميل الإضافات من قائمة الإضافات بعد عملية التثبيت لأول مرة. تحميل {0} دمج جارٍ تنزيل قائمة عمليات الدمج الموصى بها ... فشل تنزيل قائمة عمليات الدمج الموصى بها. يمكنك محاولة إعادة تنزيل عمليات الدمج لاحقًا عبر قائمة الإضافات. تهيئة المنصات والمحاكيات تهيئة المحاكيات المنصات المنصة المحاكيات المحاكي اضافة منصة اختيار أيقونة حدد الغلاف حدد الصورة حدد العنصر حدد الخلفية إختيار ملفّ حدد رابط URL اضافة محاكي المنصة(ات) المدعومة هل تريد حفظ تغييرات المنصة؟ هل تريد حفظ تغييرات المحاكي؟ قابل للتنفيذ الوسائط دليل العمل أنواع الملفات المدعومة استيراد المحاكيات… تحميل المحاكيات… تحميل وسائط مسبقة الإعداد من ملف تعريف المحاكي المعروف هل أنت متأكد أنك تود حذف {0} محاكي؟ حاليًا يتم استخدامه بواسطة {1} لعبة. هل أنت متأكد أنك تود حذف {0} منصة؟ حاليًا يتم استخدامها من طرف {1} لعبة و {2} محاكيًا. مساعدة في الإعدادات رتب حسب اتجاه الترتيب تجميع حسب تصاعدى تنازلي لا تجميع التجميع حسب المكتبة التجميع حسب الفئة التجميع حسب المنصة إظهار النوع العرض لوحة الاستطلاع لوحة التصفية أيقونة أيقونة المكتبة صورة الغلاف صورة الخلفية ترتيب حسب اسم مكتبة يدوي الاسم تثبيت محرك الأقراص إسم الحساب المنصة الفئة النوع تاريخ النشر سنة النشر المبرمج وسم الناشر حالة التثبيت مطابقة كافة عوامل التصفية اذا تم تفعيله, فقط الألعاب التي تستخدم كل العناصر في كل التصفيات سوف تكون في العرض. اذا تم إيقافه, الألعاب التي تستخدم أي عنصر من أي تصفية سوف تكون في العرض. مثبت مثبت غير مثبت مخفي المفضلة تمكين دعم HDR إذا تم تفعيله، سيتم تمكين HDR على شاشتك الرئيسية قبل بدء اللعبة. HDR غير مدعومة على شاشتك الرئيسية. اخر تشغيل الفئة الوصف دليل التثبيت صورة الغلاف الروابط مسار صورة، ROM أو ISO النوع الأنواع الشركة الشركات المبرمج المطورون الناشر الناشرون الفئة الفئات وسم وسوم خاصية خصائص التقييم العمري التقييم العمري المنطقة المناطق مصدر المصادر آخر نشاط خطأ في قاعدة البيانات فشل في فتح قاعدة بيانات المكتبة. لم يتم فتح قاعدة البيانات. تعذر الوصول إلى قاعدة بيانات المكتبة. "{0}" ملف يتم استخدامه من طرف عملية اخرى أو أنه في موقع لا يمكن الوصول إليه. فشل في إنشاء حزمة التشخيص. فشل في رفع حزمة التشخيص تلقائيًا. تم إرسال معلومات التشخيص بنجاح. تم إنشاء حزمة التشخيص وإرسالها بنجاح. الرجاء إرفاق المعرف التالي بتقرير المشكلة الخاص بك: فشل استيراد الألعاب من {0}. فشلت عملية الإستيراد ألعاب المحاكيات {0} تعذر البحث عن الألعاب من خلال ملف تعريف المحاكي. الملف الشخصي لا يحتوي على أي ملحقات أو منصات. فشل بدء Playnite. الرجاء إغلاق جميع الحالات الأخرى وحاول مرة أخرى. فشلٌ في تطبيق السِمة "{0}"، ملف تعريف الألوان "{1}" {2} لا يمكن فتح الرابط، عنوان URL غير متناسق بشكل صحيح. فشل بدء التطبيق. فشل في تهيئة عنصر عرض الويب. Playnite لا يمكنه الاستمرار في عملية بدء التشغيل. مزيد من المعلومات على https://playnite.link/cefstartup لا يمكن استيراد برامج محاكاة بسبب ملف تعريف مفقود أو تالف. فشل تنفيذ إجراء القائمة. تعديل تفاصيل اللعبة عنوان URL الصورة إضافة رابط إضافة روم حفظ التغييرات تطبيق التغيرات على اللعبة/الألعاب قيد التحرير إضافة إجراء حذف إجراء إزالة إجراء التشغيل إضافة ألعاب فحص المجلد… تم تثبيت الكشف تصفح… فتح Playnite إعدادات الملف الشخصي اسم اللعبة لا يمكن أن يكون فارغاً. لا يمكن أن يكون مسار تتبع أحداث اللعبة فارغاً اسم اللعبة لا يمكن ان يكون فارغاً قبل البحث عن بيانات التعريف. بيانات اللعبة غير صالحة أدخل رابط URL صالح يبدأ بhttp:// أو https:// حدد رابط URL فشل تحميل بيانات التعريف:{0} خطأ في التحميل تفريغ الفلاتر حساب خاص حساب عام مفتاح API خطا في بدء التشغيل خطا في المظهر مسح الكل جاري التثبيت جاري إلغاء التثبيت جاري التشغيل قيد التشغيل عنوان URL غير صالح ‮لا تفعل شيئا تصغير إعادة الواجهة استعادة النافذة فقط عند تشغيلها من واجهة المستخدم إغلاق تغيير متقدم ابداً حالة الاكمال حالة الإكتمال نقاط المستخدم نقاط النقد نقاط المجتمع البرامج النصية للعبة البرامج النصية للتطبيقات السكريبتات الإضافات مصادر بيانات التعريف الإضافات معرف ID إعادة تحميل السكريبتات التفاعلي SDK PowerShell تم إعادة تحميل جميع السكريبتات بنجاح. لم يتم العثور على ألعاب تتناسب مع معايير البحث/معايير الفلتر لم يتم العثور على العناصر التبديل إلى وضع سطح المكتب إغلاق Playnite المكتبات تحديث الكل أنشئ من طرف: النسخة: التحديثات: الوحدة: مكتبة الإحصائيات الكل لا شيء التنبيهات العرض الطول الحجم صغير عادي كبير أكبر الأكبر افتراضي تحديد تحديد الكل إلغاء تحديد الكل الأول عشوائي تحديد المستخدم تحميل المزيد شفاف تصغير توسيع تصغير الكل توسيع الكل آخر ثيمات وسائط المحاكي القوانين المدمجة قوانين مخصصة وسائط إضاقية للمحاكي تجاوز وسائط المحاكي إجراء التشغيل تحديد بيانات التعريف للاستيراد حدد الألعاب التي تود استيرادها البحث عن بيانات التعريف التحديث متوفر التغييرات منذ التحديث الأخير تحميل و تثبيت التحديثات تحقق من التحديثات خطأ فى التحديث فشل التحقق من الإصدار الجديد. لم يتم العثور على إصدار جديد، التطبيق محدّث مسبقًا. فشل تحميل التحديث وتثبيته. بعض مهام الخلفية قيد التقدم حاليًا. هل تريد إلغاءه ومتابعة التحديث؟ بعض مهام الخلفية قيد التقدم حاليًا. هل تريد إلغاءه والخروج من Playnite؟ بعض مهام الخلفية قيد التقدم حاليًا. سيؤدي تبديل الأوضاع إلى إلغاء المهمة، هل تريد التبديل على أي حال؟ يتوفر تحديث لـ Playnite إعادة تحميل قائمة المظاهر تطبيق المظاهر المحددة شاهد تغييرات الملف تطبيق السمة تلقائيُا عندما يتغير مصدر الملف وقت تشغيل السكريبت تنفيذ برنامج نصي قبل بدء اللعبة تنفيذ برنامج نصي بعد انهاء اللعبة اللعبة تنفيذ برنامج نصي بعد بدء تشغيل اللعبة التنفيذ عند تشغيل التطبيق تنفيذ عند إيقاف تشغيل التطبيق سكريبت بعد بدء اللعبة سكريبت بدء اللعبة سكريبت توقف اللعبة تنفيذ برنامج نصي عام عالمي تصفية الحالية جديد اختبار البرنامج النصي إظهار العناصر المحددة فقط. حفظ كإعداد افتراضي إضافة إلي المفضلة إزالة من المفضلة اخفي هذه اللعبة إزالة من المخفي تمكين دعم HDR تعطيل دعم HDR تعديل… حساب حجم التثبيت حساب حجم التثبيت (جميع الألعاب) حساب حجم التثبيت (البيانات المفقودة فقط) حجم التثبيت تعيين الفئة… تعيين حالة الإكمال حذف تشغيل تثبيت إعدادات اللعبة التفاصيل إلغاء التثبيت فتح موقع التثبيت إنشاء اختصار على سطح المكتب فتح الدليل المزيد تتم إدارتها من قبل البرنامج المساعد للمكتبة إدارة عملية بدء اللعبة ستتم من خلال المكون الإضافي للمكتبة المسؤول عن هذه اللعبة. لم يتم العثور على معلومات ذات صلة حول اللعبة "{0}" في الصفحة المحددة. تلميح: يمكنك استخدام عملية تحميل بيانات التعريف أكثر تقدمًا أثناء تعديل لعبة واحدة عبر خيار القائمة "تعديل". غير متاح عندما تكون بعض الإجراءات قيد التنفيذ. نص الوصف حساس لبناء HTML يتم تسجيل وقت اللعبة بالثواني. حجم التثبيت مشار إليه بوحدة البايت. يجب تحديد تاريخ الإصدار بتنسيق "يوم-شهر-عام". يمكن حذف قيم الشهر واليوم. القيم من 0 إلى 100 أو الفارغة بدون درجة. الداعمون لمطوري Playnite عبر Patrons و Ko-fi: البرمجة، الترجمة والمساهمين الآخرين بدون ترتيب معين: إلغاء اللعبة في الشاشة الستهدفة؟ مراقبة التثبيت قيد التشغيل حاليًا. هل تريد إلغاء العملية وإعادة اللعبة إلى حالتها السابقة؟ مراقبة التثبيت قيد التشغيل حاليًا. هل تريد إلغاء العملية وإعادة اللعبة إلى حالتها السابقة؟ وقت اللعب اخر تشغيل {0}ي {1}س {2}د {0} ساعات {1} دقائق {0} دقائق {0} ثواني لم تلعب بعد جاري فتح وضع سطح المكتب… فتح في وضع ملء الشاشة… جاري تحميل مكتبة الألعاب… حساب حجم التثبيت… حساب حجم التثبيت لـ {0}... فشل تثبيت ملف السكريبت. تم تثبيت السكريبت بنجاح. تثبيت السكريبت خطأ في السكريبت فشل في تنقيذ وظيفة الملحق. فتح مجلد بيانات التعريف احسب حساب حجم التثبيت تلقائياً باستخدام ROMs إذا كانت اللعبة تحتوي عليه أو وجهه تثبيت تم تعيينها {0} عميل غير مثبت. {0} عميل سيتفح الآن.المرجو تسجيل الخروج ثم إغلاق هذه الرسالة. في انتظار تسجيل دخول المستخدم، الرجاء إغلاق هذا عند الانتهاء… لم يتم العثور على دليل تثبيت اللعبة. تكوين إجراء اللعبة غير صالح. استكشاف مشاكل مزامنة الحساب وإصلاحها استكشاف الأخطاء وإصلاحها إعادة تسمية العنصر إضافة عنصر جديد أدخل الاسم أدخل اسمًا جديدًا أقل من ساعة من ساعة إلى 10 ساعات من 10 ساعات إلى 100 ساعة من 100 ساعة إلى 500 ساعة من 500 ساعة إلى 1000 ساعة اكثر من 1000 يجب إعادة تشغيل Playnite لإكمال التثبيت. هل تريد إعادة التشغيل الآن؟ لم يتم حزم الامتداد/ الملحق بشكل صحيح. لم يتم حزم الامتداد/ الملحق بشكل صحيح. فشل تحميل الإضافة "{0}" بشكل صحيح لا يمكن تحميل إضافة "{0}" ، إصدار Playnite الحالي غير مدعوم. فشل تحميل المظهر "{0}" بشكل صحيح. لا يمكن تحميل مظهر "{0}"، إصدار Playnite الحالي غير مدعوم. فشل تحميل الملحق بشكل صحيح. فشل تحميل المظهر تستخدم السِمة/الملحق إصدار API غير مدعوم. تم التثبيت بنجاح. تثبيت ملحق؟ عام فشل في تثبيت "{0}" إضافة. فشل تثبيت الملحق. {0} هل تريد تثبيت ملحق جديد؟ {0} بواسطة{1} الإصدار{2} هل تريد تحديث الملحق/الامتداد "{0}"؟ النسخة الحالية: {1} نسخة الجديدة: {2} فشل تثبيت السمة. {0} هل تريد تثبيت مظهر جديد؟ {0} بواسطة {1} الاصدار {2} هل تريد تحديث المظهر "{0}"؟ الإصدار الحالي: {1} نسخة جديدة: {2} أنت على وشك مغادرة Playnite والانتقال إلى صفحة الويب التالية باستخدام متصفح الويب الافتراضي الخاص بك. هل تريد الاستمرار؟ {0} قد تكون الصورة (الصور) المحددة كبيرة جدًا للحصول على الأداء الأمثل. يمكن أن يؤدي استخدام الصور الكبيرة جدًا إلى استجابة أسوأ لواجهة المستخدم وزيادة استخدام الذاكرة. الدقة القصوى الموصى بها: الرموز: {0} ميغا بكسل الأغلفة: {1} ميغا بكسل الخلفيات: {2} ميغا بكسل تحذير بخصوص الأداء لا تُظهر مجدّدًا الملف بالملحق {0} غير متوافق. امتداد الملف غير متوافق ملف الصورة المحدد قد يكون كبيرًا جدًا للحصول على الأداء الأمثل. هل أنت متأكد أنك تريد إلغاء تثبيت المظهر المحدد؟ سيتم وضع إلغاء التثبيت في قائمة الانتظار لبدء التطبيق التالي. لا يمكن إلغاء تثبيت السمات المضمنة. المظهر لا يدعم هذا الإصدار من Playnite. هل أنت متأكد من أنك تريد إلغاء تثبيت الملحق المحدد؟ سيتم وضع إلغاء التثبيت في قائمة الانتظار لبدء التطبيق التالي. لا يمكن إلغاء تثبيت الملحقات المضمنة. لا يدعم هذا الامتداد هذا الإصدار من Playnite. دليل التثبيت دليل البيانات جارٍ إنشاء حزمة التشخيص… يتم الآن تحميل حزمة التشخيص… استيراد ملف… ما هذا؟ هل أنت متأكد من فعل هذا؟ مجموع وقت اللعب متوسط ​​وقت اللعب أفضل وقت للعب إجمالي حجم التثبيت نظرة عامّة الشريط الجانبي عرض على الشريط الجانبي إعادة ضبط الإعدادات ستتم إعادة تعيين جميع إعدادات التطبيق إلى القيم الافتراضية، باستثناء: - موقع قاعدة البيانات - قائمة استثناءات الاستيراد - إعدادات الإضافات، بما في ذلك تكامل المكتبة إعادة تشغيل التطبيق مطلوب لإنهاء العملية. هل تريد إعادة ضبط الإعدادات؟ للمبرمجين إضافات الخارجية إدخال مسار المجلد الكامل. الإنجازات المنتدى الأخبار صفحة المتجر لم يكتمل الإعداد الأولي. سيتم الآن إعادة تشغيل Playnite إلى وضع سطح المكتب لإنهاء الإجراء. لعب مؤخرا المفضلة الأكثر تشغيلاً الكل الفلاتر المطبقة. هناك عوامل تصفية إضافية مطبقة. نتائج البحث عن: عنصر بالاسم ذاته موجود بالفعل. تقييد التحديد للفلتر الحالي اختر واحدًا آخر الإضافات... مثبت إعدادات الإضافات تصفح تحديثات التحديثات ({0}) تم نقل إدارة الإضافات والسمات المثبتة، بما في ذلك إعداداتها، إلى قائمة "الإضافات" الجديدة. يمكن هنا تكوين جميع إضافات تكامل المكتبة المثبتة حاليًا. إذا كنت تريد تثبيت عمليات تكامل إضافية أو إلغاء تثبيتها ، فاستخدم خيار "الإضافات" من القائمة الرئيسية. ثيمات لوضع Desktop ثيمات لوضع Fullscreen جار البحث… الإضافة غير متوافقة مع هذا الإصدار من Playnite. فشل تنزيل حزمة تثبيت الإضافة. فشل تنزيل بيان تثبيت الإضافة. يتطلب التطبيق إعادة التشغيل لتطبيق التغييرات المعلقة. تمت جدولة هذه الإضافة للتثبيت. تثبيت إزالة الإضافة مثبتة لا توجد تحديثات جديدة تحديث الإضافة سجل التغيير غير متوفر مجدول للتثبيت فشل التحميل رخصة مرفوضة جارٍ التنزيل {0}… جارٍ البحث عن تحديثات... يتوفر تحديث أو أكثر للإضافات. حدد الإضافات لتحديثها جزء التطوير للإضافة اتفاقية ترخيص {0} موافقة رفض تضمين إجراءات بدء اللعبة للمنصات اختر الإجراء طريقة التتبع مسار التتبع تأخير التتبع الأولي التتبع بانتظام رابط ملف محاكي سكريبت افتراضي الاجراء مجلد العملية الأصلية رسائل سجل التتبع التغييرات التالية ستستبدل بيانات جميع الألعاب المختارة حاليًا! بدون رسمي العناصر فقط في البداية والنهاية فقط حساسية التمرير تمرير سلس سرعة الحركة إزالة العنصر؟ هل أنت متأكد من أنك تريد إزالة هذا العنصر؟ إظهار الأزرار في اللوحة العلوية: إعدادات العرض العامة إعدادات التجميع إعدادات الترتيب تصفية الإعدادات المسبقة موضع أزرار الإضافات عرض فاصل القسم نقل زر القائمة الرئيسية إلى اللوحة الجانبية لوحة الاستطلاع اختيار لعبة عشوائية مشاهدة لعبة مختارة عشوائياً حدد لعبة عشوائية من العرض حفظ إعدادات التجميع والفرز إظهار كمرشح سريع في وضع Fullscreen في ال 7 أيام الأخيرة في آخر 31 يومًا في آخر 365 يومًا منذ أكثر من 365 يومًا تهيئة حفظ الإعداد تصغير بعد بدء اللعبة تصغير Playnite بعد بدء اللعبة. يمكن أن يؤدي تعطيل هذا إلى حدوث مشكلات في الألعاب لا تركز على الإدخال عند بدء التشغيل! حجم الخط حجم الخط الصغير دعم وحدة التحكم باللعبة إذا تم تعطيله، فلن يقبل Playnite أي مدخلات لواجهة XInput. عطله إذا كنت تستخدم الأدوات التي تترجم مدخلات XInput إلى مدخلات الماوس/لوحات المفاتيح وتحصل على إدخالات مزدوجة في Playnite. إظهار العناصر في القائمة الرئيسية: مقلوب X / A زر العرض الرئيسي ملزم تبديل روابط الأزرار لبدء اللعبة وإظهار صفحة تفاصيل اللعبة في العرض الرئيسي. ربط زر تبديل تأكيد/إلغاء يعكس ارتباطات زر A/B للتأكيد والإلغاء. وحدة التحكم الرئيسية فقط قبول المدخلات من وحدة التحكم الرئيسية فقط عند التمكين. زر الدليل يركز على Playnite مستوى صوت الواجهة مستوى صوت الخلفية كتم الصوت عندما يكون playnite في الخلفية فشل تهيئة واجهة الصوت. API الخارج API المستخدمة لإخراج الصوت. قم بتغييره إذا كنت تواجه مشكلات في الصوت. عام مرئيات الصوت الواجهة القوائم الإدخال {0} جارٍ البدء… {0} قيد التشغيل… حرف كبير مسافة مستوى "render" الصورة بديل متوازنة الجودة جودة: أفضل جودة صورة مع استخدام أبطأ و عالي للذاكرة. متوازن: نوعية جيدة وسريعة مع استخدام ذاكرة منخفض. بديل: جودة أفضل وسرعة متوسطة مع استخدام ذاكرة منخفض. تحديد الملف... تحديد المجلد البرنامج النصي لبدء التشغيل يرجى ملاحظة أن كلاً من الإضافات والسمات يمكن أن تؤثر بشكل كبير على أداء Playnite، واستقراره وأمانه. إذا بدأت في مواجهة بعض المشكلات بعد تثبيت سمة أو إضافة، فحاول تعطيلها/إلغاء تثبيتها أولاً لمعرفة ما إذا كانت أصل المشكلة. اختر عند بدء التشغيل اختر عند بدء التشغيل التشكيلات الجانبية المدمجة التشكيل الجانبي المدمج التشكيلات المخصصة إعداد مخصص تم التعامل معها بواسطة برنامج نصي مدمج مواصفات المحاكي مواصفات المنصة مواصفات المنطقة تنفيذ برنامج نصي قبل بدء المحاكي للتنفيذ بعد بدء تشغيل المحاكي للتنفيذ بعد الخروج من المحاكي برنامج المحاكي غير موجود. مواصفات المحاكي غير موجودة. البرنامج النصي لبدء المحاكي غير موجود. تقسيمها إلى ألعاب منفصلة دمج في لعبة واحدة ضبط المنصة تعيين المنطقة فحص المجلد إعدادات المسح استبعاد الأنماط من فحص المجموع الاختباري لن يتم فحص الملفات التي تطابق النمط (الأنماط) المحدد بحثًا عن المجموع الاختباري وستتم مطابقتها باسم الملف. انظر صفحة تعليمات المحاكي لمزيد من المعلومات. مسح باستخدام المحاكي يجب تعيين الاسم عند حفظ تكوين الجديد. لم يتم تعيين المحاكي أو ملف تعريفه. لم يتم تحديد المجلد المطلوب مسحه أو أنه غير موجود. لم يتم ضبط إعداد المسح بشكل صحيح. تضمين بالمسح التلقائي للمسح الشامل فشل فحص المجلد بحثًا عن برامج محاكاة. فشل فحص المجلد (المجلدات) بحثًا عن ألعاب تمت محاكاتها. إخفاء المستوردة الملفات الشخصية المراد استيرادها: إعدادات المسح التلقائي حفظ كإعداد المسح التلقائي يحفظ الإعدادات لاستخدامها لاحقًا أثناء تحديث المكتبة. يمكن إدارة الإعدادات المحفوظة عبر قائمة "إعداد المحاكيات". استيراد باستخدام المسارات النسبية إذا كان ممكنا استيراد ملفات اللعبة باستخدام المسارات ذات الصلة بمجلد تثبيت Playnite أو مجلد تثبيت المحاكي. فحص المجلدات الفرعية فحص داخل الأرشيف دمج الملفات ذات الصلة دمج ملفات اللعبة ذات الصلة، مثل أقراص اللعبة الفردية، تحت لعبة واحدة. إضافة ماسح إضافة ماسح موجود بدء الماسح أضف إعدادات المسح باستخدام المحاكيات لفحص مجلدات معينة. تأكد من تكوين المحاكيات بشكل صحيح قبل استيراد الألعاب (عبر المكتبة -> قائمة إعدادات المحاكيات). الحالة الافتراضية المعينة للألعاب المضافة حديثًا الحالة المخصصة للألعاب التي تم لعبها لأول مرة فشل تهيئة وقت تشغيل البرنامج النصي PowerShell. إذا كنت من مستخدمي Windows 7 ، فحاول (إعادة) تثبيت PowerShell 5.1 لإصلاح المشكلة. الإعداد المسبق للمرشح بالاسم المحدد موجود بالفعل. هل تريد تحديث الإعداد المسبق بالإعدادات الجديدة؟ سيتم إزالة هذه الكلمات من بداية ملء قيمة اسم الفرز تلقائياً: استخدم هذا لتجاهل الكلمات في بداية سلسلة لأغراض الفرز. الافتراضي هو "The" و "An" و "A". املأ اسم فرز الألعاب بدون اسم فرز الفرز ملء قيم اسم الفرز… تم اكتشاف خدمة Nahimic للعمل على نظامك. من المعروف أن هذه الخدمة تسبب مشكلات في العرض على Playnite (والتطبيقات الأخرى). إذا واجهت أي تلف في الرسومات أو مشكلات أخرى في العرض في Playnite ، فإننا نوصي بتعطيل خدمة Nahimic أو إلغاء تثبيتها تمامًا. مزيد من المعلومات على https://playnite.link/nahimicsucks يتم تشغيل Playnite بامتيازات عالية (كمسؤول). لا يوصى بهذا لأنه يمنح امتيازات عالية لجميع الإضافات المثبتة وجميع الألعاب / التطبيقات التي بدأت من Playnite! مزيد من المعلومات على https://playnite.link/adminfaq إظهار تحذير إذا كان Playnite يعمل بامتيازات مرتفعة احصل على الحجم الحقيقي على القرص عند حساب حجم الألعاب في حالة التمكين، ستكون عمليات المسح أبطأ وستحصل على الحجم الحقيقي الذي تستخدمه الملفات في محرك الأقراص. في حالة التعطيل، ستكون عمليات المسح أسرع وسوف تستخدم حجم الملفات نفسها. تم الإبلاغ عن الإضافات التالية باعتبارها إشكالية محتملة، إما بسبب تأثير الاستقرار/الأداء العالي أو مشكلات الأمان. نوصي بشدة بإلغاء تثبيتها: {0} استبعاد الملفات عبر الإنترنت من الفحص لن يتم فحص الملفات المخزنة على التخزين السحابي واستيرادها إذا لم تكن متوفرة محليًا. مدعوم فقط لـ: Google Drive و DropBox و OneDrive مسح ولكن باستخدام طريقة مبسطة بدون محتوى الملف سيتم استيراد الملفات ولكن باستخدام طريقة أقل دقة لا تتطلب تنزيل محتوى الملف وتقديمه محليًا. تطبيق على الكل تجاوز حالة التثبيت عند الضبط، سيقوم Playnite بتجاهل حالة التثبيت (بما في ذلك دليل التثبيت) المحددة بواسطة البرنامج المساعد للتكامل الذي يستورد هذه اللعبة. قد لا يعمل هذا الخيار بشكل كامل مع الإضافات التي تستخدم طريقة استيراد لعبة محددة إلا إذا أخذوا أيضًا خيار التجاوز هذا في الاعتبار. يدوياً فقط مرة في اليوم مرة في الأسبوع عند كل بدء تشغيل تحقق من تحديثات البرنامج تحقق من تحديثات الإضافات تحديث المكتبات مسح مجلدات المحاكاة تضمين الألعاب المخفية تعديل الحقول حدد / إلغاء تحديد الكل فتح تفعيل تعيين ابدأ الكتابة للبحث عن الألعاب… [F1] للحصول على المساعدة البدء ب# يجلب قائمة من الأوامر المتاحة. البدء ب/ جلب قائمة مزودي البحث المتاحين/الإضافات. كتابة الكلمة المفتاحية للبحث وتنتهي بمفاتيح SPACE على الفور إلى ذلك البحث. TAB: تبديل الإجراء ENTER: تنشيط الإجراء المحدد SHIFT-ENTER: فتح قائمة العناصر تضمين ألعاب الغير مثبتة تضمين الألعاب المخفية الالعاب الغير مثبتة متضمنة الألعاب غير المثبتة مستبعدة الالعاب المخفية متضمنة الألعاب المخفية مستبعدة تشغيل أو تثبيت انتقل إلى التفاصيل قائمة اللعبة تعديل اللعبة فتح البحث صندوق البحث زر البحث إجراء اللعبة الأساسي إجراء اللعبة الثانوي CTRL-F يفتح البحث العالمي بدلا من التركيز على مربع البحث حفظ إعدادات فلتر اللعبة بين جلسات البحث موفري البحث الكلمات الأساسية الافتراضية كلمة مفتاحية مخصصة اختصار واسع النطاق للنظام بحث Playnite إعدادات الملحقات الاستبعادات الملفات المستبعدة ذات الصلة بمجلد الفحص المجلدات المستبعدة المتعلقة بمجلد الفحص إضافة الملف إلى قائمة الاستبعاد إضافة المجلد إلى قائمة الاستبعاد الاستبعادات يمكن إضافتها فقط إلى إعدادات الماسح الضوئي المحفوظة. تم إضافة الاستثناءات إلى "{0}" ماسح ضوئي. تجاوز المنصة عند تعيين الماسح الضوئي سيقوم بتعيين هذه المنصة لجميع الألعاب، و الكتابة فوق أي منصات مكتشفة تلقائيا. تضمين الأوامر في البحث الافتراضي عند التعطيل، لن يتم تضمين الأوامر في البحث الافتراضي حتى يتم استخدام البادئة # . استخدم المطابقة الضبابية في فلتر الاسم عند التمكين، ستُطابق عملية تصفية الأسماء أسماء الألعاب بنفس طريقة البحث العام. يمكن فرض تطابق دقيق في حالات فردية عن طريق إضافة علامة التعجب ! قبل عملية التصفية. الحقول التي سيتم عرضها لنتائج اللعبة: الحالة المخفية تم إلغاء النسخ الاحتياطي للبيانات. فشل النسخ الاحتياطي للبيانات. خطأ في النسخ الاحتياطي للبيانات جارٍ النسخ الاحتياطي للبيانات… جاري استعادة البيانات من النسخ الاحتياطي… فشل في استعادة البيانات من النسخة الاحتياطية. الإعدادات مكتبة الالعاب وسائط مكتبة الالعاب الملحقات المثبتة بيانات الملحقات الثيمات المثبتة حدد البيانات المراد استعادتها من ملف النسخ الاحتياطي المحدد. سيتم إعادة التشغيل تلقائيًا لبدء عملية استعادة النسخ الاحتياطي. حدد العناصر التي سيتم إدراجها مع النسخ الاحتياطي للبيانات. يتم تضمين إعدادات التطبيق وبيانات مكتبة اللعبة بشكل افتراضي. سيقوم Playnite بإعادة التشغيل تلقائياً لبدء عملية النسخ الاحتياطي. النسخ الاحتياطي التلقائي للبيانات تردد النسخ الاحتياطي التلقائي مجلد النسخ الاحتياطي النسخ الاحتياطية المتكررة إدراج بيانات إضافية: يجب تعيين مجلد النسخ الاحتياطي إذا تم تمكين النسخ الاحتياطي التلقائي. إظهار الإشعارات لإصدارات التصحيح فقط عند التمكين، فقط التحديثات المتاحة للإصدارات الرئيسية المثبتة حاليا ستؤدي إلى تحديث الإشعار. لن تسفر الإصدارات الرئيسية الجديدة عن تحديث الإشعار. استخدام التواريخ النسبية للأسبوع الماضي استخدام التواريخ النسبية في تنسيق "اليوم"، "الأمس"، إلخ. إذا كان التاريخ أقل من أسبوع. سيتم استخدام تنسيق التاريخ المحدد لجميع التواريخ الأخرى. البحث عن صورة ويب سلسلة البحث عن الأيقونة سلسلة البحث عن صورة الغلاف سلسلة البحث عن صورة الخلفية الحصول على معلومات الإضافة… لا يوجد مصدر لبيانات التعريف إعدادات سلوك التشغيل إعدادات إستخدام الماسح تحديد ملف شخصي عند بدء التشغيل تحديد محاكي عند بدء التشغيل تلقائي مفعل دائماً معطل دائماً إمكانية الوصول (قارئ الشاشة) الدعم قائمة التطبيق قائمة اللعبة مجلد البرنامج مجلد بيانات المستخدم تم اكتشاف تلف في ملفات المكتبة، سيتم إيقاف تشغيل Playnite الآن. افتح مشكلة جديدة على صفحة GitHub الخاصة بـ Playniite مع طلب لإصلاح التلف في ملفاتك. هل تريد حفظ التغييرات التي أجريتها؟ التثبيت المحمول لم يتم العثور على أي وحدة تحكم ================================================ FILE: source/Playnite/Localization/bg_BG.xaml ================================================  Български Език на Playnite Изход Филтърът е активен Филтърът е деактивиран Допълнителни филтри Филтри Филтър Невалидни данни Да се запазят ли промените? Начална страница на www.playnite.link Изходен код в GitHub Създаване на диаг. пакет Изпращане на диаг. информация Относно Playnite Създадено от Josef Němec Присвояване на категория Задаване на категории Добавяне на категория Отметнато - Присвояване на категория Без отметка - Премахване на категорията Неопределено - Без промени (при редактиране на няколко игри) Няма категория Няма платформа Ами сега! Нещо се обърка... Възникна непоправима грешка. Ако искате да ни помогнете да коригираме този проблем, моля, опишете накратко действията, предприети преди срива, и след това изпратете диагностична информация. Ако сте онлайн, пакетът ще бъде качен на сървъра на Playnite за анализ. Като алтернатива можете да щракнете върху бутона „Докладване за срив“, за да създадете нов проблем с GitHub и да докладвате ръчно за срива. Благодаря ви за помощта. Разширение "{0}" предизвика непоправима грешка. Препоръчваме да запазите регистрационния файл и да докладвате за проблема на разработчика на разширението. Ако проблемът продължава да се появява, деактивирайте разширението. Разширение "{0}" предизвика непоправима грешка. Препоръчваме да докладвате за проблема на разработчика на разширението. Ако проблемът продължава да се появява, деактивирайте разширението. Неизвестно разширение или тема причиниха непоправима грешка. Препоръчваме да деактивирате добавките на трети страни, да изолирате проблемната и да докладвате за проблема на разработчика на добавката. Възникна непоправима грешка. Ако искате да ни помогнете да разрешим този проблем, моля, изпратете диагностична информация. Благодаря ви. Деактивиране на разширението Запазване на регистрационния файл Изпращане на диаг. информация Докладване за срив Рестартирайте Playnite Рестартиране в безопасен режим Деактивиране на всички разширения на трети страни и използване на тема по подразбиране. Излезте от Playnite Действия, предприети преди срива (на английски): Мениджър на библиотека Да се премахне ли игра(и)? Не може да се премахне - Играта или инсталаторът работят. Не може да се деинсталира - играта работи. Наистина ли искате да премахнете {0}? Сигурни ли сте, че искате да премахнете {0} игри? Сигурни ли сте, че искате да премахнете {0}? Избирането на опцията "добавяне към списък с изключения" ще попречи на игрите да бъдат импортирани отново следващия път, когато библиотеката се актуализира. Сигурни ли сте, че искате да премахнете {0} игри? Избирането на "добавяне към списък с изключения" опция ще попречи на игрите да бъдат импортирани отново следващия път, когато библиотеката се актуализира. Сигурни ли сте, че искате да премахнете {0} записа, които в момента не се използват? Няма намерени неизползвани полета. Да (добавяне към списъка с изключения) В този раздел има незапазени промени Актуализиране на формата на библиотеката с игри... Неуспешна актуализация на базата данни. Не може да се актуализира библиотеката с игри. Изискват се {0} MB свободно пространство. GameError Не може да стартира играта. „{0}“ не беше намерено в базата данни. Не може да стартира играта: {0} Не може да започне действие: {0} Не може да се отвори местоположението на играта: {0} Не може да се открие размерът на инсталираната игра: {0} Грешка при сканиране на размера на инсталацията Имаше {0} грешки по време на сканирането на инсталационния размер Неуспешно създаване на пряк път: {0} Неуспешно отваряне на ръководството: {0} Не може да се инсталира играта: {0} Не може да се деинсталира играта: {0} Няма намерени валидни действия при стартиране на играта. Когато използвате действия на емулатора, уверете се, че дефинициите на платформата съвпадат между играта и конфигурацията на емулатора. Внедряването на инсталацията не е налично. Приставката за библиотека, отговорна за тази игра, е деактивирана или не е инсталирана. Официалното изтегляне на метаданни не е налично. Не е избрана игра. Неуспешно изпълнение на скрипта на играта. Неуспешно изпълнение на скрипт на приложение. Неуспешно изпълнение на глобален скрипт. Неуспешно изпълнение на скрипта на емулатора. Неуспешно изпълнение на действие на скрипт за възпроизвеждане. PowerShell 3.0 или по-нова не е инсталирана. Не можах да определя как да стартирам играта. Активирано Изключено Премахване Премахване на неизползваните Преименуване Копиране Добавяне Икона по подразбиране Изображение на корицата по подразбиране Фоново изображение по подразбиране Край Напред Назад ГОТОВО НАЗАД ИЗЧИСТИ Изчисти Отхвърли Отхвърли всички Импортирай Име Автор Модул Поредица Версия Последно пускане Най-играни Брой пускания Размер на инсталиране Папка Бележки Добавено Дата на добавяне Модифицирано Дата на промяна Уебсайт Път OK Запази Затвори Отказ Потвърди Занули Да Не Добре дошли Локален потребител Общи Медия Връзки Инсталиране Действия Изтегля се... Изтегляне на мултимедия... Зарежда се... Тип Профил Профили Премахване Изтегляне Търсене Резолюция: Всички Мащабиране Списъчен изглед Обложки Мрежов изглед Изглед с подробности Персонализиран URL Специални благодарности Лиценз Сътрудници Излизане от Playnite... Днес Вчера Понеделник вторник сряда Четвъртък Петък Събота Неделя Миналата седмица Миналия месец Миналата година Преди повече от година 0 до 100MB 100MB до 1GB 1GB до 5GB 5GB до 10GB 10GB до 20GB 20GB до 40GB 40GB до 100GB 100GB или повече Импортирането завърши успешно. Всички игри Идентификационен номер на играта Идентификационен номер на база данни Предварителни настройки Колона Колони Ред Редове Не може да се получи икона от действието Play. Няма действие от тип файл. Изтегляне само на липсващи метаданни Активирането на тази опция ще пропусне изтеглянето на метаданни за полетата с данни, които вече съдържат информация. Избор на игри Моля, изберете кои игри да се актуализират с нови метаданни: Всички игри от базата данни Всички филтрирани в момента игри Само избрани игри Няма избрани полета за метаданни Не са избрани полета за метаданни за изтегляне. Моля, изберете поне едно и активирайте поне един доставчик на метаданни за него. Официален магазин IGDB Моля, изберете кои полета да се попълват автоматично от Playnite и кои източници да се използват за получаване на данните. Моля, помислете дали да не щракнете върху логото по-горе и да допринесете за актуализации в базата данни на igdb.com, за да подобрите данните, които Playnite използва. Изтегляне на метаданни... Импортиране на инсталирани игри... Импортиране на {0} игри… Импортиране на емулирани игри от {0}… Изтегляне на актуализации на библиотеката... Сканиране на размера на игрите в библиотеката... Размер на сканиране на импортирани игри... Актуализацията на библиотеката завърши Освобождаване на ресурси... Конфигурация Настройки… Платформи и емулатори Конфигуриране на емулатори… Мениджър на библиотека… Инструменти Изтегляне на метаданни… Софтуерни инструменти… Конфигуриране на интеграции… Отваряне на клиент на трета страна Клиенти на трети страни Актуализиране на библиотеката с игри Отмяна на актуализацията на библиотеката Актуализиране на емулирани папки Добавяне на игра Ръчно… Сканирай автоматично... Емулирана игра… Приложение на Microsoft Store… Относно Playnite Изпращане на обратна връзка Превключване към режим на цял екран Връзки Помощ Поддръжка на Patreon Подкрепи в Ko-fi Ръководство за потребителя Документация за SDK Рестартирайте системата Изключване на системата Суспендиране на системата Система за хибернация Заключи Система Отпиши Потребител Избор на произволна игра Полета за игра, които да се показват в панела с подробности: Разстояние между елементи Изчертаване на фон на елемент от мрежата Ширина на рамката на елемент от мрежата Липсващ източник на икона на игра Липсващ източник на обложката на играта Липсващ фонов източник на играта Вертикално разстояние до подробностите за играта Позиция на детайлите на мрежовия изглед Подробности за позицията на списъка с игри Изчертаване на разделител между панелите Височина на изображението на корицата на играта Височина на иконата на списъка с игри Шрифт на приложението Моноразмерен шрифт Позиция на филтърния панел Позиция на панела на Explorer Изобразяване на обложката Целево съотношение на страните Следните опции също засягат изобразяването на плочки в режим на цял екран! Режим на разтягане DVD кутия Epic Games Store GOG Galaxy 2.0 IGDB Квадрат Steam банер Вертикален капак на Steam Twitch * Изисква рестартиране за прилагане Настройки Общи Горен панел Външен вид Подробности за играта Оформление Разширени Цял екран Вход Ефективност Метаданни Актуализиране Търсене Архивиране Архивиране на данни от библиотека Възстановяване на резервно копие на данни Автоматично импортиране на промените в библиотеката Невалидно местоположение на файла на базата данни, трябва да се зададе правилен път към файла. Името на акаунта не може да бъде празно. Изтегляне на метаданни след импортиране на игри Стартиране на минимизиран Playnite Стартирайте Playnite, когато стартирате компютъра си Старт затворено в трея Неуспешно регистриране на Playnite за стартиране при стартиране на компютъра. Стартиране в режим на цял екран Асинхронно зареждане на изображение Може да подобри гладкостта на превъртането на списъците с игри в замяна на по-бавно време за зареждане на изображението. Показване на името на играта, ако липсва обложка Показване на имената на игрите в мрежов изглед Затъмняване на неинсталирани игри Показване на иконите на играта в списъка за изглед с подробности Показване на броя на елементите в описанията на групи Показване само на присвоените полета в панелите за филтриране и изследване Деактивиране на хардуерното ускорение Използвайте, когато изпитвате заекване или подобни проблеми с потребителския интерфейс Показване на скрити игри в списъците за бързо стартиране Засяга списъка за прескачане и списъците с менюта в областта. Брой елементи за бързо стартиране Използване на фоново изображение на играта като фон на прозорец Замъгляване на фон Високо качество Затъмняване на фона Показване в мрежов изглед Тема Профил на тема Тема на цял екран Профил на темата на цял екран Местоположение на базата данни Състояние на влизане: Настройки на Playnite Изчистване на уеб кеша Може да разреши проблеми, възникнали при свързването на акаунти. Показване на иконата в системната област Минимизиране на Playnite в системната област Минимизиране на Playnite в системната област, когато прозорецът на приложението е затворен Когато играта започне: След затваряне на играта: Форматирайте изиграното време, за да индикирате броя изиграни дни Формати на датите: Това ще ви излезе от всички свързани услуги. Изисква се рестартиране на приложението, искате ли да продължите? Изчистване на кеша? Изисква се рестартиране на Playnite за прилагане на нова тема Вземете още теми Създаване на нова тема Вземете още разширения Създаване на ново разширение Помогнете ни да преведем Playnite Playnite трябва да се рестартира, за да се приложат нови настройки. Рестартирай сега? Рестартирането ще отмени всички активни задачи (изтегляния), които се извършват в момента. Рестартиране на Playnite? Playnite не може да премества файловете на вашата библиотека автоматично. Трябва ръчно да преместите/копирате файловете, преди да промените местоположението. Ако няма библиотека в целевото местоположение, ще бъде създадена нова. Новото местоположение на базата данни няма да се използва, докато Playnite не бъде рестартиран. Времето за игра няма да бъде записано, ако "Затвори" действието е зададено. Брой редове Брой колони Брой редове за подробен изглед Показване на фоново изображение на главния екран Не се прилага ретроспективно за съществуващи игри без повторно изтегляне на метаданни. Импортиране на време за игра на игри в библиотеката: Конфигурира кога Playnite трябва да импортира времето за игра, отчетено от плъгините на библиотеката за игри в базата данни на Playnite. Необходима е поддръжка от плъгините на библиотеката, отговарящи за обработката на играта(ите), за да можете да използвате тази функция. Винаги: Импортира време за игра за нови импортирани и съществуващи игри в базата данни на Playnite. Само за нововнесени игри: Импортира време за игра само за нови импортирани игри. Никога: Никога не импортира време за игра при никакви обстоятелства. Винаги Само за новоимпортирани игри Никога Активиране на поддръжката на контролера в настолен режим Бутонът за ръководство отваря режим на цял екран Настройки за автоматично изтегляне на метаданни за новоимпортирани игри. Целеви дисплей Винаги използвайте основния дисплей Показване на заглавия на игри Показване на състоянието на батерията Показване на процента на батерията Показване на часовника Скриване на курсора на мишката Инсталира се само в бързи филтри Подканващи бутони Оформление Хоризонтално превъртане Изберете един от подсекциите Няма налични настройки Неуспешно зареждане на настройките Тези скриптове се изпълняват за всяка игра в библиотеката. Индивидуални скриптове могат да бъдат присвоени на всяка игра поотделно, докато редактирате подробностите за играта. Анимиране на преходи на фоново изображение Размери на шрифта Автоматично Псевдоним Сива скала ClearType Идеално Дисплей Режим на форматиране на текст Режим на изобразяване на текст Методите за изобразяване и форматиране на текст в момента не се използват за описания на игри. Предварително зареждане на фонови изображения Ако е активирано, Playnite ще изтегля фонови произведения на изкуството, докато изтегля метаданни, като използва повече дисково пространство и прави произведения на изкуството достъпни, когато сте офлайн. Ако е деактивирано, фоновата илюстрация се изтегля само когато е необходима за първи път, използвайки по-малко място, но може да доведе до забавяне, преди да се покаже илюстрацията и някои изображения може да не са налични, когато сте офлайн. Автоматично затваряне на клиент на трета страна след излизане от играта Забавяне при изключване на клиента (в секунди) Не затваряйте след игрови сесии, по-кратки от (в секунди) Автоматично затваряне на следните клиенти: Автоматично затваряне на клиенти Списък с изключения за импортиране Показване на предупреждение при присвояване на твърде голяма медия за игра Команда за отваряне на папка Предпочитана организация за оценка на възраст Актуализиране на инсталационния размер на игри при актуализация на библиотека Сканира и актуализира инсталационния размер на игрите, ако се установи, че техните файлове са били променени след последното сканиране Няма Попълване Униформа Униформа за запълване Наляво Надясно Нагоре Отдолу Грешка при импортиране Изисква се удостоверяване Неуспешно удостоверяване Алтернативен режим на изобразяване на уеб изглед Използвайте, когато имате проблеми с уеб изгледи, например диалогови прозорци за удостоверяване на интеграция. Частично зареждане на големи описания на игри Големите описания могат да причинят забележимо забавяне при избора на игри. Когато е активирано, само част от текста на описанието ще бъде първоначално зареден с опция за зареждане на останалата част при поискване. Импортиране на метаданни Изтегляне на метаданни Задайте избраната конфигурация, която да се използва за бъдещи изтегляния на метаданни. Може също да се промени в настройките на приложението. Съветник за импортиране на емулация Този съветник ще ви преведе през процеса на изтегляне и импортиране на конзолни емулатори и импортиране на емулирани игри. Имайте предвид, че винаги можете да добавите допълнителни емулатори и/или игри по-късно чрез главното меню (под менюто "Библиотека" за настройки на емулатора и менюто "Добавяне на игри" за емулирани игри ). По-долу е даден списък с емулатори, които Playnite може да разпознае и конфигурира автоматично. Можете да изтеглите инсталационни програми за емулатор от техните уебсайтове. След като инсталирате емулаторите (ръчно), можете да ги импортирате в диалоговия прозорец за конфигуриране на емулатора. Можете да импортирате всички емулатори, които са инсталирани на вашия компютър, като щракнете върху бутона „Автоматично откриване от папка…“. Playnite ще търси в избраната папка всички известни емулатори и ще предостави опцията за импортирането им. Можете да използвате този бутон няколко пъти, за да импортирате емулатори от различни папки. Емулаторите ще бъдат добавени в края на текущия списък. Можете да импортирате игри, като щракнете върху бутона „Сканиране на папка чрез емулатор“. Избирането на подходящия емулатор ще каже на Playnite кои типове файлове трябва да бъдат сканирани и импортирани. Можете да използвате този бутон няколко пъти, за да импортирате игри от различни папки. Игрите ще бъдат добавени в края на текущия списък. Няма избрани емулатори за импортиране. Няма да можете автоматично да импортирате никакви емулирани игри, без първо да конфигурирате емулатори. Сигурни ли сте, че искате да продължите и да излезете от процеса на импортиране? В Playnite няма конфигурирани емулатори. Не можете да импортирате игри, без първо да конфигурирате емулатора и да изберете подходящите типове файлове. Искате ли да добавите някои емулатори сега? Сканиране на папка чрез емулатор Избор на файлове Автоматично откриване от папка… Конфигуриране на емулатори… Сканиране... Сканиране {0}… Конфигурация за първи път Този процес ще ви преведе през автоматично импортиране и конфигуриране на външни библиотеки с игри. Playnite може автоматично да импортира игри от множество услуги за игри, като Steam или GOG. Имайте предвид, че можете също ръчно да добавите персонализирана или емулирана игра за всяка платформа по-късно от главното меню. Интегриране на библиотека Следва списъкът с някои подбрани библиотечни интеграции, поддържани от Playnite. Моля, изберете тези, които искате да инсталирате. Повече интеграции могат да бъдат инсталирани по-късно от "Добавки" меню. Конфигурацията завършена Първоначалната настройка е завършена. Не забравяйте, че можете да промените всички настройки по-късно, както и да добавите допълнителни интеграции от главното меню. Неуспешно изтегляне на едно или повече разширения. Можете да опитате да изтеглите повторно интеграции от менюто с добавки, след като съветникът за първо стартиране завърши. Изтегля се интеграция на {0}… Изтегля се списък с препоръчани интеграции... Неуспешно изтегляне на списък с препоръчани интеграции. Можете да опитате и да изтеглите повторно интеграции по-късно чрез менюто Addons. Конфигуриране на платформи и емулатори Конфигуриране на емулатори Платформи Платформа Емулатори Емулатор Добавяне на платформа Избор на икона Изберете корица Избор на изображение Избор на елемент Избор на фон Изберете файл Изберете URL Добавяне на емулатор Поддържана платформа(и) Искате ли да запазите промените в платформата? Искате ли да запазите промените в емулатора? Изпълним Аргументи Работна директория Поддържани файлови типове Импортиране на емулатори... Изтегляне на емулатори... Зареждане на предварително зададени аргументи от известен профил на емулатор Сигурни ли сте, че искате да премахнете емулатора на {0}? В момента се използва от {1} игра(и). Сигурни ли сте, че искате да премахнете платформата {0}? В момента се използва от {1} игра(и) и {2} емулатор(а). Помощ за настройки Сортиране по Посока на сортиране Групиране по Възходящо Низходящо Не групирай Групиране по библиотека Групиране по категория Групиране по платформа Тип изглед Изглед Панел за преглед Панел за филтри Икона Икона на библиотека Изображение на корицата Фоново изображение Име за сортиране Библиотека Ръководство Име Инсталиране на устройство Име на акаунт Платформа Категория Жанр Дата на издаване Година на издаване Разработчик Етикет Издател Състояние на инсталацията Съвпада с всички филтри Ако е разрешено, само игри, които използват всички елементи във всички филтри, ще бъдат включени в изгледа. Ако е деактивирано, игрите, които използват който и да е елемент във всеки филтър, ще бъдат включени в изгледа. Инсталирани Инсталирани Не инсталирани Скрити Любими Активиране на HDR поддръжка Ако е активиран, HDR ще бъде активиран на основния дисплей преди стартиране на играта. Имайте предвид, че HDR не се поддържа на вашия основен дисплей. Последно играно Категория Описание Инсталационна папка Изображение на корицата Връзки Изображение, ROM или ISO път Жанр Жанрове Фирма Компании Разработчик Разработчици Издател Издатели Категория Категории Етикет Етикети Функция Функции Възрастова оценка Възрастови оценки Регион Региони Източник Източници Последна активност Грешка в базата данни Неуспешно отваряне на библиотечна база данни. Базата данни не е отворена. Не може да се осъществи достъп до базата данни на библиотеката. Файл "{0}" се използва от друг процес или е на недостъпно място. Неуспешно създаване на диагностичен пакет. Неуспешно автоматично качване на диагностичен пакет. Диагностичната информация беше изпратена успешно. Диагностичният пакет е създаден и изпратен успешно. Моля, прикачете следния идентификатор към вашия доклад за проблем: Неуспешно импортиране на игри от {0}. Неуспешно импортиране на емулирани игри от {0}. Не може да търси игри по избран профил на емулатор. Профилът не съдържа файлови разширения или платформи. Playnite не успя да стартира. Моля, затворете всички други копия и опитайте отново. Неуспешно прилагане на тема "{0}", цветен профил "{1}" {2} Не може да се отвори връзка, URL адресът не е във валиден формат. Неуспешно стартиране на приложението. Неуспешно инициализиране на компонента за уеб изглед. Playnite не може да продължи с процеса на стартиране. Повече информация на https://playnite.link/cefstartup Не могат да се импортират емулатори поради липсващ или повреден дефиниционен файл. Неуспешно изпълнение на действие от менюто. Редактиране на подробностите за играта URL адрес на изображение Добавяне на връзка Добавяне на ROM Запазване на промените Прилагане на промени в полето към игра(и), които се редактират. Добавяне на действие Премахване Премахване на действие за възпроизвеждане Добавяне на игри Сканиране на папка… Откриване на инсталирани Преглед... Отворете Playnite Настройки на профила Името на играта не може да бъде празно. Директорията за проследяване на действие на играта не може да бъде празна. Името на играта не може да бъде празно преди търсене в метаданни. Невалидни данни за играта Въведете валиден уеб URL, започващ с http:// или https:// Изберете URL адрес Неуспешно изтегляне на метаданни: {0} Грешка при изтегляне Изчистване на филтрите Личен акаунт Публичен акаунт API ключ Грешка при стартиране Грешка в темата Изчистване на всички Инсталиране Деинсталиране Стартиране Изпълнява се Невалиден URL адрес Не правете нищо Минимизиране Прозорец за възстановяване Възстанови прозореца само когато стартира през потребителския интерфейс Затваряне Промяна Разширени Никога Статус на завършване Състояния на завършване Потребителски резултат Резултат от критик Резултат от общността Скриптове за игри Скриптове за приложения Скриптове Добавки Източници на метаданни Разширения ID на разширение Презареждане на скриптове Интерактивен SDK PowerShell Всички скриптове са презаредени успешно. Няма намерени игри за определени критерии за търсене/филтриране Няма намерени елементи Превключване към режим на работен плот Излезте от Playnite Библиотеки Актуализиране на всички Създаден от: Версия: Актуализирано: Модул: Библиотека Статистика Всички Няма Известия Ширина Височина Размер Малък Нормално Голям По-голям Най-голям По подразбиране Избор Избиране на всички Премахване на избора от всички Първо Произволно Избор на потребител Зареди още Прозрачен Свиване Разгъване Свиване на всички Разгъване на всички Други Теми Аргументи на емулатора Вградени аргументи Персонализирани аргументи Допълнителни аргументи на емулатора Замяна на аргументите на емулатора Действие за възпроизвеждане Изберете метаданни за импортиране Изберете игри за импортиране Търсене на метаданни Налична актуализация Промени от последната актуализация Изтеглете и инсталирайте актуализация Проверка за актуализации Грешка при актуализиране Неуспешна проверка за нова версия. Няма намерена нова версия, вие сте актуална. Неуспешно изтегляне и инсталиране на актуализация. В момента се изпълнява някаква фонова задача. Искате ли да го отмените и да продължите с актуализацията? В момента се изпълнява някаква фонова задача. Искате ли да го отмените и да излезете от Playnite? В момента се изпълнява някаква фонова задача. Превключването на режимите ще анулира задачата. Искате ли все пак да превключите? Налична е актуализация за Playnite Презареди списъка с теми Прилагане на избраната тема Гледайте промените във файла Автоматично прилагане на тема, когато изходният файл се промени Среда за изпълнение на скрипт Изпълняване преди стартиране на игра Изпълняване след излизане от игра Изпълняване след стартиране на игра Изпълняване при стартиране на приложението Изпълняване при изключване на приложението Скрипт за стартиране на играта Скрипт за стартиране на играта Скрипт за спряна игра Изпълняване на глобален скрипт Глобален Филтрирано Текущ Ново Тестов скрипт Показване само на избрани елементи. Запазване по подразбиране Добавяне към любими Премахване от любими Скриване на тази игра Премахване от скрити Активиране на HDR поддръжка Деактивиране на HDR поддръжка Редактиране... Изчисляване на инсталационния размер Изчисляване на инсталационния размер (Всички игри) Изчисляване на инсталационния размер (само липсващи данни) Размер за инсталиране Задаване на категория... Задаване на състояние на завършване Премахване Играйте Инсталиране Опции на играта Подробности Деинсталиране Отваряне на мястото за инсталиране Създаване на пряк път на работния плот Отворете ръководството Още Управлява се от приставката за библиотека Процесът на стартиране на играта ще се управлява от плъгина на библиотеката, отговорен за тази игра. На посочената страница не е намерена подходяща информация за играта „{0}“. Съвет: Можете да използвате по-разширен процес на изтегляне на метаданни, докато редактирате една игра чрез "Редактиране" опция от менюто. Не е налично, когато се извършва някакво действие. Текстът на описанието е чувствителен към HTML синтаксиса Времето на играта се записва в секунди. Размерът на инсталиране е посочен в байтове. Дата на издаване трябва да бъде зададена във формат „година-месец-ден“. Стойностите за месец и ден могат да бъдат пропуснати. Стойности от 0 до 100 или празни за липса на резултат. Разработката на Playnite се поддържа от следните потребители на Patreon и Ko-fi: Код, локализация и други сътрудници без определен ред: Анулиране на наблюдението на играта? Наблюдението на инсталацията се изпълнява в момента. Искате ли да отмените процеса и да върнете играта в предишното състояние? В момента се изпълнява мониторинг на изпълнението на играта. Искате ли да отмените процеса и да върнете играта в предишното състояние? Изиграно време Последно пускане {0}д {1}ч {2}мин {0} ч {1} м {0} минути {0} секунди Не е играно Отваряне на режим на работен плот... Отваряне на режим на цял екран... Зарежда се библиотека с игри... Изчисляване на инсталационния размер... Изчисляване на инсталационния размер на {0}… Неуспешно инсталиране на файл със скрипт. Скриптът е инсталиран успешно. Скрипт за инсталиране Грешка в скрипта Неуспешно изпълнение на функцията за разширение. Отворете папката с метаданни Изчисляване Автоматично изчислява инсталационния размер с помощта на ROM, ако играта има такива, или инсталационната директория, ако е зададена {0} клиент не е инсталиран. {0} клиент сега ще се отвори. Моля, влезте и затворете това съобщение. Изчакване потребителят да влезе, моля затворете това, когато сте готови... Инсталационната папка на играта не е намерена. Невалидна конфигурация на действие в играта. Отстраняване на проблеми със синхронизирането на акаунт Отстраняване на проблеми Преименуване на елемент Добавяне на нов елемент Въведете име Въведете ново име По-малко от час 1 до 10 часа 10 до 100 часа 100 до 500 часа 500 до 1000 часа Над 1000 часа Playnite трябва да се рестартира, за да завърши инсталацията. Искате ли да рестартирате сега? Разширението не е пакетирано правилно. Темата не е пакетирана правилно. Разширение "{0}" не успя да се зареди правилно. Не може да се зареди "{0}" разширение, текущата версия на Playnite не се поддържа. Тема "{0}" не успя да се зареди правилно. Не може да се зареди "{0}" тема, текущата версия на Playnite не се поддържа. Разширението не успя да се зареди правилно. Темата не успя да се зареди правилно. Темата/разширението използва неподдържана версия на API. Инсталацията беше успешна. Инсталиране на добавка? Общи Неуспешно инсталиране на "{0}" добавка. Неуспешно инсталиране на разширение. {0} Искате ли да инсталирате ново разширение? {0} от {1} Версия {2} Искате ли да актуализирате "{0}" разширение? Текуща версия: {1} Нова версия: {2} Неуспешно инсталиране на тема. {0} Искате ли да инсталирате нова тема? {0} от {1} Версия {2} Искате ли да актуализирате "{0}" тема? Текуща версия: {1} Нова версия: {2} Вие сте на път да напуснете Playnite и да навигирате до следната уеб страница, като използвате вашия уеб браузър по подразбиране. Искаш ли да продължиш? {0} Избраното изображение(а) може да е твърде голямо за оптимална производителност. Използването на много големи изображения може да доведе до по-лоша реакция на потребителския интерфейс и увеличено използване на паметта. Максимални препоръчителни разделителни способности: Икони: {0} мегапиксела Корици: {1} мегапиксела Фон: {2} мегапиксела Предупреждение за производителност Не показвай отново Файлът с разширение {0} не е съвместим. Несъвместимо файлово разширение Избраният файл с изображение може да е твърде голям за оптимална производителност. Сигурни ли сте, че искате да деинсталирате избраната тема? Деинсталирането ще бъде на опашка до следващото стартиране на приложението. Вградените теми не могат да бъдат деинсталирани. Тази тема не поддържа тази версия на Playnite. Сигурни ли сте, че искате да деинсталирате избраното разширение? Деинсталирането ще бъде на опашка до следващото стартиране на приложението. Вградените разширения не могат да бъдат деинсталирани. Това разширение не поддържа тази версия на Playnite. Инсталационна папка Папка с данни Генериране на диагностичен пакет… Качване на диагностичен пакет... Импортиране на файл... Какво е това? Сигурни ли сте, че искате да направите това? Общо време за игра Средно време за игра Най-добро време за игра Общ размер на инсталиране Общ преглед Странична лента Покажи в страничната лента Нулиране на настройките Всички настройки на приложението ще бъдат нулирани до стойностите по подразбиране, с изключение на: - Местоположение на базата данни - Импортиране на списък с изключения - Настройки на разширението, включително интеграции на библиотека За завършване на процеса е необходимо рестартиране на приложението. Искате ли да нулирате настройките? За разработчици Външни разширения Въведете пълния път на папката. Постижения Форум Новини Страница на магазин Първоначалната настройка не е завършена. Playnite сега ще се рестартира в режим на работен плот, за да завърши процедурата. Наскоро играни Любими Най-играни Всички Има приложени филтри. Има приложени допълнителни филтри. Показване на резултати от търсене за: Елемент със същото име вече съществува. Ограничаване на избора до текущия филтър Изберете друг Добавки… Инсталиран Настройки на разширенията Преглед Актуализации Актуализации ({0}) Управлението на инсталираните разширения и теми, включително техните настройки, е преместено в нов "Добавки" меню. Всички текущо инсталирани разширения за интегриране на библиотека могат да бъдат конфигурирани тук. Ако искате да инсталирате или деинсталирате допълнителни интеграции, използвайте "Добавки" опция от главното меню. Работен плот с теми Теми на цял екран Търсене... Добавката не е съвместима с тази версия на Playnite. Неуспешно изтегляне на инсталационния пакет на добавката. Неуспешно изтегляне на манифеста за инсталиране на добавка. Необходимо е рестартиране на приложението, за да се приложат чакащи промени. Тази добавка е планирана за инсталиране. Инсталиране Деинсталиране Вече инсталиран Няма намерени нови актуализации на добавки. Актуализиране на добавки Регистърът на промените не е наличен Планирано за инсталиране Неуспешно изтегляне Лицензът е отхвърлен Изтегляне на {0}… Търсят се актуализации на добавки... Търсене за актуализации на програмата… Налични са една или повече актуализации на добавките. Изберете елементи за актуализиране Екземпляр за разработка на разширение {0} лицензионно споразумение Приемам Отказ Включване на действия за игра на интегриране на библиотека Избор на действие Режим на проследяване Път за проследяване Първоначално забавяне на проследяването Честота на проследяване Връзка Файл Емулатор Скрипт По подразбиране Процес Папка Оригинален процес Име на процеса Регистриране на съобщения за проследяване Следващите промени презаписват данните за всички текущо избрани игри! Няма Униформа Само елементи Само начало и край Чувствителност при превъртане Гладко превъртане Скорост на анимацията Премахване на елемент? Сигурни ли сте, че искате да премахнете този елемент? Показване на бутони в горния панел: Общи настройки на изгледа Настройки за групиране Настройки за сортиране Предварително зададени филтри Позиция на елементите на приставката Ширина на разделителя на секции Преместване на бутона на главното меню в страничната лента Панел за преглед Произволен избор на игра Преглежда селектора за произволни игри Изберете произволна игра от изгледа Запазване на настройките за групиране и сортиране Показване като бърз филтър в режим на цял екран През последните 7 дни През последните 31 дни През последните 365 дни Преди повече от 365 дни Конфигуриране Запазване на предварителна настройка Минимизиране след стартиране на играта Минимизиране на Playnite след стартиране на игра. Деактивирането на това може да доведе до проблеми с игрите, които не получават фокус върху входа при стартиране! Размер на шрифта Малък размер на шрифта Поддръжка на игрови контролери Ако е деактивирано, Playnite няма да приеме входове на XInput интерфейс. Деактивирайте, ако използвате инструменти, които преобразуват XInput входове във входове от мишка/клавиатура и получавате двойни входове в Playnite. Показване на елементи в главното меню: Свързване на обърнат X/A бутон за основен изглед Разменя връзките на бутоните за стартиране на игра и показване на страница с подробности за играта в основния изглед. Размяна на обвързване на бутона за потвърждение/отмяна Инвертира A/B обвързвания на бутони за потвърждение и анулиране. Само основен контролер Приема входове само от основния контролер, когато е активиран. Бутонът за ръководство фокусира Playnite Сила на звука на интерфейса Сила на фона Заглушаване във фонов режим Неуспешно инициализиране на аудио интерфейс. API за изход API, използван за аудио изход. Променете, ако имате проблеми със звука. Общи Визуални елементи Аудио Оформление Менюта Вход {0} стартира... {0} работи... Главни букви Интервал Програма за мащабиране на рендиране на изображение Алтернатива Балансиран Качество Качество: Най-добро качество на изображението, бавно, високо използване на паметта. Балансиран: Добро качество, бързо, малко използване на паметта. алтернатива: По-добро качество, средна скорост, малко използване на паметта. Изберете файл... Изберете папка... Стартов скрипт Моля, имайте предвид, че както разширенията, така и темите могат значително да повлияят на производителността, стабилността и сигурността на Playnite. Ако започнете да изпитвате проблеми след инсталиране на тема или разширение, опитайте първо да ги деактивирате/деинсталирате, за да видите дали те са в основата на проблема. Избор при стартиране Избиране при стартиране Вградени профили Вграден профил Персонализирани профили Персонализиран профил Обработва се от вграден скрипт Спецификация на емулатора Спецификация на платформата Спецификация на региона Изпълняване преди стартиране на емулатора Изпълняване след стартиране на емулатора Изпълняване след излизане от емулатора Изпълнимият файл на емулатора не е намерен. Спецификацията на емулатора не е намерена. Скриптът за стартиране на емулатора не е намерен. Разделяне като отделни игри Сливане в една игра Задаване на платформа Задаване на регион Сканиране на папка Конфигурации на сканиране Изключване на модели от сканиране на контролна сума Файловете, съответстващи на определен шаблон(и), няма да бъдат сканирани за контролна сума и ще бъдат съпоставени по име на файл. Вижте помощната страница на емулатора за повече информация. Сканиране с емулатор Името трябва да бъде зададено при запазване на нова конфигурация. Емулатор или профил на емулатор не е зададен. Директорията за сканиране не е посочена или не съществува. Конфигурацията на сканиране не е зададена правилно. Включване в автоматичното сканиране при групово сканиране Неуспешно сканиране на папката за емулатори. Неуспешно сканиране на папка(и) за емулирани игри. Скриване на импортираните Профили за импортиране: Конфигурации за автоматично сканиране Запазване като конфигурация за автоматично сканиране Запазва конфигурацията за по-късна употреба по време на актуализацията на библиотеката. Запазените конфигурации могат да се управляват чрез "Конфигуриране на емулатори" меню. Импортиране чрез относителни пътища Ако е възможно, импортирайте файлове с игри, като използвате пътища, свързани с инсталационната папка на Playnite или инсталационната папка на емулатора. Сканиране на подпапки Сканиране на вътрешните архиви Обединяване на свързани файлове Обединете свързани файлове с игри, като отделни дискове с игри, в един запис за игра. Добавяне на скенер Добавяне на запазен скенер Стартиране на сканирането Добавете конфигурация(и) за сканиране с емулатори за сканиране на определени папки. Уверете се, че емулаторите са правилно конфигурирани, преди да импортирате игри (чрез менюто Библиотека -> Конфигуриране на емулатори). Статус по подразбиране, присвоен на новодобавени игри Статус, присвоен на игри, играни за първи път Неуспешно инициализиране на времето за изпълнение на скрипт PowerShell. Ако сте потребител на Windows 7, опитайте (пре)инсталирайте PowerShell 5.1, за да коригирате проблема. Вече съществува предварително зададен филтър с посочено име. Актуализиране на предварителна настройка с нови настройки? Тези думи ще бъдат премахнати от началото на автоматично попълнената стойност за име за сортиране: Използвайте това за игнориране на думи в началото на низ с цел сортиране. По подразбиране е "The", "An" и "A". Попълване на име за сортиране за игри без такова Сортиране Попълване на стойности на имената за сортиране… Установено е, че услугата Nahimic работи на вашата система. Известно е, че тази услуга причинява проблеми с изобразяването на Playnite (и други приложения). Ако срещнете повреда на графиката или други проблеми с изобразяването в Playnite, препоръчваме да деактивирате или напълно да деинсталирате услугата Nahimic. Повече информация на https://playnite.link/nahimicsucks Playnite работи с повишени привилегии (като администратор). Това не се препоръчва, тъй като дава повишени привилегии на всички инсталирани разширения и всички игри/приложения, стартирани от Playnite! Повече информация на https://playnite.link/adminfaq Показва предупреждение, ако Playnite работи с повишени привилегии Получете реалния размер на устройството, когато изчислявате размера на игрите Ако е активирано, сканирането ще бъде по-бавно и ще получи реалния размер, който файловете използват в устройството. Ако е деактивирано, сканирането ще бъде по-бързо и ще използва размера на самите файлове. Следните добавки са докладвани като потенциално проблемни или поради силно въздействие върху стабилността/производителността, или поради проблеми със сигурността. Силно препоръчваме да ги деинсталирате: {0} Изключване на онлайн файлове от сканиране Файловете, съхранявани в облачно хранилище, няма да бъдат сканирани и импортирани, ако не са налични локално. Поддържа се само за: Google Drive, DropBox, OneDrive Сканиране, но с помощта на опростен метод без файлово съдържание Файловете ще бъдат импортирани, но с помощта на по-малко точен метод, който не изисква съдържанието на файла да бъде изтеглено и представено локално. Прилагане към всички Замяна на състоянието на инсталиране Когато е зададено, Playnite ще игнорира състоянието на инсталация (включително инсталационна директория), зададено от плъгина за интегриране, който импортира тази игра. Тази опция може да не работи напълно с плъгини, които използват конкретен метод за импортиране на игри, освен ако не вземат предвид и тази опция за отмяна. Само ръчно Веднъж на ден Веднъж седмично При всяко стартиране Проверете за актуализации на програмата Проверка за актуализации на добавки Актуализиране на библиотеки Сканиране на папки за емулация Включване на скрити игри Редактиране на полета Избиране/Премахване на всички Отвори Активиране Присвояване Започнете да пишете, за да търсите игри… [F1] за помощ Започването с # показва списък с налични команди. Започвайки с /, извежда списък с налични доставчици/плъгини за търсене. Въвеждането на ключова дума за търсене и завършването с ИНТЕРВАЛ незабавно превключва към това търсене. TAB: действие за превключване ENTER: активиране на избраното действие SHIFT-ENTER: отваряне на менюто с елементи Включване на деинсталирани игри Включване на скрити игри Включени са деинсталирани игри Деинсталираните игри са изключени Включени скрити игри Скритите игри са изключени Играйте или инсталирайте Отидете на подробности Меню на играта Редактиране на играта Отворете търсене Поле за търсене Бутон за търсене Основно действие в играта Вторично действие в играта CTRL-F отваря глобално търсене, вместо да фокусира полето за търсене Запазване на настройките на филтъра на играта между сесиите за търсене Доставчици на търсене Ключова дума по подразбиране Персонализирана ключова дума Пряк път за цялата система Търсене в Playnite Настройки на разширението Изключения Изключени файлове спрямо папката за сканиране Изключени папки спрямо папката за сканиране Добавяне на файл към списъка с изключения Добавяне на папка към списъка с изключения Изключения могат да се добавят само към запазени конфигурации на скенера. Изключенията са добавени към "{0}" скенер. Замяна на платформа Когато е зададено, скенерът ще присвои тази платформа на всички игри, като презапише всички автоматично открити платформи. Включване на команди в търсенето по подразбиране Когато е деактивирано, командите няма да бъдат включени в търсенето по подразбиране, докато не се използва # префикс. Използвайте размито съвпадение във филтъра за имена Когато е активиран, филтърът за имена ще съпоставя имената на игрите по същия начин като глобалното търсене. Строгото съвпадение може да бъде наложено за отделен случай чрез префикс филтър със ! символ. Полета за показване на резултатите от играта: Скрито състояние Архивирането на данни бе отменено. Неуспешно архивиране на данни. Грешка при архивиране на данни Извършва се архивиране на данни... Възстановяване на данни от резервно копие... Неуспешно възстановяване на данни от резервно копие. Настройки Библиотека с игри Носител на библиотека с игри Инсталирани разширения Данни за разширенията Инсталирани теми Изберете данни, които да бъдат възстановени от посочения архивен файл. Playnite автоматично ще се рестартира, за да започне процес на възстановяване на резервно копие. Изберете елементи, които да бъдат включени в архивирането на данни. Настройките на приложението и данните от библиотеката с игри са включени по подразбиране. Playnite автоматично ще се рестартира, за да стартира процеса на архивиране. Автоматично архивиране на данни Честота на автоматично архивиране Архивна папка Ротиращи архиви Включете допълнителни данни: Папката за архивиране трябва да бъде зададена, ако е активирано автоматично архивиране. Показване на известия само за издания на корекции Когато е активирано, само наличните актуализации за текущо инсталираната основна версия ще доведат до известие за актуализация. Новите основни версии няма да доведат до известие за актуализация. Използване на относителни дати за последната седмица Използване на относителни дати в "Днес", "Вчера" и т.н. формат, ако датата е на по-малко от седмица. Посоченият формат на датата ще се използва за всички останали дати. Търсене на изображения в мрежата Низ за търсене на изображение на икона Низ за търсене на изображение на корицата Низ за търсене на фоново изображение Получаване на информация за добавката... Няма наличен източник на метаданни Настройки за действие за възпроизвеждане Използване на настройките на скенера Избор на профил при стартиране Избор на емулатор при стартиране Автоматично Винаги включен Винаги изключено Поддръжка за достъпност (екранен четец) Меню на приложението Меню на играта Програмна папка Директория с потребителски данни Открито е повреда на файла на библиотеката, Playnite ще се изключи. Отворете нов проблем в GitHub страницата на Playnite с искане за коригиране на повреда във вашите файлове. Искате ли да запазите направените промени? Портативна инсталация Няма открити контролери ================================================ FILE: source/Playnite/Localization/ca_ES.xaml ================================================  Català Idioma de Playnite Sortir Filtre actiu Filtre desactivat Filtres addicionals Filtres Filtre Dades no vàlides Vols desar els canvis? Pàgina web a www.playnite.link Codi font a GitHub Crear paquet de diagnosi Enviar informació del sistema Quant a Playnite Creat per Josef Němec Assignar categoria Establir categories Afegir categoria Seleccionat - Assignar categoria No seleccionat - Eliminar categoria Indeterminat - Sense canvis (en editar diversos jocs) Sense categoria Sense plataforma Vaja! Alguna cosa ha anat malament… S'ha produït un error irrecuperable. Si ens vols ajudar a solucionar el problema, descriu breument les accions preses abans del problema i envia la informació de diagnosi. Si estàs connectat a Internet, el paquet es penjarà al servidor de Playnite per a analitzar-se. També pots clicar al botó 'Informar d'una fallada' per crear una nova incidència al Github i informar manualment del problema. Gràcies per la teva ajuda. L'extensió "{0}" ha provocat un error irrecuperable. Es recomana desar el fitxer de registre i informar del problema al desenvolupador de l'extensió. Si el problema es repeteix, desactiva l'extensió. L'extensió "{0}" ha provocat un error irrecuperable. Es recomana informar del problema al desenvolupador de l'extensió. Si el problema es repeteix, desactiva l'extensió. Una extensió desconeguda o un tema ha provocat un error irrecuperable. Recomanem desactivar els complements de tercers, aïllar el problema i informar el problema al desenvolupador del complement. S'ha produït un error irrecuperable. Si vols ajudar-nos a arreglar aquest problema, envia'ns la informació de diagnosi. Gràcies. Desactivar extensió Desar fitxer de registre Envia informació de diagnosi Informar d'una fallada Reiniciar Playnite Reiniciar en mode segur Desactivant totes les extensions de tercers i utilitzant el tema predeterminat. Sortir de Playnite Accions preses abans de la fallada (en anglès): Organitzador de la biblioteca Eliminar joc(s)? No es pot eliminar: el joc o l'instal·lador s'està executant. No es pot desinstal·lar: el joc s'està executant. Segur que vols eliminar {0}? Segur que vols eliminar {0} jocs? Segur que vols eliminar {0}? Si selecciones l'opció "Afegir a la llista d'exclusions", s'evitarà que el joc es torni a importar la pròxima vegada que s'actualitzi la biblioteca. Segur que vols eliminar {0} jocs? Si selecciones l'opció "Afegir a la llista d'exclusions", s'evitarà que el joc es torni a importar la pròxima vegada que s'actualitzi la biblioteca. Segur que vols eliminar {0} entrades que no s'estan utilitzant actualment? No s'han trobat camps sense utilitzar Sí (afegir a la llista d'exclusions) Hi ha canvis sense desar en aquesta secció Actualitzant format de la biblioteca de jocs… Ha fallat l’actualització de la base de dades. No es pot actualitzar la biblioteca de jocs. Es requereixen {0} MB d’espai lliure. Error del joc No es pot iniciar el joc. '{0}' no s’ha trobat a la base de dades. No es pot iniciar el joc: {0} No es pot iniciar l’acció: {0} No es pot obrir la ubicació del joc: {0} No s'ha pogut detectar la mida de la instal·lació del joc: {0} Error en escanejar la mida de la instal·lació Hi ha hagut {0} errors en escanejar la mida de la instal·lació No s’ha pogut crear la drecera: {0} No s'ha pogut obrir el manual: {0} No es pot instal·lar el joc: {0} No es pot desinstal·lar el joc: {0} No s'han trobat accions d'inici de joc vàlides. Quan utilitzis accions d'emulador, assegura't que les definicions de la plataforma coincideixin entre el joc i la configuració de l'emulador. La implementació de la instal·lació no està disponible. El connector de la biblioteca responsable d'aquest joc està desactivat o no està instal·lat. La baixada de metadades oficials no està disponible. No hi ha cap joc seleccionat. Ha fallat l'execució de l'acció de l'script de joc Ha fallat l'execució de l'acció de l'script de l'aplicació. Ha fallat l'execució de l'acció de l'script global Ha fallat l'execució de l'acció de l'script de l'emulador. Ha fallat l'execució de l'acció de l'script de joc. PowerShell 3.0 o una versió més recent no està instal·lat. No s'ha pogut determinar com obrir el joc. Activat Desactivat Eliminar Eliminar els no utilitzats Canviar el nom Copiar Afegir Icona per defecte Imatge de caràtula per defecte Imatge de fons per defecte Finalitzar Següent Enrere FET ENRERE NETEJAR Netejar Descartar Descartar tot Importar Nom Autor Mòdul Sèrie Versió Darrera partida Joc més utilitzat Nombre de partides Mida de la instal·lació Carpeta Notes Afegit el Data d'addició Modificat el Data de modificació Pàgina web Ruta D'acord Desar Tancar Cancel·lar Confirmar Reinciar No Benvingut Usuari local General Multimèdia Enllaços Instal·lació Accions Descarregant… Descarregant multimèdia... Carregant... Tipus Perfil Perfils Eliminar Descarregar Cercar Resolució: Qualsevol Zoom Vista de llista Caràtules Vista de quadrícula Vista de detalls Personalitzat URL Agraïments especials Llicència Col·laboradors Sortint de Playnite… Avui Ahir Dilluns Dimarts Dimecres Dijous Divendres Dissabte Diumenge La setmana passada El mes passat L'any passat Fa més d'un any 0 a 100 MB 100 MB a 1 GB 1 GB a 5 GB 5 GB a 10 GB 10 GB a 20 GB 20 GB a 40 GB 40 GB a 100 GB 100 GB o més La importació s'ha completat correctament. Tots els jocs Id del joc Id de la base de dades Ajustaments per defecte Columna Columnes Filera Files No s'ha pogut obtenir la icona de l'acció Jugar. No hi ha cap acció de tipus fitxer present. Només descarregar metadades que faltin Activar aquesta opció farà que s'ometi la descàrrega de metadades pels camps que ja contenen informació. Selecció de jocs Selecciona els jocs que s’hagin d'actualitzar amb les noves metadades: Tots els jocs de la base de dades Tots els jocs filtrats actualment Només els jocs seleccionats No s'ha seleccionat cap camp de metadades No s'ha seleccionat cap camp de metadades per descarregar. Si us plau, com a mínim, selecciona un camp i un proveïdor de metadades que li correspongui. Botiga oficial IGDB Selecciona quins camps ha d'emplenar automàticament Playnite i quines fonts s’utilitzaran per obtenir les dades. Pots fer clic al logo de dalt i contribuir a la base de dades d'igdb.com per a millorar les dades que utilitza Playnite. Descarregant metadades… Important jocs instal·lats… Important jocs de {0}… Important jocs emulats de {0}… Descarregant actualitzacions de la biblioteca… Escanejant la mida dels jocs de la biblioteca... Escanejant mida dels jocs importats... L’actualització de la biblioteca ha finalitzat Alliberant recursos… Configuració Configuració… Plataformes i emuladors Configurar emuladors… Organitzador de la biblioteca… Eines Descarregar metadades… Eines de programari… Configurar integracions… Obrir client de tercers Clients de tercers Actualitzar biblioteca de jocs Cancel·lar actualització de la biblioteca Actualitzar carpetes emulades Afegir joc Manualment… Escanejar automàticament… Joc emulat… Aplicació del Microsoft Store… Quant a Playnite Enviar comentaris Canviar al mode de pantalla completa Enllaços Ajuda Donar suport al Patreon Suport a Ko-fi Manual d'usuari Documentació del SDK Reiniciar el sistema Tancar el sistema Suspendre el sistema Hibernar el sistema Bloquejar sistema Tancar sessió de l'usuari Escollir un joc a l'atzar Camps del joc que es mostraran al panell de detalls: Espai entre elements Mostrar imatge de fons dels elements de la quadrícula Amplada de la vora dels elements de la quadrícula Font de la icona faltant del joc Font de la caràtula faltant del joc Font de la imatge de fons faltant del joc Espai vertical dels detalls del joc Posició de detalls en la vista de quadrícula Posició de la llista de jocs en la vista detallada Posar un separador entre panells Alçada d'imatge de caràtula de joc Alçada d'icona de la llista de jocs Font de l'aplicació Font monoespaiada Posició del panell de filtres Posició del panell d'exploració Renderització de la caràtula Relació d'aspecte desitjada Les següents opcions també afecten la renderització dels mosaics en mode de pantalla completa. Mode d'estirament Caixa de DVD Epic Games Store GOG Galaxy 2.0 IGDB Quadrat Bàner de Steam Caràtula vertical de Steam Twitch * Cal reiniciar per aplicar els canvis Configuració General Panell superior Aparença Detalls del joc Disposició Avançat Pantalla completa Entrada Rendiment Metadades Actualització Cerca Còpia de seguretat Fer una còpia de seguretat de la biblioteca Restaurar còpia de seguretat de les dades Importar els canvis a la biblioteca automàticament La ubicació del fitxer de base de dades no és vàlida; cal definir la ruta del fitxer adequada. El nom del compte no pot estar buit. Descarregar metadades després d'importar jocs Iniciar Playnite minimitzat Iniciar Playnite quan s'engegui l'ordinador Iniciar tancat a la safata No s’ha pogut registrar Playnite per iniciar-se quan s'obri l’ordinador. Iniciar en mode de pantalla completa Càrrega d’imatges asíncrona Millora la suavitat de desplaçament de les llistes de jocs a canvi de temps de càrrega d'imatges més lents. Mostrar el nom del joc si no hi ha imatge de caràtula Mostrar noms dels jocs en la vista de quadrícula Enfosquir els jocs no instal·lats Mostrar les icones dels jocs en la vista de detalls Mostrar el recompte d’elements a les descripcions de grups Mostrar només els camps assignats als panells de filtre i exploració Desactivar l'acceleració de hardware Fes-ho servir quan tinguis problemes de rendiment o similars a la IU Mostrar els jocs ocults a les llistes d'inici ràpid Afecta el Jump List i les llistes del menú de safata. Nombre d'elements a la llista d'inici ràpid Utilitzar la imatge de fons del joc com a fons per la finestra Desenfocar el fons Alta qualitat Enfosquir el fons Mostrar en vista de quadrícula Tema Perfil del tema Tema de pantalla completa Perfil del tema de pantalla completa Localització de la base de dades Estat d'inici de sessió: Configuració de Playnite Esborrar la memòria cau web Pot resoldre els problemes que es trobin mentre s'enllacen els comptes. Mostrar icona a la safata del sistema Minimitzar Playnite a la safata del sistema Minimitzar Playnite a la safata del sistema quan la finestra de l’aplicació estigui tancada Quan el joc s'iniciï: Quan el joc es tanqui: Format de temps reproduït per indicar el nombre de dies jugats Formats de dates: Això et tancarà la sessió a tots els serveis enllaçats. Cal reiniciar l’aplicació, vols continuar? Esborrar memòria cau? Es requereix reiniciar Playnite per a aplicar el tema nou Obtenir més temes Crear nou tema Obtenir més extensions Crear nova extensió Ajuda'ns a traduir Playnite Cal reiniciar Playnite per poder aplicar la nova configuració. Reiniciar ara? Si es reinicia, es cancel·laran les tasques actives (baixades) en curs. Reiniciar Playnite? Playnite no pot moure els fitxers de la biblioteca automàticament. Has de moure/copiar manualment els fitxers abans de canviar la ubicació. Si no hi ha cap biblioteca a la ubicació de destinació, se'n crearà una de nova. La nova ubicació de la base de dades no s'utilitzarà fins que es reiniciï Playnite. El temps de joc no quedarà registrat si es defineix l'acció "Tancar". Nombre de files Nombre de columnes Nombre de files de vista de detalls Mostrar imatge de fons a la pantalla principal No s'aplica retrospectivament als jocs existents sense tornar a descarregar les metadades. Importar temps de joc dels jocs de la biblioteca: Configura quan ha d'importar Playnite el temps de joc indicat pels plugins de la biblioteca per a jocs a la base de dades de Playnite. Es necessita el suport dels connectors de la biblioteca encarregats de gestionar els jocs per poder utilitzar aquesta funció. Sempre: Importa el temps de joc per a nous jocs importats i existents a la base de dades de Playnite. Només pels jocs recentment importats: Importa temps de joc només per als nous jocs importats. Mai: Mai no importa temps de joc sota cap circumstància. Sempre Només pels jocs recentment importats Mai Activar la compatibilitat amb el comandament en mode d'escriptori El botó Guia obre el mode de pantalla completa Configuració de descàrrega automàtica de metadades per a jocs recentment importats. Pantalla objectiu Utilitzar sempre la pantalla principal Mostrar títols dels jocs Mostrar estat de la bateria Mostrar percentatge de la bateria Mostrar rellotge Amagar el cursor del ratolí Només instal·lats a filtres ràpids Estil dels botons Disposició Desplaçament horitzontal Selecciona una de les subseccions No hi ha una configuració disponible No s'ha pogut carregar la configuració Aquests scripts són executats per cada joc de la biblioteca. Es poden assignar scripts individuals per cada joc per separat mentre s'editen els detalls del joc. Animar transicions de les imatges de fons Mides de les fonts Automàtic Aliàsing Escala de grisos ClearType Ideal Pantalla Mode de format de text Mode de renderització de text Actualment no s’utilitzen mètodes de renderització i format de text per a la descripció de jocs. Precarregar les imatges de fons Si s'activa, Playnite descarregarà imatges d'art de fons mentre descarrega metadades, utilitzant més espai de disc i fent l'art disponible quan s'estigui fora de línia. Si es desactiva, les imatges d'art de fons seran descarregades només quan es necessitin per primera vegada, utilitzant menys espai, però pot causar un retard abans que l'art sigui presentat i algunes imatges poden no estar disponibles quan s'estigui fora de línia. Tancar automàticament el client de tercers després de sortir del joc Retard d'apagada del client (en segons) No tancar després de les sessions de joc de menys de (en segons) Tancar automàticament els següents clients: Tancar clients automàticament Importar llista d'exclusions Mostrar avís quan s'assigni multimèdia del joc molt gran Ordre d’obertura de directori Organització de classificació per edat preferida Actualitzar la mida de la instal·lació de jocs en actualitzar la biblioteca Escaneja i actualitza la mida de la instal·lació de jocs si es detecta que s'han modificat els seus fitxers des de l'últim escaneig Cap Omplir Uniforme Emplenar uniformement Esquerra Dreta Dalt Baix Error d'importació Es requereix autenticació L'autenticació ha fallat Mode de renderització de vista web alternatiu Utilitza'l quan tinguis problemes amb les vistes web, per exemple, els diàlegs d'autenticació d'integració. Càrrega parcial de descripcions llargues de jocs Les descripcions llargues poden causar un lag notable en seleccionar jocs. Si s'activa, només es carregarà inicialment una part del text de la descripció amb una opció de carregar la resta sota demanda. Importació de metadades Descarregar metadades Estableix la configuració seleccionada perquè s'utilitzi a totes les descàrregues futures de metadades. També es pot canviar a les opcions de l'aplicació. Assistent d'importació d'emulació Aquest assistent et guiarà durant el procés de descàrrega i importació d’emuladors de consola i d’importació de jocs emulats. Recorda que sempre pots afegir emuladors i/o jocs addicionals a través del menú principal (al menú "Biblioteca" per a la configuració de l’emulador i al menú "Afegir jocs" per als jocs emulats). A continuació es mostra una llista d'emuladors que Playnite pot reconèixer i configurar automàticament. Pots descarregar instal·ladors d'emuladors des dels seus llocs web. Un cop tinguis els emuladors instal·lats (manualment), pots importar-los al diàleg de configuració de l'emulador. Pots importar qualsevol emulador instal·lat al teu PC fent clic a "Auto-detectar des de carpeta…". Playnite cercarà a la carpeta seleccionada qualsevol emulador conegut i permetrà importar-los. Pots importar des de diverses carpetes repetint l'acció, els emuladors seran afegits al final de la llista actual. Pots importar jocs prement el botó "Escanejar carpeta mitjançant l’emulador". La selecció de l’emulador apropiat indicarà a Playnite quins tipus de fitxers haurien de ser escanejats i importats. Pots importar des de múltiples carpetes utilitzant aquest botó diverses vegades, els jocs s'afegiran a la part inferior de la llista actual. No hi ha cap emulador seleccionat per importar. No podràs importar automàticament cap joc emulat sense configurar primer els emuladors. Segur que vols continuar i sortir del procés d'importació? No hi ha cap emulador configurat a Playnite. No pots importar jocs sense configurar primer l’emulador i seleccionar els tipus de fitxer adequats. Vols afegir alguns emuladors ara? Escanejar carpeta mitjançant l’emulador Seleccionar fitxers Detectar automàticament des de carpeta… Configurar emuladors… Escanejant… Escanejant {0}... Primera configuració Aquest assistent et guiarà a través del procés d’importació automàtica i la configuració de biblioteques de jocs externes. Playnite pot importar automàticament jocs des de diversos serveis com ara Steam o GOG. Tingues en compte que sempre pots afegir qualsevol joc personalitzat o emulat per a qualsevol plataforma manualment des del menú principal. Integració de biblioteques A continuació es presenta una llista d'algunes integracions de biblioteques que són compatibles amb Playnite. Si us plau, selecciona les que vulguis instal·lar. Pots instal·lar més integracions posteriorment des del menú "Complements". Configuració finalitzada La configuració inicial ha estat completada. Recorda que pots canviar tots els paràmetres posteriorment, així com afegir integracions addicionals des del menú principal. No s'han pogut baixar una o més extensions. Pots provar de tornar a descarregar les integracions des del menú de complements un cop finalitzi l'assistent de la primera execució. Descarregant integració de {0}... Descarregant llista d'integracions recomanades... No s'ha pogut descarregar la llista d'integracions recomanades. Pots intentar tornar a descarregar les integracions més tard mitjançant el menú de complements. Configurar plataformes i emuladors Configurar emuladors Plataformes Plataforma Emuladors Emulador Afegir plataforma Seleccionar icona Seleccionar caràtula Seleccionar imatge Seleccionar element Seleccionar fons Seleccionar fitxer Seleccionar URL Afegir emulador Plataformes compatibles Vols desar els canvis de la plataforma? Vols desar els canvis de l’emulador? Executable Paràmetres Directori de treball Tipus de fitxers admesos Importar emuladors… Descarregar emuladors… Carregar arguments predefinits del perfil conegut de l’emulador Segur que vols eliminar l'emulador {0}? Actualment, està sent utilitzat per {1} joc(s). Segur que vols eliminar la plataforma {0}? Actualment, està sent utilitzat per {1} joc(s) i {2} emulador(s). Ajuda de configuració Ordenar per Direcció de l'ordenació Agrupar per Ascendent Descendent No agrupar Agrupar per biblioteca Agrupar per categoria Agrupar per plataforma Tipus de vista Visualització Panell d'exploració Panell de filtres Icona Icona de biblioteca Imatge de caràtula Imatge de fons Nom d'ordenació Biblioteca Manual Nom Unitat d'instal·lació Nom del compte Plataforma Categoria Gènere Data de llançament Any de llançament Desenvolupador Etiqueta Editor Estat de la instal·lació Coincidir amb tots els filtres Si està activat, només s'inclouran a la vista els jocs que utilitzen tots els elements de tots els filtres. Si està desactivat, els jocs que utilitzen qualsevol element en qualsevol filtre s'inclouran a la vista. Instal·lat Instal·lat No instal·lat Ocult Preferit Activar compatibilitat amb HDR Si es selecciona, el HDR s'activarà a la pantalla principal abans d'iniciar el joc. Tingues en compte que el HDR no és compatible amb la teva pantalla principal. Última partida Categoria Descripció Carpeta d'instal·lació Imatge de caràtula Enllaços Ruta d'Imatge, ROM o ISO Gènere Gèneres Companyia Companyies Desenvolupador Desenvolupadors Editor Editors Categoria Categories Etiqueta Etiquetes Característica Característiques Classificació per edat Classificacions per edat Regió Regions Font Fonts Activitat recent Error de la base de dades No s’ha pogut obrir la base de dades de la biblioteca. La base de dades no està oberta. No es pot accedir a la base de dades de biblioteca. El fitxer "{0}" està sent utilitzat per un altre procés o es troba en una ubicació inaccessible. No s’ha pogut crear el paquet de diagnosi. No s’ha pogut carregar automàticament el paquet de diagnosi. La informació de diagnosi s'ha enviat correctament. El paquet de diagnosi s’ha creat i carregat correctament. Si us plau, adjunta el següent ID al teu informe d'error: No s’han pogut importar els jocs de {0}. No s’han pogut importar jocs emulats de {0}. No es poden cercar jocs pel perfil d’emulador seleccionat. El perfil no conté extensions de fitxers ni plataformes. Playnite no s'ha pogut iniciar. Tanca totes les instàncies del programa en execució i torna-ho a provar. No s’ha pogut aplicar el tema "{0}", perfil de color "{1}" {2} No es pot obrir l’enllaç, l’URL no té un format vàlid. No s’ha pogut iniciar l’aplicació. No s'ha pogut iniciar el component de vista web. Playnite no pot continuar amb el procés d'arrancada. Més informació a https://playnite.link/cefstartup No es poden importar emuladors perquè falta un fitxer de definició o està corromput. No s’ha pogut executar l'acció del menú. Editar els detalls del joc URL d'imatge Afegir enllaç Afegir ROM Desar canvis Aplicar els canvis de camp als jocs que s'estan editant. Afegir acció Eliminar Eliminar acció de Jugar Afegir jocs Escanejar carpeta… Detectar instal·lats Navegar… Obrir Playnite Opcions del perfil El nom del joc no pot estar buit. El directori de seguiment d'accions del joc no pot estar buit. El nom del joc no pot estar buit abans de cercar metadades. Dades de joc no vàlides Introdueix un URL web vàlid que comenci per http:// o https:// Seleccionar URL No s'ha pogut descarregar les metadades: {0} Error de descàrrega Esborrar els filtres Compte privat Compte públic Clau API Error d’inici Error del tema Esborrar tot Instal·lant Desinstal·lant Iniciant En funcionament URL no vàlid No fer res Minimitzar Restaurar la finestra Restaurar finestra només quan s'iniciï des de la IU Tancar Canviar Avançat Mai Estat de finalització Estats de finalització Puntuació dels usuaris Puntuació de la crítica Puntuació de la comunitat Scripts del joc Scripts de l'aplicació Scripts Plugins Fonts de metadades Extensions ID de l'extensió Recarregar scripts PowerShell SDK interactiu Tots els scripts s'han carregat correctament. No s'han trobat jocs per la cerca o filtre especificat No s'ha trobat cap element Canviar al mode d'escriptori Sortir de Playnite Biblioteques Actualitzar-ho tot Creat per: Versió: Actualitzat: Mòdul: Biblioteca Estadístiques Tots Cap Notificacions Amplada Alçada Mida Petita Normal Gran Més gran La més gran Per defecte Seleccionar Seleccionar-ho tot Desseleccionar-ho tot Primer Aleatori A selecció de l'usuari Carregar-ne més Transparent Contraure Desplegar Contraure tot Desplegar-ho tot Altres Temes Paràmetres de l'emulador Paràmetres incorporats Paràmetres personalitzats Paràmetres addicionals de l'emulador Substituir els paràmetres de l'emulador Acció de Jugar Seleccionar les metadades per importar Seleccionar jocs per importar Cerca de metadades Actualització disponible Canvis des de l'última actualització Descarregar i instal·lar l'actualització Buscar actualitzacions Error d’actualització No s'ha pogut comprovar si hi ha una versió nova. No s’ha trobat cap versió nova, estàs al dia. No s'ha pogut descarregar i instal·lar l'actualització. Hi ha alguna tasca en curs en segon pla. Vols cancel·lar-la i continuar amb l'actualització? Hi ha alguna tasca en curs en segon pla. Vols cancel·lar-la i sortir de Playnite? Hi ha alguna tasca en curs en segon pla. Si es canvia de mode, la tasca es cancel·larà. Vols canviar de totes maneres? Hi ha disponible una actualització per Playnite Tornar a carregar la llista de temes Aplicar el tema seleccionat Veure els canvis dels fitxers Aplicar automàticament el tema quan canviï el fitxer font Temps d'execució del script Executar abans d'iniciar un joc Executar després de sortir d'un joc Executar després que s'hagi iniciat un joc Executar en iniciar aplicació Executar en tancar aplicació Script d'inici del joc Script de joc iniciat Script de joc aturat Executar script global Global Filtrat Actual Nou Provar script Mostrar només els elements seleccionats. Desar com a predeterminat Afegir a Preferits Eliminar de Preferits Ocultar aquest joc Treure d'Ocults Activar compatibilitat amb HDR Desactivar compatibilitat amb HDR Editar… Calcular mida de la instal·lació Calcular mida de la instal·lació (tots els jocs) Calcular mida de la instal·lació (només les dades que faltin) Mida de la instal·lació Establir categoria… Establir estat de finalització Eliminar Jugar Instal·lar Opcions del joc Detalls Desinstal·lar Obrir l'ubicació de l'instal·lació Crear accés directe a l'escriptori Obrir el manual Més Gestionat pel plugin de biblioteca El procés d'inici del joc serà gestionat pel plugin de la biblioteca responsable d'aquest joc. No s'ha trobat cap informació important sobre el joc '{0}' en la pàgina especificada. Consell: pots utilitzar el procés de descàrrega de metadades més avançat mentre edites un joc a través de l'opció "Editar" del menú. No està disponible quan hi ha alguna acció en curs. El text de descripció és sensible a la sintaxi HTML El temps de joc s'enregistra en segons. La mida de la instal·lació s'indica en bytes. La data de llançament s'ha d'establir en format "any-mes-dia". Els valors del mes i el dia es poden ometre. Valors de 0 a 100 (o deixa-ho buit per no indicar una puntuació). El desenvolupament de Playnite compta amb el suport d'aquests mecenes i membres de Ko-fi: Codi, localització i altres col·laboradors sense cap ordre particular: Cancel·lar la monitorització del joc? Actualment, s’està executant la supervisió de la instal·lació. Vols cancel·lar el procés i tornar el joc a l'estat anterior? Actualment, s’està executant la supervisió de l’execució del joc. Vols cancel·lar el procés i tornar el joc a l'estat anterior? Temps jugat Última partida {0}d {1}h {2}m {0}h {1}m {0} minuts {0} segons No jugat Obrint mode d'escriptori… Obrint mode de pantalla completa… Carregant la biblioteca de jocs… Calculant la mida de la instal·lació... Calculant la mida de la instal·lació de {0}... No s’ha pogut instal·lar el fitxer d'script. Script instal·lat correctament. Instal·lar script Error d'script No s’ha pogut executar la funció d’extensió. Obrir carpeta de metadades Calcular Calcula automàticament la mida de la instal·lació mitjançant les ROMs si el joc en té alguna o s'ha establit el directori d'instal·lació El client de {0} no està instal·lat. Ara s'obrirà el client de {0}. Inicia sessió i tanca aquest missatge. Esperant que l'usuari iniciï la sessió. Si us plau, tanca aquest missatge quan ho hagis fet… No s'ha trobat la carpeta d’instal·lació del joc. Configuració d’acció de joc no vàlida. Resolució de problemes de sincronització del compte Resolució de problemes Renomenar element Afegir nou element Introdueix el nom Introdueix el nou nom Menys d'una hora 1 a 10 hores 10 a 100 hores 100 a 500 hores 500 a 1000 hores Més de 1000 hores S'ha de reiniciar Playnite per a completar la instal·lació. Vols reiniciar-lo ara? L'extensió no està empaquetada correctament. El tema no està empaquetat correctament. No s'ha pogut carregar correctament l'extensió "{0}". No es pot carregar l'extensió "{0}", la versió actual de Playnite no és compatible. No s'ha pogut carregar correctament el tema "{0}". No es pot carregar el tema "{0}", la versió actual de Playnite no és compatible. No s'ha pogut carregar correctament l'extensió. No s'ha pogut carregar correctament el tema. Aquest tema o extensió utilitza una versió de l'API que no és compatible. La instal·lació s'ha realitzat correctament. Instal·lar complement? General No s'ha pogut instal·lar el complement "{0}". No s'ha pogut instal·lar l'extensió. {0} Vols instal·lar una nova extensió? {0} Per {1} Versió {2} Vols actualitzar l'extensió "{0}"? Versió actual: {1} Nova versió: {2} No ha pogut instal·lar el tema. {0} Vols instal·lar un tema nou? {0} Per {1} Versió {2} Vols actualitzar el tema "{0}"? Versió actual: {1} Nova versió: {2} Estàs a punt de sortir de Playnite i dirigir-te a la pàgina web següent utilitzant el teu navegador per defecte. Vols continuar? {0} Les imatges seleccionades poden ser molt grans per un rendiment òptim. Utilitzar imatges molt grans pot causar una pitjor resposta de l'aplicació i un increment de l'ús de la memòria. Màxima resolució recomanada: Icones: {0} megapíxels Caràtules: {1} megapíxels Imatges de fons: {2} megapíxels Avís de rendiment No mostrar de nou El fitxer amb extensió {0} no és compatible. Extensió de fitxer no compatible El fitxer d'imatge seleccionat pot ser massa gran pel rendiment òptim. Segur que vols desinstal·lar el tema seleccionat? La desinstal·lació es produirà la pròxima vegada que el programa s'executi. Els temes preinstal·lats no es poden desinstal·lar. Aquest tema no és compatible amb aquesta versió de Playnite. Segur que vols desinstal·lar l'extensió seleccionada? La desinstal·lació es produirà la pròxima vegada que el programa s'executi. Les extensions preinstal·lades no es poden desinstal·lar. Aquesta extensió no és compatible amb aquesta versió de Playnite. Carpeta d'instal·lació Carpeta de dades Generant paquet de diagnosi… Pujant paquet de diagnosi… Importar fitxer… Què és això? Segur que ho vols fer? Temps total de joc Temps mitjà de joc Temps màxim de joc Mida total de la instal·lació Resum Barra lateral Mostrar a la barra lateral Restablir configuració Tota la configuració de l'aplicació es restablirà als seus valor per defecte, excepte: - La localització de la base de dades - La llista d'exclusió d'importació - La configuració d'extensions, incloses les integracions de biblioteques. És necessari reiniciar l'aplicació per finalitzar el procés. Vols restablir la configuració? Per a desenvolupadors Extensions externes Introdueix la ruta completa de la carpeta. Assoliments Fòrum Noticies Pàgina de la botiga La configuració inicial no s'ha completat. Playnite es reiniciarà en mode d'escriptori per a finalitzar el procés. Jugat recentment Preferits El més jugat Tots S'han aplicat filtres. S'han aplicat filtres addicionals. Mostrant resultats de la cerca per: Ja existeix un element amb el mateix nom. Limitar selecció al filtre actual Seleccionar un altre Complements… Instal·lat Configuració d'extensions Exploració Actualitzacions Actualitzacions ({0}) La gestió d'extensions i temes instal·lats, incloent-hi la seva configuració, s'ha traslladat a un nou menú anomenat "Complements". Totes les extensions d'integració de biblioteques actualment instal·lades es poden configurar aquí. Si vols instal·lar o desinstal·lar integracions addicionals, pots utilitzar l'opció "Complements" del menú principal. Temes (escriptori) Temes (pantalla completa) Cercant… El complement no és compatible amb aquesta versió de Playnite. No s'ha pogut descarregar el paquet d'instal·lació del complement. No s'ha pogut descarregar el manifest d'instal·lació del complement. És necessari reiniciar l'aplicació per poder aplicar els canvis pendents. Aquest complement té programada la seva instal·lació. Instal·lar Desinstal·lar Ja instal·lat No s'han trobat noves actualitzacions de complements. Actualitzar complements El registre de canvis no està disponible Té programada la seva instal·lació La descàrrega ha fallat S'ha rebutjat la llicència Descarregant {0}... Cercant actualitzacions de complements... Hi ha disponibles una o més actualitzacions de complements. Selecciona els elements que vols actualitzar Instància de desenvolupament d'extensió Acord de llicència de {0} Acceptar Rebutjar Incloure accions de Jugar de la integració de la biblioteca. Selecciona una acció Mode de seguiment Ruta de seguiment Retard de seguiment inicial Freqüència de seguiment Enllaç Fitxer Emulador Script Per defecte Procés Carpeta Procés original Registrar missatges de seguiment Els següents canvis sobreescriuen les dades per tots els jocs actualment seleccionats! Cap Uniforme Només elements. Només inici i final Sensibilitat de desplaçament Desplaçament suau Velocitat d'animació Eliminar element? Segur que vols eliminar aquest element? Mostrar botons al panell superior: Configuració de visualització general Configuració d'agrupament Configuració d'ordenació Predefinits de filtratge Posició dels elements de plugin Amplada del separador de secció Moure el botó del menú principal a la barra lateral Panell d'exploració Selector de joc aleatori Selector de joc aleatori de vistes Seleccionar joc aleatori de la vista Desar la configuració d'agrupament i d'ordenació Mostrar com a filtre ràpid en el mode de pantalla completa En els darrers 7 dies En els darrers 31 dies En els darrers 365 dies Fa més de 365 dies Configurar Desar predefinit Minimitzar després d'iniciar el joc Minimitzar Playnite després que s'iniciï un joc. Desactivar això pot portar problemes amb jocs que no rebin informació d'entrada a l'inici! Mida de la font Mida de la font petita Compatibilitat amb comandament de jocs Si es desactiva, Playnite no acceptarà cap entrada de comandament de joc. Desactiva-ho si utilitzes eines que tradueixen les entrades de comandament de joc a les entrades ratolí/teclat i reps entrades dobles a Playnite. Mostrar elements al menú principal: Assignació dels botons X/A invertida a la vista principal Intercanvia els botons per iniciar un joc i mostrar la pàgina de detalls del joc a la vista principal. Intercanviar l'assignació de botons de confirmació/cancel·lació Inverteix l'assignació dels botons A/B per la confirmació i la cancel·lació. Només el comandament primari Només acceptar entrades del comandament primari quan aquesta opció està activada. El botó Guia porta Playnite a primer pla Volum de la interfície Volum en segon pla Silenciar quan s'estigui en segon pla No s'ha pogut iniciar la interfície d'àudio. API de sortida API utilitzada per a la sortida d'àudio. Modifica-ho si tens problemes amb el so. General Visuals Àudio Disposició Menús Entrada {0} s'està iniciant... {0} s'està executant… Majúscules Espai Escalador de renderització d'imatge Alternatiu Equilibrat Qualitat Qualitat: La millor qualitat d'imatge, lent, alt consum de memòria. Equilibrat: Bona qualitat, ràpid, baix consum de memòria. Alternatiu: Millor qualitat, velocitat mitjana, baix consum de memòria. Seleccionar fitxer... Seleccionar carpeta... Script d'inici Tingues en compte que tant les extensions com els temes poden afectar en gran manera el rendiment, l'estabilitat i la seguretat de Playnite. Si comences a experimentar alguns problemes després d'instal·lar un tema o una extensió, prova primer de desactivar-los/desinstal·lar-los per veure si són l'arrel del problema. Seleccionar a l'iniciar Seleccionar a l'iniciar Perfils integrats Perfil integrat Perfils personalitzats Perfil personalitzat Gestionat per un script incorporat Especificació d'emulador Especificació de plataforma Especificació de regió Executar abans d'iniciar l'emulador Executar després que s'hagi iniciat l'emulador Executar després de sortir de l'emulador No s'ha trobat l'executable de l'emulador. No s'ha trobat l'especificació de l'emulador. No s'ha trobat l'script d'inici de l'emulador. Dividir com a jocs separats Fusionar en un joc Definir plataforma Definir regió Escanejar carpeta Escanejar configuracions Excloure patrons de l'escaneig de suma de verificació Els fitxers que coincideixin amb els patrons especificats no s'escanejaran per a la suma de verificació i es relacionaran amb el nom del fitxer. Visita la pàgina d'ajuda de l'emulador per a més informació. Escanejar amb emulador El nom s'ha de definir en desar la nova configuració. L'emulador o el perfil d'emulador no està definit. El directori a escanejar no està especificat o no existeix. Els paràmetres d'escaneig no estan configurats correctament. Incloure l'escaneig automàtic en l'escaneig massiu No s'ha pogut escanejar la carpeta per cercar emuladors. No s'han pogut escanejar les carpetes per a cercar a jocs emulats. Ocultar importats Perfils per importar: Configuracions d'escaneig automàtic Desar com a configuració d'escaneig automàtic Desa la configuració per a la seva utilització posterior durant l'actualització de la biblioteca. Les configuracions desades es poden gestionar mitjançant el menú "Configurar emuladors". Importar mitjançant rutes relatives Si és possible, importar fitxers del joc mitjançant rutes relatives a la carpeta d'instal·lació de Playnite o la carpeta d'instal·lació de l'emulador. Escanejar subcarpetes Escanejar dins dels arxius Fusionar fitxers relacionats Fusionar fitxers del joc relacionats, com per exemple discs del joc individuals, sota una sola entrada del joc. Afegir escàner Afegir escàner desat Iniciar escaneig Afegir les configuracions d'escaneig amb emuladors per escanejar carpetes específiques. Assegura't que els emuladors estiguin configurats correctament abans d'importar jocs (mitjançant el menú Biblioteca -> Configurar emuladors). Estat per defecte assignat a jocs afegits recentment Estat assignat a jocs jugats per primer cop No s'ha pogut iniciar el temps d'execució del script de PowerShell. Si ets usuari de Windows 7, prova de (re)instal·lar PowerShell 5.1 per resoldre el problema. El predefinit de filtratge amb el nom especificat ja existeix. Vols actualitzar el predefinit amb la nova configuració? Aquestes paraules s'eliminaran del principi del valor de nom d'ordenació omplert automàticament: Utilitza això per ignorar paraules al principi d'una cadena per propòsits d'ordenació. El valor per defecte és "The", "An" i "A". Omplir el nom d'ordenació de jocs sense un Ordenació Omplint valors de nom d'ordenació... S'ha detectat que el servei Nahimic s'està executant al teu sistema. Se sap que aquest servei causa problemes de renderització a Playnite (i altres aplicacions). Si trobes alguna corrupció de gràfics o altres problemes de renderització a Playnite, es recomana desactivar o desinstal·lar completament el servei Nahimic. Més informació a https://playnite.link/nahimicsucks Playnite s'està executant amb privilegis elevats (com a administrador). Això no es recomana, ja que dóna privilegis elevats a totes les extensions instal·lades i a tots els jocs/aplicacions iniciats des de Playnite! Més informació a https://playnite.link/adminfaq Mostrar avís si Playnite està funcionant amb privilegis elevats Obtenir la mida real en el disc dur quan es calculi la mida dels jocs Si s'activa, els escaneigs seran més lents i obtindran la mida real que els fitxers ocupen en la unitat. Si es desactiva, els escaneigs seran més ràpids i utilitzaran la mida dels mateixos fitxers. S'ha informat que els complements següents són potencialment problemàtics, ja sigui a causa de l'alt impacte en l'estabilitat/rendiment o per problemes de seguretat. Et recomanem que els desinstal·lis: {0} Excloure fitxers en línia de l'escaneig Els fitxers emmagatzemats al núvol no s'escanejaran i importaran si no estan disponibles de manera local. Només és compatible amb Google Drive, DropBox i OneDrive Escanejar, però utilitzant el mètode simplificat sense el contingut del fitxer S'importaran els fitxers, però utilitzant un mètode menys precís que no requereix que el contingut dels fitxers es descarregui i estigui present de manera local. Aplicar-ho a tots Ignorar l'estat de la instal·lació Si es defineix, Playnite ignorarà l'estat de la instal·lació (incloent-hi el directori d'instal·lació) establert pel plugin d'integració que importa aquest joc. Aquesta opció podria no funcionar completament amb plugins que utilitzen un mètode específic d'importació de jocs, a menys que també tinguin en compte aquesta opció d'ignorar. Només manualment Un cop al dia Un cop per setmana A cada inici Cercar actualitzacions del programa Cercar actualitzacions de complements Actualitzar biblioteques Escanejar carpetes d'emulació Incloure jocs ocults Editar els camps Seleccionar/Desseleccionar-ho tot Obrir Activar Asignar Comença a teclejar per a cercar jocs... [F1] per ajuda Començar amb # mostra una llista de les ordres disponibles. Començar amb / mostra una llista dels proveïdors/plugins de cerca disponibles. Començar teclejant la paraula a cercar i acabant amb ESPAI canvia immediatament a aquella cerca. TABULADOR: Canviar acció ENTER: Activar l'acció seleccionada MAJÚSCULES-ENTER: Obrir menú d'elements Incloure jocs desinstal·lats Incloure jocs ocults Jocs desinstal·lats inclosos Jocs desinstal·lats exclosos Jocs ocults inclosos Jocs ocults exclosos Jugar o instal·lar Anar als detalls Menú del joc Editar joc Obrir cerca Quadre de cerca Botó de cerca Acció principal del joc Acció secundària del joc CTRL-F obre la cerca global en lloc d'activar el quadre de cerca Desar configuració de filtratge de jocs entre sessions de cerca Proveïdors de cerca Paraula clau per defecte Paraula clau personalitzada Drecera global del sistema Cerca de Playnite Configuració de l'extensió Exclusions Fitxers exclosos relatius a la carpeta d'escaneig Carpetes excloses relatives a la carpeta d'escaneig Afegir fitxer a la llista d'exclusions Afegir carpeta a la llista d'exclusions Les exclusions només es poden afegir a configuracions desades de l'escàner. S'han afegit exclusions a l'escàner "{0}". Substituir plataforma Si es defineix, l'escàner assignarà aquesta plataforma a tots els jocs, sobreescrivint qualsevol altra plataforma que es detecti automàticament. Incloure ordres a la cerca per defecte Si es desactiva, les ordres no s'inclouran en la casella de cerca fins que s'utilitzi el prefix #. Utilitzar una coincidència aproximada en el filtre de noms Si s'activa, el filtre de noms coincidirà amb els noms dels jocs de la mateixa manera que la cerca global. Es pot aplicar una coincidència estricta en un cas individual afegint el prefix "!" al filtre. Camps que es mostraran per als resultats de jocs: Estat ocult S'ha cancel·lat la còpia de seguretat de les dades. Ha fallat la còpia de seguretat de les dades. Error de còpia de seguretat de dades Còpia de seguretat de les dades en progrés... S'estan restaurant les dades a partir de la còpia de seguretat... No s'han pogut restaurar les dades a partir de la còpia de seguretat. Configuració Biblioteca de jocs Multimèdia de la biblioteca de jocs Extensions instal·lades Dades d'extensions Temes instal·lats Selecciona les dades a restaurar des del fitxer de còpia de seguretat especificat. Playnite es reiniciarà automàticament per iniciar el procés de restauració de la còpia de seguretat. Selecciona els elements per incloure a la còpia de seguretat de les dades. La configuració de l'aplicació i les dades de la biblioteca de jocs s'inclouen per defecte. Playnite es reiniciarà automàticament per iniciar el procés de còpia de seguretat. Còpia de seguretat de dades automàtica Freqüència de la còpia de seguretat automàtica Carpeta de còpies de seguretat Rotació de còpies de seguretat Incloure dades addicionals: És necessari definir la carpeta de còpia de seguretat si està activada la còpia de seguretat automàtica. Mostra notificacions només per a llançaments de petites actualitzacions Si s'activa, només les actualitzacions disponibles per a la versió principal instal·lada actualment mostraran una notificació d'actualització. Les noves versions principals no mostraran una notificació d'actualització. Utilitzar dates relatives per la setmana passada Utilitzar dates relatives en format "Avui", "Ahir", etc. si la data és inferior a una setmana. El format de data especificat s'utilitzarà per a les altres dates. Cerca d'imatges web Cadena de cerca de la imatge d'icona Cadena de cerca de la imatge de caràtula Cadena de cerca de la imatge de fons Obtenint informació del complement... No hi ha una font de metadades disponible Configuració de l'acció de Jugar Utilitzar configuració de l'escàner Seleccionar perfil en iniciar Seleccionar emulador en iniciar Automàtic Sempre activat Sempre desactivat Compatibilitat d'accessibilitat (lector de pantalla) Menú d'aplicació Menú del joc Carpeta del programa Directori de dades de l'usuari S'ha detectat un fitxer de la biblioteca danyat. Playnite ara es tancarà. Obre una nova incidència a la pàgina de GitHub de Playnite amb una sol·licitud per corregir els teus fitxers danyats. Vols desar els canvis que has fet? Instal·lació portàtil No s'han detectat comandaments ================================================ FILE: source/Playnite/Localization/cs_CZ.xaml ================================================  Čeština Jazyk Playnite Ukončit Filtr Aktivován Filtr Deaktivován Dodatečné filtry Filtry Filtr Neplatná Data Uložit změny? Domovská stránka na www.playnite.link Zdrojový kód na GitHubu Vytvořit diag. balíček Odeslat diagnostické informace O Playnite Vytvořil Josef Němec Přidělit Kategorii Nastavit Kategorie Přidat Kategorii Zaškrtnuto - Přiřadit kategorii Nezaškrtnuto - Odstranit kategorii Neurčen - Žádné změny (při úpravách více her) Bez Kategorie Bez Platformy Jejda! Něco se pokazilo... Nastala závažná chyba. Pokud nám chcete pomoci chybu opravit, prosím krátce popište Váš postup před tím, než program spadl, a poté odešlete diagnostické informace. Pokud jste připojeni k internetu, tento balíček bude nahrán na server Playnite k analýze. Alternativně můžete použít tlačítko 'Nahlásit pád' pro vytvoření hlášení na GitHubu a nahlásit chybu manuálně. Děkujeme za pomoc. Rozšíření "{0}" způsobilo závažnou chybu. Doporučujeme uložit diagnostické soubory a nahlášení problému jejímu vývojáři. Pokud tento problém přetrvává, rozšíření zakažte. Rozšíření "{0}" způsobilo závažnou chybu. Doporučujeme nahlásit problém jejímu vývojáři. Pokud tento problém přetrvává, rozšíření zakažte. Neznámé rozšíření nebo motiv způsobily neopravitelnou chybu. Doporučujeme zakázat všechna rozšíření třetích stran, izolovat problematické rozšíření a nahlásit problém jeho vývojáři. Nastala závažná chyba. Pokud nám chcete pomoci tento problém opravit, odešlete prosím diagnostické informace. Děkujeme. Zakázat rozšíření Uložit log soubory Odeslat diag. info Nahlásit Pád Restartovat Playnite Restartovat v Bezpečném Režimu Zakazuji všechna rozšíření třetích stran a používám výchozí motiv. Opustit Playnite Akce provedené před pádem programu (v angličtině): Správce Knihovny Odstranit Hru(y)? Nelze odstranit - Hra nebo instalátor běží. Nelze odinstalovat - Hra je spuštěná. Opravdu chcete odstranit {0}? Opravdu chcete odstranit {0} her? Opravdu chcete odstranit {0}? Zvolení "přidat do seznamu výjimek" bude hru ignorovat při budoucích aktualizací knihovny. Opravdu chcete tuto odstranit {0} her? Zvolením možnosti "přidat do seznamu výjimek" hry vyjmete z příštích aktualizací knihovny. Jste si jisti, že chcete odstranit {0} záznamů, které nejsou současně používané? Žádná nepoužitá pole nebyla nalezena. Ano (přidat do seznamu výjimek) V této sekci jsou neuložené změny Aktualizace formátu herní knihovny… Aktualizace databáze selhala. Nelze aktualizovat knihovnu her. Je třeba {0} MB volného prostoru. Chyba hry Nelze spustit hru. '{0}' se nenachází v databázi. Nelze spustit hru: {0} Nelze spustit akci: {0} Nelze otevřít umístění hry: {0} Nelze zjistit velikost instalace hry: {0} Chyba při skenování velikosti instalace Při prohledávání velikosti instalace došlo k {0} chybám Nepodařilo se vytvořit zástupce: {0} Selhalo otevření Příručky: {0} Nelze nainstalovat hru: {0} Nelze odinstalovat hru: {0} Nebyly nalezeny žádné spouštěcí akce hry. Používáte-li emulátorové akce, ujistěte se, že definice platformy jsou stejné v konfiguraci hry i emulátoru. Instalační funkce není dostupná. Plugin knihovny, která je zodpovědná za tuto hru, není nainstalován či povolen. Stažení oficiálních metadat není k dispozici. Není vybrána žádná hra. Spuštění skriptu hry selhalo. Spuštění skriptu aplikace selhalo. Spuštění globálního skriptu selhalo. Spuštění emulačního skriptu selhalo. Spuštění skriptu akce Hrát selhalo. PowerShell 3.0 nebo novější není nainstalován. Nepodařilo se určit postup kterým hru spustit. Povoleno Zakázáno Odstranit Odstranit nepoužité Přejmenovat Kopírovat Přidat Výchozí Ikona Výchozí Obrázek Obalu Výchozí Obrázek na Pozadí Dokončit Další Zpět HOTOVO ZPĚT VYČISTIT Vyčistit Zrušit Zrušit vše Importovat Jméno Autor Modul Série Verze Naposledy Hráno Nejvíce Hráno Počet Zahrání Velikost instalace Složka Poznámky Přidáno Datum Přidání Změněno Datum Změny Webová stránka Cesta OK Uložit Zavřít Zrušit Potvrdit Obnovit Ano Ne Vítejte Místní Uživatel Obecné Média Odkazy Instalace Akce Stahování… Stahování médií... Načítání… Typ Profil Profily Odstranit Stáhnout Hledat Rozlišení: Všechny Přiblížení Zobrazení Seznamu Obaly Zobrazení Mřížky Zobrazení Podrobností Vlastní URL Zvláštní poděkování Licence Přispěvatelé Zavírání Playnite… Dnes Včera Pondělí Úterý Středa Čtvrtek Pátek Sobota Neděle Minulý Týden Minulý Měsíc Za poslední Rok Před více než rokem 0 až 100MB 100MB až 1GB 1GB až 5GB 5GB až 10GB 10GB až 20GB 20GB až 40GB 40GB až 100GB 100GB nebo více Import úspěšně dokončen. Všechny Hry ID Hry ID Databáze Předvolby Sloupec Sloupce Řádek Řádky Nelze získat ikonu z akce Hrát. Není nastavena žádná akce typu "Soubor". Stáhnout pouze chybějící metadata Povolení této možnosti způsobí, že se přeskočí stahování metadat pro datová pole, která už informace obsahují. Výběr her Prosím vyberte, které hry by měly být aktualizovány o nová metadata: Všechny hry v databázi Všechny aktuálně filtrované hry Jen vybrané hry Nejsou vybrána žádná pole metadat Nejsou vybrána žádná pole metadat ke stažení. Vyberte prosím alespoň jedno a povolte pro ně alespoň jednoho poskytovatele metadat. Oficiální Obchod IGDB Prosím vyberte, která pole by měl Playnite automaticky doplnit a které zdroje by měly být použity k získání těchto údajů. Prosím zvažte přispění aktualizacemi do databáze igdb.com (kliknutím na logo výše), čímž zlepšíte data, která Playnite používá. Stahují se metadata… Importují se instalované hry… Importují se hry z platformy {0}… Importují se emulované hry z {0}… Stahují se aktualizace knihoven… Vypočítaní velikosti her v knihovně… Vypočítaní velikosti importovaných her… Aktualizace knihovny dokončena Uvolňují se zdroje… Konfigurace Nastavení… Platformy a Emulátory Nastavit Emulátory… Správce Knihovny… Nástroje Stáhnout Metadata… Softwarové Nástroje... Nastavit Integraci... Otevřít Klienta Třetí Strany Klienty Třetích Stran Aktualizovat Herní Knihovnu Zrušit Aktualizaci Knihovny Aktualizovat Emulované Adresáře Přidat Hru Ručně… Automaticky Skenovat… Emulovaná Hra… Microsoft Store aplikace… O Playnite Odeslat Zpětnou Vazbu Přepnout do Celé Obrazovky Odkazy Nápověda Podpořit na Patreonu Podpořit na Ko-fi Uživatelská příručka Dokumentace SDK Restartovat Systém Vypnout Systém Uspat Systém Hibernovat Systém Uzamknout Systém Odhlásit Uživatele Vybrat Náhodnou Hru Pole k zobrazení na panelu detailů: Mezery mezi položkami Vykreslovat pozadí položek v mřížce Šířka ohraničení položky v mřížce Pro chybějící zdroj ikony hry Pro chybějící zdroj obálky hry Pro chybějící zdroj pozadí hry Vertikální odsazení podrobností o hře Pozice detailů v zobrazení mřížky Pozice seznamu her v zobrazení detailů Vykreslovat oddělovač mezi panely Výška obrázku obálky hry Výška ikony v seznamu her Písmo aplikace Neproporcionální písmo Pozice panelu s filtry Pozice panelu průzkumníka Vykreslování obrázků obálek Cílový poměr stran Následující možnosti také ovlivňují vykreslování dlaždic v režimu Celé Obrazovky! Způsob roztáhnutí DVD Box Epic Games Store GOG Galaxy 2.0 IGDB Čtverec Steam Banner Vertikální Steam obal Twitch * Toto nastavení se projeví až po restartu Nastavení Obecné Horní panel Vzhled Detaily hry Rozložení Pokročilé Celá Obrazovka Vstup Výkon Metadata Aktualizování Hledat Zálohovat Zálohovat data knihovny Obnovit zálohu Importovat změny v knihovně automaticky Chybné umístění souboru databáze, nastavte správné umístění. Název účtu nemůže být prázdný. Stáhnout metadata po importu her Spustit Playnite minimalizovaný Spustit Playnite při přihlášení Spustit zavřeno do lišty Nepodařilo se zaregistrovat Playnite pro start při spuštění počítače. Spouštět v režimu Celé Obrazovky Asynchronní načítání obrázků Může zlepšit plynulost skrolování v seznamech her, ale obrázky se načítají pomaleji. Zobrazit jméno hry, pokud chybí obrázek obálky Zobrazit jména her v zobrazení mřížky Ztmavit nenainstalované hry Zobrazit ikony her v režimu zobrazení podrobností Zobrazit počty položek v popiscích skupin Ukazovat pouze přiřazená pole ve filtrovacím a průzkumném panelu Zakázat hardwarovou akceleraci Použijte pokud se setkáváte se sekáním či podobnými problémy Ukazovat skryté hry v seznamech rychlého spuštění Ovlivní systémovou lištu a tlačítko aplikace v hlavním panelu. Počet položek v rychlém spuštění Použít obrázek pozadí hry jako pozadí celého okna Rozostřit pozadí Vysoká Kvalita Ztmavit pozadí Zobrazit v Zobrazení mřížky Motiv Vzhledu Profil Motivu Vzhledu Motiv režimu Celé Obrazovky Profil Motivu režimu Celé Obrazovky Umístění Databáze Stav přihlášení: Natavení Playnite Vymazat webovou mezipaměť Může vyřešit problémy při propojování účtů. Zobrazit ikonu v systémové liště Minimalizovat Playnite do systémové lišty Minimalizovat Playnite do systémové lišty když je okno aplikace uzavřeno Při spuštění hry: Po ukončení hry: Zobrazit odehraný čas jako počet dnů Formát datumu: Tato akce Vás odhlásí ze všech propojených služeb. Je vyžadován restart aplikace. Chcete pokračovat? Vyčistit Mezipaměť? Pro změnu vzhledu je třeba restartovat Playnite Získat více motivů Vytvořit nový motiv Získat více rozšíření Vytvořit nové rozšíření Pomozte nám přeložit Playnite Playnite musí být restartován pro aplikování nových nastavení. Restartovat nyní? Restartování zruší všechny aktivní úlohy (stahování), které právě běží. Restartovat Playnite? Playnite nemůže přesouvat soubory knihovny automaticky. Musíte je přesunout (či zkopírovat) sami, než umístění knihovny změníte. Pokud v cílovém umístění není žádná knihovna, bude tam vytvořena nová. Nové umístění databáze nebude použito do restartu aplikace. Čas hraní nebude zaznamenáván pokud je nastavena akce "Zavření". Počet řádků Počet sloupců Počet řádků v zobrazení detailů Zobrazit Obrázek Pozadí na Hlavní Obrazovce Neaplikuje se zpětně na hry co už existují - pro ty je třeba znovu stáhnout metadata. Importovat dobu hraní her v knihovně: Konfiguruje kdy Playnite importuje dobu hraní pomocí pluginů pro hry v databázi Playnite. Pro použití této funkce je třeba podporovat pluginy, které mají na starosti manipulaci se hrou. Vždy: Importuje čas pro nově importované i existující hry v databázi Playnite. Pouze pro nově importované: Importuje čas pouze pro nově importované hry. Nikdy: Za žádných okolností neimportovat čas hraní. Vždy Pouze pro nově importované Nikdy Povolit podporu ovladače v režimu Plochy Tlačítko Guide otevírá režim Celé obrazovky Automatické stažení metadat pro nově importované hry. Cílový displej Vždy používat primární displej Zobrazit Názvy Her Zobrazit Stav Baterie Zobrazit Stav Baterie v Procentech Zobrazit Hodiny Skrýt Kurzor Myši Pouze nainstalované hry v Rychlých Filtrech Popisky Tlačítek Rozložení Horizontální Skrolování Vyberte jednu z podkategorií Nejsou dostupná žádná nastavení Nepodařilo se načíst nastavení Tyto skripty jsou spuštěny pro každou hru v knihovně. Jednotlivé skripty můžou být přiřazeny ke každé hře zvlášť, v úpravách detailů hry. Animovat přechody mezi obrázky na pozadí Velikosti písma Automaticky Vyhlazené Černobíle ClearType Ideální Obrazovka Režim formátování textu Režim vykreslování textu Nastavení vykreslování a formátování textu v současnosti neovlivní text popisků her. Přednačítat obrázky na pozadí Pokud je zapnuto, Playnite stáhne obrázky na pozadí při stahování metadat. Bude použito více místa na disku a obrázky budou dostupné offline. Pokud není povoleno, obrázky na pozadí jsou staženy jen když jsou potřeba. Je použito méně místa na disku, ale před zobrazením obrázku na pozadí může dojít k prodlevě a některé obrázky nemusejí být dostupné offline. Automaticky zavřít klienta třetí strany po ukončení hry Zpozdění vypnutí klienta (v sekundách) Nezavírat klienta pokud je herní doba kratší než (v sekundách) Automaticky zavřít tyto klienty: Automatické Ukončení Klientů Seznam Výjimek z Importu Zobrazit varování při přidávání extrémně velkých herních medií Příkaz pro otevření adresáře Preferovaná organizace věkového omezení Aktualizovat velikost nainstalovaných her při aktualizaci knihovny Vypočítá a aktualizuje velikost nainstalovaných her, pokud je detekováno, že jejich soubory byly změněny od posledního skenování Žádný Vyplnit Jednotně Jednotně vyplnit Vlevo Vpravo Nahoře Dole Chyba importu Vyžaduje se přihlášení Přihlášení se nepodařilo Alternativní vykreslování webového obsahu Použijte pokud se setkáváte s problémy s webovým obsahem, například přihlašovací okna pro integrace. Částečné načítání velkých popisů hry Velké popisy hry můžou způsobit znatelné zpoždění při výběru hry. Pokud je povoleno, bude zpočátku načtena pouze část textu popisu s možností načíst zbytek na vyžádání. Import Metadat Stáhnout Metadata Použít vybraná nastavení při příštím stahování metadat Lze také změnit v nastavení aplikace. Průvodce Importem Emulátoru Tento průvodce Vás provede procesem stahovaní a importovaní konzolových emulátorů, a importováním emulovaných her. Mějte na paměti, že později můžete vždy přidat další emulátory a/nebo hry prostřednictvím hlavního menu (v menu "Nástroje" pro nastavení emulátorů, a "Přidat hry" pro emulované hry). Níže je seznam emulátorů, které Playnite dokáže rozpoznat a automaticky nastavit. Instalační programy emulátorů můžete stáhnout z jejich stránek. Až budete mít emulátory manuálně nainstalované, můžete je importovat v dialogu nastavení emulátorů. Můžete importovat jakékoli emulátory které jsou nainstalované na Vašem počítači kliknutím na tlačítko 'Autodetekce Ze Složky...'. Playnite prohledá zvolenou složku a vyhledá jakékoli známé emulátory a poskytne možnost je importovat. Toto tlačítko může použít několikrát pro importování emulátorů z různých složek. Emulátory budou přidány do dolní části aktuálního seznamu. Hry můžete importovat pomocí stisknutí tlačítka 'Skenovat složku pomocí Emulátoru'. Vybráním odpovídajícího emulátoru uvědomí Playnite o tom, které typy souborů by měly být skenovány a importovány. Toto tlačítko může použít několikrát pro import her z různých složek. Hry budou přidány do dolní části aktuálního seznamu. Nejsou zvoleny žádné emulátory pro import. Bez nastavení emulátorů nelze automaticky importovat emulované hry. Jste si jisti, že chcete pokračovat a ukončit proces importu? V Playnite nejsou nastaveny žádné emulátory. Nemůžete importovat hry, dokud nejprve nenastavíte emulátor a nevybere příslušné typy souborů. Chcete nyní přidat emulátory? Skenovat složku pomocí Emulátoru Vybrat soubory Autodetekce Ze Složky... Nastavit Emulátory… Skenování… Skenuji {0}... Úvodní Nastavení Tento průvodce Vás provede automatickým importem a procesem nastavení externích herních knihoven. Playnite může automaticky importovat hry z více herních služeb, jako je Steam nebo GOG. Mějte na paměti, že můžete později manuálně přidat jakoukoli vlastní hru pro jakoukoli platformu kliknutím na tlačítko 'Playnite' v hlavním menu. Integrace Knihovny Níže je seznam některých vybraných integrací knihoven které Playnite podporuje. Prosím vyberte ty které chcete nainstalovat. Více integrací můžete nainstalovat později z nabídky "Rozšíření". Konfigurace Dokončena Počáteční nastavení bylo dokončeno. Pamatujte, že všechna nastavení a dodatečné integrace můžete vždy upravit z hlavní nabídky. Nepodařilo se stáhnout jedno či více rozšíření. Můžete se pokusit znovu stáhnout integrace z nabídky rozšíření až skončí nastavení při prvním spuštění. Stahuji integraci {0}... Stahování seznamu doporučených integrací… Nepodařilo se stáhnout seznam doporučených integrací. Můžete zkusit stáhnout integrace znovu pomocí menu doplňků. Nastavit Platformy a Emulátory Nastavit Emulátory Platformy Platforma Emulátory Emulátor Přidat Platformu Vybrat Ikonu Vybrat Obálku Vybrat Obrázek Vybrat Položku Vybrat Pozadí Vybrat Soubor Vybrat adresu URL Přidat Emulátor Podporovaná Platforma(y) Chcete uložit změny v platformách? Chcete uložit změny v emulátorech? Spustitelný soubor Argumenty Pracovní Adresář Podporované Formáty Souborů Importovat Emulátory… Stáhnout Emulátory… Načíst předvolby argumentů ze známých profilů emulátorů Opravdu chcete odstranit emulátor {0}? Současně ho využívá {1} her. Opravdu chcete odstranit platformu {0}? Současně ji využívá {1} her a {2} emulátorů. Nápověda k nastavení Seřadit Podle Směr řazení Seskupit Podle Vzestupně Sestupně Neseskupovat Seskupit podle Knihovny Seskupit podle Kategorie Seskupit podle Platformy Typ Zobrazení Zobrazení Panel Průzkumníka Panel Filtrů Ikona Ikona knihovny Obrázek na Obalu Obrázek na Pozadí Jméno, dle kterého řadit Knihovna Příručka Jméno Disk instalace Název Účtu Platforma Kategorie Žánr Datum Vydání Rok Vydání Vývojář Štítek Vydavatel Stav Instalace Shrnout všechny filtry Je-li zapnuto, v pohledu budou zahrnuty jen ty položky, které splňují všechny zadané filtry. Je-li vypnuto, budou zobrazeny položky, které splňují alespoň jeden filtr. Instalováno Instalováno Nenainstalováno Skrytý Oblíbený Zapnout podporu HDR Je-li povoleno, bude HDR povoleno na primárním displeji před spuštěním hry. Upozorňujeme, že HDR není podporováno na vašem primárním displeji. Naposledy Hráno Kategorie Popis Instalační Adresář Obrázek na Obalu Odkazy Cesta k Obrazu, ROM nebo ISO Žánr Žánry Společnost Společnosti Vývojář Vývojáři Vydavatel Vydavatelé Kategorie Kategorie Štítek Štítky Vlastnost Vlastnosti Věkové Omezení Věková Omezení Region Regiony Zdroj Zdroje Nedávná aktivita Chyba databáze Nepodařilo se otevřít databázi knihovny. Databáze není otevřena. Nelze přistoupit k databázi knihovny. Soubor "{0}" je používán jiným procesem, nebo je v nedostupném umístění. Nepodařilo se vytvořit diagnostický balíček. Nepodařilo se automaticky nahrát diagnostický balíček. Diagnostické informace byly úspěšně odeslány. Diagnostický balíček byl vytvořen a úspěšně odeslán. K hlášení problému, prosím, připojte následující ID: Nepodařilo se importovat hry z {0}. Nepodařilo se importovat emulované hry z {0}. Nelze vyhledat hry podle vybraného profilu emulátoru, protože neobsahuje žádné přípony souborů nebo platformy. Playnite se nepodařilo spustit. Prosím zavřete všechny ostatní instance Playnite a zkuste to znovu. Nelze aplikovat motiv "{0}" s barevným profilem "{1}" {2} Nelze otevřít adresu, URL není platného formátu. Nepodařilo se spustit aplikaci. Chyba při spuštění komponentu web vize. Playnite nemůže pokračovat ve spouštění. Pro více informací navštivte: https://playnite.link/cefstartup Nelze importovat emulátory z důvodu chybějícího nebo poškozeného definičního souboru. Nepodařilo se spustit menu akci. Upravit Detaily Hry URL obrázku Přidat Odkaz Přidat ROM Uložit Změny Aplikovat změny polí pro hru (hry) které jsou upravovány. Přidat Akci Odstranit Akci Odstranit Akci Hrát Přidat Hry Skenovat Adresář… Detekovat Nainstalované Procházet… Otevřít Playnite Natavení Profilu Jméno hry nemůže být prázdné. Složka sledování pro akci hry nemůže být prázdná. Před vyhledáním metadat je třeba doplnit název hry. Neplatné údaje u hry Vložte platný formát adresy URL začínající http:// nebo https:// Vybrat adresu URL Selhalo stahování metadat: {0} Chyba při Stahování Zrušit Filtry Soukromý účet Veřejný Účet API Klíč Chyba při Spouštění Chyba Motivu Vzhledu Vymazat Vše Probíhá instalace Probíhá odinstalace Spouštění Spuštěno Neplatná URL Nedělat nic Minimalizovat Obnovit okno Obnovit okno pouze při spuštění z uživatelského rozhraní Zavřít Změnit Pokročilé Nikdy Stav Dokončení Stavy dokončení Hodnocení Uživatelů Hodnocení Recenzentů Hodnocení Komunity Skripty hry Skripty aplikací Skripty Pluginy Zdroje Metadat Rozšíření ID Rozšíření Znovu načíst Skripty Interaktivní PowerShell SDK Všechny skripty se úspěšně znovu načetly. V rámci zadaných kritérií nebyla nalezena žádná odpovídající hra Žádné položky nenalezeny Přepnout do režimu Plochy Opustit Playnite Knihovny Aktualizovat Vše Vytvořil: Verze: Aktualizováno: Modul: Knihovna Statistiky Vše Nic Oznámení Šířka Výška Velikost Malý Normální Velký Větší Největší Výchozí Vybrat Vybrat Vše Zrušit výběr všech První Náhodně Zadat uživatelem Načíst více Průhledné Sbalit Rozbalit Sbalit Vše Rozbalit Vše Ostatní Motivy Argumenty Emulátoru Vestavěné Argumenty Vlastní Argumenty Dodatečné Argumenty Emulátoru Nahradit Argumenty pro Emulátor Akce Hrát Vyberte metadata k importovaní Vyberte Hry pro Import Hledání metadat Dostupná Aktualizace Změny od poslední aktualizace Stáhnout a Nainstalovat Aktualizaci Zkontrolovat Aktualizace Chyba Aktualizace Nepodařilo se zkontrolovat aktualizace. Nebyla nalezena novější verze, vaše verze je aktuální. Nepodařilo se stáhnout a nainstalovat aktualizaci. Nějaká úloha na pozadí stále běží. Chcete ji zrušit a pokračovat s aktualizací? Nějaká úloha na pozadí stále běží. Chcete jí zrušit a ukončit Playnite? Nějaká úloha na pozadí stále běží. Změnou režimu bude zrušena, chcete i přesto změnit režim? Aktualizace pro Playnite je dostupná Obnovit seznam motivů vzhledu Použít vybraný motiv vzhledu Sledovat změny souboru Automaticky aplikovat motiv vzhledu, když se zdrojový soubor změní Běhové prostředí skriptů Provést před spuštěním hry Provést po ukončení hry Provést po spuštění hry Spustit při startu aplikace Spustit při zastavení aplikace Hra spouští skript Hra spustila skript Hra zastavila skript Spustit globální skript Globální Filtrované Současné Nové Testovat skript Zobrazit pouze zvolené předměty. Uložit jako výchozí Přidat do Oblíbených Odstranit z Oblíbených Skrýt tuto hru Odstranit ze Skrytých Zapnout podporu HDR Vypnout podporu HDR Upravit… Vypočítat velikost instalace Vypočítat velikost instalace (všechny hry) Vypočítat velikost instalace (pouze chybějící data) Velikost instalace Nastavit Kategorii… Nastavit Stav Dokončení Odstranit Hrát Instalovat Možnosti Hry Podrobnosti Odinstalovat Otevřít Umístění Instalace Vytvořit Zástupce na Ploše Otevřít Příručku Více Spravováno pomocí pluginu knihovny Proces spuštení hry bude ovládán knihovnou pluginu odpovědným za tuto hru. Žádné informace o hře '{0}' nebyly na zadané stránce nalezeny. Tip: Můžete použít pokročilejší stažení metadat, pokud hru upravujete pomocí položky "Upravit" v menu. Není dostupné když probíhají jiné akce. V popisku lze uplatnit značky HTML Herní čas je počítán v sekundách. Velikost instalace je uvedena v bajtech. Datum vydání musí být ve formátu 'rok-měsíc-den'. Hodnoty pro měsíc a den můžou být vynechány. Hodnoty od 0 do 100, případně prázdné pokud skóre není. Vývoj Playnite podporují tito patroni a členové Ko-fi: Kód, lokalizace a další přispěvatelé, nijak neseřazeno: Zrušit monitorování hry? Monitorování instalace právě běží. Chtete tento proces zrušit a vrátit hru do předchozího stavu? Monitorovaní spouštění hry právě beží. Chtete tento proces zrušit a vrátit hru do předchozího stavu? Odehraný Čas Naposledy Hráno {0}d {1}h {2}m {0}h {1}m {0} minut {0} sekund Zatím Nehrané Otevírání režimu Plochy… Otevírání režimu Celé Obrazovky… Načítání herní knihovny… Vypočítání velikosti instalace… Vypočítání velikosti instalace {0}… Instalace souboru skriptu se nepodařila. Skript byl úspěšně nainstalován. Nainstalovat Skript Chyba Skriptu Nepodařilo se spustit funkci rozšíření. Otevřít složku metadat Vypočítat Automaticky vypočítává velikost instalace podle přiřazených ROMů, pokud hra nějaké má, nebo podle instalačního adresáře Klient {0} není nainstalován. Klient {0} bude nyní otevřen, prosím přihlaste se a poté tuto zprávu zavřete. Čekám na přihlášení uživatele, zavřete prosím toto okno, až budete hotovi... Instalační adresář hry nebyl nalezen. Neplatné nastavení akce hry. Řešení problémů se synchronizací účtu Řešení potíží Přejmenovat položku Přidat novou položku Zadejte název Zadejte nový název Méně než hodina 1 až 10 hodin 10 až 100 hodin 100 až 500 hodin 500 až 1000 hodin 1000+ Playnite musí být restartován pro dokončení instalace. Chcete nyní restartovat? Rozšíření není správně zabaleno. Motiv není správně zabalen. Rozšíření "{0}" se nepodařilo správně načíst. Nelze načíst rozšíření "{0}", současná verze Playnite není podporována. Motiv "{0}" se nepodařilo správně načíst. Nelze načíst motiv "{0}", současná verze Playnite není podporována. Rozšíření se nepodařilo správně načíst. Motiv se nepodařil správně načíst. Motiv Vzhledu/Rozšíření používá nepodporovanou verzi API. Instalace byla úspěšná. Nainstalovat rozšíření? Obecné Selhala instalace rozšíření "{0}". Nepodařilo se nainstalovat rozšíření. {0} Chcete nainstalovat nové rozšíření? {0} Od {1} Verze {2} Chcete aktualizovat rozšíření "{0}"? Současná verze: {1} Nová verze: {2} Nepodařilo se nainstalovat motiv. {0} Chcete nainstalovat nový motiv? {0} Od {1} Verze {2} Chcete aktualizovat motiv "{0}"? Současná verze: {1} Nová verze: {2} Chystáte se opustit Playnite a otevřít následující webovou stránku pomocí výchozího webového prohlížeče. Chcete pokračovat? {0} Zvolený obrázek (obrázky) můžou být pro optimální výkon příliš velké. Používaní velkých obrázků muže mýt za následek horší odezvu uživatelského rozhraní a zvýšení využití paměti. Maximální doporučovaná rozlišení: Ikony: {0} mega pixelů Obaly: {1} mega pixelů Obrázky na pozadí: {2} mega pixelů Varování o Výkonu Příště Nezobrazovat Soubor s koncovkou {0} není podporovaný. Nepodporovaná koncovka souboru Zvolený obrázek je nejspíš příliš velký pro dosažení optimálního výkonu. Opravdu chcete odinstalovat vybraný motiv? Odinstalace bude zařazena do fronty pro další spuštění aplikace. Vestavěné motivy nelze odinstalovat. Tento motiv není podporován touto verzí Playnite. Opravdu chcete odinstalovat vybrané rozšíření? Odinstalace bude zařazena do fronty pro další spuštění aplikace. Vestavěná rozšíření nelze odinstalovat. Toto rozšíření není podporováno touto verzí Playnite. Instalační adresář Datový adresář Generování diagnostického balíku... Nahrávaní diagnostického balíku... Importovat soubory... Co je tohle? Opravdu toto chcete udělat? Celkový čas hraní Průměrný herní čas Nejdelší herní čas Celková velikost instalace Přehled Postranní panel Zobrazit na postranním panelu Resetovat nastavení Všechna nastavení aplikace budou resetována do výchozích hodnot, kromě: - Umístění databáze - Seznamu výjimek z importu - Nastavení rozšíření, včetně integrací knihoven Pro dokončení procesu bude vyžadován restart. Chcete nastavení resetovat? Pro vývojáře Externí rozšíření Zadejte úplnou cestu k složce. Úspěchy Fórum Novinky Stránka v obchodě Počáteční nastavení nebylo dokončeno. Playnite se nyní restartuje do Režimu Plochy, aby proces dokončil. Nedávno Hrané Oblíbené Nejvíce Hrané Vše Jsou použity filtry. Jsou použity dodatečné filtry. Výsledky hledání pro: Položka s tímto jménem již existuje. Limitovat výběr na aktuální filtr Vybrat další Rozšíření... Instalováno Nastavení rozšíření Procházet Aktualizace Aktualizace ({0}) Správa instalovaných rozšíření a motivů, včetně jejich nastavení, byla přesunuta do nové nabídky "Rozšíření". Všechna instalovaná rozšíření pro integraci knihoven můžou být nastavena zde. Pokud chcete instalovat nebo odinstalovat dodatečně integrace, použijte možnost "Rozšíření" z hlavní nabídky. Motivy režimu plochy Motivy Režimu Celé Obrazovky Hledání... Rozšíření není kompatibilní s touto verzí Playnite. Nepodařilo se stáhnout instalační balík rozšíření. Nepodařilo se stáhnout instalační manifest rozšíření. Je vyžadován restart aplikace pro použití čekajících změn. Toto rozšíření je plánováno k instalaci. Instalovat Přeinstalovat Odinstalovat Již nainstalováno Nenalezeny žádné nové aktualizace rozšíření. Aktualizovat rozšíření Seznam změn není k dispozici Naplánováno k instalaci Stažení selhalo Licence zamítnuta Stahování {0}... Vyhledávají se aktualizace rozšíření… Vyhledávají se aktualizace programu… Jsou dostupné aktualizace pro některá rozšíření. Vyberte položky k aktualizaci Instance pro vývoj rozšíření Souhlas s licencí {0} Přijmout Zamítnout Zahrnout akce Hrát z integrace knihovny Zvolte akci Režim sledování Cesta ke sledování Počáteční zpoždění sledování Frekvence sledování Odkaz Soubor Emulátor Skript Výchozí Proces Adresář Původní proces Název procesu Ukládat logy o sledování Následující změny přepisují data pro všechny aktuálně vybrané hry! Žádné Jednotné Pouze položky Pouze začátek a konec Citlivost skrolování Jemné skrolování Rychlost animace Odstranit položku? Určitě chcete odstranit tuto položku? Zobrazit tlačítka na horním panelu: Obecná nastavení pohledu Nastavení seskupování Nastavení řazení Předvolby filtrů Pozice položek pluginů Šířka oddělovače sekcí Přesunout tlačítko hlavního menu do postranního panelu Panel průzkumníka Vybírač náhodných her Náhodný výběr hry z rozhraní Vyberte náhodnou hru z pohledu Uložit nastavení seskupování a řazení Zobrazit jako rychlý filtr v režimu Celé Obrazovky Za posledních 7 dní Za posledních 31 dní Za posledních 365 dní Před více jak 365 lety Konfigurovat Uložit předvolbu Minimalizovat po spuštění hry Minimalizovat Playnite po spuštění hry. Vypnutí této možnosti může vést k problémům se vstupem po spuštění hry! Velikost písma Malá velikost písma Povolit podporu API pro herního ovladače Podpora pro herní ovladače Je-li zakázáno, Playnite nebude přijímat žádné vstupy herních ovladačů. Zakažte, pokud používáte ovladače které překládají vstupy herního ovladače na vstupy myši/klávesnice a získáváte duplikované vstupy v Playnite. Zobrazit položky na hlavní nabídce: Prohodit využití tlačítek X/A na hlavní stránce Prohodí využití tlačítek pro spuštění hry a zobrazení informací o hře na hlavní stránce. Prohodit místa potvrzení a zrušení Prohodit A a B pro potvrzení a zrušení Pouze hlavní ovladač Přijmout pouze vstupy z hlavního ovladače. Guide tlačítko přepne na Playnite Hlasitost rozhraní Hlasitost na pozadí Umlčet v pozadí Nepodařilo se inicializovat audio rozhraní. Výstupní API API používané pro audio výstup. Změňte pokud se setkáváte s problémy se zvukem. Obecné Vzhled Audio Rozložení Nabídky Vstup {0} se spouští... {0} běží... Velk.pís. Mezerník Škálovač vykreslování obrázků Alternativní Vyvážený Kvalita Kvalita: Nejlepší kvalita obrazu, pomalé, vysoké využití paměti. Vyvážený: Dobrá kvalita, rychlé, nízké využití paměti. Alternativní: Lepší kvalita, pomalejší, nízké využití paměti. Vybrat soubor... Vybrat adresář... Skript při spuštění Mějte na paměti, že rozšíření a motivy můžou vysoce ovlivnit výkon, stabilitu a bezpečnost Playnite. Pokud se setkáte s potížemi po instalaci motivu nebo rozšíření, zkuste je nejdříve vypnout/odinstalovat pro zjištění, jsou-li zdrojem problému. Zvolit při spuštění Zvolit při spuštění Vestavěné profily Vestavěný profil Vlastní profily Vlastní profil Spravováno vestavěným skriptem Specifikace emulátoru Specifikace platformy Specifikace regionu Provést před spuštěním emulátoru Provést po spuštění emulátoru Provést po ukončení emulátoru Spustitelný soubor emulátoru nenalezen. Specifikace emulátoru nenalezena. Skript při spuštění emulátoru nenalezen. Oddělit jako odlišné hry Sloučit do jedné hry Nastavit platformu Nastavit region Skenovat adresář Nastavení skenování Vyloučit vzorce ze skenu kontrolního součtu Soubory shodující se s zadanými vrozci nebudou skenovány pro kontrolní součet a budou vyhodnoceny podle názvu souboru. Přečtěte si stránku pro pomoc s emulátory pro více informací. Skenovat s emulátorem Při ukládání nové konfigurace musí být zadáno jméno. Emulátor nebo profil emulátoru není nastaven. Adresář ke skenování není zadán nebo neexistuje. Nastavení skenování není správně nastaveno. Zahrnout v souhrnném auto-skenu Nepodařilo se skenovat složku pro emulátory. Nepodařilo se skenovat složku(ky) pro emulované hry. Skrýt importované Profily k importování: Nastavení automatických skenování Uložit jako nastavení auto-skenování Uloží nastavení pro pozdější použití při aktualizaci knihovny. Uložená nastavení mohou být spravována v nabídce "Nastavit emulátory". Importovat pomocí relativních cest Pokud možno, importujte herní soubory pomocí cest relativních k instalační složce Playnite nebo instalační složce emulátoru. Skenovat podsložky Skenovat uvnitř archivů Sloučit související soubory Sloučit související herní soubory, jako například jednotlivé disky, pod jednu hru. Přidat skener Přidat uložený skener Spustit sken Přidejte emulátorové konfigurace pro skenování specifických adresářů. Ujistěte se, že jsou emulátory před importováním her správně nastavené (přes nabídku Knihovna -> Nastavit emulátory) Výchozí stav nastavený nové přidaným hrám Stav nastavený poprvé hraným hrám Nepodařilo se inicializovat PowerShell script runtime. Pokud jste uživatel Windows 7, zkuste (pře)instalovat PowerShell 5.1 pro vyřešní problému. Předvolba filtrů se zadaným jménem již existuje. Aktualizovat předvolbu s novými nastaveními? Automaticky vyplnit název pro řazení hry Když upravíte hru, přidáte hry prostřednictvím aktualizace knihovny, skenováním emulátorů nebo normálním skenování složek, tato volba automaticky vyplní pole "Jméno, dle kterého řadit". Například, "The Witcher 3" získá jméno pro řazení "Witcher 03". Tato volba nikdy nenastaví název, který je identicky jako jméno hry a automaticky aktualizuje jméno pro řazení pouze pokud je pole prázdné. Tato slova budou odstraněna ze začátku hodnoty pro jméno řazení: Použijte pro ignorování slov na začátku řetězce pro účely řazení. Výchozí hodnoty jsou "The", "An" a "A". Vyplňte řadící název u her které žádný nemají Řazení Vyplňování jmen pro řazení… Zjistili jsme, že na Vašem počítači běží služba Nahimic. Tato služba prokazatelně způsobuje vykreslovací problémy nejen aplikaci Playnite. Pokud se v Playnite setkáte s grafickými potížemi, doporučujeme zakázání či úplné odinstalování služby Nahimic. Více informací na https://playnite.link/nahimicsucks Playnite běží s povýšenými oprávněními (jako administrátor). Toto není doporučeno, protože tato oprávnění poskytuje také všem instalovaným rozšířením a hrám/aplikacím spuštěným z Playnite! Více informací na https://playnite.link/adminfaq Zobrazit varování pokud Playnite běží s povýšenými oprávněními Vypočítat skutečnou velikost na disku při výpočtu velikosti her Pokud je povoleno, skenování bude pomalejší a bude reprezentovat skutečnou velikost fyzicky obsazenou na disku. Je-li vypnuto, skenování bude rychlejší a bude používat velikost samotných souborů. Následující rozšíření byla nahlášena jako potenciálně problematická, buď kvůli vysokému dopadu na stabilitu/výkon nebo bezpečnostním potížím. Důrazně doporučujeme, abyste je odinstalovali: {0} Nezahrnovat ve skenu online soubory Soubory uložené v cloud úložišti nebudou skenovány a importovány, nejsou-li dostupné lokálně. Podporováno pouze pro: Google Drive, DropBox, OneDrive Skenovat s použitím zjednodušené metody bez obsahu souborů Soubory budou importovány, ale s použitím méně přesné metody, která nevyžaduje stažení obsahu souboru. Použít pro všechny Přepsat stav instalace Pokud je nastaveno, Playnite ignoruje stav instalace (včetně instalačního adresáře) nastavený integračním doplňkem, který naimportoval tuto hru. Tato volba nemusí plně fungovat s doplňky, které používají alternanitvní metodu pro import her, pokud také nezohlední toto manuální nastavení stavu instalace. Pouze ručně Jednou denně Jednou týdně Při každém startu Zkontrolovat aktualizace programu Zkontrolovat aktualizace doplňků Aktualizovat knihovny Skenovat emulované adresáře Zahrnout skryté hry Upravit pole Vybrat / zrušit výběr všech Otevřít Aktivovat Přiřadit Začněte psát pro hledání her… [F1] pro nápovědu # na začátku zobrazí seznam dostupných příkazů. / na začátku zobrazí sezam dostupných poskytovatelů/doplňků vyhledávání. Napsáním klíčového slova pro vyhledávání a ukončení mezerníkem okamžitě přepne na poskytovatele vyhledávání. TAB: přepnout akci ENTER: aktivovat vybranou akci SHIFT-ENTER: otevřít menu položky Zahrnout odinstalované hry Zahrnout skryté hry Odinstalované hry zahrnuty Odinstalované hry vyloučeny Skryté hry zahrnuty Skryté hry vyloučeny Hrát nebo nainstalovat Přejít na podrobnosti... Menu hry Upravit hru Otevírání vyhledávání Vyhledávací pole Tlačítko vyhledávání Primární akce hry Sekundární akce hry CTRL-F otevře globální vyhledávání místo aktivace vyhledávacího pole Uložit nastavení filtru pro vyhledání her mezi vyhledávacími relacemi Poskytovatelé vyhledávání Výchozí klíčové slovo Vlastní klíčové slovo Systémová klávesová zkratka Playnite vyhledávání Nastavení rozšíření Výjimky Vyloučené soubory relativně ke skenovanému adresáři Vyloučené adresáře relativně ke skenovanému adresáři Přidat soubor do seznamu vyloučení Přidat adresář do seznamu vyloučení Vyloučení lze přidat pouze do uložených skenovacích konfigurací. Vyloučení byla přidána do skeneru "{0}". Přepsat platformu Skener přiřadí tuto platformu všem hrám nehledě na automaticky detekovanou platformu. Zahrnout příkazy ve výchozím hledání Pokud je zakázáno, příkazy nebudou zahrnuty ve výchozím vyhledávání dokud nebude použit # prefix. Použít přibližné hledání ve filtru jména Pokud je povoleno, filtr jmen bude vyhledávat jména her stejně jako globální vyhledávání. Přísné hledání může být vynuceno v jednotlivých filtrech prefixem !. Zobrazit pole hry ve výsledcích hledání: Skrytý stav Zálohování dat bylo zrušeno. Zálohování dat selhalo. Chyba při zálohování dat Probíhá zálohování dat… Probíhá obnovení dat ze zálohy… Nepodařilo se obnovit data ze zálohy. Nastavení Herní knihovna Média knihovny her Nainstalovaná rozšíření Data nainstalovaných rozšíření Nainstalovaná témata Vyberte data, která mají být obnovena ze zadaného záložního souboru. Playnite se automaticky restartuje a zahájí proces obnovy. Vyberte položky, které mají být zahrnuty do zálohy dat. Nastavení aplikace a herní knihovna je zahrnuta automaticky. Playnite se automaticky restartuje pro spuštění procesu zálohování. Automatické zálohování dat Frekvence automatického zálohování Adresář pro zálohu Rotující zálohy Zahrnout další data: Adresář zálohy musí být nastaven pokud je zapnuto automatické zálohování. Zobrazit oznámení pouze pro aktualizace zahrnující opravy Pokud je zapnuto, pouze aktualizace zahrnující opravy pro aktuálně nainstalovanou verzi budou zobrazí oznámení. Vydání nové významné verze oznámení o aktualizaci nezobrazí. Použít relativní zobrazení u dat za poslední týden Použít relativní zobrazení ve formátu "Dnes", "Včera" atd., pokud je datum nižší než týden. Zadaný formát data bude použit pro všechny ostatní data. Hledání obrázků na webu Řetězec pro hledání ikon Řetězec pro hledání obalu Řetězec pro hledání pozadí Získávání informací o doplňku… Není k dispozici žádný zdroj metadat Nastavení akce Hrát Použít nastavení skeneru Vybrat profil při spuštění Vybrat emulátor při spuštění Automaticky Vždy zapnuto Vždy vypnuto Podpora přístupnosti (čtečka obrazovky) Menu aplikace Menu hry Adresář aplikace Adresář uživatelských dat Bylo zjištěno poškození souboru knihovny, Playnite se nyní vypne. Reportujte problém na Playnite stránce GitHubu s požadavkem na opravu souborů. Chcete uložit provedené změny? Přenosná instalace Nebyly nalezeny žádné ovladače ================================================ FILE: source/Playnite/Localization/cy_GB.xaml ================================================  ================================================ FILE: source/Playnite/Localization/da_DK.xaml ================================================  Engelsk Playnite sprog Afslut Filter aktivt Filter inaktivt Yderligere filtrer Filtre Filter Ugyldig data Gem ændringer? Hjemmeside på www.playnite.link Se kildekoden på GitHub Opret diag. pakke Send diagnose-information Om Playnite Lavet af Josef Němec Tildel kategori Sæt kategorier Tilføj kategori Afkrydset - Sæt kategori Uden afkrydsning - Fjern kategori Ubestemt - Ingen ændring (flere spil under redigering) Ingen kategori Ingen platform Hovsa! Noget gik galt. En uoprettelig fejl er opstået. Hvis du vil hjælpe os med at ordne problemet, så beskriv venligst de handlinger du udførte før nedbruddet og send så så diagnostisk information. Hvis du er online, vil data blive uploadet til Playnite serveren til analyse. Alternativt, kan du klikke på 'Rapportér nedbrud' knappe for at oprette et nyt GitHub issue og rapportere nedbruddet manuelt. Tak for din hjælp. Udvidelse "{0}" har forårsaget en uoprettelig fejl. Vi anbefaler at du gemmer log filen og rapporterer problemet til udvidelsens udvikler. Hvis problemet fortsætter med at opstå, deaktiver da udvidelsen. Udvidelse "{0}" har forårsaget en uoprettelig fejl. Vi anbefaler at du rapporterer problemet til udvidelsens udvikler. Hvis problemet fortsætter med at opstå, deaktiver da udvidelsen. Ukendt udvidelse eller tema forårsagede en uoprettelig fejl. Vi anbefaler at deaktiverer tredjeparts tilføjelser, isolere den problematiske og rapporterer denne til dens udvikler. Uoprettelig fejl opstod. Hvis du vil hjælpe os med at rette problemet, så send os diagnostisk information. Tak. Deaktiver udvidelse Gem log fil Send diagnostisk info Anmeld crash Genstart Playnite Genstart i sikker tilstand Deaktiverer alle 3'de-parts udvidelser og anvender standard tema. Luk Playnite Handlinger udført før nedbruddet (på Engelsk): Administrer bibliotek Fjern spil? Kan ikke fjerne - Spil eller installation kører. Kan ikke afinstallere - Spillet kører. Er du sikker på at du vil fjerne {0}? Er du sikker på at du vil fjerne {0} spil? Er du sikker på, at du vil fjerne {0}? Valg af "Tilføj til udelukkelsesliste" vil forhindre spillet i at blive importeret igen, næste gang biblioteket opdateres. Er du sikker på, at du vil fjerne {0} spil? Ved at vælge "Tilføj til udelukkelsesliste" vil spillet ikke blive importeret igen, næste gang biblioteket opdateres. Er du sikker på, at du vil fjerne {0} enheder, der i øjeblikket ikke er i brug? Ingen ubrugte felter fundet. Ja (Tilføj til undtagelsesliste) Der er ændringer i denne sektion, som ikke er gemt. Opdaterer spilbibliotekets format… Database-opdatering fejlede. Spilbiblioteket kunne ikke opdateret. Der kræves {0} MBs fri plads, for at kunne gennemføre dette. GameError Kan ikke starte spillet. '{0}' blev ikke fundet i databasen. Kan ikke starte spillet: {0} Kan ikke gennemføre handling: {0} Kan ikke åbne spil-placering: {0} Kunne ikke finde størrelsen på installeret spil: {0} Installationstørrelses scanningsfejl Der opstod {0} fejl under scanningen af installationsstørrelsen Fejl ved oprettelse af genvej: {0} Kunne ikke åbne manual: {0} Kan ikke installere spil: {0} Kan ikke afinstallere spil: {0} Ingen gyldige handlinger til spilopstart fundet. Ved brug af emulator-handlinger skal du sørge for, at platform-definitionerne stemmer overens mellem spillets og emulatorens konfiguration. Installationsimplementering er ikke tilgængelig. Det biblioteksplugin, der er ansvarligt for spillet, er deaktiveret eller ikke installeret. Officiel metadata-download er ikke tilgængeligt. Intet spil valgt. Spillets scriptudførelse mislykkedes. Applikationens scriptudførelse mislykkedes. Udførelse af globalt script mislykkedes. Emulatorens scriptudførelse mislykkedes. Afspil scripthandling mislykkedes. PowerShell 3.0 eller nyere er ikke installeret. Lykkedes ikke at afgøre, hvordan spillet skulle startes. Aktivér Deaktiveret Fjern Fjern ubrugte Omdøb Kopier Tilføj Standardikon Standard coverbillede Standard Baggrundsbillede Færdig Næste Tilbage FÆRDIG TILBAGE RYD Ryd Luk Luk alle Importer Navn Forfatter Modul Serie Version Sidst spillet Mest spillede Antal afspilninger Installationsstørrelse Folder Noter Tilføjet Tilføjelsesdato Ændret Ændringsdato Hjemmeside Sti OK Gem Luk Annullér Bekræft Nulstil Ja Nej Velkommen Lokal Bruger Generelt Medier Links Installation Handlinger Downloader… Downloader medier... Indlæser… Type Profil Profiler Fjern Download Søg Opløsning: Alle Zoom Vis som liste Forsidebillede Gittervisning Detaljeret Oversigt Brugerdefineret URL-adresse Særlig tak til Licens Bidragsydere Afslutter Playnite… I dag I går Mandag Tirsdag Onsdag Torsdag Fredag Lørdag Søndag Sidste uge Sidste måned Sidste år For mere end et år siden 0 til 100MB 100MB til 1GB 1GB til 5GB 5 GB til 10 GB 10GB til 20GB 20GB til 40GB 40GB til 100GB 100GB eller mere Import lykkedes. Alle spil Spil ID Database-ID Forudindstillinger Kolonne Kolonner Række Rækker Kunne ikke hente ikon fra Play handling. Der er ingen handling af File type til stede. Download kun manglende metadata Aktivering af denne indstilling vil springe over downloading af metadata for datafelter, der allerede indeholder information. Spiludvalg Vælg venligst hvilke spil der skal opdateres med ny metadata: Alle spil i databasen Alle filtrerede spil Kun valgte spil Officiel Butik IGDB Vælg hvilke felter der automatisk skal udfyldes af Playnite og hvilke kilder der skal bruges til at hente data fra. Overvej venligst at klikke på logoet ovenfor og bidrage med opdateringer til igdb.com database for at forbedre data Playnite anvendelser. Henter metadata... Importerer installerede spil… Importerer {0} spil… Importerer emulerede spil fra {0}… Downloader biblioteksopdateringer… Scanner størrelsen af spil i biblioteket… Scanner størrelse af importerede spil… Biblioteksopdatering færdig Frigiver ressourcer… Konfiguration Indstillinger… Platforme og emulatorer Konfigurer emulatorer… Bibliotekshåndtering… Værktøjer Download Metadata… Software Værktøjer… Konfigurer Integrationer… Åbn 3. Parts Klient Tredjepartsklienter Opdater Spilbiblioteket Afbryd Biblioteksopdatering Opdater Emulerede Mapper TIlføj spil Manuelt… Scan Automatisk… Emuleret Spil… Microsoft Store Applikation... Om Playnite Indsend feedback Skift til Fuldskærmstilstand Links Hjælp Støt Playnite på Patreon Support på Ko-fi Brugervejledning SDK dokumentation Genstart System Sluk Systemet Suspendér Systemet Dvaletilstand Lås Systemet Vælg et Tilfældigt Spil Element afstand Tegn gitterelementets baggrund Bredde af gitterelementets ramme Manglende spil-ikonkilde Manglende spil-ikonkilde Manglende spilbaggrundskilde Lodret afstand til spildetaljer Placering af detaljer i Gittervisning Detaljer vis spillisteposition Skrifttype til programmet Placering af filterpanel Placering af explorerpanel Rendering af omslagskunst DVD Boks Epic Games Store GOG Galaxy 2.0 IGDB Firkantet Steam Banner Steam vertikal cover Twitch * Kræver genstart for at anvende Indstillinger Generelt Øverste panel Udseende Spildetaljer Layout Avanceret Fuld skærm Input Ydeevne Metadata Opdaterer Søg Sikkerhedskopi Sikkerhedskopiér Biblioteksdata Gendan Datasikkerhedskopi Importér automatisk ændringer i biblioteket Ugyldig placering af databasefil, korrekt filsti skal angives. Navnefeltet må ikke være tomt. Download metadata efter import af spil Start Playnite minimeret Start Playnite når du starter din computer Start lukket i bakken Kunne ikke registrere Playnite til at starte, når computeren starter. Start i fuldskærmstilstand Asynkron billedindlæsning Vis spilnavne i Gittervisning Slør baggrund Vis i Gittervisning Tema Temaprofil Fuldskærmsvisning Databaseplacering Login-status: Playnite indstillinger Ryd Cache Formatér tid spillet for at angive antallet af spillede dage Ryd cache? Playnite genstart påkræves for at anvende nyt tema Find flere temaer Opret et nyt tema Hjælp os med at oversætte Playnite Genstart Playnite? Guide-knappen åbner fuldskærmstilstand Vis batteristatus Vis batteri-procent Vis ur Layout Ingen Udfyld Uniform Venstre Højre Top Bund Importeringsfejl Godkendelse krævet Godkendelse mislykkedes Delvis indlæsning af store spilbeskrivelser Store beskrivelser kan forårsage mærkbar forsinkelse, når du vælger spil. Når aktiveret, vil kun en del af beskrivelse tekst i første omgang blive indlæst med en mulighed for at indlæse resten på efterspørgslen. Download metadata Scan mappe ved hjælp af Emulator Vælg filer Scanner... Første gangs opsætning Platforme Platform Emulatorer Emulator Vælg ikon Vælg Omslag Vælg fil Vælg URL Tilføj emulator Eksekverings-fil Argumenter Arbejdsmappe Understøttede filtyper Sorter efter Gruppér efter Stigende Faldende Gruppere efter kategori Gruppere efter platform Vis type Vis Ikon Coverbillede Baggrundsbillede Bibliotek Navn Kontonavn Platform Kategori Genre Udgivelsesdato Udgivelsesår Udvikler Tag Udgiver Installeret Installeret Skjult Favorit Aktiver HDR-understøttelse Hvis aktiveret, vil HDR blive aktiveret på den primære skærm, før spillet startes. Bemærk, at HDR ikke er understøttet på din primære skærm. Sidst spillet Kategori Beskrivelse Coverbillede Links Genre Firma Udvikler Udgiver Kategori Tag Tags Aldersklassificering Region Kilde Indtast en gyldig URL som starter med http:// eller https:// Vælg URL Ryd filtre API Nøgle Temafejl Ryd alle Installerer Afinstallerer Starter Kører Ugyldig URL Gør ingenting Minimer Luk Skift Avanceret Aldrig Brugerscore Scripts Plugins Udvidelser Genindlæs scripts Luk Playnite Biblioteker Opdatér alle Oprettet af: Version: Modul: Bibliotek Alle Ingen Notifikationer Bredde Højde Størelse Lille Normal Stor Større Størst Standard Opdateringsfejl Aktiver HDR-understøttelse Deaktivér HDR-understøttelse Sæt kategori… Fjern Spil Installér Spil indstillinger Detaljer Afinstaller Åben installationsplacering Opret skrivebordsgenvej Mere Tid spillet Sidst spillet {0}d {1}t {2}m {0}t {1}m {0} minutter {0} sekunder Omdøb genstand Indtast navn Indtast nyt navn 1 til 10 timer 10 til 100 timer 100 til 500 timer 500 til 1000 timer 1.000+ Installation fuldført. Installér udvidelse? Samlet installationsstørrelse Senest spillet Favoritter Mest spillede Alle Søgeresultater for: En genstand med samme navn findes allerede. Sporingssti Ingen Ensartet Kun elementer Kun start og slut Brugerdefineret profil Sammenflet relaterede filer Flet relaterede spilfiler, såsom individuelle spildiske, under et spil indlæg. Brug fuzzy matching i navnefilter Når aktiveret, vil navnefilter matche spilnavne på samme måde som global søgning. Streng matchning kan håndhæves på en individuel sag ved at præfikse filter med ! tegn. Programmappe Bruger datamappe Biblioteksfil korruption er blevet opdaget, Playnite vil nu lukke. Åbn et nyt problem på Playnites GitHub-side med en anmodning om at fikse korruptionen i dine filer. ================================================ FILE: source/Playnite/Localization/de_DE.xaml ================================================  Deutsch Playnite-Sprache Beenden Filter aktiv Filter deaktiviert Weitere Filter Filter Filter Ungültige Daten Änderungen speichern? Homepage: www.playnite.link Quellcode auf GitHub Diagnosedaten erzeugen Diagnosedaten senden Über Playnite von Josef Němec Kategorie zuweisen Kategorien festlegen Kategorie hinzufügen Aktiviert - Kategorie zuweisen Deaktiviert - Kategorie entfernen Unentschieden - Keine Änderungen (beim Bearbeiten mehrerer Spiele) Keine Kategorie Keine Plattform Hoppla! Etwas ist schief gelaufen … Ein nicht behebbarer Fehler ist aufgetreten. Falls du uns bei diesem Problem helfen möchtest, beschreibe bitte kurz die vor dem Absturz unternommenen Aktionen und sende dann die Diagnosedaten. Falls du online bist, werden diese zur Analyse auf den Playnite-Server hochgeladen. Ersatzweise kannst du auf die Schaltfläche »Absturz melden« klicken, um einen neuen Fehlerbericht auf GitHub zu erstellen und den Absturz manuell zu melden. Vielen Dank für deine Hilfe. Erweiterung »{0}« hat einen nicht behebbaren Fehler verursacht. Wir empfehlen, die Diagnosedatei zu speichern und das Problem dem Entwickler der Erweiterung zu melden. Falls das Problem wiederholt auftritt, deaktiviere die Erweiterung. Erweiterung »{0}« hat einen nicht behebbaren Fehler verursacht. Wir empfehlen, das Problem dem Entwickler der Erweiterung zu melden. Falls das Problem wiederholt auftritt, deaktiviere die Erweiterung. Unbekannte Erweiterung oder Design verursachte einen nicht behebbaren Fehler. Wir empfehlen alle Drittanbieter-Add-ons zu deaktivieren, um das problematische zu isolieren und das Problem dem Entwickler des Add-ons mitzuteilen. Ein nicht behebbarer Fehler ist aufgetreten. Falls du uns bei diesem Problem helfen möchtest, sende bitte Diagnoseinformationen. Vielen Dank. Erweiterung deaktivieren Protokolldatei speichern Diagnoseinformationen senden Absturz melden Playnite neu starten Im Sicheren Modus neu starten Alle Drittanbieter-Erweiterungen werden deaktiviert und das Standarddesign verwendet. Playnite beenden Unternommene Aktionen vor dem Absturz (in Englisch): Bibliotheksverwaltung Spiel(e) entfernen? Entfernen nicht möglich - Spiel oder Installationsprogramm werden derzeit ausgeführt. Deinstallieren nicht möglich - Spiel läuft gerade. Möchtest du {0} wirklich entfernen? Möchtest du {0} Spiele wirklich entfernen? Möchtest du {0} wirklich entfernen? Die Auswahl der Option »Zur Ausschlussliste hinzufügen« wird bei zukünftigen Bibliotheksaktualisierungen das erneute Importieren des Spiels verhindern. Möchtest du {0} Spiele wirklich entfernen? Die Auswahl der Option »Zur Ausschlussliste hinzufügen« wird bei zukünftigen Bibliotheksaktualisierungen das erneute Importieren der Spiele verhindern. Möchtest du wirklich {0} derzeit nicht verwendete Einträge entfernen? Keine unbenutzen Felder gefunden. Ja (zur Ausschlussliste hinzufügen) Es existieren ungespeicherte Änderungen in diesem Abschnitt Spielebibliotheksformat wird aktualisiert … Datenbank konnte nicht aktualisiert werden. Spielebibliothek kann nicht aktualisiert werden. Es werden {0} MB freier Speicherplatz benötigt. Spielfehler Spiel kann nicht gestartet werden: »{0}« wurde in der Datenbank nicht gefunden. Spiel kann nicht gestartet werden: {0} Aktion kann nicht gestartet werden: {0} Installationspfad kann nicht geöffnet werden: {0} Installationsgröße konnte nicht ermitteln werden: {0} Fehler bei Ermittlung der Installationsgröße Beim Ermitteln der Installationsgröße sind {0} Fehler aufgetreten Verknüpfung konnte nicht erstellt werden: {0} Handbuch konnte nicht geöffnet werden: {0} Spiel kann nicht installiert werden: {0} Spiel kann nicht deinstalliert werden: {0} Keine gültigen Start-Aktionen gefunden. Achte bei Emulator-Aktionen darauf, dass die Plattformdefinitionen von Spiel- und Emulatorkonfiguration übereinstimmen. Installations-Implementierung nicht verfügbar. Das Bibliotheks-Plugin für dieses Spiel ist deaktiviert oder nicht installiert. Offizielle Metadaten sind nicht zum Herunterladen verfügbar. Kein Spiel ausgewählt. Skriptaktion des Spiels konnte nicht ausgeführt werden. Anwendungsskript konnte nicht ausgeführt werden. Globale Skriptaktion konnte nicht ausgeführt werden. Emulator-Skript konnte nicht ausgeführt werden. »Spielen«-Skriptaktion konnte nicht ausgeführt werden. PowerShell 3.0 oder neuer ist nicht installiert. Es konnte nicht herausgefunden werden, wie das Spiel gestartet wird. Aktiviert Deaktiviert Entfernen Nicht Verwendete entfernen Umbenennen Kopieren Hinzufügen Standard Icon Standard-Coverbild Standard-Hintergrundbild Abschließen Weiter Zurück FERTIG ZURÜCK LEEREN Zurücksetzen Verwerfen Alle verwerfen Importieren Name Autor Modul Serie Version Zuletzt gespielt Meistgespielt Spielhäufigkeit Installationsgröße Ordner Anmerkungen Hinzugefügt Hinzufügedatum Geändert Änderungsdatum Website Pfad OK Speichern Schließen Abbrechen Bestätigen Zurücksetzen Ja Nein Willkommen Lokaler Nutzer Allgemein Medieninhalte Links Installation Aktionen Wird heruntergeladen … Medieninhalte werden heruntergeladen … Wird geladen … Typ Profil Profile Entfernen Herunterladen Suchen Auflösung: Beliebig Vergrößerung Listenansicht Cover Rasteransicht Detailansicht Benutzerdefiniert URL Besonderer Dank Lizenz Mitwirkende Playnite wird beendet … Heute Gestern Montag Dienstag Mittwoch Donnerstag Freitag Samstag Sonntag Letzte Woche Letzter Monat Letztes Jahr Vor mehr als einem Jahr 0 bis 100MB 200MB bis 1GB 1GB bis 5GB 5GB bis 10GB 10GB bis 20GB 20GB bis 40GB 40GB bis 100GB 100GB oder mehr Import erfolgreich abgeschlossen. Alle Spiele Spiel-ID Datenbank-ID Voreinstellungen Spalte Spalten Zeile Zeilen Es konnte kein Icon aus der Spiel Aktion abgerufen werden. Es ist keine Aktion des »Datei« Typs vorhanden. Nur fehlende Metadaten herunterladen Bei aktivierter Option werden für Datenfelder, die bereits Informationen enthalten, keine Metadaten heruntergeladen. Spieleauswahl Bitte wähle aus, für welche Spiele die Metadaten aktualisiert werden sollen: Alle Spiele der Datenbank Alle derzeit den Filter passierende Spiele Nur die ausgewählten Spiele Keine Metadatenfelder ausgewählt Es sind keine Metadatenfelder zum Download ausgewählt. Bitte wähle mindestens eines aus und aktiviere dafür mindestens einen Metadatenanbieter. Offizieller Shop IGDB Bitte wähle die von Playnite automatisch auszufüllenden Felder und die hierfür zu verwendenden Quellen aus. Zur Verbesserung der von Playnite verwendeten Daten kannst du auf das oben dargestellte Logo klicken und Aktualisierungen zur igdb.com-Datenbank beitragen. Metadaten werden heruntergeladen … Installierte Spiele werden importiert … Spiele von {0} werden importiert … Emulierte Spiele werden von {0} importiert … Bibliotheksaktualisierungen werden heruntergeladen … Größe der Bibliotheksspiele wird ermittelt … Größe der importierten Spiele wird ermittelt … Bibliotheksaktualisierung abgeschlossen Ressourcen werden freigegeben … Konfiguration Einstellungen … Plattformen & Emulatoren Emulatoren konfigurieren … Bibliotheksverwaltung … Werkzeuge Metadaten herunterladen … Software-Werkzeuge … Integrationen konfigurieren … Drittanbieter-Client öffnen Drittanbieter-Clients Spielebibliothek aktualisieren Bibliotheksaktualisierung abbrechen Emulierte Ordner aktualisieren Spiel hinzufügen Manuell … Automatisch durchsuchen … Emuliertes Spiel … Microsoft-Store-Anwendung … Über Playnite Feedback senden Zu Vollbildmodus wechseln Links Hilfe Auf Patreon unterstützen Unterstützung auf Ko-Fi Benutzerhandbuch SDK-Dokumentation System neu starten System herunterfahren System-Stand-by System-Ruhezustand System sperren Benutzer abmelden Zufälliges Spiel auswählen In der Detailleiste anzuzeigende Datenfelder: Elementabstände Hintergrund für Rasterelemente zeichnen Elementrandbreite in Rasteransicht Quelle für fehlende Spiel Icon Quelle für fehlende Spielecover Quelle für fehlende Spielhintergründe Vertikaler Abstand zu Spieldetails Detailleistenposition in Rasteransicht Spielelistenposition in Detailansicht Trennlinien zwischen Leisten zeichnen Höhe der Spieletitelbilder Höhe des Spiellisten Icons Anwendungsschriftart Nichtproportionale Schriftart Position der Filterleiste Position der Explorerleiste Coverdarstellung Seitenverhältnis für Ausgabe Die folgenden Optionen beeinflussen auch das Rendering der Kacheln im Vollbildmodus! Gestreckt DVD-Box Epic Games Shop GOG Galaxy 2.0 IGDB Quadratisch Steam-Banner Steam-Vertikalcover Twitch * Zum Anwenden Neustart erforderlich Einstellungen Allgemein Obere Leiste Erscheinungsbild Spieldetails Layout Erweitert Vollbild Eingabe Leistung Metadaten Aktualisierung Suche Sicherung Bibliotheksdaten sichern Datensicherung wiederherstellen Bibliotheksänderungen automatisch importieren Ungültiger Pfad zur Datenbank. Name des Nutzerkontos darf nicht leer sein. Metadaten nach Spieleimport herunterladen Playnite minimiert starten Playnite beim Start des Computers starten Minimiert in Infobereich starten Autostart für Playnite konnte nicht registriert werden. Im Vollbildmodus starten Asynchrones Laden von Bildern Ermöglicht weicheres Scrolling der Spielelisten zum Preis längerer Bildladezeiten. Spielename anzeigen, falls Titelbild fehlt Name des Spiels unterhalb Cover anzeigen Nicht installierte Spiele abdunkeln Spiel Icons in Detailansichtsliste anzeigen Spieleanzahl in Gruppenbeschreibung anzeigen In Filter- und Explorerleisten nur zugewiesene Felder anzeigen Hardwarebeschleunigung deaktivieren Verwende diese Option bei Ruckeln oder ähnlichen Problemen der Benutzeroberfläche Versteckte Spiele in Schnellstartlisten anzeigen Beeinflusst Sprung- und Infobereichslisten. Anzahl der Schnellstart-Elemente Hintergrundbild des Spiels als Fensterhintergrund verwenden Hintergrund weichzeichnen Hohe Qualität Hintergrund abdunkeln In Rasteransicht anzeigen Design Designprofil Design für Vollbildmodus Designprofil für Vollbildmodus Speicherort der Datenbank Anmeldestatus: Playnite-Einstellungen Webcache leeren Löst eventuell Probleme beim Verknüpfen von Nutzerkonten. Taskleistenicon anzeigen Playnite in Infobereich minimieren Playnite in den Infobereich minimieren, wenn das Anwendungsfenster geschlossen ist Beim Spielstart: Nach Spielende: »Zeit gespielt« formatieren, um die Anzahl der gespielten Tage anzugeben Datumsformate: Du wirst von allen verbundenen Diensten abgemeldet und ein Neustart wird erforderlich. Möchtest du fortfahren? Cache leeren? Anwenden eines neuen Designs erfordert einen Neustart von Playnite Weitere Designs beziehen Neues Design erstellen Weitere Erweiterungen beziehen Neue Erweiterung erstellen Hilf uns, Playnite zu übersetzen Playnite muss für das Anwenden der geänderten Einstellungen neu gestartet werden. Jetzt neu starten? Ein Neustart wird alle derzeit aktiven Aufgaben (Downloads) abbrechen. Playnite neu starten? Playnite kann deine Bibliotheksdateien nicht automatisch verschieben. Daher musst du vor dem Wechsel des Speicherorts die Dateien manuell verschieben bzw. kopieren. Falls am Zielort keine Bibliothek vorhanden ist, wird eine neue erstellt. Der neue Speicherort der Datenbank wird erst nach einem Neustart von Playnite verwendet. Spielzeit wird nicht erfasst, falls »Schließen«-Aktion festgelegt wurde. Zeilenanzahl Spaltenanzahl Zeilenanzahl in Detailansicht Hintergrundbild in Hauptbildschirm anzeigen Hat ohne erneutes Herunterladen der Metadaten keinen Einfluss auf bereits in der Bibliothek befindliche Spiele. Spielzeit von Spielen der Bibliothek importieren: Konfiguriert, wann Playnite die von Bibliotheks-Plugins gemeldeten Spielzeiten für Spiele der Playnite-Datenbank importieren soll. Hierfür müssen die entsprechenden Plugins dieses Feature unterstützen. Immer: Importiert die Spielzeit für neu importierte und vorhandene Spiele der Playnite-Datenbank. Nur für neu importierte Spiele: Importiert die Spielzeit nur für neu importierte Spiele. Niemals: Importiert die Spielzeit unter keinen Umständen. Immer Nur für neu importierte Spiele Niemals Controllerunterstützung im Desktopmodus aktivieren Guide-Taste öffnet Vollbildmodus Einstellungen für das automatische Herunterladen von Metadaten bei neu importierten Spielen. Zielbildschirm Immer primären Bildschirm verwenden Spieletitel anzeigen Akkustatus anzeigen Akkuladung in Prozent anzeigen Uhr anzeigen Mauszeiger ausblenden Nur Installierte in Schnellfilter Beschriftung der Controllertasten Layout Horizontales Scrollen Wähle einen der Unterabschnitte aus Keine Einstellungen verfügbar Einstellungen konnten nicht geladen werden Diese Skripte werden für jedes Spiel der Bibliothek ausgeführt. Individuelle Skripte können für jedes Spiel beim Bearbeiten der Spieldetails separat zugewiesen werden. Wechsel von Hintergrundbildern animieren Schriftgrößen Automatisch Aliased Graustufen ClearType Optimal Bildschirm Modus für Textformatierung Modus für Textrendering Methoden für Textrendering und -formatierung werden derzeit nicht für Spielbeschreibungen verwendet. Hintergrundbilder im Voraus laden Falls aktiviert, wird Playnite Hintergrundbilder gleichzeitig mit den Metadaten herunterladen. Dies verbraucht mehr Speicherplatz auf der Festplatte, macht die Bilder jedoch auch offline verfügbar. Falls deaktiviert, werden Hintergrundbilder erst dann heruntergeladen, wenn sie zum ersten Mal benötigt werden. Dies verbraucht weniger Speicherplatz auf der Festplatte, führt jedoch möglicherweise zu einer Verzögerung vor dem Anzeigen der Bilder und einige Bilder sind offline eventuell nicht verfügbar. Drittanbieter-Client nach dem Beenden des Spiels automatisch schließen Zeitverzögerung für das Herunterfahren des Clients (in Sekunden) Nicht schließen bei Spielsitzungen kürzer als (in Sekunden) Folgende Clients automatisch schließen: Clients autom. schließen Import-Ausschlussliste Warnung anzeigen, wenn zu große Medieninhalte zugewiesen werden Befehl zum Öffnen von Ordnern Bevorzugte Organisation für Alterseinstufung Installationsgröße beim Bibliotheksupdate aktualisieren Ermittelt und aktualisiert die Installationsgröße, falls Dateiänderungen seit dem letzten Scan erkannt wurden Keine Ausfüllen Einheitlich Ausfüllen & Seitenverhältnis beibehalten Links Rechts Oben Unten Fehler beim Import Authentifizierung erforderlich Authentifizierung fehlgeschlagen Alternativer Modus für WebView-Rendering Bei Problemen mit WebViews verwenden, zum Beispiel bei Dialogen der Integrations-Authentifizierung. Partielles Laden umfangreicher Spielbeschreibungen Große Beschreibungen können zu spürbaren Verzögerungen beim Auswählen von Spielen führen. Falls aktiviert, wird zunächst nur ein Teil des Beschreibungstextes mit der Option geladen, den Rest bei Bedarf zu laden. Metadatenimport Metadaten herunterladen Gewählte Konfiguration für alle zukünftigen Metadatendownloads festlegen. Kann auch in den Anwendungseinstellungen geändert werden. Assistent für Emulationsimport Dieser Assistent hilft dir beim Herunterladen und Importieren von Konsolenemulatoren und beim Importieren emulierter Spiele. Beachte, dass du später jederzeit weitere Emulatoren und/oder Spiele hinzufügen kannst (für Emulatoreinstellungen im Menü »Bibliothek« und für emulierte Spiele im Menü »Spiele hinzufügen«). Die folgende Liste enthält Emulatoren, die Playnite automatisch erkennen und konfigurieren kann. Du kannst die Installationsdateien der Emulatoren von der jeweiligen Website herunterladen. Sobald du die Emulatoren manuell installiert hast, kannst du sie im Konfigurationsdialog für Emulatoren importieren. Du kannst jeden auf deinem PC installierten Emulator importieren, indem du auf die Schaltfläche »Ordner autom. durchsuchen …« klickst. Playnite wird den ausgewählten Ordner nach allen bekannten Emulatoren durchsuchen und diese optional importieren. Du kannst diese Schaltfläche mehrfach verwenden, um Emulatoren aus verschiedenen Ordnern zu importieren. Die Emulatoren werden am Ende der aktuellen Liste hinzugefügt. Du kannst Spiele importieren, indem du auf die Schaltfläche »Ordner via Emulator durchsuchen« klickst. Die Auswahl des passenden Emulators ermöglicht Playnite, die entsprechenden Dateitypen zu suchen und zu importieren. Du kannst diese Schaltfläche mehrfach verwenden, um Spiele aus verschiedenen Ordnern zu importieren. Die Spiele werden am Ende der aktuellen Liste hinzugefügt. Du hast keinen Emulator ausgewählt, weshalb Playnite nicht in der Lage sein wird Spiele automatisch zu importieren. Bist du sicher, dass du den Import abbrechen möchtest? Derzeit sind in Playnite keine Emulatoren konfiguriert. Du kannst keine Spiele importieren, ohne zuvor den Emulator zu konfigurieren und die passenden Dateitypen auszuwählen. Möchtest du jetzt einige Emulatoren hinzufügen? Ordner via Emulator durchsuchen Dateien auswählen Ordner autom. durchsuchen … Emulatoren konfigurieren … Suchvorgang … {0} wird durchsucht … Einrichtungsassistent Dieser Assistent hilft dir beim automatischen Importieren von Spielen und der Konfiguration externer Spielebibliotheken. Playnite kann Spiele von mehreren Spieleplattformen wie Steam oder GOG automatisch importieren und zudem deine Bibliothek durch automatisches Aktualisieren während des Programmstarts aktuell halten. Natürlich kannst du auch später Spiele sämtlicher Plattformen manuell hinzufügen, indem du im Hauptmenü auf die Schaltfläche »Playnite« klickst. . Bibliotheksintegration Spiele automatisch von den unten aufgeführten Diensten importieren. Spätere Spieländerungen (Installationsstatus) werden beim Starten von Playnite automatisch oder aber manuell ausgelöst aktualisiert. Die ausgewählten Einstellungen beeinflussen die ursprünglichen und alle nachfolgenden Importe. Konfiguration abgeschlossen Die Ersteinrichtung wurde abgeschlossen. Du kannst jedoch alle Einstellungen auch später im Menü »Einstellungen« ändern. Weiterhin kannst du jederzeit weitere Spiele inzufügen, indem du auf das Playnite-Logo klickst. Mindestens eine Erweiterung konnte nicht heruntergeladen werden. Du kannst versuchen, diese erneut aus dem Add-on-Menü herunterzuladen, nachdem der Assistent für die erste Ausführung abgeschlossen ist. {0}-Integration wird heruntergeladen … Liste der empfohlenen Integrationen wird heruntergeladen … Liste der empfohlenen Integrationen konnte nicht heruntergeladen werden. Du kannst die Integrationen später über das Addon-Menü erneut herunterladen. Plattformen & Emulatoren konfigurieren Emulatoren konfigurieren Plattformen Plattform Emulatoren Emulator Plattform hinzufügen Icon auswählen Cover auswählen Bild auswählen Element auswählen Hintergrund auswählen Datei auswählen URL auswählen Emulator hinzufügen Unterstützte Plattformen Möchtest du die Plattformänderungen speichern? Möchtest du die Emulatoränderungen speichern? Ausführbare Datei Argumente Arbeitsverzeichnis Unterstützte Dateitypen Emulatoren importieren … Emulatoren herunterladen … Argumentevoreinstellung aus bekanntem Emulatorprofil laden Möchtest du den Emulator »{0}« wirklich entfernen? Er wird derzeit von {1} Spiel(en) verwendet. Möchtest du die Plattform »{0}« wirklich entfernen? Sie wird derzeit von {1} Spiel(en) und {2} Emulator(en) verwendet. Hilfe zu Einstellungen Sortieren nach Sortierrichtung Gruppieren nach Aufsteigend Absteigend Nicht gruppieren Nach Bibliiothek gruppieren Nach Kategorie gruppieren Nach Plattform gruppieren Ansichtstyp Anzeige Explorerleiste Filterleiste Icon Bibliotheken Icon Coverbild Hintergrundbild Sortiername Bibliothek Handbuch Name Installationslaufwerk Kontoname Plattform Kategorie Genre Erscheinungsdatum Erscheinungsjahr Entwickler Schlagwort Publisher Installationsstatus Entspricht allen Filtern Falls aktiviert, werden nur Spiele angezeigt, die zu alle Einträge in allen Filtern passen. Falls deaktiviert, werden alle Spiele angezeigt, die zu einem beliebigen Eintrag eines beliebigen Filters passen. Installiert Installiert Nicht installiert Versteckt Favorit HDR-Unterstützung aktivieren Falls aktiviert, wird HDR vor Starten des Spiels auf dem Hauptbildschirm aktiviert. Beachte, dass HDR auf deinem Hauptbildschirm nicht unterstützt wird. Zuletzt gespielt Kategorie Beschreibung Installationsverzeichnis Coverbild Links Image-, ROM- oder ISO-Pfad Genre Genres Unternehmen Unternehmen Entwickler Entwickler Publisher Publisher Kategorie Kategorien Schlagwort Schlagwörter Feature Features Alterseinstufung Alterseinstufungen Region Regionen Quelle Quellen Letzte Aktivität Datenbankfehler Datenbank konnte nicht geöffnet werden. Datenbank ist nicht geöffnet. Kein Zugriff auf Bibliotheksdatenbank: Datei »{0}« wird von einem anderen Prozess verwendet oder befindet sich an einem unerreichbaren Speicherort. Diagnosedaten konnten nicht erzeugt werden. Diagnosedaten konnten nicht automatisch hochgeladen werden. Diagnosedaten wurden erfolgreich versendet. Die Diagnosedaten wurden erfolgreich erzeugt und übermittelt. Bitte ergänze deinen Problembericht um folgende Kennung: Spiele von {0} konnten nicht importiert werden. Emulierte Spiele konnten von {0} nicht importiert werden. Mit dem ausgewählten Emulatorprofil kann nicht nach Spielen gesucht werden, da es keine Dateinamenerweiterungen oder Plattformen enthält. Playnite konnte nicht gestartet werden. Bitte beende alle anderen Instanzen von Playnite und versuche es erneut. Design »{0}« mit Farbprofil »{1}« konnte nicht angewendet werden {2} Link kann nicht geöffnet werden, da die URL ein ungültiges Format besitzt. Anwendung konnte nicht gestartet werden. Fehler beim Initialisieren der WebView-Komponente. Playnite kann den Startvorgang nicht fortsetzen. Mehr Informationen unter https://playnite.link/cefstartup Emulatoren können nicht importiert werden aufgrund fehlender oder beschädigter Definitionsdatei. Menüaktion konnte nicht ausgeführt werden. Spieldetails bearbeiten Bild-URL Link hinzufügen ROM hinzufügen Änderungen speichern Feldänderungen auf bearbeitete(s) Spiel(e) anwenden. Aktion hinzufügen Aktion entfernen »Spielen«-Aktion entfernen Spiele hinzufügen Ordner durchsuchen … Installierte ermitteln Durchsuchen … Playnite öffnen Profileinstellungen Spielname darf nicht leer sein. Tracking-Verzeichnis für Spielaktion darf nicht leer sein. Spielname darf vor der Metadatensuche nicht leer sein. Ungültige Spieledaten Bitte gib eine gültige, mit »http://« oder »https://« beginnende URL ein URL auswählen Metadaten konnten nicht heruntergeladen werden: {0} Fehler beim Herunterladen Filter zurücksetzen Privates Konto Öffentliches Konto API-Schlüssel Fehler beim Starten Fehler beim Anwenden des Designs Alle zurücksetzen Wird installiert Wird deinstalliert Wird gestartet Wird ausgeführt Ungültige URL Nichts tun Minimieren Fenster wiederherstellen Fenster nur wiederherstellen, falls von Benutzeroberfläche gestartet Schließen Ändern Erweitert Nie Spielfortschritt Spielfortschritte Nutzerwertung Expertenwertung Communitywertung Spielskripte Anwendungsskripte Skripte Plugins Metadatenquellen Erweiterungen Erweiterungs-ID Skripte neu laden Interaktives PowerShell-SDK Alle Skripte erfolgreich neu geladen. Für die angegebenen Such-/Filterkriterien wurden keine Spiele gefunden Keine Inhalte gefunden Zu Desktopmodus wechseln Playnite beenden Bibliotheken Alle aktualisieren Erstellt von: Version: Aktualisiert: Modul: Bibliothek Statistik Alle Keine Benachrichtigungen Breite Höhe Größe Klein Normal Groß Größer Sehr groß Standard Auswählen Alle auswählen Alles abwählen Erstes Zufällig Nutzerauswahl Mehr laden Transparent Einklappen Ausklappen Alle einklappen Alle ausklappen Andere Designs Emulator-Argumente Eingebaute Argumente Benutzerdefinierte Argumente Weitere Emulator-Argumente Emulator-Argumente aufheben »Spielen«-Aktion Zu importierende Metadaten auswählen Zu importierende Spiele auswählen Metadatensuche Aktualisierung verfügbar Änderungen seit der letzten Aktualisierung Aktualisierung herunterladen und installieren Nach Aktualisierungen suchen Fehler beim Aktualisieren Fehler bei der Suche nach Aktualisierungen. Keine neue Version gefunden. Playnite ist aktuell. Aktualisierung konnte nicht heruntergeladen und installiert werden. Ein Hintergrundprozess wird gerade ausgeführt. Möchtest du ihn abbrechen und mit der Aktualisierung fortfahren? Ein Hintergrundprozess wird gerade ausgeführt. Möchtest du ihn abbrechen und Playnite beenden? Ein Hintergrundprozess wird gerade ausgeführt. Wenn du den Modus wechselst, wird der Prozess abgebrochen. Möchtest du trotzdem wechseln? Für Playnite ist eine Aktualisierung verfügbar Designliste erneut laden Ausgewähltes Design anwenden Dateiänderungen überwachen Design bei Änderung der Quelldatei automatisch anwenden Skriptlaufzeit Vor Spielstart auszuführendes Skript Nach Spielende auszuführendes Skript Nach Spielstart auszuführendes Skript Bei Anwendungsstart ausführen Bei Anwendungsbeendigung ausführen Spiel startet Skript Spiel startete Skript Spiel beendete Skript Globales Skript ausführen Global Gefiltert Aktuell Neu Skript testen Nur ausgewählte Elemente anzeigen. Als Standard speichern Zu Favoriten hinzufügen Aus Favoriten entfernen Dieses Spiel verstecken Nicht mehr verstecken HDR-Unterstützung aktivieren HDR-Unterstützung deaktivieren Bearbeiten … Installationsgröße berechnen Installationsgröße berechnen (alle Spiele) Installationsgröße berechnen (nur fehlenden Daten) Installationsgröße Kategorie zuweisen … Spielfortschritt festlegen Entfernen Spielen Installieren Spieloptionen Details Deinstallieren Installationsort öffnen Desktopverknüpfung erstellen Handbuch öffnen Mehr Von Bibliotheks-Plugin verwaltet Der Spielstart wird vom für dieses Spiel verantwortlichen Bibliotheks-Plugin gesteuert. Auf der angegebenen Seite wurde zum Spiel »{0}« keine relevanten Informationen gefunden. Tipp: Du kannst einen erweiterten Dialog zum Herunterladen von Metadaten verwenden, indem du den Menüpunkt »Bearbeiten« eines einzelnen Spiels anwählst. Nicht verfügbar, solange eine andere Aktion ausgeführt wird. Beschreibungstext muss HTML-Syntax verwenden Spielzeit wird in Sekunden gemessen. Installationsgröße wird in Bytes angegeben Das Erscheinungsdatum muss im Format »Jahr-Monat-Tag« festgelegt werden. Die Werte für Monat und Tag können ausgelassen werden. Werte von 0 bis 100 oder leer für keine Bewertung. Playnite's Entwicklung wird von diesen Schirmherren und Ko-Fi-Mitgliedern unterstützt: Mitwirkende bei Code, Lokalisierung etc: Spielüberwachung beenden? Die Installationsüberwachung wird derzeit ausgeführt. Möchtest du den Vorgang abbrechen und den vorherigen Zustand des Spiels wiederherstellen? Die Ausführungsüberwachung für das Spiel wird derzeit ausgeführt. Möchtest du den Vorgang abbrechen und den vorherigen Zustand des Spiels wiederherstellen? Zeit gespielt Zuletzt gespielt {0}d {1}h {2}min {0}h {1}min {0} Minuten {0} Sekunden Nicht gespielt Desktopmodus wird geöffnet … Vollbildmodus wird geöffnet … Spielebibliothek wird geladen … Installationsgröße wird berechnet … Installationsgröße von {0} wird berechnet … Skriptdatei konnte nicht installiert werden. Skript wurde erfolgreich installiert. Skript installieren Skriptfehler Erweiterungsfunktion konnte nicht ausgeführt werden. Metadatenverzeichnis öffnen Berechnen Errechnet automatisch die Installationsgröße über die Roms, wenn das Spiel welche hat, oder über das Installationsverzeichnis, wenn es gesetzt wurde {0}-Client ist nicht installiert. Der {0}-Client wird nun gestartet. Bitte melde dich an und schließe danach diese Nachricht. Es wird auf die Nutzeranmeldung gewartet. Bitte schließen, sobald du damit fertig bist … Installationsverzeichnis des Spiels nicht gefunden. Ungültige Aktionskonfiguration für dieses Spiel. Problembehebung bei der Kontosynchronisation Problembehebung Eintrag umbenennen Neuen Eintrag hinzufügen Name eingeben Neuen Namen eingeben Weniger als eine Stunde 1 bis 10 Stunden 10 bis 100 Stunden 100 bis 500 Stunden 500 bis 1.000 Stunden Mehr als 1.000 Stunden Playnite muss zum Abschließen der Installation neu gestartet werden. Möchtest du jetzt neu starten? Erweiterung ist nicht ordnungsgemäß verpackt. Design ist nicht ordnungsgemäß verpackt. Erweiterung »{0}« konnte nicht ordnungsgemäß geladen werden. Die Erweiterung »{0}« kann nicht geladen werden, da sie die derzeitige Playnite-Version nicht unterstützt. Design »{0}« konnte nicht ordnungsgemäß geladen werden. Das Design »{0}« kann nicht geladen werden, da es die derzeitige Playnite-Version nicht unterstützt. Erweiterung konnte nicht ordnungsgemäß geladen werden. Design konnte nicht ordnungsgemäß geladen werden. Design/Erweiterung verwendet eine nicht unterstützte API-Version. Installation war erfolgreich. Add-on installieren? Allgemein Das Add-on »{0}« konnte nicht aktualisiert werden. Erweiterung konnte nicht installiert werden. {0} Möchtest du eine neue Erweiterung installieren? {0} Von {1} Version {2} Möchtest du die Erweiterung »{0}« aktualisieren? Installierte Version: {1} Neue Version: {2} Design konnte nicht installiert werden. {0} Möchtest du ein neues Design installieren? {0} Von {1} Version {2} Möchtest du das Design »{0}« aktualisieren? Installierte Version: {1} Neue Version: {2} Du bist im Begriff, Playnite zu verlassen und mit deinem Standard-Webbrowser die folgende Webseite zu öffnen. Möchtest du fortfahren? {0} Die ausgewählten Bilder sind möglicherweise zu groß für optimale Leistung. Das Verwenden sehr großer Bilder führt unter Umständen zu einer schlechteren Reaktionsfähigkeit der Benutzeroberfläche und erhöhtem Speicherbedarf. Empfohlene Maximalauflösungen: Icon: {0} Megapixel Cover: {1} Megapixel Hintergründe: {2} Megapixel Leistungswarnung Nicht erneut zeigen Datei mit Erweiterung {0} ist nicht kompatibel. Inkompatible Dateierweiterung Die ausgewählte Bilddatei ist für optimale Leistung möglicherweise zu groß. Möchtest du das ausgewählte Design wirklich deinstallieren? Die Deinstallation wird beim nächsten Start von Playnite ausgeführt. Integrierte Designs können nicht deinstalliert werden. Dieses Design wird von dieser Playnite-Version nicht unterstützt. Möchtest du die ausgewählte Erweiterung wirklich deinstallieren? Die Deinstallation wird beim nächsten Start von Playnite ausgeführt. Integrierte Erweiterungen können nicht deinstalliert werden. Diese Erweiterung wird von dieser Playnite-Version nicht unterstützt. Installationsverzeichnis Datenverzeichnis Diagnosedaten werden erzeugt … Diagnosedaten werden hochgeladen … Datei importieren … Was ist das? Bist du sicher, dass du das tun möchtest? Gesamtspielzeit Durchschnittl. Spielzeit Höchstspielzeit Gesamtinstallationsgröße Übersicht Seitenleiste In Seitenleiste anzeigen Einstellungen zurücksetzen Alle Anwendungseinstellungen werden auf ihren Standardwert zurückgesetzt, ausgenommen: - Datenbankverzeichnis - Import-Ausschlussliste - Erweiterungseinstellungen, einschließlich Bibliotheksintegrationen Zum Abschließen ist ein Neustart erforderlich. Möchtest du die Einstellungen zurücksetzen? Für Entwickler Externe Erweiterungen Gib den vollständigen Ordnerpfad ein. Errungenschaften Forum Neuigkeiten Shopseite Die Ersteinrichtung ist nicht abgeschlossen. Playnite wird nun im Desktopmodus neu starten, um die Einrichtung abzuschließen. Kürzlich gespielt Favoriten Meistgespielt Alle Es sind Filter aktiv. Es sind weitere Filter aktiv. Suchergebnisse für: Ein Element mit demselben Namen existiert bereits. Auswahl auf aktuellen Filter begrenzen Anderes auswählen Add-ons … Installiert Erweiterungseinstellungen Durchsuchen  Aktualisierungen Aktualisierungen ({0}) Die Verwaltung installierter Erweiterungen und Designs wurden einschließlich ihrer Einstellungen in das neue Menü »Add-ons« verschoben. Alle aktuell installierten Erweiterungen für die Bibliotheksintegration können hier konfiguriert werden. Falls du weitere Integrationen installieren oder deinstallieren möchtest, verwende im Hauptmenü die Option »Add-ons«. Desktop-Designs Vollbild-Designs Wird durchsucht … Das Add-on ist mit dieser Version von Playnite nicht kompatibel. Das Installationspaket des Add-ons konnte nicht heruntergeladen werden. Das Installationsmanifest des Add-ons konnte nicht heruntergeladen werden. Ein Neustart ist erforderlich, um ausstehende Änderungen anzuwenden. Dieses Add-on ist für die Installation vorgemerkt. Installieren Wiederinstallieren Deinstallieren Bereits installiert Keine neuen Aktualisierungen für Add-ons gefunden. Add-ons aktualisieren Änderungsprotokoll ist nicht verfügbar Für Installation vorgemerkt Herunterladen gescheitert Lizenz abgelehnt {0} wird heruntergeladen … Aktualisierungen für Add-ons werden gesucht … Suche nach Programmaktualisierungen... Für mindestens ein Add-on ist eine Aktualisierung verfügbar. Wähle die zu aktualisierenden Elemente aus Instanz für Erweiterungsentwicklung {0} Lizenzvereinbarung Zustimmen Ablehnen »Spielen«-Aktionen der Bibliotheksintegration miteinbeziehen Aktion auswählen Trackingmodus Tracking-Pfad Anfängliche Tracking-Verzögerung Tracking-Frequenz Link Datei Emulator Skript Standard Prozess Ordner Originalprozess Prozessname Protokollmeldungen aufzeichnen Folgende Änderungen überschreiben Daten aller aktuell ausgewählten Spiele! Keiner Einheitlich Nur Elemente Nur Anfang und Ende Scroll-Empfindlichkeit Sanftes Scrollen Animationsgeschwindigkeit Element entfernen? Möchtest du dieses Element wirklich entfernen? Schaltflächen in oberer Leiste anzeigen: Allgemeine Ansichtseinstellungen Gruppierungseinstellungen Sortierungseinstellungen Filtervoreinstellungen Position von Plugin-Elementen Breite der Abschnittstrenner Hauptmenü-Schaltfläche in Seitenleiste verschieben Explorerleiste Zufällige Spieleauswahl Zufällige Spielauswahl Zufälliges Spiel aus der aktuellen Ansicht auswählen Gruppierungs- und Sortiereinstellungen speichern Als Schnellfilter im Vollbildmodus anzeigen Letzte 7 Tage Letzte 31 Tage Letzte 365 Tage Vor mehr als 365 Tagen Konfigurieren Voreinstellung speichern Nach Starten des Spiels minimieren Playnite nach dem Starten des Spiels minimieren. Das Deaktivieren dieser Option kann zu Problemen mit Spielen führen, die beim Start keinen Eingabefokus erhalten! Schriftgröße Schriftgröße klein Unterstützung für Spielcontroller-API aktivieren Unterstützung für Spielcontroller Falls deaktiviert, wird Playnite keinerlei Eingaben von Spielcontrollern akzeptieren. Deaktivieren, falls du Tools verwendest, die Eingaben von Spielcontrollern in Maus-/Tastatureingaben übersetzen und du die Eingaben in Playnite doppelt erhälst. Elemente im Hauptmenü anzeigen: Invertierte Schaltflächenbindung der X/A-Hauptansicht Vertauscht Schaltflächenbindungen zum Starten eines Spiels und zum Anzeigen der Spieledetailsseite in der Hauptansicht. Bindung der Bestätigungs-/Abbruchtaste tauschen Invertiert die A/B-Tastenbindungen für Bestätigung und Abbruch. Nur Primärcontroller Falls aktiviert, werden nur Eingaben des Primärcontrollers akzeptiert. Guide-Taste fokussiert Playnite Lautstärke Oberfläche Lautstärke Hintergrund Im Hintergrund stummschalten Audio-Schnittstelle konnte nicht initialisiert werden. Ausgabe-API Für die Audioausgabe verwendete API. Ändern bei Problemen mit der Tonausgabe. Allgemein Anzeige Audio Layout Menüs Eingabe {0} startet … {0} läuft … Umsch Leertaste Bild-Render-Scaler Alternativ Ausgewogen Qualität Qualität: Beste Bildqualität, langsam, hoher Speicherverbrauch. Ausgewogen: Gute Bildqualität, schnell, geringer Speicherverbrauch. Alternativ: Bessere Bildqualität, mittlere Geschwindigkeit, geringer Speicherverbrauch. Datei auswählen … Ordner auswählen … Skript zum Starten Bitte beachte, dass sowohl Erweiterungen als auch Designs die Leistung, Stabilität und Sicherheit von Playnite stark beeinflussen können. Falls nach dem Installieren von Designs oder Erweiterungen Probleme auftreten, versuche zuerst, diese zu deaktivieren/deinstallieren, um zu überprüfen, ob sie die Ursache des Problems sind. Bei Start auswählen Bei Start auswählen Integrierte Profile Integriertes Profil Personalisierte Profile Personalisiertes Profil Verwaltet von einem integrierten Skript Spezifikation Emulator Spezifikation Plattform Spezifikation Region Vor Start des Emulators ausführen Nach Start des Emulators ausführen Nach Verlassen des Emulators ausführen Ausführebare Programmdatei des Emulators nicht gefunden. Spezifikation des Emulators nicht gefunden. Skript zum Starten des Emulators nicht gefunden. Als separate Spiele aufteilen Zu einem Spiel zusammenfassen Plattform festlegen Region festlegen Ordner durchsuchen  Scan-Konfigurationen Ausschlussmuster für Prüfsummen-Scan Mit angebenen Muster(n) übereinstimmende Dateien werden nicht für die Prüfsumme durchsucht und werden über den Dateinamen zugeordnet. Siehe Emulator-Hilfeseite für weitere Informationen. Mit Emulator durchsuchen Der Name muss festgelegt sein, um eine neue Konfiguration zu speichern. Emulator oder Emulatorprofil ist nicht festgelegt. Zu durchsuchendes Verzeichnis ist nicht angegeben oder existiert nicht. Scan-Konfiguration ist nicht richtig festgelegt. In automatischen Massenscan miteinbeziehen Ordner konnte nicht nach Emulatoren durchsucht werden. Ordner konnte(n) nicht nach emulierten Spielen durchsucht werden. Importierte ausblenden Zu importierende Profile: Konfigurationen für Auto-Scan Als Konfiguration für Auto-Scan speichern Speichert die Konfiguration während einer Bibliotheksaktualisierung für eine spätere Verwendung. Gespeicherte Konfigurationen können über »Emulatoren konfigurieren« verwaltet werden. Mit relativen Pfaden importieren Falls möglich, beim Importieren von Spieledateien Pfade relativ zum Installationsordner von Playnite oder des Emulators verwenden. Unterordner durchsuchen Archive durchsuchen Zusammengehörige Dateien zusammenführen Zusammengehörige Spieldateien, wie einzelne Spieldiscs, unter einem Spieleintrag zusammenfassen. Scanner hinzufügen Gespeicherten Scanner hinzufügen Suche starten Füge Scan-Konfigurationen mit Emulatoren hinzu, um bestimmte Ordner zu durchsuchen. Achte vor Importieren der Spiele darauf, dass die Emulatoren korrekt konfiguriert wurden (über Bibliothek → Emulatoren konfigurieren). Neu hinzugefügten Spielen zugeordneter Standardstatus Erstmalig gespielten Spielen zugeordneter Status Initialisierung der PowerShell-Skript-Laufzeitumgebung ist fehlgeschlagen. Falls du Windows 7 nutzt, versuche PowerShell 5.1 (erneut) zu installieren, um das Problem zu beheben. Filtervoreinstellung mit angegebenem Namen existiert bereits. Voreinstellung mit neuen Einstellungen aktualisieren? Fülle automatisch fehlende Sortiernamen für Massen-hinzugefügte oder bearbeitete Spiele Wenn du ein Spiel bearbeitest, füge Spiele über ein Bibliotheksupdate, einen Emulator Ordner oder einen normalen Ordner-Scan hinzu füllen Sie automatisch das Feld "Sortiername" mit einer besser sortierbaren Darstellung des Spielnamens aus. Zum Beispiel "The Witcher 3" bekommt einen Sortiernamen von "Witcher 03". Dies wird niemals einen Sortiernamen angeben, der sich nicht vom Spielnamen unterscheidet, und es wird nur automatisch die Sortierung leerer Namen aktualisieren. Diese Wörter werden vom Anfang des automatisch ausgefüllten Sortiernamens entfernt: Verwende diese Option, um für Sortierzwecke Wörter am Anfang einer Zeichenkette zu ignorieren. Standard ist »The«, »An« und »A«. Sortiernamen ausfüllen für Spiele ohne Sortiernamen Sortierung Sortiernamen werden mit Werten gefüllt … Auf deinem System wurde der Nahimic-Dienst erkannt. Dieser Dienst ist dafür bekannt, Rendering-Probleme in Playnite (und anderen Anwendungen) zu verursachen. Falls du in Playnite Grafikfehler oder andere Darstellungsprobleme feststellst, empfehlen wir, den Nahimic-Dienst zu deaktivieren oder vollständig zu deinstallieren. Mehr Informationen unter https://playnite.link/nahimicsucks Playnite wird mit erhöhten Berechtigungen ausgeführt (als Administrator). Dies wird nicht empfohlen, da dann auch alle installierten Erweiterungen und über Playnite gestarteten Spiele/Anwendungen mit erhöhten Berechtigungen ausgeführt werden. Mehr Informationen unter https://playnite.link/adminfaq Warnung anzeigen, falls Playnite mit erhöhten Berechtigungen ausgeführt wird Beim Berechnen der Installationsgröße die tatsächliche Größe auf dem Laufwerk ermitteln Wenn aktiviert, werden die Scans langsamer und die tatsächliche Größe ermittelt, die die Dateien auf dem Laufwerk einnehmen. Wenn deaktiviert, werden die Scans schneller und die Größe der Dateien selbst wird verwendet. Folgende Add-on(s) wurden als potentiell problematisch gemeldet, entweder wegen starker Beeinträchtigung der Stabilität/Leistung oder wegen Sicherheitsbedenken. Wir empfehlen nachdrücklich diese zu deinstallieren: {0} Online-Dateien von Scan ausschließen Dateien aus Cloud-Speichern werden nicht durchsucht und importiert, sofern sie nicht lokal verfügbar sind. Nur Unterstützung für: Google Drive, DropBox, OneDrive Mit vereinfachter Methode ohne Dateiinhalt durchsuchen Dateien werden importiert, aber mit einer ungenaueren Methode, die nicht voraussetzt, dass der Dateiinhalt heruntergeladen wird und lokal verfügbar ist. Auf alle anwenden Installationsstatus übergehen Falls aktiviert, ignoriert Playnite den Installationszustand (einschließlich des Installationsverzeichnisses), der von dem Integrations-Plugin festgelegt wurde, das dieses Spiel importiert. Diese Option funktioniert möglicherweise nicht vollständig mit Plugins, die eine bestimmte Methode zum Importieren von Spielen verwenden, es sei denn, diese berücksichtigen auch diese Option. Nur manuell Täglich Wöchentlich Bei jedem Start Nach Programmaktualisierungen suchen Nach Add-on-Aktualisierungen suchen Bibliotheken aktualisieren Emulationsordner durchsuchen Versteckte Spiele einbeziehen Felder bearbeiten Alle aus-/abwählen Öffnen Aktivieren Zuweisen Tippen, um nach Spielen zu suchen … [F1] für Hilfe Die Eingabe von »#« als erstes Zeichen zeigt eine Liste der verfügbaren Befehle an. Die Eingabe von »/« als erstes Zeichen zeigt eine Liste der verfügbaren Suchanbieter/Plugins an. Die Eingabe des Such-Schlüsselworts gefolgt von einem Leerzeichen wechselt sofort zu dieser Suche. TAB: Aktion wechseln EINGABETASTE: Ausgewählte Aktion aktivieren UMSCHALT-EINGABETASTE: Elementmenü öffnen Nicht installierte Spiele einbeziehen Versteckte Spiele einbeziehen Nicht installierte Spiele eingeschlossen Nicht installierte Spiele ausgeschlossen Versteckte Spiele eingeschlossen Versteckte Spiele ausgeschlossen Spielen oder installieren Details aufrufen Spielmenü Spiel bearbeiten Suche öffnen Suchfeld Such-Schaltfläche Primäre Spielaktion Sekundäre Spielaktion STRG-F öffnet die globale Suche, anstatt auf das Suchfeld zu fokussieren Spielefiltereinstellungen zwischen Suchläufen speichern Suchanbieter Standard-Schlüsselwort Personalisiertes Schlüsselwort Systemweiter Tastaturkurzbefehl Playnite-Suche Erweiterungseinstellungen Ausschlüsse Ausgeschlossene Dateien relativ zum Scan-Ordner Ausgeschlossene Ordner relativ zum Scan-Ordner Datei zur Ausschlussliste hinzufügen Ordner zur Ausschlussliste hinzufügen Ausschlüsse können lediglich zu gespeicherten Scannerkonfigurationen hinzugefügt werden. Ausschlüsse wurden zum »{0}«-Scanner hinzugefügt. Plattform übergehen Falls aktiviert, wird der Scanner diese Plattform allen Spielen zuweisen und jegliche automatisch erkannten Plattformen übergehen. Befehle in Standardsuche einbeziehen Falls deaktiviert, werden Befehle nicht in der Standardsuche berücksichtigt, es sei denn, es wird das Zeichen »#« vorangestellt. Unscharfe Suche im Namensfilter verwenden Falls aktiviert, wird der Namensfilter auf dieselbe Weise mit Spielenamen abgeglichen, wie in der globalen Suche. Strenge Übereinstimmung kann im Einzelfall erzwungen werden, indem dem Filter das Zeichen »!« vorangestellt wird. Für Spieleergebnisse anzuzeigende Felder: Versteckter Status Datensicherung wurde abgebrochen. Datensicherung gescheitert. Datensicherungsfehler Datensicherung in Arbeit … Daten werden aus Sicherung wiederhergestellt … Daten konnten aus Sicherung nicht wiederhergestellt werden. Einstellungen Spielebibliothek Medien aus Spielebibliothek Installierte Erweiterungen Erweiterungsdaten Installierte Designs Wählen Sie die Daten aus, die aus der angegebenen Sicherungsdatei wiederhergestellt werden sollen. Playnite wird automatisch neu starten, um die Sicherungswiederherstellung zu starten. Elemente zur Datensicherung auswählen. Anwendungseinstellungen und Spielbibliotheksdaten sind standardmäßig enthalten. Playnite wird automatisch neu gestartet, um die Sicherung zu starten. Automatische Datensicherung Häufigkeit der automatischen Datensicherung Sicherungsordner Rotierende Sicherungen Zusätzliche Daten einbeziehen: Bei aktivierter automatischer Datensicherung muss ein Sicherungsordner festgelegt sein. Nur Benachrichtigungen für Patch-Änderungen anzeigen Wenn aktiviert, werden nur Aktualisierungen für derzeit installierte Hauptversionen zu einer Updatebenachrichtigung führen. Neue Hauptversionen werden nicht zu einer Updatebenachrichtigung führen. Relative Datumsangabe für die letzte Woche verwenden Relative Datumsangabe ("Heute", "Gestern", etc.) für Daten werden, die nicht mehr als eine Woche zurückliegen. Das angegebene Datumsformat wird für alle anderen Daten verwendet. Web-Bildsuche Suchbegriff für Icon Bilder Suchzeichenfolge für Coverbilder Suchzeichenfolge für Hintergrundbilder Informationen zu Add-ons werden abgerufen … Keine Metadatenquelle verfügbar Einstellungen für »Spielen«-Aktion Scannereinstellungen verwenden Profil beim Start auswählen Emulator beim Start auswählen Automatisch Immer an Immer aus Unterstützung für Barrierefreiheit (Bildschirmleser) Anwendungsmenü Spielmenü Programmordner Benutzerdatenverzeichnis Die Bibliotheksdatei wurde beschädigt, Playnite wird nun heruntergefahren. Melde das Problem auf der GitHub-Seite von Playnite mit einer Anfrage, die Beschädigung deiner Dateien zu beheben. Möchtest du die vorgenommenen Änderungen speichern? Portable Installation Keine Controller erkannt ================================================ FILE: source/Playnite/Localization/el_GR.xaml ================================================  Greek Γλώσσα Έξοδος Φίλτρο Ενεργό Φίλτρο Απενεργοποιημένο Επιπρόσθετα φίλτρα Φίλτρα Φιλτράρισμα Μη Έγκυρα Δεδομένα Αποθήκευση Αλλαγών? Ιστοσελίδα www.playnite.link Πηγαίος Κώδικας στο GitHub Δημιουργία diag. πακέτου Αποστολή διαγνωστικών δεδομένων Σχετικά με το Playnite Φτιάχτηκε από τον Josef Němec Αντιστοίχιση κατηγορίας Ορισμός Κατηγοριών Προσθήκη Κατηγορίας Επιλεγμένο - Αντιστοίχιση κατηγορίας Μη επιλεγμένο - Κατάργηση κατηγορίας Απροσδιόριστο - Καμία αλλαγή (σε επεξεργασία πολλών παιχνιδιών) Χωρίς Κατηγορία Χωρίς Πλατφόρμα Ουπς! Κάτι πήγε στραβά... Προέκυψε μη ανακτήσιμο σφάλμα. Αν επιθυμείς, μπορείς να μας βοηθήσεις να διορθώσουμε αυτό το πρόβλημα, παρακαλούμε περιέγραψε συνοπτικά τις ενέργειες σου πριν το σφάλμα, και μετά στείλε διαγνοστικές πληροφορίες. Αν είσαι συνδεδεμένος, το πακέτο θα ανεβεί στον διακοσμιτή του Playnite για ανάλυση. Εναλλακτικά, μπορείς να πατήσεις στο 'Αναφορά Σφάλματος' για την δημιουργία ενός GitHub issue και να αναφέρεις το σφάλμα χειροκίνητα. Ευχαριστούμε για την βοήθεια. Η επέκταση "{0}" προκάλεσε ένα μη ανακτήσιμο σφάλμα. Συνιστούμε να αποθηκεύσετε το αρχείο καταγραφής και να αναφέρετε το ζήτημα στον προγραμματιστή της επέκτασης. Εάν το ζήτημα συνεχίσει να εμφανίζεται, απενεργοποιήστε την επέκταση. Η επέκταση "{0}" προκάλεσε ένα μη ανακτήσιμο σφάλμα. Συνιστούμε να αναφέρετε το ζήτημα στον προγραμματιστή της επέκτασης. Εάν το ζήτημα συνεχίσει να εμφανίζεται, απενεργοποιήστε την επέκταση. Μια άγνωστη επέκταση ή ένα θέμα προκάλεσε ένα μη ανακτήσιμο σφάλμα. Συνιστούμε να απενεργοποιήσετε τα πρόσθετα τρίτων, να απομονώσετε το προβληματικό πρόσθετο και να αναφέρετε το θέμα στον προγραμματιστή του. Παρουσιάστηκε μη ανακτήσιμο σφάλμα. Αν θέλετε να μας βοηθήσετε να διορθώσουμε αυτό το πρόβλημα, παρακαλούμε να στείλετε διαγνωστικές πληροφορίες. Σας ευχαριστούμε. Απενεργοποίηση επεκτάσεων Αποθήκευση αρχείου καταγραφής Αποστολή διαγράμματος πληροφοριών Αναφορά Σφάλματος Επανεκκίνηση Playnite Επανεκκίνηση σε ασφαλή λειτουργία Απενεργοποίηση όλων των επεκτάσεων τρίτων και χρήση προεπιλεγμένου θέματος. Έξοδος απ'το Playnite Ενέργειες που πάρθηκαν πριν το σφάλμα (στα Αγγλικά) Διαχείριση Συλλογής Κατάργηση Παιχνιδιών? Δεν είναι δυνατή η αφαίρεση - Το παιχνίδι ή το πρόγραμμα εγκατάστασης εκτελείται. Δεν είναι δυνατή η απεγκατάσταση - Το παιχνίδι εκτελείται. Είστε βέβαιοι ότι θέλετε να αφαιρέσετε το {0}; Είστε βέβαιοι ότι θέλετε να καταργήσετε {0} παιχνίδια; Είστε βέβαιοι ότι θέλετε να αφαιρέσετε {0}? Επιλέγοντας τη ρύθμιση "προσθήκη στη λίστα αποκλεισμών" αποτρέπετε το παιχνίδι από το να προστεθεί ξανά την επόμενη φορά που η βιβλιοθήκη θα ενημερωθεί. Είστε βέβαιοι ότι θέλετε να αφαιρέσετε {0} παιχνίδια; Επιλέγοντας τη ρύθμιση "προσθήκη στη λίστα αποκλεισμών" αποτρέπετε το παιχνίδι από το να προστεθεί ξανά την επόμενη φορά που η βιβλιοθήκη θα ενημερωθεί. Είστε βέβαιοι ότι θέλετε να αφαιρέσετε {0} προσθήκες που δεν χρησιμοποιούνται αυτή την στιγμή; Δεν βρέθηκαν αχρησιμοποίητα πεδία. Ναι (προσθήκη στη λίστα εξαιρέσεων) Υπάρχουν αλλαγές σε αυτή την ενότητα που δεν έχουν αποθηκευτεί Η βιβλιοθήκη παιχνιδιών ενημερώνεται… Η ενημέρωση της βάσης δεδομένων απέτυχε. Η συλλογή παιχνιδιών δεν μπορεί να ενημερωθεί. Χρειάζονται {0} MB ελεύθερου χώρου. Σφάλμα παιχνιδιού Δεν είναι δυνατή η έναρξη παιχνιδιού. '{0}' δεν βρέθηκε στη βάση δεδομένων. Δεν είναι δυνατή η έναρξη του παιχνιδιού: {0} Δεν είναι δυνατή η έναρξη της εντολής: {0} Δεν είναι δυνατό να ανοίξει η θέση του παιχνιδιού: {0} Αδυναμία εντοπισμού μεγέθους εγκατάστασης παιχνιδιού: {0} Σφάλμα σάρωσης μεγέθους εγκατάστασης Υπήρξαν {0} σφάλματα κατά τη σάρωση μεγέθους εγκατάστασης Δεν ήταν δυνατή η δημιουργία συντόμευσης: {0} Απέτυχε το άνοιγμα του εγχειριδίου: {0} Δεν είναι δυνατή η εγκατάσταση του παιχνιδιού: {0} Δεν είναι δυνατή η απεγκατάσταση του παιχνιδιού: {0} Εντοπίστηκαν εσφαλμένες ρυθμίσεις εκκίνησης παιχνιδιού. Όταν χρησιμοποιείτε ενέργειες εξομοιωτή, βεβαιωθείτε ότι οι ορισμοί της πλατφόρμας ταιριάζουν με εκείνες των ρυθμίσεων του παιχνιδιού και του εξομοιωτή. Η υλοποίηση της εγκατάστασης δεν μπορεί να πραγματοποιηθεί. Το πρόσθετο βιβλιοθήκης που αφορά αυτό το παιχνίδι δεν έχει εγκατασταθεί ή είναι απενεργοποιημένο. Η λήψη των επίσημων μεταδεδομένων δεν είναι διαθέσιμη. Δεν έχει επιλεχθεί παιχνίδι. Η εκτέλεση σεναρίου του παιχνιδιού απέτυχε. Η εκτέλεση σεναρίου της εφαρμογής απέτυχε. Η καθολική εκτέλεση σεναρίου απέτυχε. Η εκτέλεση σεναρίου του εξομοιωτή απέτυχε. Η εκτέλεση σεναρίου παιξίματος απέτυχε. Το PowerShell 3.0 ή νεότερη έκδοση δεν είναι εγκατεστημένη. Ο τρόπος εκκίνησης παιχνιδιού δεν μπορεί να καθοριστεί. Ενεργοποιημένο Απενεργοποιημένο Αφαίρεση Αφαίρεση αχρησιμοποίητων Μετονομασία Αντιγραφή Προσθήκη Προεπιλεγμένο εικονίδιο Προκαθορισμένη Εικόνα Εξώφυλλου Προεπιλεγμένη Εικόνα Παρασκηνίου Τερματισμός Επόμενο Πίσω ΟΛΟΚΛΗΡΩΣΗ ΠΙΣΩ ΚΑΘΑΡΙΣΜΟΣ Εκκαθάριση Απόρριψη Απόρριψη Όλων Εισαγωγή Όνομα Δημιουργός Πρόσθετο Σειρά Έκδοση Τελευταία Συνεδρία Παίχτηκαν Περισσότερο Μετρητής Παιχνιδιού Μέγεθος Εγκατάστασης Φάκελος Σημειώσεις Προστέθηκε Ημερομηνία Προσθήκης Τροποποιήθηκε Ημερομηνία Τροποποίησης Ιστότοπος Διαδρομή αρχείου ΟΚ Αποθήκευση Κλείσιμο Ακύρωση Επιβεβαίωση Επαναφορά Ναι Όχι Καλώς Ήρθατε Τοπικός Χρήστης Γενικά Πολυμέσα Σύνδεσμοι Εγκατάσταση Ενέργειες Γίνεται λήψη... Γίνεται λήψη πολυμέσων... Φόρτωση… Τύπος Προφίλ Προφίλ Αφαίρεση Λήψη Αναζήτηση Ανάλυση: Όλες Μεγέθυνση Προβολή Λίστας Εξώφυλλα Προβολή Πλακιδίων Λεπτομερής Προβολή Προσαρμοσμένο URL Ιδιαίτερες ευχαριστίες Άδεια Συνεισφέροντες Γίνεται έξοδος από το Playnite… Σήμερα Χθες Δευτέρα Τρίτη Τετάρτη Πέμπτη Παρασκευή Σάββατο Κυριακή Προηγούμενη Eβδομάδα Προηγούμενος Mήνας Το Προηγούμενο Έτος Περισσότερο από ένα χρόνο πριν 0 έως 100MB 100MB έως 1GB 1GB έως 5GB 5GB έως 10GB 10GB έως 20GB 20GB έως 40GB 40GB έως 100GB 100GB ή περισσότερα Η εισαγωγή ολοκληρώθηκε με επιτυχία. Όλα τα παιχνίδια ID Παιχνιδιού ID Βάσης Δεδομένων Προεπιλογές Στήλη Στήλες Σειρά Σειρές Αδυναμία λήψης εικονιδίου της Ενέργειας Αναπαραγωγής. Δεν υπάρχει καμία ενέργεια στον τύπο "αρχείο". Λήψη μόνο των μεταδεδομένων που λείπουν Η ενεργοποίηση αυτής της επιλογής θα παρακάμψει τη λήψη μεταδεδομένων στα πεδία δεδομένων όπου υπάρχουν ήδη πληροφορίες. Επιλογή παιχνιδιών Επιλέξτε ποια παιχνίδια θα ενημερωθούν με νέα μεταδεδομένα: Όλα τα παιχνίδια της βάσης δεδομένων Όλα τα παιχνίδια σε φίλτρο αυτή τη στιγμή. Μόνο τα επιλεγμένα παιχνίδια Δεν έχουν επιλεχθεί πεδία μεταδεδομένων Δεν έχουν επιλεχθεί πεδία μεταδεδομένων για κατέβασμα. Παρακαλώ επιλέξτε τουλάχιστον ένα πεδίο και ενεργοποιήστε έστω έναν πάροχο μεταδεδομένων Επίσημο Κατάστημα IGDB Επιλέξτε ποια πεδία πρέπει να συμπληρωθούν αυτόματα από το Playnite και ποιες πηγές θα πρέπει να χρησιμοποιηθούν για την απόκτηση των δεδομένων. Κάντε κλικ στο παραπάνω λογότυπο για να συνεισφέρετε στην ενημέρωση της βάσης δεδομένων του igdb.com και τη βελτίωση των πληροφοριών των χρηστών του Playnite. Λήψη metadata... Γίνεται εισαγωγή εγκατεστημένων παιχνιδιών.... Εισαγωγή {0} παιχνιδιών… Εισαγωγή παιχνιδιών εξομοιωτή από {0}… Γίνεται λήψη ενημερώσεων βιβλιοθήκης... Γίνεται σάρωση του μεγέθους των παιχνιδιών στη βιβλιοθήκη... Γίνεται σάρωση του μεγέθους εισαγόμενων παιχνιδιών… Η ενημέρωση της βιβλιοθήκης ολοκληρώθηκε Απελευθέρωση πόρων... Διαμόρφωση Ρυθμίσεις... Πλατφόρμες και εξομοιωτές Επεξεργασία Εξομοιωτών… Διαχειριστής Βιβλιοθήκης… Εργαλεία Λήψη Μεταδεδομένων Εργαλεία λογισμικού… Επεξεργασία Ενσωματώσεων... Άνοιγμα 3rd Party Client Προγράμματα τρίτων Ενημέρωση Βιβλιοθήκης Παιχνιδιών Ακύρωση Ενημέρωσης Βιβλιοθήκης Ενημέρωση Φακέλων Εξομοιωτή Προσθήκη Παιχνιδιού Χειροκίνητα... Αυτόματη Σάρωση... Παιχνίδι Εξομοίωσης... Εφαρμογή Microsoft Store... Σχετικά με το Playnite Αποστολή Σχολίων Μεταφορά σε Λειτουργία Πλήρους Οθόνης Σύνδεσμοι Βοήθεια Υποστήριξη στο Patreon Υποστήριξη στο Ko-fi Εγχειρίδιο χρήστη Οδηγίες SDK Επανεκκίνηση του Συστήματος Απενεργοποίηση Συστήματος Αναστολή Λειτουργίας Αδρανοποίηση Κλείδωμα Συστήματος Αποσύνδεση Χρήστη Επιλογή Τυχαίου Παιχνιδιού Πεδία παιχνιδιού που θα εμφανίζονται στον πίνακα λεπτομερειών: Διάστιχο αντικειμένων Σχεδίαση παρασκηνίου των στοιχείων πλακιδίων Πλάτος ορίου περιγράμματος πλακιδίων Το εικονίδιο παιχνιδιού δεν έχει πηγή Το εξώφυλλο παιχνιδιού δεν έχει πηγή Το φόντο παιχνιδιού δεν έχει πηγή Κάθετη απόσταση των λεπτομερειών παιχνιδιού Θέση λεπτομερειών Προβολής πλακιδίων Θέση λίστας παιχνιδιών στην Προβολή παιχνιδιών Εμφάνιση διαχωριστικού μεταξύ των πινάκων Μέγεθος εξωφύλλου παιχνιδιού Μέγεθος εικονιδίου της Λίστας παιχνιδιών Γραμματοσειρά εφαρμογής Μονοδιαστηματική γραμματοσειρά Θέση Πίνακα Φίλτρων Θέση Πίνακα Εξερεύνησης Αποτύπωση εικαστικού εξώφυλλων Αναλογία διαστάσεων Οι παρακάτω επιλογές επηρεάζουν την αποτύπωση των πλακιδίων και στη λειτουργία πλήρους οθόνης! Μορφή γεμίσματος Κουτί από DVD Epic Games Store GOG Galaxy 2.0 IGDB Τετράγωνο Κάδρο Steam Εξώφυλλο Steam Twitch * Απαιτείται επανεκκίνηση για επιβεβαίωση Ρυθμίσεις Γενικά Άνω Εργαλειοθήκη Εμφάνιση Λεπτομέρειες Παιχνιδιού Διάταξη Για Προχωρημένους Πλήρης Οθόνη Εισαγωγή Απόδοση Μεταδεδομένα Ενημέρωση... Αναζήτηση Αντίγραφο Ασφαλείας Αντίγραφο Ασφαλείας Δεδομένων Βιβλιοθήκης Επαναφορά Δεδομένων Αντιγράφων Ασφαλείας Αυτόματη εισαγωγή στη βιβλιοθήκη Μη έγκυρη θέση αρχείου βάσης δεδομένων, πρέπει να οριστεί κατάλληλη διαδρομή αρχείου. Το όνομα λογαριασμού δεν μπορεί να είναι κενό. Λήψη μεταδεδομένων μετά την εισαγωγή παιχνιδιών Εκκίνηση του Playnite σε ελαχιστοποίηση Εκκίνηση του Playnite κατά την εκκίνηση του υπολογιστή σας Εκκίνηση ως κλειστό στη γραμμή ειδοποιήσεων Η εντολή έναρξης του Playnite κατά την εκκίνηση του υπολογιστή δεν καταχωρήθηκε. Εκκίνηση σε Λειτουργία Πλήρους Οθόνης Ασύγχρονη φόρτωση εικόνων Βελτιώνει την ομαλότητα κύλισης στις λίστες παιχνιδιών μειώνοντας ωστόσο τους χρόνους φόρτωσης των εικόνων. Εμφάνιση του ονόματος παιχνιδιού όταν λείπει το εξώφυλλο Εμφάνιση ονομάτων παιχνιδιών στην Προβολή πλακιδίων Σκίαση των μη εγκατεστημένων παιχνιδιών Εμφάνιση εικονιδίων παιχνιδιού στην προβολή λίστας Εμφάνιση του αριθμού αντικειμένων στις περιγραφές της ομάδας Εμφάνιση μόνο των καθορισμένων πεδίων στα φίλτρα και στον πίνακα εξερεύνησης Απενεργοποίηση της επιτάχυνσης υλικού Χρησιμοποιήστε όταν αντιμετωπίζετε προβλήματα τραυλισμού της οθόνης ή παρόμοια προβλήματα του UI Εμφάνιση των κρυφών παιχνιδιών στις λίστες γρήγορης εκκίνησης Επηρεάζει τη Λίστα Πρόσφατων και τη λίστα της γραμμής ειδοποιήσεων. Αριθμός των αντικειμένων γρήγορης εκκίνησης Χρήση της εικόνας παρασκηνίου ως εικόνα παραθύρου Θόλωμα παρασκηνίου Υψηλή Ποιότητα Σκοτεινή εικόνα παρασκηνίου Εμφάνιση στην Προβολή Πλακιδίων Θέμα Προφίλ Θέματος Θέμα Πλήρους Οθόνης Προφίλ θέματος πλήρους οθόνης Τοποθεσία βάσης δεδομένων Κατάσταση σύνδεσης: Ρυθμίσεις Playnite Καθαρισμός web cache Μπορεί να επιλύσει προβλήματα που συναντώνται όταν συνδέονται λογαριασμοί. Εμφάνιση του εικονιδίου της γραμμής ειδοποιήσεων Ελαχιστοποίηση Playnite στη γραμμή ειδοποιήσεων Ελαχιστοποίηση Playnite στη γραμμή ειδοποιήσεων όταν το παράθυρο της εφαρμογής είναι κλειστό Κατά την έναρξη του παιχνιδιού: Μετά το κλείσιμο του παιχνιδιού: Διαμόρφωση Χρόνου Παιξίματος ώστε να εμφανίζει τις μέρες παιξίματος Μορφή ημερομηνίας: Αυτό θα σας αποσυνδέσει από όλες τις συνδεδεμένες υπηρεσίες. Απαιτείται επανεκκίνηση της εφαρμογής, θέλετε να προχωρήσετε? Καθαρισμός Cache? Απαιτείται επανεκκίνηση του Playnite για την εφαρμογή νέου θέματος Λήψη περισσότερων θεμάτων Δημιουργία νέου θέματος Λήψη περισσότερων επεκτάσεων Δημιουργία νέας επέκτασης Βοηθήστε μας να μεταφράσουμε το Playnite Απαιτείται επανεκκίνηση του Playnite για να ισχύσουν οι αλλαγές. Επανεκκίνηση τώρα; Η επανεκκίνηση θα ακυρώσει κάθε ενεργή εργασία (λήψεις) που βρίσκεται σε εξέλιξη. Επανεκκίνηση Playnite; Το Playnite δεν μπορεί να μετακινήσει αυτόματα τα αρχεία βιβλιοθήκης. Πρέπει να μετακινήσετε/αντιγράψετε χειροκίνητα τα αρχεία πριν αλλάξετε τη τοποθεσία. Αν δεν υπάρχει βιβλιοθήκη στην τοποθεσία προορισμού, θα δημιουργηθεί μια νέα. Η νέα θέση της βάσης δεδομένων δεν θα χρησιμοποιηθεί μέχρι να γίνει επανεκκίνηση του Playnite Ο χρόνος παιχνιδιού δε θα καταγράφεται εάν έχει οριστεί η εντολή "Κλείσιμο". Αριθμός σειρών Αριθμός στηλών Αριθμός γραμμών λεπτομερής προβολής Εμφάνιση εικόνας παρασκηνίου στην Κύρια Οθόνη Δεν ισχύει αναδρομικά στα υπάρχοντα παιχνίδια χωρίς την εκ νέου λήψη των μεταδεδομένων. Εισαγωγή χρόνου παιχνιδιού στη βιβλιοθήκη: Ρυθμίζει την συχνότητα με την οποία το Playnite μπορεί να εισάγει τον χρόνο παιχνιδιού που αναφέρεται στα πρόσθετα βιβλιοθήκης, από την βάση δεδομένων του Playnite. Η υποστήριξη από τα πρόσθετα βιβλιοθήκης, που είναι υπεύθυνα για τον χειρισμό των παιχνιδιών, είναι αναγκαία για την χρήση αυτού του χαρακτηριστικού. Πάντα: Εισάγει τον χρόνο παιχνιδιού τόσο για τα νέα, όσο και για τα ήδη υπάρχοντα παιχνιδια στην βάση δεδομένων του Playnite. Μόνο για τα νεοεισαχθέντα παιχνίδια: Εισάγει τον χρόνο παιχνιδιού μόνο για τις νέες εισαγωγές παιχνιδιών. Ποτέ: Δεν εισάγεται κανένας χρόνος παιχνιδιού. Πάντα Μόνο για τα νεοεισαχθέντα παιχνίδια Ποτέ Ενεργοποίηση υποστήριξης χειριστηρίου στη Λειτουργία Επιφάνειας Εργασίας Το Κουμπί Οδηγού ανοίγει τη Λειτουργία Πλήρους Οθόνης Ρυθμίσεις αυτόματης λήψης μεταδεδομένων για νεοεισαχθέντα παιχνίδια. Στοχευμένη Προβολή Χρήση πάντοτε της κύριας οθόνης Εμφάνιση Πλακιδίων Παιχνιδιών Εμφάνιση Κατάστασης Μπαταρίας Εμφάνιση Ποσοστού Μπαταρίας Εμφάνιση Ρολογιού Απόκρυψη του δείκτη ποντικιού Εμφάνιση των εγκατεστημένων μόνο με τα Γρήγορα Φίλτρα Κουμπί Υπενθυμίσεων Διάταξη Οριζόντια Ολίσθηση Επιλέξτε μία από τις υποενότητες Δεν υπάρχουν διαθέσιμες ρυθμίσεις Η φόρτωση των ρυθμίσεων απέτυχε Τα σενάρια αυτά εκτελούνται για κάθε παιχνίδι στη βιβλιοθήκη. Τα μεμονωμένα σενάρια μπορούν να αντιστοιχιστούν σε κάθε παιχνίδι ξεχωριστά κατά την επεξεργασία των λεπτομερειών του παιχνιδιού. Δημιουργία έμψυχων μεταβάσεων εικόνων παρασκηνίου Μεγέθη γραμματοσειρών Αυτόματα Με αλλοίωση Κλίμακα του γκρι Καθαρού Τύπου Ιδανικό Προβολή Λειτουργία μορφοποίησης κειμένου Λειτουργία απόδοσης κειμένου Η απόδοση κειμένου και οι μέθοδοι διαμόρφωσης δε χρησιμοποιούνται αυτή τη στιγμή για τις περιγραφές παιχνιδιών. Πρόωρη φόρτωση των εικόνων παρασκηνίου Αν ενεργοποιηθεί, το Playnite θα κατεβάσει το εξώφυλλο παρασκηνίου κατά τη λήψη μεταδεδομένων, χρησιμοποιώντας περισσότερο χώρο στο δίσκο και καθιστώντας διαθέσιμο το εξώφυλλο όταν είναι εκτός σύνδεσης. Εάν απενεργοποιηθεί, το εξώφυλλο στο παρασκήνιο λαμβάνεται μόνο όταν απαιτείται, χρησιμοποιώντας λιγότερο χώρο, αλλά μπορεί να οδηγήσει σε καθυστέρηση πριν εμφανιστεί το εξώφυλλο και κάποιες εικόνες ενδέχεται να μην είναι διαθέσιμες όταν είναι εκτός σύνδεσης. Αυτόματο κλείσιμο του προγράμματος τρίτων μετά το κλείσιμο του παιχνιδιού Καθυστέρηση τερματισμού του προγράμματος (σε δευτερόλεπτα) Να μην κλείνει μετά από συνεδρίες παιχνιδιού μικρότερες από (σε δευτερόλεπτα) Αυτόματο κλείσιμο των παρακάτω προγραμμάτων: Αυτόματο Κλείσιμο Προγραμμάτων Λίστα Εξαιρούμενων Εισαγωγών Εμφάνιση προειδοποίησης κατά την ανάθεση πολύ μεγάλων πολυμέσων του παιχνιδιού Άνοιγμα εντολής φακέλου Προτιμώμενος οργανισμός αξιολόγησης ηλικίας Ενημέρωση μεγέθους εγκατάστασης παιχνιδιών κατά την ενημέρωση βιβλιοθήκης Σαρώνει και ενημερώνει το μέγεθος εγκατάστασης των παιχνιδιών, εφόσον εντοπιστεί ότι τα αρχεία τους έχουν τροποποιηθεί από την τελευταία σάρωση Κανένα Γέμισμα Ενσωμάτωση Ομοιόμορφο για γέμισμα Αριστερά Δεξιά Κορυφή Κάτω Σφάλμα Εισαγωγής Απαιτείται πιστοποίηση Η ταυτοποίηση απέτυχε Εναλλακτική λειτουργία προβολής ιστού Χρησιμοποιήστε όταν αντιμετωπίζετε προβλήματα με τις προβολές ιστού, για παράδειγμα στους διαλόγους ταυτοποίησης ενσωμάτωσης. Μερική φόρτωση των μεγάλων περιγραφών των παιχνιδιών Οι μεγάλες περιγραφές παιχνιδιών μπορούν να προκαλέσουν αξιοσημείωτες καθυστερήσεις όταν επιλέγετε παιχνίδια. Όταν είναι ενεργοποιημένες, μόνο ένα μέρος των περιγραφών θα φορτώσει άμεσα και το υπόλοιπο κατ' απαίτηση του χρήστη. Εισαγωγή Metadata Λήψη Μεταδεδομένων Ορισμός επιλεγμένων παραμέτρων που θα χρησιμοποιηθούν για οποιεσδήποτε μελλοντικές λήψεις μεταδεδομένων. Μπορεί επίσης να αλλάξει στις ρυθμίσεις της εφαρμογής. Οδηγός Εισαγωγής Εξομοιωτών Αυτός ο οδηγός θα σας καθοδηγήσει στη διαδικασία λήψης και εισαγωγής εξομοιωτών και εισαγωγής παιχνιδιών για εξομοιωτές. Λάβετε υπόψη ότι μπορείτε πάντα να προσθέσετε επιπλέον εξομοιωτές ή/και παιχνίδια αργότερα μέσω του κύριου μενού (στο μενού "Βιβλιοθήκη" για τις ρυθμίσεις εξομοιωτή και το μενού "Προσθήκη Παιχνιδιών" για εξομοιωμένα παιχνίδια). Παρακάτω είναι μια λίστα με εξομοιωτές που το Playnite μπορεί να αναγνωρίσει και να ρυθμίσει αυτόματα. Μπορείτε να κατεβάσετε εξομοιωτές εγκατάστασης από τις ιστοσελίδες τους. Μόλις εγκαταστήσετε τους εξομοιωτές (χειροκίνητα), μπορείτε να τους εισαγάγετε στο διάλογο ρυθμίσεων εξομοιωτή. Μπορείτε να εισαγάγετε όλους τους εξομοιωτές που είναι εγκατεστημένοι στον υπολογιστή σας, κάνοντας κλικ στο κουμπί 'Αυτόματη ανίχνευση από φάκελο…'. Το Playnite θα αναζητήσει τον επιλεγμένο φάκελο για όλους τους γνωστούς εξομοιωτές και θα παρέχει την επιλογή για την εισαγωγή τους. Μπορείτε να χρησιμοποιήσετε αυτό το κουμπί πολλές φορές για την εισαγωγή εξομοιωτών από διαφορετικούς φακέλους. Οι εξομοιωτές θα προστεθούν στο κάτω μέρος της τρέχουσας λίστας. Μπορείτε να εισάγετε παιχνίδια κάνοντας κλικ στο κουμπί 'Σάρωση Φακέλου Χρησιμοποιώντας Εξομοιωτή'. Επιλέγοντας τον κατάλληλο εξομοιωτή θα πει στο Playnite ποιοι τύποι αρχείων θα πρέπει να σαρωθούν και να εισαχθούν. Μπορείτε να χρησιμοποιήσετε αυτό το κουμπί πολλές φορές για να εισαγάγετε παιχνίδια από διαφορετικούς φακέλους. Τα παιχνίδια θα προστεθούν στο κάτω μέρος της τρέχουσας λίστας. Δεν υπάρχουν επιλεγμένοι εξομοιωτές για εισαγωγή. Δεν θα μπορείτε να εισάγετε αυτόματα οποιαδήποτε παιχνίδια χωρίς να ρυθμίσετε πρώτα τους εξομοιωτές. Είστε σίγουροι ότι θέλετε να συνεχίσετε και να τελειώσει η διαδικασία εισαγωγής; Δεν υπάρχουν εξομοιωτές ρυθμισμένοι στο Playnite. Δεν μπορείτε να εισάγετε παιχνίδια χωρίς πρώτα να ρυθμίσετε τον εξομοιωτή και επιλέγοντας τους κατάλληλους τύπους αρχείων. Θέλετε να προσθέσετε μερικούς εξομοιωτές τώρα; Σάρωση φακέλου χρησιμοποιώντας τον εξομοιωτή Επιλέξτε αρχεία Αυτόματη ανίχνευση από φάκελο... Επεξεργασία Εξομοιωτών… Σάρωση… Σάρωση {0}… Ρυθμίσεις Πρώτης Εκκίνησης Αυτή η διαδικασία θα σας καθοδηγήσει στην αυτόματη εισαγωγή και διαμόρφωση εξωτερικών βιβλιοθηκών παιχνιδιών. Το Playnite μπορεί να εισάγει αυτόματα παιχνίδια από πολλαπλές υπηρεσίες παιχνιδιών, όπως το Steam ή το GOG. Λάβετε υπόψη ότι μπορείτε επίσης να προσθέσετε χειροκίνητα οποιοδήποτε προσαρμοσμένο ή εξομοιωμένο παιχνίδι για οποιαδήποτε πλατφόρμα αργότερα από το κύριο μενού. Ενσωμάτωση Βιβλιοθήκης Ακολουθεί η λίστα με μερικές επιμελημένες ενσωματώσεις βιβλιοθηκών που υποστηρίζει το Playnite . Παρακαλώ επιλέξτε αυτά που θέλετε να εγκαταστήσετε. Περισσότερες ενσωματώσεις μπορούν να εγκατασταθούν αργότερα από το μενού "Πρόσθετα". Η διαμόρφωση ολοκληρώθηκε Η αρχική ρύθμιση έχει ολοκληρωθεί. Μπορείτε να αλλάξετε όλες τις ρυθμίσεις αργότερα, καθώς και να προσθέσετε πρόσθετες ενσωματώσεις από το κύριο μενού. Αποτυχία λήψης μιας ή περισσότερων επεκτάσεων. Μπορείτε να προσπαθήσετε να κατεβάσετε ξανά τις ενσωματώσεις από το μενού πρόσθετων μετά την ολοκλήρωση του Οδηγού για πρώτη φορά. Λήψη ενσωμάτωσης {0}… Λήψη λίστας προτεινόμενων ενσωματώσεων… Αποτυχία λήψης της λίστας προτεινόμενων ενσωματώσεων. Μπορείτε να δοκιμάσετε και να ξανακατεβάσετε τις ενσωματώσεις αργότερα από το μενού "Πρόσθετα". Ρυθμίστε πλατφόρμες και εξομοιωτές Επεξεργασία Εξομοιωτών Πλατφόρμες Πλατφόρμα Εξομοιωτές Εξομοιωτής Προσθήκη πλατφόρμας Επιλογή εικονιδίου Επιλογή εξώφυλλου Επιλογή εικόνας Επιλογή Στοιχείου Επιλογή φόντου Επιλογή αρχείου Επιλογή URL Προσθήκη εξομοιωτή Υποστηριζόμενες πλατφόρμες Θέλετε να αποθηκεύσετε τις αλλαγές της πλατφόρμας; Θέλετε να αποθηκεύσετε τις αλλαγές εξομοιωτών; Εκτελέσιμο Παράμετροι Κατάλογος Εργασίας Υποστηριζόμενοι Τύποι Αρχείων Εισαγωγή Εξομοιωτών Λήψη Εξομοιωτών Φόρτωση προεπιλογής παραμέτρων από το γνωστό προφίλ εξομοιωτή Είστε βέβαιοι ότι θέλετε να καταργήσετε τον εξομοιωτή {0}; Αυτή τη στιγμή χρησιμοποιείται από {1} παιχνίδι(α). Είστε βέβαιοι ότι θέλετε να καταργήσετε την πλατφόρμα {0}; Αυτή τη στιγμή χρησιμοποιείται από {1} παιχνίδι(α) και {2} εξομοιωτή(ες). Βοήθεια για τις ρυθμίσεις Ταξινόμηση κατά Κατεύθυνση Ταξινόμησης Ομαδοποίηση κατά Αύξουσα Φθίνουσα Μην ομαδοποιείς Ομαδοποίηση ανά Συλλογή Ομαδοποίηση ανά Κατηγορία Ομαδοποίηση ανά Πλατφόρμα Τύπος προβολής Προβολή Πίνακας Εξερεύνησης Πίνακας Φίλτρων Εικονίδιο Εικονίδιο Βιβλιοθήκης Εικόνα Εξώφυλλου Εικόνα Φόντου Ταξινόμηση ονόματος Βιβλιοθήκη Εγχειρίδιο Ονομα Δίσκος Εγκατάστασης Όνομα Λογαριασμού Πλατφόρμα Κατηγορία(ες) Είδος Ημερομηνία Έκδοσης Έτος Κυκλοφορίας Δημιουργός Ετικέτα Εκδότης Κατάσταση Εγκατάστασης Ταίριασμα όλων των φίλτρων Αν ενεργοποιηθεί, μόνο τα παιχνίδια που χρησιμοποιούν όλα τα αντικείμενα σε όλα τα φίλτρα θα συμπεριληφθούν στην προβολή. Εάν απενεργοποιηθεί, παιχνίδια που χρησιμοποιούν οποιοδήποτε στοιχείο σε οποιοδήποτε φίλτρο θα περιλαμβάνονται στην προβολή. Εγκατεστημένα Εγκατεστημένο Μη εγκατεστημένο Κρυφά Αγαπημένα Ενεργοποίηση Υποστήριξης HDR Εάν είναι ενεργοποιημένο, το HDR θα ενεργοποιείται στην κύρια οθόνη πριν την εκκίνηση του παιχνιδιού. Σημείωση. Το HDR δεν υποστηρίζεται από την κύρια οθόνη σας. Παίχτηκαν τελευταία Κατηγορία Περιγραφή Κατάλογος εγκατάστασης Εικόνα εξωφύλλου Σύνδεσμοι Διαδρομή Image/ISO Είδος Είδη παιχνιδιών Εταιρεία Εταιρείες Κατασκευαστής Δημιουργοί Εκδότης Εκδότες Κατηγορία Κατηγορίες Ετικέτα Ετικέτες Λειτουργία Λειτουργίες Αξιολόγηση ηλικίας Ηλικιακή ταξινόμηση Περιοχή Περιοχές Πηγή Πηγές Πρόσφατη δραστηριότητα Σφάλμα βάσης δεδομένων Αποτυχία ανοίγματος βάσης δεδομένων βιβλιοθήκης. Η βάση δεδομένων δεν έχει ανοίξει. Δεν είναι δυνατή η πρόσβαση στη βάση δεδομένων της βιβλιοθήκης. File "{0}" is being used by another process or it's in inaccessible location. Αποτυχία δημιουργίας πακέτου διαγνωστικών. Αποτυχία αυτόματης μεταφόρτωσης του πακέτου διάγνωσης. Οι πληροφορίες διαγνωστικών στάλθηκαν επιτυχώς. Το πακέτο διαγνωστικών έχει δημιουργηθεί και υποβληθεί με επιτυχία. Επισυνάψτε το ακόλουθο αναγνωριστικό στην αναφορά προβλήματός σας: Αποτυχία εισαγωγής παιχνιδιών από {0}. Αποτυχία εισαγωγής εξομοιωμένων παιχνιδιών από {0}. Δεν είναι δυνατή η αναζήτηση παιχνιδιών στο επιλεγμένο προφίλ εξομοιωτή. Το προφίλ δεν περιέχει επεκτάσεις αρχείων ή πλατφόρμες. Το Playnite απέτυχε να ξεκινήσει. Κλείστε όλες τις τρέχουσες διεργασίες και δοκιμάστε ξανά. Αποτυχία εφαρμογής του θέματος "{0}", προφίλ χρωμάτων "{1}" {2} Δεν είναι δυνατή η σύνδεση, η διεύθυνση URL δεν είναι σε έγκυρη μορφή. Το άνοιγμα της εφαρμογής απέτυχε Αποτυχία αρχικοποίησης εφαρμογής προβολής ιστοσελίδας. Το Playnite δεν μπορεί να συνεχίσει με τη διαδικασία εκκίνησης. Περισσότερες πληροφορίες στο https://playnite.link/cefstartup Δεν είναι δυνατή η εισαγωγή εξομοιωτών λόγω του αρχείου ορισμού που λείπει ή είναι κατεστραμμένο. Αποτυχία εκτέλεσης ενέργειας μενού. Επεξεργασία λεπτομερειών παιχνιδιού URL εικόνας Προσθήκη συνδέσμου Προσθήκη ROM Αποθήκευση Αλλαγών Εφαρμογή αλλαγών στο πεδίο που επεξεργάζονται στο παιχνίδι(ους). Προσθέστε ενέργεια Διαγραφή ενέργειας Κατάργηση ενέργειας αναπαραγωγής Προσθήκη παιχνιδιών Σάρωση Φακέλου... Ανίχνευση Εγκατεστημένων Περιήγηση... Άνοιγμα Playnite Ρυθμίσεις προφίλ Το όνομα παιχνιδιού δεν μπορεί να είναι άδειο. Ο κατάλογος παρακολούθησης ενεργειών παιχνιδιού δεν μπορεί να είναι κενός. Το όνομα παιχνιδιού δεν μπορεί να είναι κενό πριν από την αναζήτηση metadata. Μη έγκυρα δεδομένα παιχνιδιού Εισάγετε έγκυρο URL Web ξεκινώντας με http:// ή ◆ :// Επιλέξτε URL Αποτυχία λήψης metadata: {0} Σφάλμα λήψης Καθαρισμός φίλτρων Ιδιωτικός λογαριασμός Δημόσιος Λογαριασμός κλειδί API Σφάλμα εκκίνησης σφάλμα θέματος καθαρισμός όλων Εγκατάσταση Απεγκατάσταση Εκκίνηση Τρέξιμο Μη έγκυρο URL καμία ενέργεια ελαχιστοποίηση Επαναφορά Επαναφορά μόνο όταν εκκινηθεί από το UI κλείσιμο αλλαγή Προχωρημένα Ποτέ Κατάσταση ολοκλήρωσης Καταστάσεις Ολοκλήρωσης Βαθμολογία Χρηστών Βαθμολογία Κριτικών Βαθμολογία κοινότητας Σενάρια παιχνιδιού Σενάρια εφαρμογής Σενάρια Πρόσθετα Πηγές Μεταδεδομένων Επεκτάσεις Αναγνωριστικά επέκτασης Επαναφόρτωση Scripts Διαδραστικό SDK PowerShell Όλα τα scripts επαναφορτώθηκαν επιτυχημένα. Δεν βρέθηκαν παιχνίδια για συγκεκριμένα κριτήρια αναζήτησης/φίλτρου Δεν βρέθηκαν στοιχεία Εναλλαγή σε λειτουργία επιφάνειας εργασίας Εξοδος Playnite Βιβλιοθήκες Ενημέρωση Όλων Δημιουργήθηκε από Έκδοση: Ενημερώθηκε: Πρόσθετα (Modules) Συλλογή Στατιστικά Όλα Κανένα Ειδοποιήσεις Πλάτος Ύψος Μέγεθος Μικρή Κανονική Μεγάλη Μεγαλύτερη Πολύ μεγάλη Προεπιλογή Επιλογή Επιλογή όλων Αποεπιλογή όλων Πρώτο Τυχαίο Επιλογή χρήστη Φόρτωση περισσοτέρων Διαφάνεια Σύμπτυξη Ανάπτυξη Σύμπτυξη Όλων Ανάπτυξη Όλων Άλλο Θέματα Παράμετροι Εξομοιωτή Ενσωματωμένες Παράμετροι Προσαρμοσμένες Παράμετροι Πρόσθετες Παράμετροι Εξομοιωτή Αντικατάσταση Παραμέτρων Εξομοιωτή Ενέργεια Παιξίματος Επιλογή μεταδεδομένων για εισαγωγή Επιλέξτε Παιχνίδια για εισαγωγή αναζήτηση metadata Διαθέσιμη Ενημέρωση Αλλαγές από την τελευταία ενημέρωση Λήψη και Εγκατάσταση Ενημέρωσης Έλεγχος για Ενημερώσεις Σφάλμα Ενημέρωσης Απέτυχε ο έλεγχος για νέα έκδοση. Δεν βρέθηκε νέα έκδοση, είστε ενημερωμένοι. Αποτυχία λήψης και εγκατάστασης της ενημέρωσης. Κάποια εργασία στο παρασκήνιο είναι σε εξέλιξη. Θέλετε να την ακυρώσετε και να προχωρήσετε με την ενημέρωση; Κάποια εργασία στο παρασκήνιο είναι σε εξέλιξη. Θέλετε να την ακυρώσετε και να βγείτε από το Playnite; Κάποια εργασία στο παρασκήνιο είναι σε εξέλιξη. Η εναλλαγή λειτουργιών θα ακυρώσει την εργασία, θέλετε να αλλάξετε οπωσδήποτε; Μία ενημέρωση για το Playnite είναι διαθέσιμη Ανανέωση της λίστας θεμάτων Εφαρμογή επιλεγμένου θέματος Παρακολούθηση αλλαγών αρχείων Αυτόματη εφαρμογή του θέματος όταν αλλάζει το αρχείο προέλευσης Εκτέλεση σεναρίου Εκτέλεση πριν την έναρξη του παιχνιδιού Εκτέλεση μετά την έξοδο από ένα παιχνίδι Εκτέλεση μετά την εκκίνηση του παιχνιδιού Εκτέλεση κατά την εκκίνηση της εφαρμογής Εκτέλεση κατά τον τερματισμό της εφαρμογής Σενάριο έναρξης παιχνιδιού Σενάριο τρέχοντος παιχνιδιού Σενάριο διακοπής παιχνιδιού Εκτέλεση καθολικού σεναρίου Καθολικά Φιλτραρισμένα Τρέχοντα Νέα Δοκιμαστικό σενάριο Εμφάνιση μόνο επιλεγμένων αντικειμένων. Αποθήκευση ως προεπιλογή Προσθήκη στα αγαπημένα Αφαίρεση από τα αγαπημένα Απόκρυψη αυτού του παιχνιδιού Κατάργηση από τα Κρυμμένα Ενεργοποίηση Υποστήριξης HDR Απενεργοποίηση Υποστήριξης HDR Επεξεργασία… Υπολογισμός μεγέθους εγκατάστασης Υπολογίστε το μέγεθος εγκατάστασης (όλα τα παιχνίδια) Υπολογισμός μεγέθους εγκατάστασης (Μόνο ελλειπή δεδομένα) Μέγεθος Εγκατάστασης Ορισμός Κατηγοριών Ορισμός Κατάστασης Ολοκλήρωσης Αφαίρεση΄ Παίξε Εγκατάσταση Επιλογές Παιχνιδιού Λεπτομέρειες Απεγκατάσταση Ανοίξτε την τοποθεσία εγκατάστασης Δημιουργία συντόμευσης στην επιφάνεια εργασίας Άνοιγμα Εγχειριδίου Περισσότερα Διαχείριση από το πρόσθετο βιβλιοθήκης Η διαδικασία έναρξης του παιχνιδιού θα γίνει από το πρόσθετο βιβλιοθήκης που είναι υπεύθυνο για αυτό το παιχνίδι. Δεν βρέθηκαν σχετικές πληροφορίες σχετικά με το παιχνίδι '{0}' στη συγκεκριμένη σελίδα. Συμβουλή: Μπορείτε να χρησιμοποιήσετε πιο προηγμένη διαδικασία λήψης μεταδεδομένων κατά την επεξεργασία ενός παιχνιδιού μέσω της επιλογής μενού "Επεξεργασία". Δεν είναι διαθέσιμο όταν κάποια ενέργεια είναι σε εξέλιξη. Το κείμενο περιγραφής είναι ευαίσθητο στη σύνταξη HTML Ο χρόνος παιχνιδιού καταγράφεται σε δευτερόλεπτα. Το μέγεθος εγκατάστασης υποδεικνύεται σε bytes. Η ημερομηνία κυκλοφορίας πρέπει να οριστεί σε μορφή 'χρόνος-μήνας-ημέρα'. Οι τιμές μήνα και ημέρας μπορούν να παραλειφθούν. Τιμές από 0 έως 100 ή κενές για καμία βαθμολογία. Η ανάπτυξη του Playnite υποστηρίζεται από αυτούς τους patrons και τα μέλη του Ko-fi: Κώδικας, εντοπιότητα και άλλοι συνεισφέροντες χωρίς συγκεκριμένη σειρά: Ακύρωση παρακολούθησης παιχνιδιών? Η παρακολούθηση της εγκατάστασης εκτελείται αυτή τη στιγμή. Θέλετε να ακυρώσετε τη διαδικασία και να επιστρέψετε το παιχνίδι στην προηγούμενη κατάσταση; Η παρακολούθηση της εγκατάστασης εκτελείται αυτή τη στιγμή. Θέλετε να ακυρώσετε τη διαδικασία και να επιστρέψετε το παιχνίδι στην προηγούμενη κατάσταση; Χρόνος παιχνιδιού Τελευταίο παιχνίδι {0}η {1}ω {2}μ {0}ω {1}λ {0} λεπτά {0} δευτερόλεπτα Δεν παίχτηκε Άνοιγμα λειτουργίας επιφάνειας εργασίας… Άνοιγμα λειτουργίας πλήρους οθόνης… Φόρτωση βιβλιοθήκης παιχνιδιών… Υπολογισμός μεγέθους εγκατάστασης… Γίνεται υπολογισμός μεγέθους εγκατάστασης του {0}… Απέτυχε η εγκατάσταση script. Script εγκαταστάθηκε με επιτυχίαinstalled successfully. Εγκατάσταση Script Σφάλμα σεναρίου Αποτυχία εκτέλεσης λειτουργίας επεκτάσεων. Άνοιγμα φακέλου μεταδεδομένων Υπολογισμός Υπολογίζει αυτόματα το μέγεθος εγκατάστασης χρησιμοποιώντας τις ROM (εάν το παιχνίδι έχει κάποιο) ή τον κατάλογο εγκατάστασης (εάν έχει οριστεί) Ο πελάτης {0} δεν είναι εγκατεστημένος. Ο πελάτης {0} πρόκειται να ανοίξει. Παρακαλώ συνδεθείτε και μετά κλείστε αυτό το μήνυμα. Αναμονή για σύνδεση του χρήστη. Παρακαλώ κλείστε το παράθυρο μόλις τελειώσει η διαδικασία... Ο φάκελος εγκατάστασης του παιχνιδιού δεν βρέθηκε. Μη έγκυρη διαμόρφωση ενέργειας παιχνιδιού. Αντιμετώπιση προβλημάτων συγχρονισμού λογαριασμού Αντιμετώπιση προβλημάτων Μετονομασία στοιχείου Προσθήκη νέου στοιχείου Εισάγετε όνομα Εισάγετε νέο όνομα Λιγότερο από μια ώρα 1 έως 10 ώρες 10 έως 100 ώρες 100 έως 500 ώρες 500 έως 1000 ώρες 1000+ Πρέπει να γίνει επανεκκίνηση του Playnite για να ολοκληρωθεί η εγκατάσταση. Θέλετε να κάνετε επανεκκίνηση τώρα; Η επέκταση δεν τακτοποιήθηκε σωστά. Το θέμα δεν τακτοποιήθηκε σωστά. Η επέκταση "{0}" απέτυχε να φορτώσει σωστά. Η επέκταση «{0}» δεν μπορεί να φορτώσει, η τρέχουσα έκδοση Playnite δεν υποστηρίζεται. Το θέμα "{0}" απέτυχε να φορτώσει σωστά. Το θέμα «{0}» δεν μπορεί να φορτώσει, η τρέχουσα έκδοση Playnite δεν υποστηρίζεται. Η επέκταση απέτυχε να φορτώσει σωστά. Το θέμα απέτυχε να φορτώσει σωστά. Το θέμα/επέκταση χρησιμοποιεί μη υποστηριζόμενη έκδοση API. Η Εγκατάσταση ήταν επιτυχής. Εγκατάσταση επέκτασης; Γενικά Αποτυχία εγκατάστασης πρόσθετου "{0}". Αποτυχία εγκατάστασης επέκτασης. {0} Θέλετε να εγκαταστήσετε μια νέα επέκταση; {0} Από {1} Έκδοση {2} Θέλετε να ενημερώσετε την επέκταση "{0}"; Τρέχουσα έκδοση: {1} Νέα έκδοση: {2} Αποτυχία εγκατάστασης θέματος. {0} Θέλετε να εγκαταστήσετε ένα νέο θέμα; {0} Από {1} Έκδοση {2} Θέλετε να ενημερώσετε το θέμα "{0}"; Τρέχουσα έκδοση: {1} Νέα έκδοση: {2} Πρόκειται να εξέλθετε από το Playnite και να μεταβείτε στην ακόλουθη ιστοσελίδα χρησιμοποιώντας το προεπιλεγμένο πρόγραμμα περιήγησης. Θέλετε να συνεχίσετε; {0} Η επιλεγμένη εικόνα(ες) μπορεί να είναι πολύ μεγάλη για βέλτιστη απόδοση. Χρησιμοποιώντας πολύ μεγάλες εικόνες μπορεί να οδηγήσει σε χειρότερη απόκριση UI και αυξημένη χρήση μνήμης. Μέγιστες συνιστώμενες αναλύσεις: Εικονίδια: {0} μέγιστα εικονοστοιχεία Εξώφυλλο: {1} μέγιστα εικονοστοιχεία Φόντα: {2} μέγα εικονοστοιχεία Προειδοποίηση Απόδοσης Να μην εμφανιστεί ξανά Το αρχείο με επέκταση {0} δεν είναι συμβατό. Μη συμβατή επέκταση αρχείου Το επιλεγμένο αρχείο εικόνας μπορεί να είναι πολύ μεγάλο για βέλτιστη απόδοση. Είστε σίγουροι ότι θέλετε να απεγκαταστήσετε το επιλεγμένο θέμα; Η απεγκατάσταση θα δρομολογηθεί στην επόμενη εκκίνηση της εφαρμογής. Τα ενσωματωμένα θέματα δεν μπορούν να απεγκατασταθούν. Αυτό το θέμα δεν υποστηρίζει αυτήν την έκδοση του Playnite. Είστε σίγουροι ότι θέλετε να απεγκαταστήσετε το επιλεγμένο θέμα; Η απεγκατάσταση θα δρομολογηθεί στην επόμενη εκκίνηση της εφαρμογής. Οι ενσωματωμένες επεκτάσεις δεν μπορούν να απεγκατασταθούν. Αυτή η επέκταση δεν υποστηρίζει αυτήν την έκδοση του Playnite. Φάκελος εγκατάστασης Φάκελος δεδομένων Δημιουργία πακέτου διαγνωστικών… Μεταφόρτωση πακέτου διαγνωστικών… Εισαγωγή αρχείου… Τί είναι αυτό; Είστε σίγουροι ότι θέλετε να το κάνετε αυτό; Συνολικός χρόνος παιξίματος Μέσος χρόνος παιχνιδιού Κορυφαίος χρόνος παιξίματος Συνολικό μέγεθος εγκατάστασης Επισκόπηση Πλευρική μπάρα Προβολή στην πλευρική μπάρα Επαναφορά ρυθμίσεων Θα γίνει επαναφορά όλων των ρυθμίσεων εφαρμογής στις προεπιλεγμένες τιμές, εξαιρουμένων: - Τοποθεσία βάσης δεδομένων - Εισαγωγή λίστας αποκλεισμών - Ρυθμίσεις επέκτασης, συμπεριλαμβανομένης της ενσωμάτωσης βιβλιοθήκης Η επανεκκίνηση της εφαρμογής απαιτείται για να ολοκληρωθεί η διαδικασία. Θέλετε να επαναφέρετε τις ρυθμίσεις? Για προγραμματιστές Εξωτερικές επεκτάσεις Εισαγωγή πλήρους διαδρομής φακέλου. Επιτεύγματα Φόρουμ Νέα Σελίδα Καταστήματος Η αρχική εγκατάσταση δεν ολοκληρώθηκε. Το Playnite θα επανεκκινήσει τώρα σε Λειτουργία Επιφάνειας Εργασίας για να ολοκληρώσετε τη διαδικασία. Παίχτηκαν Πρόσφατα Αγαπημένα Παίχτηκαν Περισσότερο Όλα Υπάρχουν φίλτρα που εφαρμόζονται. Έχουν εφαρμοστεί επιπλέον φίλτρα. Αποτελέσματα αναζήτησης για: Υπάρχει ήδη ένα στοιχείο με το ίδιο όνομα. Περιορισμός επιλογής στο τρέχων φίλτρο Επιλογή άλλου Πρόσθετα … Εγκατεστημένα Ρυθμίσεις επεκτάσεων Περιήγηση Ενημερώσεις Ενημερώσεις ({0}) Η διαχείριση των εγκατεστημένων επεκτάσεων και θεμάτων, συμπεριλαμβανομένων των ρυθμίσεών τους, έχει μεταφερθεί στο νέο μενού "Πρόσθετα". Όλες οι εγκατεστημένες επεκτάσεις ενσωμάτωσης βιβλιοθήκης μπορούν να ρυθμιστούν εδώ. Αν θέλετε να εγκαταστήσετε ή να απεγκαταστήσετε πρόσθετες ενσωματώσεις, χρησιμοποιήστε την επιλογή "Πρόσθετα" από το κύριο μενού. Θέματα Επιφάνειας Εργασίας Θέματα Πλήρους Οθόνης Πραγματοποιείται Αναζήτηση… Το πρόσθετο δεν είναι συμβατό με αυτήν την έκδοση του Playnite. Αποτυχία λήψης του πακέτου εγκατάστασης πρόσθετου. Αποτυχία λήψης δήλωσης εγκατάστασης πρόσθετου. Απαιτείται επανεκκίνηση της εφαρμογής για την εφαρμογή εκκρεμών αλλαγών. Αυτό το πρόσθετο έχει προγραμματιστεί για εγκατάσταση. Εγκατάσταση Απεγκατάσταση Ήδη εγκατεστημένο Δε βρέθηκαν νέες ενημερώσεις πρόσθετου. Ενημέρωση πρόσθετων Το αρχείο αλλαγών δεν είναι διαθέσιμο Προγραμματισμένο για εγκατάσταση Η λήψη απέτυχε Η άδεια απορρίφθηκε Λήψη {0}… Γίνεται έλεγχος για ενημερώσεις πρόσθετων … Μία ή περισσότερες ενημερώσεις προσθέτων είναι διαθέσιμες Επιλέξτε στοιχεία για ενημέρωση Παράδειγμα ανάπτυξης επεκτάσεων {0} Άδεια χρήσης Αποδοχή Άρνηση Συμπερίληψη ενεργειών ενσωμάτωσης βιβλιοθήκης Επιλέξτε ενέργεια Λειτουργία Παρακολούθησης Διαδρομή Παρακολούθησης Καθυστέρηση αρχικής παρακολούθησης Συχνότητα παρακολούθησης Σύνδεσμος Αρχείο Εξομοιωτής Σενάριο Προεπιλογή Διεργασία Φάκελος Αρχική διεργασία Καταγραφή ίχνους μηνυμάτων Οι ακόλουθες αλλαγές αντικαθιστούν δεδομένα για όλα τα τρέχοντα επιλεγμένα παιχνίδια! Κανένα Ενσωμάτωση Μόνο αντικείμενα Μόνο αρχή και τέλος Ευαισθησία κύλισης Ομαλότητα κύλισης Ταχύτητα κίνησης Αφαίρεση στοιχείου; Είστε βέβαιοι ότι θέλετε να αφαιρέσετε αυτό το στοιχείο; Εμφάνιση κουμπιών στον επάνω πίνακα: Γενικές ρυθμίσεις προβολής Ρυθμίσεις ομαδοποίησης Ρυθμίσεις ταξινόμησης Προεπιλογές φίλτρων Θέση στοιχείων πρόσθετων Πλάτος διαχωριστικού τμήματος Μετακίνηση του κύριου κουμπιού μενού στην πλαϊνή μπάρα Πίνακας Εξερεύνησης Τυχαία επιλογή παιχνιδιού Προβολές τυχαίου επιλογέα παιχνιδιού Επιλογή τυχαίου παιχνιδιού από την προβολή Αποθήκευση ρυθμίσεων ομαδοποίησης και ταξινόμησης Εμφάνιση ως γρήγορο φίλτρο στη Λειτουργία Πλήρους Οθόνης Τις τελευταίες 7 ημέρες Τις τελευταίες 31 ημέρες Τις τελευταίες 365 ημέρες Περισσότερες από 365 ημέρες πριν Διαμόρφωση Αποθήκευση προεπιλογής Ελαχιστοποίηση μετά την έναρξη παιχνιδιού Ελαχιστοποίηση Playnite μετά την εκκίνηση ενός παιχνιδιού. Η απενεργοποίηση αυτού μπορεί να οδηγήσει σε προβλήματα σε παιχνίδια που δεν περιλαμβάνουν εστίαση εισόδου κατά την εκκίνηση! Μέγεθος Γραμματοσειράς Μέγεθος Γραμματοσειράς Μικρό Υποστήριξη χειριστηρίου παιχνιδιών Αν απενεργοποιηθεί, το Playnite δεν θα δέχεται καμία είσοδο χειριστηρίου παιχνιδιού. Απενεργοποιήστε το αν χρησιμοποιείτε εργαλεία που μεταφράζουν εισόδους χειριστηρίου παιχνιδιών στις εισόδους ποντικιού/πληκτρολογίου και λαμβάνετε διπλές εισόδους στο Playnite. Εμφάνιση στοιχείων στο κύριο μενού: Εναλλαγή κουμπιών κεντρικής προβολής X/A Εναλλάσσει την διαμόρφωση κουμπιών για την εκκίνηση παιχνιδιού και την εμφάνιση των λεπτομερειών παιχνιδιού στην κεντρική προβολή. Αλλαγή κουμπιού επιβεβαίωσης/ακύρωσης Εναλλάσσει τα κουμπιά A/B για την επιβεβαίωση και ακύρωση. Κύριο χειριστήριο μόνο Αποδοχή εισόδων από τον πρωτεύον χειριστήριο μόνο όταν είναι ενεργοποιημένο. Το κουμπί οδηγού να ανοίγει το Playnite Ένταση ήχου διεπαφής Ένταση ήχου φόντου Σίγαση όταν βρίσκεστε στο παρασκήνιο Αποτυχία πραγματοποίησης της διεπαφής ήχου. API εξόδου Το API που χρησιμοποιείται για την έξοδο ήχου. Αλλάξτε αν αντιμετωπίζετε προβλήματα με τον ήχο. Γενικά Εμφάνιση Ήχος Διάταξη Menus Εισαγωγή {0} αρχίζει… {0} εκτελείται… Κεφαλαία Space bar Μεγεθυντής απόδοση εικόνας Εναλλακτικό Ισορροπημένο Ποιοτικό Ποιοτικό: Καλύτερη ποιότητα εικόνας, αργή, υψηλή χρήση μνήμης. Ισορροπημένο: Καλή ποιότητα, γρήγορη, χαμηλή χρήση μνήμης. Εναλλακτικό: Καλύτερη ποιότητα, μέτρια ταχύτητα, χαμηλή χρήση μνήμης. Επιλογή αρχείου Επιλογή φακέλου Σενάριο εκκίνησης Σημειώστε ότι τόσο οι επεκτάσεις όσο και τα θέματα μπορούν να επηρεάσουν σημαντικά την απόδοση, τη σταθερότητα και την ασφάλεια του Playnite. Εάν αρχίσετε να αντιμετωπίζετε προβλήματα μετά την εγκατάσταση ενός θέματος ή μιας επέκτασης, δοκιμάστε να το απενεργοποιήσετε/απεγκαταστήσετε πρώτα για να δείτε αν είναι η ρίζα του προβλήματος. Επιλέξτε κατά την εκκίνηση Επιλέξτε κατά την εκκίνηση Ενσωματωμένα προφίλ Ενσωματωμένο προφίλ Προσαρμοσμένα προφίλ Προσαρμοσμένο προφίλ Χειρίζεται από ένα ενσωματωμένο σενάριο Προδιαγραφή εξομοιωτή Προδιαγραφή πλατφόρμας Προδιαγραφές περιοχής Εκτέλεση πριν την έναρξη εξομοιωτή Εκτέλεση μετά την εκκίνηση του εξομοιωτή Εκτέλεση μετά την έξοδο εξομοιωτή Το εκτελέσιμο εξομοιωτή δεν βρέθηκε. Δεν βρέθηκαν προδιαγραφές εξομοιωτή. Το σενάριο εκκίνησης εξομοιωτή δεν βρέθηκε. Διαχωρισμός ως ξεχωριστά παιχνίδια Συγχώνευση σε ένα παιχνίδι Ορισμός πλατφόρμας Ορισμός περιοχής Σάρωση φακέλου Σάρωση παραμέτρων Εξαίρεση προτύπων από αθροιστική σάρωση Τα αρχεία που ταιριάζουν με το συγκεκριμένο μοτίβο(α) δε θα σαρωθούν για αθροιστικό έλεγχο και θα ταιριάξουν με το όνομα αρχείου. Δείτε τη σελίδα βοήθειας του εξομοιωτή για περισσότερες πληροφορίες. Σάρωση με εξομοιωτή Το όνομα πρέπει να οριστεί κατά την αποθήκευση νέων ρυθμίσεων. Δεν έχει οριστεί εξομοιωτής ή προφίλ εξομοιωτή. Ο κατάλογος σάρωσης δεν έχει καθοριστεί ή δεν υπάρχει. Η ρύθμιση παραμέτρων σάρωσης δεν έχει οριστεί σωστά. Συμπερίληψη στη μαζική σάρωση αυτόματης σάρωσης Αποτυχία σάρωσης φακέλου για τους εξομοιωτές. Αποτυχία σάρωσης φακέλου(ων) για προσομοιωμένα παιχνίδια. Απόκρυψη εισαγόμενων Προφίλ για εισαγωγή: Ρυθμίσεις αυτόματης σάρωσης Αποθήκευση ρυθμίσεων αυτόματης σάρωσης Αποθηκεύει τις ρυθμίσεις για μεταγενέστερη χρήση κατά την ενημέρωση της βιβλιοθήκης. Οι αποθηκευμένες ρυθμίσεις μπορούν να τροποποιηθούν μέσω του μενού "Ρύθμιση εξομοιωτών". Εισαγωγή με χρήση σχετικών διαδρομών Η εισαγωγή, όσο είναι δυνατόν, αρχείων παιχνιδιών χρησιμοποιώντας διαδρομές σε σχέση με το φάκελο εγκατάστασης του Playnite ή το φάκελο εγκατάστασης του εξομοιωτή. Σάρωση υποφακέλων Σάρωση μέσα σε αρχειοθήκες Συγχώνευση σχετικών αρχείων Συγχώνευση αρχείων του παιχνιδιού, όπως μεμονωμένοι δίσκοι παιχνιδιού, κάτω από μία καταχώρηση παιχνιδιού. Προσθήκη σαρωτή Προσθήκη αποθηκευμένου σαρωτή Έναρξη σάρωσης Προσθήκη διαμόρφωσης σάρωσης με εξομοιωτές για σάρωση συγκεκριμένων φακέλων. Βεβαιωθείτε ότι οι εξομοιωτές έχουν ρυθμιστεί σωστά πριν από την εισαγωγή παιχνιδιών (μέσω του μενού Βιβλιοθήκη -> Ρύθμιση εξομοιωτών). Η προεπιλεγμένη κατάσταση έχει ανατεθεί σε νέα παιχνίδια που προστέθηκαν H κατάσταση που έχει ανατεθεί σε παιχνίδια που παίζονται για πρώτη φορά Αποτυχία πραγματοποίησης του σεναρίου εκτέλεσης PowerShell. Αν είστε χρήστης των Windows 7, δοκιμάστε (επαν)εγκατάσταση του PowerShell 5.1 για να διορθώσετε το πρόβλημα. Προεπιλεγμένο φίλτρο με καθορισμένο το όνομα υπάρχει ήδη. Ενημέρωση της προεπιλογής με τις νέες ρυθμίσεις; Αυτές οι λέξεις θα αφαιρεθούν εξ αρχής από την αυτόματη τιμή του Ονόματος Ταξινόμησης: Χρησιμοποιήστε αυτό για να αγνοήσετε λέξεις στην αρχή μιας συμβολοσειράς για λόγους ταξινόμησης. Η προεπιλογή είναι "Η", "Ένα" και "Α". Συμπληρώστε το όνομα ταξινόμησης σε παιχνίδια που δεν έχουν Ταξινόμηση Πλήρωση τιμών ονόματος ταξινόμησης… Η υπηρεσία Nahimic έχει εντοπιστεί ότι εκτελείται στο σύστημά σας. Αυτή η υπηρεσία είναι γνωστό ότι προκαλεί προβλήματα απόδοσης στο Playnite (και σε άλλες εφαρμογές). Εάν αντιμετωπίσετε οποιαδήποτε φθορά γραφικών ή άλλα ζητήματα απόδοσης στο Playnite, σας συνιστούμε να απενεργοποιήσετε ή να απεγκαταστήσετε εντελώς την υπηρεσία Nahimic. Περισσότερες πληροφορίες στο https://playnite.link/nahimicsucks Το Playnite εκτελείται με αυξημένα δικαιώματα (ως διαχειριστής). Αυτό δεν συνιστάται, δεδομένου ότι παρέχει αυξημένα δικαιώματα σε όλες τις εγκατεστημένες επεκτάσεις και όλα τα παιχνίδια/εφαρμογές που ξεκίνησαν από το Playnite! Περισσότερες πληροφορίες στο https://playnite.link/adminfaq Εμφάνιση προειδοποίησης αν το Playnite εκτελείται με αυξημένα δικαιώματα Εμφάνιση του πραγματικού μεγέθους στη μονάδα δίσκου κατά τον υπολογισμό του μεγέθους των παιχνιδιών Αν ενεργοποιηθεί, οι σαρώσεις θα είναι πιο αργές και θα λαμβάνουν το πραγματικό μέγεθος που χρησιμοποιούν τα αρχεία στο δίσκο. Αν απενεργοποιηθεί, οι σαρώσεις θα είναι ταχύτερες και θα χρησιμοποιούν το μέγεθος των ίδιων των αρχείων. Τα ακόλουθα πρόσθετα έχουν αναφερθεί ως δυνητικά προβληματικά, είτε λόγω υψηλού αντίκτυπου στην σταθερότητα/απόδοση είτε λόγω ζητημάτων ασφάλειας. Συνιστούμε ανεπιφύλακτα την απεγκατάσταση τους: {0} Εξαίρεση online αρχείων από την σάρωση Τα αρχεία που αποθηκεύονται στο cloud storage δεν θα σαρωθούν και εισαχθούν αν δεν είναι διαθέσιμα τοπικά. Υποστηρίζονται μόνο για: Google Drive, DropBox, OneDrive Σάρωση αλλά με απλοποιημένη μέθοδο χωρίς τα περιεχόμενα του αρχείου Τα αρχεία θα εισαχθούν αλλά χρησιμοποιώντας λιγότερο ακριβή μέθοδο που δεν απαιτεί τη λήψη του περιεχομένου του αρχείου και την εμφάνισή του τοπικά. Εφαρμογή σε όλα Παράκαμψη κατάστασης εγκατάστασης Όταν οριστεί, το Playnite θα αγνοήσει την κατάσταση εγκατάστασης (συμπεριλαμβανομένου του καταλόγου εγκατάστασης) που έχει οριστεί από το πρόσθετο ενσωμάτωσης που εισάγει αυτό το παιχνίδι. Αυτή η επιλογή μπορεί να μην λειτουργεί πλήρως με πρόσθετα που χρησιμοποιούν συγκεκριμένη μέθοδο εισαγωγής παιχνιδιού, εκτός αν λάβουν υπόψη αυτή την επιλογή. Χειροκίνητα μόνο Μία φορά την ημέρα Μία φορά την εβδομάδα Σε κάθε εκκίνηση Έλεγχος για ενημερώσεις προγράμματος Έλεγχος για ενημερώσεις πρόσθετων Ενημέρωση βιβλιοθηκών Σάρωση φακέλων εξομοίωσης Συμπερίληψη κρυφών παιχνιδιών Επεξεργασία πεδίων Επιλογή / Αποεπιλογή όλων Άνοιγμα Ενεργοποίηση Ανάθεση Πληκτρολογείστε για αναζήτηση παιχνιδιών … Για βοήθεια πατήστε [F1] Ξεκινώντας με # εμφανίζεται μια λίστα με τις διαθέσιμες εντολές. Ξεκινώντας με / εμφανίζει μια λίστα διαθέσιμων παρόχων αναζήτησης/προσθηκών. Πληκτρολογώντας μία λέξη-κλειδί στην αναζήτηση και τελειώνοντας με SPACE μεταβαίνει αμέσως σε αυτήν την αναζήτηση. TAB: εναλλαγή ενέργειας ENTER: ενεργοποίηση επιλεγμένης ενέργειας SHIFT-ENTER: άνοιγμα μενού στοιχείων Συμπερίληψη μη εγκατεστημένων παιχνιδιών Συμπερίληψη κρυφών παιχνιδιών Περιλαμβάνονται μη εγκατεστημένα παιχνίδια Περιλαμβάνονται μη εγκατεστημένα παιχνίδια Περιλαμβάνονται κρυφά παιχνίδια Εξαιρούνται τα κρυφά παιχνίδια Αναπαραγωγή ή εγκατάσταση Μεταβείτε στις λεπτομέρειες Μενού παιχνιδιού Επεξεργασία Παιχνιδιού Άνοιγμα αναζήτησης Πλαίσιο αναζήτησης Πλήκτρο αναζήτησης Κύρια ενέργεια παιχνιδιού Δευτερεύουσα ενέργεια παιχνιδιού Το CTRL-F ανοίγει την παγκόσμια αναζήτηση αντί για την εστιασμένη αναζήτηση Αποθήκευση ρυθμίσεων φίλτρου παιχνιδιού μεταξύ των συνεδριών αναζήτησης Πάροχος αναζήτησης Προεπιλεγμένες Λέξεις-κλειδιά Προσαρμοσμένη λέξη-κλειδί Μεγάλη συντόμευση συστήματος Αναζήτηση Playnite Ρυθμίσεις επεκτάσεων Εξαιρέσεις Εξαιρούμενοι φάκελοι σχετικοί με τους φακέλους σάρωσης Εξαιρούμενοι φάκελοι σχετικοί με τους φακέλους σάρωσης Προσθήκη αρχείου στη λίστα εξαιρέσεων Προσθήκη φακέλου στη λίστα εξαιρέσεων Οι εξαιρέσεις μπορούν να προστεθούν μόνο στις αποθηκευμένες ρυθμίσεις σαρωτών. Οι εξαιρέσεις έχουν προστεθεί στον σαρωτή "{0}". Παράκαμψη πλατφόρμας Όταν οριστεί, ο σαρωτής θα καταχωρήσει αυτή την πλατφόρμα σε όλα τα παιχνίδια, αντικαθιστώντας τυχόν αυτόματα εντοπισμένες πλατφόρμες. Συμπερίληψη εντολών στην προεπιλεγμένη αναζήτηση Όταν απενεργοποιηθεί, οι εντολές δεν θα περιλαμβάνονται στην προεπιλεγμένη αναζήτηση μέχρι να χρησιμοποιηθεί το πρόθεμα #. Χρήση ασαφούς ταιριάσματος στο φίλτρο ονόματος Όταν είναι ενεργοποιημένο, το όνομα φίλτρου θα ταιριάζει με τα ονόματα παιχνιδιών με τον ίδιο τρόπο όπως και η γενική αναζήτηση. Η αυστηρή αντιστοίχιση μπορεί να επιβληθεί σε μια μεμονωμένη περίπτωση με πρόθεμα φίλτρου τον χαρακτήρα "!" Πεδία προς εμφάνιση για τα αποτελέσματα του παιχνιδιού: Κρυφή Κατάσταση Το αντίγραφο ασφαλείας των δεδομένων ακυρώθηκε. Αποτυχία αντιγράφου ασφαλείας δεδομένων. Σφάλμα αντιγράφου ασφαλείας δεδομένων H αντιγραφή ασφαλείας δεδομένων πραγματοποιείται … Επαναφορά δεδομένων από αντίγραφο ασφαλείας… Αποτυχία επαναφοράς δεδομένων από αντίγραφο ασφαλείας. Ρυθμίσεις Βιβλιοθήκη παιχνιδιών Πολυμέσα βιβλιοθήκης παιχνιδιών Εγκατεστημένες επεκτάσεις Δεδομένα επεκτάσεων Εγκατεστημένα θέματα Επιλέξτε τα δεδομένα που θα αποκατασταθούν από το καθορισμένο αρχείο αντιγράφου ασφαλείας. Το Playnite θα επανεκκινήσει αυτόματα για να ξεκινήσει η διαδικασία επαναφοράς αντιγράφων ασφαλείας. Επιλέξτε τα στοιχεία που θα συμπεριληφθούν σε αντίγραφα ασφαλείας δεδομένων. Οι ρυθμίσεις εφαρμογών και τα δεδομένα της βιβλιοθήκης παιχνιδιών περιλαμβάνονται από προεπιλογή. Το Playnite θα επανεκκινήσει αυτόματα για να ξεκινήσει η διαδικασία δημιουργίας αντιγράφων ασφαλείας. Αυτόματα αντίγραφα ασφαλείας δεδομένων Συχνότητα αυτόματων αντιγράφων ασφαλείας Φάκελος αντιγράφων ασφαλείας Εναλλασσόμενα αντίγραφα ασφαλείας Συμπερίληψη πρόσθετων δεδομένων: Ο φάκελος αντιγράφων ασφαλείας πρέπει να οριστεί αν είναι ενεργοποιημένο το αυτόματο αντίγραφο ασφαλείας. Εμφάνιση ειδοποιήσεων μόνο για διορθωτικές ενημερώσεις Όταν ενεργοποιηθεί, μόνο οι διαθέσιμες ενημερώσεις για την τρέχουσα εγκατεστημένη κύρια έκδοση θα έχουν ως αποτέλεσμα την ειδοποίηση ενημέρωσης. Οι νέες σημαντικές εκδόσεις δεν θα οδηγήσουν στην ειδοποίηση ενημέρωσης. Χρήση σχετικών ημερομηνιών για την προηγούμενη εβδομάδα Χρησιμοποιήστε σχετικές ημερομηνίες σε μορφή "Σήμερα", "Χθες" κλπ αν η ημερομηνία είναι μικρότερη από μια εβδομάδα. Η καθορισμένη μορφή ημερομηνίας θα χρησιμοποιηθεί για όλες τις άλλες ημερομηνίες. Αναζήτηση εικόνας Web Στοιχείο αναζήτησης εικονιδίου Στοιχείο αναζήτησης εξώφυλλου εικόνας Συμβολοσειρά αναζήτησης εικόνας φόντου Λήψη πληροφοριών πρόσθετου… Δεν υπάρχει διαθέσιμη πηγή μεταδεδομένων Ρυθμίσεις ενέργειας αναπαραγωγής Ρυθμίσεις σαρωτή Επιλογή προφίλ κατά την εκκίνηση Επιλέξτε εξομοιωτή κατά την εκκίνηση Αυτόματο Πάντα ενεργό Πάντα απενεργοποιημένο Υποστήριξη προσβασιμότητας (οθόνη αναγνώστη) Μενού εφαρμογής Μενού παιχνιδιού Φάκελος προγράμματος Κατάλογος δεδομένων χρήστη Έχουν εντοπιστεί κατεστραμμένα αρχεία βιβλιοθήκης, το Playnite θα τερματιστεί. Ανοίξτε νέο ζήτημα στη σελίδα GitHub του Playnite με αίτημα να διορθωθεί το πρόβλημα στα αρχεία σας. Θέλετε να αποθηκεύσετε τις αλλαγές που κάνατε? Φορητή εγκατάσταση Δεν εντοπίστηκαν χειριστήρια ================================================ FILE: source/Playnite/Localization/en_US.xaml ================================================  English Made by Josef Němec Finish Author GOG Galaxy 2.0 Twitch Use when experiencing stuttering or similar UI issues Failed to initialize XInput interface. Installing Running Plugins Update Available Download and Install Update Script runtime Remove from Favorites Cancel game monitoring? Time Played Less than an hour 1 to 10 hours 10 to 100 hours 100 to 500 hours 500 to 1000 hours 1000+ Recently Played ================================================ FILE: source/Playnite/Localization/eo_UY.xaml ================================================  Esperanto Eliri Filtriloj Filtrilo Pri Playnite Farita de Josef Němec Raporti kraŝon Forigu ludo(j)n? Ebligita Forigu Kopii Aldonu Finas Nomo Aŭtoro Modulo Serioj Serĉi ================================================ FILE: source/Playnite/Localization/es_ES.xaml ================================================  Español Idioma de Playnite Salir Filtro activo Filtro deshabilitado Filtros adicionales Filtros Filtro Datos inválidos ¿Guardar cambios? Página web en www.playnite.link Código fuente en GitHub Crear paquete diag. Enviar información diagnóstica Acerca de Playnite Creado por Josef Němec Asignar categoría Establecer categorías Añadir categoría Seleccionado - Asignar categoría No seleccionado - Eliminar categoría Indeterminado - Sin cambios (al editar múltiples juegos) Sin categoría Sin plataforma ¡Ups! Algo salió mal… Se ha producido un error irrecuperable. Si quieres ayudarnos a solucionar este problema, por favor, describe brevemente las acciones realizadas antes del fallo y envía la información de diagnóstico. Si estás conectado a Internet, el paquete se subirá al servidor de Playnite para su análisis. También puedes hacer clic en el botón "Informar de un fallo" para crear una nueva incidencia en GitHub e informar del fallo manualmente. Gracias por tu ayuda. La extensión "{0}" provocó un error irrecuperable. Recomendamos guardar el archivo de registro e informar del problema al desarrollador de la extensión. Si el problema se repite, desactiva la extensión. La extensión "{0}" provocó un error irrecuperable. Recomendamos informar del problema al desarrollador de la extensión. Si el problema se repite, desactiva la extensión. Extensión desconocida o un tema causó un error irrecuperable. Recomendamos deshabilitar los complementos de terceros, aislando el problemático y reportando el problema al desarrollador del complemento. Ha ocurrido un error irrecuperable. Si quieres ayudarnos a solucionar este problema, por favor, envía la información de diagnóstico. Gracias. Desactivar extensión Guardar archivo de registro Enviar información de diagnóstico Informar de un error Reiniciar Playnite Reiniciar en modo seguro Desactivando todas las extensiones de terceros y utilizando el tema por defecto. Salir de Playnite Acciones realizadas antes del fallo (en inglés): Administrador de biblioteca ¿Eliminar juego(s)? No se puede eliminar: el juego o el instalador se están ejecutando. No se puede desinstalar: el juego se está ejecutando. ¿Estas seguro que quieres eliminar {0}? ¿Estás seguro de que quieres eliminar {0} juegos? ¿Está seguro de que quieres eliminar {0}? Seleccionar la opción "añadir a la lista de exclusión" evitará que los juegos se vuelvan a importar la próxima vez que se actualice la biblioteca. ¿Está seguro de que desea eliminar {0} juegos? Seleccionar la opción "añadir a la lista de exclusión" evitará que los juegos se vuelvan a importar la próxima vez que se actualice la biblioteca. ¿Estás seguro de que quieres eliminar {0} entradas que actualmente no están en uso? No se han encontrado campos sin utilizar. Sí (añadir a la lista de exclusión) Hay cambios sin guardar en esta sección. Actualizando el formato de la biblioteca de juegos… Error al actualizar la base de datos. No se puede actualizar la biblioteca. Se necesitan {0} MBs de espacio libre en el disco. Error del Juego No se puede iniciar el juego. '{0}' no se ha encontrado en la base de datos. No se puede iniciar el juego: {0} No se puede iniciar la acción: {0} No se puede abrir la ubicación del juego: {0} No se pudo detectar el tamaño de la instalación del juego: {0} Error al escanear el tamaño de instalación Hubo {0} errores durante el escaneo del tamaño de instalación Error al crear acceso directo: {0} Error al abrir el manual: {0} No se puede instalar el juego: {0} No se puede desinstalar el juego: {0} No se encontraron acciones de inicio de juego válidas. Al usar acciones de emulador, asegúrate de que las definiciones de la plataforma coincidan entre el juego y la configuración del emulador. La implementación de la instalación no está disponible. El complemento de biblioteca responsable de este juego está desactivado o no está instalado. La descarga de metadatos oficiales no está disponible. No hay ningún juego seleccionado. La ejecución de la acción del script del juego ha fallado. Falló la ejecución del script de aplicación. La ejecución de la acción global del script ha fallado. Falló la ejecución del script del emulador. La ejecución del script de jugar falló. PowerShell 3.0 o posterior no está instalado. No se pudo determinar cómo comenzar el juego. Activado Deshabilitado Eliminar Eliminar los no usados Renombrar Copiar Añadir Icono predeterminado Carátula por defecto Imagen de fondo por defecto Finalizar Siguiente Volver LISTO VOLVER VACIAR Vaciar Descartar Descartar todo Importar Nombre Autor Módulo Serie Versión Ultima partida Más jugado Veces jugado Tamaño de instalación Carpeta Notas Añadido Se añadió el día Modificado Fecha de modificación Página web Ruta OK Guardar Cerrar Cancelar Confirmar Restablecer No Bienvenido Usuario local General Multimedia Enlaces Instalación Acciones Descargando… Descargando multimedia… Cargando… Tipo Perfil Perfiles Eliminar Descargar Buscar Resolución: Cualquiera Zoom Vista de lista Carátulas Vista de cuadrícula Vista detallada Personalizado URL Agradecimientos especiales Licencia Colaboradores Saliendo de Playnite… Hoy Ayer Lunes Martes Miércoles Jueves Viernes Sábado Domingo La semana pasada El mes pasado El año pasado Hace más de un año 0 a 100MB 100MB a 1GB 1GB a 5GB 5GB a 10GB 10GB a 20GB 20GB a 40GB 40GB a 100GB 100 GB o más Importación completada correctamente. Todos los juegos Id del juego Id de la base de datos Ajustes por defecto Columna Columnas Fila Filas No se pudo obtener el icono de la acción de juego. No hay acción de tipo archivo presente. Descargar solo metadatos faltantes Activar esta opción saltará las descargas de metadatos de campos que ya contengan información. Selección de juegos Selecciona qué juegos deben actualizarse con nuevos metadatos: Todos los juegos de la base de datos Todos los juegos filtrados actualmente Solo los juegos seleccionados No hay campos de metadatos seleccionados No se han seleccionado campos de metadatos para la descarga. Por favor, selecciona al menos uno, y habilita al menos un proveedor de metadatos para este. Tienda oficial IGDB Selecciona qué campos debe rellenar automáticamente Playnite y qué fuentes utilizar para obtener los datos. Considera la posibilidad de hacer clic en el logo de arriba y contribuir con actualizaciones a la base de datos de igdb.com para mejorar los datos que utiliza Playnite. Descargando metadatos… Importando juegos instalados… Importando juegos de {0}… Importando juegos emulados de {0} ... Descargando actualizaciones de la biblioteca… Escaneando el tamaño de los juegos en la librería… Escaneando el tamaño de los juegos importados… Actualización de biblioteca finalizada Liberando recursos… Configuración Configuración… Plataformas y Emuladores Configurar Emuladores… Administrador de la biblioteca… Herramientas Descargar Metadatos… Herramientas de software… Configurar integraciones… Abrir Cliente de Tercero Clientes de terceros Actualizar la Biblioteca de Juegos Cancelar la actualización de la biblioteca Actualizar carpetas emuladas Agregar Juego Manualmente… Escanear Automáticamente… Juego Emulado… Aplicación de la Microsoft Store Acerca de Playnite Enviar retroalimentación Cambiar al modo de pantalla completa Enlaces Ayuda Apoyar en Patreon Apoyar en Ko-fi Manual de usuario Documentación de SDK Reiniciar el sistema Apagar el sistema Suspender el sistema Hibernar el sistema Bloquear sistema Cerrar sesión de usuario Elegir un juego aleatorio Campos del juego que se mostrarán en el panel de detalles: Espacio entre elementos Mostrar imagen de fondo Ancho del borde del elemento de cuadrícula Falta una fuente para el icono del juego Falta una fuente para la carátula del juego Falta una fuente para la imagen de fondo del juego Espacio vertical de los detalles del juego Posición de los detalles de la vista de cuadrícula Posición de la lista de juegos en vista de detalles Dibujar separador entre paneles Alto de imagen de la carátula del juego Altura del icono de la lista de juegos Fuente de la aplicación Fuente monoespaciada Posición del panel de filtros Posición del panel de exploración Renderizado de la carátula Relación de aspecto objetivo Las siguientes opciones también afectan el renderizado de mosaicos en el modo de pantalla completa. Modo de estiramiento Caja de DVD Epic Games Store GOG Galaxy 2.0 IGDB Cuadrado Banner de Steam Carátula vertical de Steam Twitch * Requiere reiniciar para aplicar Configuración General Panel superior Apariencia Detalles del juego Disposición Avanzado Pantalla Completa Entrada Rendimiento Metadatos Actualizando Buscar Copia de seguridad Copiar datos de la biblioteca Restaurar copia de seguridad Importar cambios en la biblioteca automáticamente Ubicación del archivo de base de datos no es válida, se debe establecer una ruta de archivo adecuada. El nombre de la cuenta no puede estar vacía. Descargar metadatos después de importar los juegos Iniciar Playnite minimizado Iniciar Playnite al arrancar el ordenador Iniciar cerrado en la bandeja No se ha podido registrar Playnite para que se inicie al arrancar el ordenador. Iniciar en modo de pantalla completa Carga de imágenes asíncrona Mejora la suavidad del desplazamiento de las listas de juegos a cambio de que los tiempos de carga de las imágenes sean más lentos. Mostrar nombre del juego si no hay imagen de carátula Mostrar nombres de juegos en la vista de cuadrícula Oscurecer juegos no instalados Mostrar iconos de juegos en la vista de detalles Mostrar número de elementos en las descripciones de los grupos Mostrar solo los campos asignados en los paneles de filtro y explorador Desactivar aceleración por hardware Usar si la interfaz da tirones o problemas similares Mostrar juegos ocultos en listas de inicio rápido Afecta a los menús del sistema y al área de notificación. Número de elementos de lanzamiento rápido Usar la imagen de fondo del juego como fondo de la ventana Desenfocar el fondo Alta calidad Oscurecer el fondo Mostrar en la vista de cuadrícula Tema Perfil del tema Tema de pantalla completa Perfil del tema de pantalla completa Ubicación de la base de datos Estado de inicio de sesión: Configuración de Playnite Borrar caché web Puede resolver problemas encontrados al vincular cuentas. Mostrar icono en la bandeja del sistema Minimizar Playnite a la bandeja del sistema Minimizar Playnite a la bandeja al cerrar la ventana de la aplicación Al iniciar un juego: Después de cerrar el juego: Formatear tiempo de juego para indicar el número de días jugados Formatos de fechas: Esto lo desconectará de todos los servicios vinculados. Se requiere reiniciar la aplicación, ¿Quieres proceder? ¿Limpiar cache? Se requiere reiniciar Playnite cuando se cambia el tema Obtener más temas Crear un tema nuevo Obtener más extensiones Crear nueva extensión Ayúdanos a traducir Playnite Playnite debe reiniciarse para aplicar las nuevas configuraciones. ¿Reiniciar ahora? Reiniciar cancelara las tareas activas (descargas) en curso. ¿Reiniciar Playnite? Playnite no puede mover los archivos de su biblioteca automáticamente. Debe mover/copiar manualmente los archivos antes de cambiar la ubicación. Si no hay una biblioteca en la ubicación de destino, se creará una nueva. La nueva ubicación de la base de datos no se utilizará hasta que se reinicie Playnite. El tiempo de juego no se registrará si se establece la acción "Cerrar". Número de filas Número de columnas Número de filas en la Vista de Detalles Mostrar imagen de fondo en la pantalla principal No se aplica a juegos existentes sin volver a descargar los metadatos. Importar tiempo de juego de juegos en la biblioteca: Configura cuándo debe importar Playnite el tiempo de juego reportado por los plugins de la biblioteca para juegos en la base de datos de Playnite. Para poder utilizar esta característica es necesario el soporte de los plugins de la biblioteca a cargo de manejar los juegos. Siempre: Importa el tiempo de juego para nuevos juegos importados y existentes en la base de datos de Playnite. Sólo para los juegos recién importados: Importa tiempo de juego sólo para los nuevos juegos importados. Nunca: Nunca importa tiempo de juego bajo ninguna circunstancia. Siempre Sólo para los juegos recién importados Nunca Habilitar soporte para control en modo escritorio Botón guía abre el modo pantalla completa Configuración de descarga automática de metadatos para juegos recién importados. Pantalla objetivo Siempre usar la pantalla primaria Mostrar títulos de juegos Mostrar estado de la batería Mostrar porcentaje de la batería Mostrar reloj Ocultar el cursor del ratón Solo instalados en los filtros rápidos Indicaciones de botón Disposición Desplazamiento horizontal Selecciona una de las subsecciones No hay ajustes disponibles No se pudo cargar la configuración Estos scripts son ejecutados para todos los juegos en la libreria. Scripts individuales pueden ser asignados a cada juego por separado mientras editas los detalles del juego. Animar transiciones de imágenes de fondo Tamaño de letra Auto Aliased Escala de grises ClearType Ideal Pantalla Modo de formato de texto Modo de renderizado de texto Los métodos de renderizado y formato de texto no se utilizan actualmente para las descripciones de juegos. Precargar imágenes de fondo Si está habilitado, Playnite descargará ilustraciones de fondo mientras descarga metadatos, usando más espacio en disco y haciendo que las ilustraciones estén disponibles sin conexión. Si está deshabilitado, la ilustración de fondo se descarga solo cuando se necesita por primera vez, usando menos espacio, pero puede provocar un retraso antes de que se muestre la ilustración y algunas imágenes pueden no estar disponibles sin conexión. Cerrar automáticamente el cliente de terceros después de salir del juego Retraso de cierre del cliente (en segundos) No cerrar después de sesiones de juego más cortas que (en segundos) Cerrar automáticamente los siguientes clientes: Cerrar automáticamente clientes Importar lista de exclusión Mostrar aviso cuando se asignen medios del juego muy grandes Comando de apertura de directorio Organización de clasificación por edad preferida Actualizar el tamaño de instalación de juegos al actualizar la biblioteca Escanea y actualiza el tamaño de instalación de los juegos si se detecta que sus archivos han sido modificados desde el último escaneo Ninguno Rellenar Uniforme Uniforme para rellenar Izquierda Derecha Arriba Abajo Error de Importación Autenticación requerida Autenticación fallida Modo de redenderizado de vista web alternativo Use cuando se te tengan problemas con vistas web, por ejemplo, en diálogos de autenticación de integraciones. Carga parcial de grandes descripciones de juegos Las descripciones grandes pueden causar retrasos notables al seleccionar juegos. Cuando está habilitado, sólo una parte del texto de descripción se cargará inicialmente con una opción para cargar el resto bajo demanda. Importación de Metadatos Descargar Metadatos Usar configuración seleccionada para cualquier descarga de metadatos futura. También puede cambiarse en las opciones de aplicación. Asistente de Importación de Emulación Este asistente te ayudará a descargar e importar emuladores de consolas, así como a importar juegos emulados. Ten en cuenta que siempre puede agregar emuladores y/o juegos adicionales más tarde a través del menú principal (en el menú "Biblioteca" para la configuración del emulador y el menú "Agregar juegos" para juegos emulados). A continuación se muestra una lista de emuladores que Playnite puede reconocer y configurar automáticamente. Puede descargar los instaladores de los emuladores de sus sitios web. Una vez que hayas instalado los emuladores (manualmente), puedes importarlos en el cuadro de diálogo de configuración del emulador. Puedes importar cualquier emulador que esté instalado en tu PC haciendo clic en el botón 'Autodetectar desde carpeta...'. Playnite buscará en la carpeta seleccionada cualquier emulador conocido y ofrecerá la opción de importarlo. Puedes utilizar este botón varias veces para importar emuladores de diferentes carpetas. Los emuladores se añadirán al final de la lista actual. Puedes importar juegos haciendo clic en el botón "Escanear carpeta usando el emulador". Al seleccionar el emulador adecuado, Playnite sabrá qué tipos de archivos se deben escanear e importar. Puedes utilizar este botón varias veces para importar juegos de diferentes carpetas. Los juegos se añadirán al final de la lista actual. No hay emuladores seleccionados para la importación. No podrá importar automáticamente ningún juego emulado sin configurar primero los emuladores. ¿Seguro que quieres continuar y salir del proceso de importación? No hay emuladores configurados en Playnite. No puede importar juegos sin antes configurar el emulador y seleccionar los tipos de archivo apropiados. ¿Quieres agregar algunos emuladores ahora? Escanear carpeta usando emulador Seleccionar archivos Detectar automáticamente desde carpeta… Configurar Emuladores… Escaneando… Escaneando {0} ... Primera configuración Este asistente te ayudará a importar y configurar automáticamente las bibliotecas externas de juegos. Playnite puede importar automáticamente juegos de múltiples servicios, como Steam o GOG, y también mantener tu biblioteca al día actualizándola automáticamente cuando se inicie la aplicación. Ten en cuenta que puedes añadir manualmente cualquier juego de forma personalizada para cualquier plataforma más adelante, haciendo clic en el botón 'Playnite' del menú principal. Integración de bibliotecas A continuación se presentan la lista de algunas integraciones curadas de bibliotecas que Playnite soporta. Por favor, seleccione las que desea instalar. Se pueden instalar más integraciones más adelante desde el menú "Complementos". Configuración Finalizada La configuración inicial se ha completado. Recuerda que puedes cambiar todos los ajustes más tarde en el menú "Configuración". También puedes añadir cualquier otro juego más adelante haciendo clic en el menú del logotipo de Playnite. Error al descargar una o más extensiones. Puedes intentar volver a descargar las integraciones desde el menú de complementos después de finalizar el asistente de primera ejecución. Descargando integración de {0} ... Descargando lista de integraciones recomendadas… No se ha podido descargar la lista de integraciones recomendadas. Puedes intentar volver a descargar las integraciones más tarde a través del menú de complementos. Configurar Plataformas y Emuladores Configurar Emuladores Plataformas Plataforma Emuladores Emulador Añadir Platforma Seleccionar Icono Seleccionar carátula Seleccionar imagen Seleccionar elemento Seleccionar imagen de fondo Seleccionar Archivo Seleccionar URL Añadir Emulador Platforma(s) Soportadas ¿Desea guardar los cambios de plataforma? ¿Desea guardar los cambios de emulador? Ejecutable Argumentos Directorio de Trabajo Tipos de Archivos Soportados Importar Emuladores… Descargar Emuladores… Cargar argumentos preestablecidos del perfil conocido del emulador ¿Estás segura de que quieres eliminar el emulador {0}? Actualmente está siendo utilizado por {1} juego(s). ¿Estás segura de que quieres eliminar la plataforma {0}? Actualmente está siendo utilizado por {1} juego(s) y {2} emulador(es). Ayuda de Configuración Ordenar por Dirección de Ordenamiento Agrupar por Ascendente Descendente No agrupar Agrupar por biblioteca Agrupar por Categoría Agrupar por Plataforma Tipo de vista Vista Panel de exploración Panel de filtros Icono Icono de biblioteca Carátula Imágen de Fondo Orden de nombre Biblioteca Manual Nombre Unidad de instalación Nombre de la cuenta Plataforma Categoría Género Fecha de Lanzamiento Año de lanzamiento Desarrollador Etiqueta Editor Estado de instalación Coincidir con todos los filtros Si está habilitado, solo los juegos que usan todos los elementos en todos los filtros se incluirán en la vista. Si está deshabilitado, los juegos que usan cualquier elemento en cualquier filtro se incluirán en la vista. Instalado Instalado No instalado Oculto Favorito Habilitar soporte HDR Si está activado, el HDR se habilitará en la pantalla principal antes de comenzar el juego. Tenga en cuenta que el HDR no está soportado en su pantalla principal. Jugado por última vez Categoría Descripción Directorio de instalación Carátula Enlaces Ruta de Imágen, ROM o ISO Género Géneros Empresa Compañias Desarrollador Desarrolladores Editor Editores Categoría Categorías Etiqueta Etiquetas Característica Características Clasificación por edad Clasificación por edad Región Regiones Fuente Fuentes Actividad Reciente Error de la Base de Datos. Error al Abrir la Base de Datos. La Base de Datos no está abierta. No se puede acceder a la Base de Datos. El archivo "{0}" está siendo usado por otro proceso o se encuentra en una locación inaccesible. Error al crear el paquete de diagnósticos. Error al enviar automáticamente el paquete de diagnósticos. La información de diagnóstico se ha enviado correctamente. El paquete de diagnóstico se ha creado y enviado correctamente. Adjunte el siguiente ID a su informe de problemas: Error al importar los juegos de {0}. Error al importar juegos emulados de {0}. No se pueden buscar juegos con el perfil del emulador seleccionado. El perfil no contiene extensiones de archivos o plataformas. Playnite no pudo iniciarse. Cierre todas las demás instancias y vuelva a intentarlo. Error al aplicar el tema "{0}", perfil de color "{1}" {2} No se puede abrir el enlace, la URL no está en formato válido. No se pudo iniciar la aplicación. Error al inicializar el componente de vista web. Playnite no puede continuar con el proceso de inicio. Más información en https://playnite.link/cefstartup No se pueden importar emuladores porque falta o está dañado un archivo de definición. No se pudo ejecutar la acción del menú. Editar Detalles del Juego URL de la imagen Agregar Enlace Agregar rom Guardar Cambios Aplicar cambios de campo a los juegos que se están editando. Agregar Acción Borrar Acción Eliminar acción de Jugar Agregar Juegos Escanear Carpeta… Detectar Instalados Navegar… Abrir Playnite Ajustes de Perfil El nombre del juego no puede estar vacío. El directorio de seguimiento de acción de juego no puede estar vacío. El nombre del juego no puede estar vacío antes de buscar metadatos. Datos de juego inválidos Ingrese una URL web válida comenzando por http:// o https:// Seleccionar URL Error al descargar los metadatos: {0} Error de Descarga Borrar filtros Cuenta Privada Cuenta Pública Clave de API Error de Inicio Error del Tema Borrar Todo Instalando Desinstalando Iniciando Ejecutando URL inválida No hacer nada Minimizar Restaurar ventana Restaurar sólo cuando se inicia desde la interfaz Cerrar Cambiar Avanzado Nunca Estado de finalización Estados de finalización Puntuación del usuario Valoración de la crítica Valoración de la comunidad Scripts del juego Scripts de aplicación Scripts Complementos Fuentes de metadatos Extensiones ID de extensión Recargar Scripts PowerShell SDK Interactivo Todos los scripts fueron recargados correctamente. No se encontraron juegos para los criterios de búsqueda/filtro especificados No se encontraron elementos Cambiar al modo de escritorio Salir de Playnite Bibliotecas Actualizar todo Creado por: Versión: Actualizado: Módulo: Biblioteca Estadísticas Todos Ninguno Notificaciones Ancho Alto Tamaño Pequeña Normal Grande Más grande La más grande Predeterminado Seleccionar Seleccionar todos Deseleccionar todo Primero Aleatorio Selección de usuario Cargar más Transparente Contraer Expandir Contraer todas Expandir todas Otro Temas Argumentos del Emulador Argumentos incorporados Argumentos personalizados Argumentos del Emulador Adicionales Reemplazar argumentos del emulador Acción de Jugar Seleccionar metadatos para importar Seleccione Juegos para Importar Búsqueda de metadatos Actualización Disponible Cambios desde la última actualización Instalar Actualización Comprobar actualizaciones Error al actualizar Error al comprobar actualizaciones. No hay nuevas versiones disponibles. Error al descargar e instalar la actualización. Una tarea en segundo plano está actualmente en curso. ¿Quieres cancelarla y continuar con la actualización? Una tarea en segundo plano está actualmente en curso. ¿Quieres cancelarla y salir de Playnite? Una tarea en segundo plano está actualmente en curso. Cambiar modos cancelara la tarea, ¿Quieres cambiar de todos modos? Hay una actualización para Playnite disponible Volver a cargar la lista de temas Aplicar tema seleccionado Ver cambios de archivos Aplicar el tema automáticamente cuando el archivo de fuente cambie Entorno de ejecución de script Script para ejecutar antes de comenzar un juego Script para ejecutar después de salir de un juego Script para ejecutar después de que se inicia un juego Ejecutar al iniciar la aplicación Ejecutar al cerrar la aplicación Script de juego iniciando Script de juego iniciado Script de juego detenido Ejecutar script global Global Filtrada Actual Nuevo Probar script Mostrar sólo los elementos seleccionados Guardar como predeterminado Agregar a Favoritos Eliminar de Favoritos Ocultar este juego Eliminar de Oculto Habilitar soporte HDR Desactivar soporte HDR Editar… Calcular tamaño de instalación Calcular tamaño de instalación (Todos los juegos) Calcular tamaño de instalación (Sólo con datos faltantes) Tamaño de instalación Establecer categoría… Establecer estado de finalización Eliminar Iniciar Instalar Opciones de Juego Detalles Desinstalar Abrir la ubicación del juego Crear acceso directo en el escritorio Abrir manual Más Administrado por el complemento de la biblioteca. El proceso de inicio del juego será administrado por el complemento de la biblioteca responsable de este juego. No se ha encontrado información relevante sobre el juego '{0}' en la página especificada. Sugerencia: puede usar un proceso de descarga de metadatos más avanzado mientras edita un solo juego a través de la opción de menú "Editar". No disponible cuando alguna acción está en proceso. El texto de descripción es sensible a la sintaxis HTML El tiempo de juego se registra en segundos. El tamaño de instalación está indicado en bytes. La fecha de lanzamiento debe establecerse en el formato 'año-mes-día'. Los valores del mes y el día se pueden omitir. Valores entre 0 y 100 o dejar vacío para no puntuar. El desarrollo de Playnite está apoyado por estos patrocinadores y miembros de Ko-fi: Código, traductores y otros contribuyentes en ningún orden en particular: ¿Cancelar monitoreo de juegos? El monitoreo de la instalación se está ejecutando actualmente. ¿Quieres cancelar el proceso y devolver el juego al estado anterior? El monitoreo de la ejecución del juego se está ejecutando actualmente. ¿Quieres cancelar el proceso y devolver el juego al estado anterior? Tiempo Jugado Jugado por última vez {0}d {1}h {2}m {0} h {1} min {0} minutos {0} segundos Nunca Abriendo Modo de Escritorio… Abriendo Modo de Pantalla Completa… Cargando biblioteca de juegos… Calculando tamaño de instalación… Calculando tamaño de instalación de {0}… Error al instalar el archivo script. Script instalado correctamente. Instalar Script Error en el Script Error al ejecutar función de la extensión. Abrir carpeta de metadatos Calcular Calcula automáticamente el tamaño de instalación usando Roms si el juego tiene alguno o el directorio de instalación si se ha establecido El cliente {0} no está instalado. El cliente {0} se abrirá. Por favor, inicia sesión y cierra este mensaje. Esperando a que el usuario inicie sesión, por favor cierre esto cuando haya terminado… Directorio de instalación del juego no encontrado. Configuración de acción de juego inválida. Resolución de problemas de sincronización de cuentas Resolución de problemas Renombrar elemento Añadir nuevo elemento Introduce nombre Introduce nuevo nombre Menos de una hora De 1 a 10 horas De 10 a 100 horas De 100 a 500 horas De 500 a 1000 horas 1000+ Playnite debe reiniciarse para completar la instalación. ¿Quieres reiniciar ahora? La extensión no está empaquetada correctamente. El tema no está empaquetado correctamente. La extensión "{0}" no se pudo cargar correctamente. No se puede cargar la extensión "{0}", la versión actual de Playnite no es compatible. El tema "{0}" no se pudo cargar correctamente. No se puede cargar el tema "{0}", la versión actual de Playnite no es compatible. La extensión no se cargó correctamente. El tema no se cargó correctamente. Tema/Extensión está usando una versión de API no soportada. La instalación fue exitosa. ¿Instalar complemento? Genérica Error al instalar el complemento "{0}". No se pudo instalar la extensión. {0} ¿Quieres instalar una nueva extensión? {0} Por {1} Versión {2} ¿Quieres actualizar la extensión "{0}"? Versión actual: {1} Nueva versión: {2} No se pudo instalar el tema. {0} ¿Quieres instalar un tema nuevo? {0} Por {1} Versión {2} ¿Quieres actualizar el tema "{0}"? Versión actual: {1} Nueva versión: {2} Está a punto de salir de Playnite y navegar a la siguiente página web utilizando su navegador web predeterminado. ¿Quieres continuar? {0} Las imágenes seleccionadas pueden ser demasiado grandes para un rendimiento óptimo. El uso de imágenes muy grandes puede provocar una peor capacidad de respuesta de la interfaz de usuario y un mayor uso de la memoria. Resoluciones máximas recomendadas: Iconos: {0} megapíxeles Carátulas: {1} megapíxeles Fondos: {2} megapíxeles Advertencia de rendimiento No mostrar de nuevo El archivo con extensión {0} no es compatible. Extensión del archivo no válida El archivo de imagen seleccionado puede ser demasiado grande para un rendimiento óptimo. ¿Está seguro de que desea desinstalar el tema seleccionado? La desinstalación se pondrá en cola para el próximo inicio de la aplicación. Los temas predeterminados no se pueden desinstalar. Este tema no es compatible con esta versión de Playnite. ¿Está seguro de que desea desinstalar la extensión seleccionada? La desinstalación se pondrá en cola para el próximo inicio de la aplicación. Las extensiones predeterminadas no se pueden desinstalar. Esta extensión no es compatible con esta versión de Playnite. Directorio de instalación Directorio de datos Generando paquete de diagnóstico… Subiendo paquete de diagnóstico… Importar archivo… ¿Qué es esto? ¿Estás seguro de que quieres hacer esto? Tiempo total de juego Tiempo promedio de juego Tiempo máximo de juego Tamaño total de instalación Vista general Barra lateral Mostrar en la barra lateral Restablecer ajustes Todas los ajustes de la aplicación se restablecerán a valores predeterminados, excluyendo: - Ubicación de la base de datos - Lista de exclusión de importación - Configuración de extensiones, incluyendo las integraciones de bibliotecas. El reinicio de la aplicación se requiere para finalizar el proceso. ¿Quieres restablecer los ajustes? Para desarrolladores Extensiones externas Ingrese la ruta completa de la carpeta. Logros Foro Noticias Página de la tienda La configuración inicial no está completa. Playnite se reiniciará en el modo de escritorio para finalizar el procedimiento. Jugado recientemente Favoritos Más jugado Todo Hay filtros aplicados. Hay filtros adicionales aplicados. Resultados de la búsqueda de: Ya existe un elemento con el mismo nombre. Limitar selección al filtro actual Eligir otro Complementos… Instalado Ajustes de Extensiones Navegar Actualizaciones Actualizaciones ({0}) La gestión de extensiones y temas instalados, incluida su configuración, se ha trasladado a un nuevo menú "Complementos". Todas las extensiones de integración de bibliotecas actualmente instaladas se pueden configurar aquí. Si deseas instalar o desinstalar integraciones adicionales, use la opción "Complementos" desde el menú principal. Temas de Escritorio Temas de Pantalla completa Buscando... El complemento no es compatible con esta versión de Playnite. Error al descargar el paquete de instalación del complemento. Error al descargar el paquete de manifiesto de instalación del complemento. Se requiere reiniciar la aplicación para aplicar cambios pendientes. Este complemento está programado para su instalación. Instalar Desinstalar Ya instalado No se han encontrado nuevas actualizaciones de complementos. Actualizar complementos Registro de cambios no disponible Programado para instalación Descarga fallida Licencia rechazada Descargando {0}... Buscando actualizaciones para complementos ... Una o más actualizaciones de complementos están disponibles. Seleccione los elementos para actualizar Instancia de desarrollo de extensión Acuerdo de licencia de {0} Aceptar Rechazar Incluir las acciones de juego de la integración de la biblioteca. Seleccione una accion Modo de seguimiento Ruta de seguimiento Retraso inicial de seguimiento Frecuencia de seguimiento Link Archivo Emulador Script Predeterminado Proceso Carpeta Proceso original Registrar mensajes trace ¡Los siguientes cambios sobrescriben los datos para todos los juegos seleccionados actualmente! Ninguno Uniforme Sólo objetos Inicio y final solamente Sensibilidad de desplazamiento Desplazamiento suave Velocidad de la animación ¿Remover elemento? ¿Estás seguro de que quieres eliminar este elemento? Mostrar botones en el panel superior: Ajustes de vista general Ajustes de agrupación Ajustes de ordenación Filtros preestablecidos Posición de los elementos de plugin Ancho de separador de sección Mover el botón del menú principal a la barra lateral. Panel de exploración Selector de juego aleatorio Selector de juego aleatorio de vistas Seleccionar juego aleatorio de la vista Guarda los ajustes de agrupación y ordenación Mostrar como filtro rápido en modo de pantalla completa En los últimos 7 días En los últimos 31 días En los últimos 365 días Hace más de 365 días Configurar Guardar preselección Minimizar después de iniciar juego. Minimizar Playnite después de que se inicie un juego. ¡Desactivar esto puede llevar a problemas en juegos no recibiendo información de entrada en el inicio! Tamaño de fuente Tamaño de fuente pequeño Soporte para mandos de juegos Si se desactiva, Playnite no aceptará ninguna entrada de mando de juego. Desactiva esta opción si utilizas herramientas que traducen las entradas del mando de juego a entradas de ratón/teclado y se estan obteniendo entradas dobles en Playnite. Mostrar elementos en el menú principal: Botones X/A en vista principal invertidos. Intercambia botones para comenzar un juego y mostrar la página de detalles del juego en la vista principal. Intercambiar enlace de botón de confirmación/cancelación Invierte los enlaces del botón A/B para confirmación y cancelación. Sólo control primario Sólo aceptar entradas del control primario cuando esté activado. El botón de la guía enfoca Playnite Volumen de interfaz Volumen de fondo Silenciar cuando se este de fondo Error al inicializar la interfaz de audio. API de salida API utilizada para la salida de audio. Cambie si se esta experimentando problemas con el sonido. General Visuales Audio Disposición Menus Entrada {0} se está iniciando... {0} se está ejecutando... Mayúsculas Espacio Escalador de representación de la imagen Alternativo Equilibrado Calidad Calidad: Mejor calidad de imagen, lento y alto consumo de memoria. Equilibrado: Buena calidad, rápido y bajo consumo de memoria. Alternativo: Mejor calidad, velocidad media y bajo consumo de memoria. Selecciona archivo… Selecciona carpeta... Script de inicio Ten en cuenta que tanto las extensiones como los temas pueden afectar en gran medida el rendimiento, la estabilidad y la seguridad de Playnite. Si comienza a experimentar algunos problemas después de instalar un tema o una extensión, intenta deshabilitarlos/desinstalarlos primero para ver si son la raíz del problema. Seleccionar en el inicio Elegir al inicio Perfiles incorporados Perfil incorporado Perfiles personalizados Perfil personalizado Manejado por un script incorporado Especificación del emulador Especificación de la plataforma Especificación de la región Ejecutar antes de comenzar el emulador Ejecutar después de que se inicie emulador Ejecutar después de salir del emulador Ejecutable del emulador no encontrado. No se ha encontrado la especificación del emulador. Script de inicio del emulador no encontrado. Divididir como juegos separados Combinar en un juego Establecer plataforma Establecer región Escanear Carpeta Escanear configuraciones Patrones de exclusión para el escanéo checksum Los archivos que coincidan con los patrones especificados no se escanearán para la suma de comprobación y se coincidirán con el nombre del archivo. Consulta la página de ayuda del emulador para obtener más información. Escanear con emulador El nombre debe configurarse al guardar la nueva configuración. El emulador o el perfil emulador no se ha establecido. El directorio para escanear no se ha especificado o no existe. La configuración de escaneo no está configurada correctamente. Incluir en escaneo automático en masa Error al escanear la carpeta para emuladores. Error al analizar la(s) carpeta(s) para juegos emulados. Ocultar importados Perfiles a importar: Configuraciones de escaneo automático Guardar como configuración de escaneo automático Guarda la configuración para su uso posterior durante la actualización de la biblioteca. Las configuraciones guardadas se pueden administrar a través del menú "Configurar emuladores". Importar usando rutas relativas Si es posible importar archivos de juego usando rutas relativas a la carpeta de instalación de Playnite o la carpeta de instalación del emulador. Escanear subcarpetas Escanear dentro de archivos Fusionar archivos relacionados Combinar archivos de juegos relacionados, como discos de juegos individuales, bajo una sola entrada. Añadir escáner Añadir escáner guardado Iniciar escaneo Agregar la(s) configuración(s) de escaneo con los emuladores para escanear carpetas específicas. Asegurate de que los emuladores estén configurados correctamente antes de importar juegos (a través del menú Biblioteca -> Configurar Emuladores). Estado predeterminado asignado a juegos recién agregados Estado asignado a juegos jugados por primera vez Error al inicializar el tiempo de ejecución del script de PowerShell. Si eres usuario de Windows 7, (re)intente instalando PowerShell 5.1 para solucionar el problema. El filtro preestablecido con el nombre especificado ya existe. ¿Actualizar filtro con las nuevas configuraciones? Estas palabras se eliminarán de el principio del valor de nombre automáticamente rellenado: Utiliza esto para ignorar las palabras al principio de una cadena para propósitos de ordenación. El valor predeterminado es "The", "An", y "A". Rellena el nombre de ordenación de los juegos sin uno Ordenado Rellenando valores de nombre de ordenamiento… Se ha detectado que el servicio Nahimic se está ejecutando en su sistema. Se sabe que este servicio causa problemas de renderización en Playnite (y otras aplicaciones). Si encuentras alguna corrupción de gráficos u otros problemas de renderización en Playnite, se recomienda deshabilitar o desinstalar completamente el servicio Nahimic. Más información en https://playnite.link/nahimicsucks Playnite se está ejecutando con privilegios elevados (como administrador). ¡Esto no se recomienda, ya que da privilegios elevados a todas las extensiones instaladas y todos los juegos/aplicaciones iniciadas desde Playnite! Más información en https://playnite.link/adminfaq Mostrar advertencia si Playnite se está ejecutando con privilegios elevados Obtener el tamaño real en la unidad al calcular el tamaño de los juegos Si está activado, los escaneos serán más lentos y obtendrán el tamaño real que los archivos usan en la unidad. Si está desactivado, los escaneos serán más rápidos y usarán el tamaño de los archivos mismos. Los siguientes complementos se han reportado como potencialmente problemáticos, ya sea debido a un alto impacto en estabilidad/rendimiento o problemas de seguridad. Te recomendamos encarecidamente que los desinstales: {0} Excluir archivos en línea de escaneo Los archivos almacenados en el almacenamiento en la nube no serán escaneados e importados si no están disponibles localmente. Soporte únicamente para: Google Drive, Dropbox y OneDrive Escanear pero usando el método simplificado sin el contenido de archivo Los archivos se importaran pero utilizando un método menos preciso que no requiere que el contenido de los archivos se descargue y este presente localmente. Aplicar a todos Anular el estado de instalación Cuando se establece, Playnite ignorará el estado de instalación (incluyendo el directorio de instalación) establecido por el plugin de integración que importa este juego. Esta opción puede no funcionar completamente con plugins que usan un método específico de importación de juegos, a menos que también tengan en cuenta esta opción de anulación Sólo manualmente Una vez al día Una vez a la semana En cada inicio Buscar actualizaciones del programa Buscar actualizaciones de complementos Actualizar bibliotecas Escanear carpetas de emulación Incluir juegos ocultos Editar campos Seleccionar / Deseleccionar todo Abrir Activar Asignar Empieza a escribir para buscar juegos… [F1] para obtener ayuda A partir de # mostrara una lista de comandos disponibles. A partir de / aparecera una lista de proveedores de búsqueda/plugins disponibles. Escribiendo la palabra clave de búsqueda y terminando con SPACE cambia inmediatamente a esa búsqueda. TAB: cambiar acción ENTER: activar acción seleccionada SHIFT-ENTER: abrir menú de elementos Incluir juegos desinstalados Incluir juegos ocultos Juegos desinstalados incluidos Juegos desinstalados excluidos Juegos ocultos incluidos Juegos ocultos excluidos Jugar o instalar Ir a detalles Menú del juego Editar juego Abrir búsqueda Barra de búsqueda Botón de búsqueda Acción principal del juego Acción secundaria del juego CTRL-F abre la búsqueda global en lugar de enfocar la caja de búsqueda Guardar ajustes de filtro de juegos entre sesiones de búsqueda Proveedores de búsqueda Palabras clave por defecto Palabras clave personalizada Atajo global del sistema Búsqueda de Playnite Configuración de Extensiones Exclusiones Archivos excluidos relativos al directorio de escaneo Directorios excluidos relativos al directorio de escaneo Añadir archivo a la lista de exclusión Añadir directorio a la lista de exclusión Las exclusiones sólo pueden añadirse a las configuraciones guardadas del escáner. Se han añadido exclusiones al escáner "{0}". Reemplazar plataforma Cuando se establece el escáner asignará esta plataforma a todos los juegos, sobrescribiendo cualquier plataforma detectada automáticamente. Incluye comandos en la búsqueda por defecto Cuando se desactiva, los comandos no se incluirán en la búsqueda por defecto hasta que se utilice # el prefijo. Usar coincidencia difusa en el filtro de nombres Cuando está activado, el filtro de nombres coincidirá con los nombres del juego de la misma manera que la búsqueda global. La coincidencia estricta se puede forzar en un caso individual al utilizar el cáracter ! de prefijo en el filtro. Campos a mostrar para los resultados de juegos: Estado oculto Copia de seguridad de datos fue cancelada. Error en la copia de seguridad. Error de copia de seguridad de datos Copia de seguridad de datos en progreso… Restaurando datos desde la copia de seguridad… Error al restaurar los datos de la copia de seguridad. Configuración Biblioteca de juegos Media de la biblioteca de juegos Extensiones instaladas Datos de extensiones Temas instalados Selecciona los datos a restaurar del archivo de copia de seguridad especificado. Playnite se reiniciará automáticamente para iniciar el proceso de restauración de la copia de seguridad. Selecciona los elementos a incluir en los datos de la copia de seguridad. Los ajustes de la aplicación y los datos de la biblioteca de juegos se incluyen por defecto. Playnite se reiniciará automáticamente para iniciar el proceso de copia de seguridad. Copia de seguridad automática Frecuencia de copia de seguridad automática Carpeta de las copias de seguridad Rotando copias de seguridad Incluir datos adicionales: Es necesario establecer la carpeta de copia de seguridad si la copia automática está habilitada. Mostrar notificaciones sólo para los lanzamientos de parches Si se activa, sólo se notificarán las actualizaciones disponibles para la versión principal instalada actualmente. Las nuevas versiones principales no darán como resultado una notificación de actualización. Usar fechas relativas para la última semana Usar fechas relativas en formato "Hoy", "Ayer", etc. si la fecha es menor de una semana de edad. El formato de fecha especificado se utilizará para todas las demás fechas. Búsqueda de imágenes web Cadena de búsqueda de imágenes de icono Cadena de búsqueda de imagen de portada Cadena de búsqueda de imágenes de fondo Obteniendo información del complemento… No hay fuentes de metadatos disponibles Ajustes de Acción de Juego Usar ajustes de escáner Seleccionar perfil a iniciar Seleccionar emulador al iniciar Automático Siempre activado Siempre desactivado Soporte de accesibilidad (lector de pantalla) Menú de aplicación Menú del juego Carpeta del programa Directorio de datos de usuario Se ha detectado corrupción en el archivo de la biblioteca, Playnite se cerrará. Abre un nuevo problema en la página de GitHub de Playnite con una solicitud para corregir la corrupción en tus archivos. ¿Quieres guardar los cambios que realizados? Instalación portátil No se han detectado mandos de juego ================================================ FILE: source/Playnite/Localization/et_EE.xaml ================================================  Eesti Playnite'i keel Välju Filter aktiivne Filter keelatud Täiendavad filtrid Filtrid Filter Vigased andmed Salvesta muudatused? Koduleht www.playnite.link Lähtekood GitHub'is Loo diagn. pakk Saada diagn. teave Playnite'i teave Lõi Josef Němec Kategooria määramine Määra kategooriad Lisa kategooria Märgistatud - Määra kategooria Märgistamata - Eemalda kategooria Määratlemata - Ei muuda (mitme mängu korraga muutmisel) Kategooriata Platvormita Ups! Miskit läks valesti… Ilmnes parandamatu tõrge. Kui soovid aidata meil seda probleemi lahendada, kirjelda lühidalt enne kokkujooksmist tehtud toiminguid ja seejärel saada diagnostikateave. Kui oled internetiga ühendatud, siis laaditakse pakett analüüsimiseks Playnite'i serverisse. Võid ka klõpsata nupul "Teavita krahhist", et teenuses GitHub luua uus probleemiteade ja teavitada krahhist käsitsi. Täname abi eest. Laiendus "{0}" põhjustas parandamatu tõrke. Soovitame salvestada logifaili ja teavitada sellest probleemist laienduse arendajat. Probleemi järjepideval ilmnemisel keela laiendus. Laiendus "{0}" põhjustas parandamatu tõrke. Soovitame sellest probleemist teavitada laienduse arendajat. Probleemi järjepideval ilmnemisel keela laiendus. Tundmatu laiendus või teema põhjustas parandamatu tõrke. Soovitame keelata 3. osapoole lisad, isoleerida problemaatilise ja teavitada sellest veast lisa arendajat. Ilmnes parandamatu tõrge. Kui soovid aidata meil seda probleemi lahendada, saada meile diagnostikateave. Aitäh. Keela laiendus Salvesta logifail Saada diagn. teave Teavita krahhist Taaskäivita Playnite Taaskäivita turvarežiimis Keelates kõik 3. osapoole laiendused ja kasutades vaikimisi teemat. Sulge Playnite Enne krahhi tehtud toimingud (inglise keeles): Teegihaldur Eemalda mäng(ud)? Ei saa eemaldada - Mäng või installija on töötab. Ei saa desinstallida - Mäng töötab. Kas oled kindel, et tahad eemaldada mängu {0}? Kindel, et soovite eemaldada {0} mängu? Kas oled kindel, et soovid eemaldada mängu {0}? Kui valid valiku "lisa välistamiste loendisse", ei impordita mängu uuesti järgmisel teegi värskendamisel. Kas oled kindel, et soovid eemaldada {0} mängu? Kui valid valiku "lisa välistamiste loendisse", ei impordita mänge uuesti järgmisel teegi värskendamisel. Kas oled kindel, et tahad eemaldada {0} kirjet, mida hetkel ei kasutata? Kasutamata väljasid ei leitud. Jah (lisa välistamiste loendisse) Siin jaotises on muudatusi, mida pole salvestatud. Mänguteegi formaadi uuendamine Andmebaasi värskendamine ebaõnnestus. Ei saa mängude teeki värskendada. Nõutud on {0} MB vaba ruumi. MänguViga Ei saa mängu käivitada. Andmebaasist ei leitud: '{0}'. Ei saa käivitada mängu: {0} Ei saa tegevust käivitada: {0} Ei saa avada mängu asukohta: {0} Ei saanud tuvastada mängu installi suurust: {0} Viga installi suuruse skannimisel Installi suuruse arvutamisel esines {0} viga Ei saanud otseteed luua: {0} Ei saanud avada juhendit: {0} Ei saa installida mängu: {0} Ei saa desinstallida mängu: {0} Sobivaid mängu käivitustegevusi ei leitud. Emulaatori tegevuste kasutamisel veendu, et platvormi määratlused vastaksid mängu ja emulaatori seadistusele. Installimine pole veel programmis lubatud. Selle mängu eest vastutav teegi plugin on keelatud või installimata. Ametlikud metaandmed ei ole allalaadimiseks saadaval. Mäng pole valitud. Mängu skripti teostamine ebaõnnestus. Rakenduse skripti teostamine ebaõnnestus. Globaalse skripti teostamine ebaõnnestus. Emulaatori skripti teostamine ebaõnnestus. Mängu tegevuse skripti käivitamine ebaõnnestus. PowerShell 3.0 või uuem ei ole paigaldatud. Ei suutnud kindlaks määrata, kuidas mängu käivitada. Lubatud Keelatud Eemalda Eemalda kasutamata Nimeta ümber Kopeeri Lisa Vaikimisi ikoon Vaikimisi kaanepilt Vaikimisi taustapilt Lõpeta Järgmine Tagasi VALMIS TAGASI TÜHJENDA Tühjenda Hülga Hülga kõik Impordi Nimi Autor Moodul Seeriad Versioon Viimati mängitud Enim mängitud Mängukordade arv Installi suurus Kaust Märkmed Lisatud Lisamiskuupäev Muudetud Muutmiskuupäev Veebileht Rada OK Salvesta Sulge Tühista Kinnita Lähtesta Jah Ei Tere tulemast Kohalik kasutaja Üldine Meedia Viited Paigaldamine Tegevused Allalaadimine… Meedia allalaadimine… Laadimine… Liik Profiil Profiilid Eemalda Laadi alla Otsi Resolutsioon: Mis tahes Suumi Nimekirja vaade Kaanepildid Ruudustiku vaade Üksikasjalik vaade Kohandatud URL Erilised tänud Litsents Panustajad Playnite'i sulgemine… Täna Eile Esmaspäev Teisipäev Kolmapäev Neljapäev Reede Laupäev Pühapäev Eelmine nädal Eelmine kuu Eelmine aasta Rohkem kui aasta tagasi 0 kuni 100MB 100MB kuni 1GB 1GB kuni 5GB 5GB kuni 10GB 10GB kuni 20GB 20GB kuni 40GB 40GB kuni 100GB 100GB või rohkem Importimine on edukalt lõpetatud. Kõik mängud Mängu id Andmebaasi id Eelseadistused Veerg Veerud Rida Read Ei suutnud hankida ikooni Mängimise tegevusest. Tüübiga Fail tegevust pole olemas. Laadi alla ainult puuduvad metaandmed Selle suvandi lubamisel jäetakse metaandmete allalaadimine vahele nende andmeväljade puhul, kus juba on andmeid. Mängude valimine Palun vali, milliste mängude metaandmeid värskendada: Kõik mängud andmebaasist Kõik hetkel filtreeritud mängud Ainult valitud mängud Ühtegi metaandmete välja pole valitud Allalaadimiseks pole valitud ühtegi metaandmete välja. Valige vähemalt üks ja aktiveerige selle jaoks vähemalt üks metaandmete pakkuja. Ametlik pood IGDB Palun vali, millised väljad peaks Playnite automaatselt andmetega täitma ja millistest allikatest andmeid hankima. Palun kaalu ülaloleval logol klõpsamist ja teenuse igdb.com andmebaasi täiendamist, et täiustada Playnite'i kasutatavaid andmeid. Metaandmete allalaadimine… Paigaldatud mängude importimine… {0}'i mängude importimine… Emuleeritud mängude importimine kohast {0}… Teegi värskenduste allalaadimine… Teegis olevate mängude suuruse skannimine… Imporditud mängude suuruse skannimine… Teegi värskendamine valmis Ressursside vabastamine… Konfiguratioon Seaded… Platvormid ja Emulaatorid Seadista emulaatoreid… Teegihaldur… Tööriistad Laadi alla metaandmed… Tarkvara vahendid… Seadista integratsioone… Ava 3. osapoole klient 3. osapoole kliendid Värskenda mänguteeki Tühista teegi värskendamine Värskenda emuleeritud kaustasid Lisa mäng Käsitsi… Skanni automaatselt… Emuleeritud mäng… Microsoft Store'i rakendus… Playnite'i teave Saada tagasiside Lülitu täisekraani režiimi Viited Abi Toeta Patreonis Toeta platvormil Ko-fi Kasutusjuhend SDK dokumentatsioon Taaskäivita süsteem Lülita süsteem välja Peata süsteem Pane süsteem talveunne Lukustussüsteem Logi kasutaja välja Vali juhuslik mäng Üksikasjade paneelil kuvatavad mängu väljad: Üksuste vahe Ilusta ruudustiku üksuste taust Ruudustiku piirjoonte paksus Puuduva mänguikooni allikas Puuduva mängu kaanepildi allikas Puuduva mängu taustpildi allikas Mängu üksikasjade vertikaalne vahe Ruudustiku vaates üksikasjade asukoht Mängude loendi asukoht üksikasjade vaates Joonesta paneelide vahele eraldaja Mängude kaanepildi kõrgus Mängude loendi ikooni kõrgus Rakenduse font Ühelaiuste märkidega font Filtrite paneeli asukoht Exploreri paneeli asukoht Kaanepildi renderdamine Siht-kuvasuhe Järgnevad suvandid mõjutavad ka paanide kuvamist Täisekraani režiimis! Venitusrežiim DVD karp Epic Games Store GOG Galaxy 2.0 IGDB Ruut Steami bänner Steam vertikaalne kaanepilt Twitch * Rakendamiseks vajab taaskäivitamist Seaded Üldine Ülemine paneel Välimus Mängu üksikasjad Paigutus Täpsem Täisekraan Sisend Jõudlus Metaandmed Värskendamine Otsing Varundamine Varunda teegi andmed Taasta andmete varukoopia Impordi teegi muudatused automaatselt Vigane andmebaasifaili asukoht, sisestama peab õige failiraja. Konto nimi ei saa olla tühi. Pärast mängude importimist laadi alla metaandmed Käivita Playnite minimeerituna Arvuti käivitamisel käivita Playnite Käivita süsteemisalve minimeerituna Playnite'i registreerimine arvuti käivitumisel käivituma ebaõnnestus. Käivita täisekraanirežiimis Asünkrooniline piltide laadimine Võib parandada mänguloendite kerimise sujuvust, vastutasuks aeglasema piltide laadimisaja eest. Näita kaanepildi puudumisel mängu nime Ruudustiku vaates näita mängude nimesid Tumenda paigaldamata mängud Näita üksikasjalikus vaates mängude ikoone Näita rühmade kirjeldustes üksuste arvu Näita paneelidel Filtrid ja Explorer ainult määratud välju Keela riistvara graafikakiirendi Kasuta katkendliku kuva või sarnaste kasutajaliidese probleemide korral Näita kiirkäivitusloendites peidetud mänge Mõjutab Tööülesannete ja süsteemisalve menüüloendeid. Kiirkäivitusüksuste arv Kasuta akna taustana mängu taustapilti Hägusta tausta Kõrge kvaliteet Tumenda tausta Näita ruudustiku vaates Teema Teema profiil Täisekraani teema Täisekraani teema profiil Andmebaasi asukoht Sisselogimise olek: Playnite'i seaded Tühjenda veebi vahemälu Võib lahendada kontode linkimisel ilmnenud probleeme. Kuva süsteemisalves ikoon Minimeeri Playnite süsteemisalve Minimeeri Playnite süsteemisalve, kui rakenduse aken on suletud Kui mäng käivitub: Pärast mängu sulgemist: Mängimisaegu kuvades näita mängitud päevade arvu Kuupäevade formaadid: See logib sind välja kõikidest lingitud teenustest. Vajalik on rakenduse taaskäivitamine, kas tahad jätkata? Tühjenda vahemälu? Uue teema rakendamiseks on vajalik Playnite'i taaskäivitamine Hangi rohkem teemasid Loo uus teema Hangi rohkem laiendusi Loo uus laiendus Aidake meil tõlkida Playnite'i Uute seadete rakendamiseks tuleb Playnite taaskäivitada. Taaskäivitada kohe? Taaskäivitamine tühistab kõik käimasolevad aktiivsed toimingud (allalaadimised). Taaskäivita Playnite? Playnite ei saa teegi faile automaatselt teisaldada. Sa pead enne asukoha muutmist failid käsitsi teisaldama/kopeerima. Kui sihtkohas pole mingit teeki, luuakse uus. Uut andmebaasi asukohta hakatakse kasutama pärast Playnite'i taaskäivitamist. Mänguaega ei salvestata, kui on valitud "Sulge". Ridade arv Veergude arv Kuvatavaid ridu üksikasjade vaates Näita peaekraanil taustapilti Ei rakendu tagasiulatuvalt olemasolevatele mängudele ilma metaandmete uuesti allalaadimiseta. Impordi teegis olevate mängude mänguaeg: Seadistab, millal peaks Playnite importima Playnite'i andmebaasis olevate mängude teegi pluginate poolt teatatud mänguaja. Selle funktsiooni kasutamiseks peavad mängude haldamise eest vastutavad teegi pluginad seda toetama. Alati: Impordib äsja imporditud ja Playnite'i andmebaasis olemasolevate mängude mänguaja. Ainult äsja imporditud mängudel: Impordib ainult äsja imporditud mängude mänguaja. Mitte kunagi: Ei impordi mänguaega mitte mingil juhul. Alati Ainult äsja imporditud mängudel Mitte kunagi Luba juhtpuldi tugi Töölaua režiimis Nupp Guide avab Täisekraani režiimi Automaatse metaandmete allalaadimise sätted värskelt imporditud mängudele. Sihtkuvar Alati kasuta esmast kuvarit Näita mängude pealkirju Kuva aku olek Näita aku laetuse protsenti Näita kella Peida hiirekursor 'Ainult Installitud' kiirfiltrites Juhtpuldi nuppude stiil Paigutus Horisontaalne kerimine Vali üks alajaotis Sätteid pole saadaval Sätete laadimine ebaõnnestus Need skriptid teostatakse igal teegis oleval mängul. Igale mängule eraldi saab skripte määrata mängu üksikasju muutes. Animeeri taustapiltide üleminekud Kirja suurused Automaatne Sakiline Hallskaala ClearType Ideaalne Ekraan Teksti vormindamisrežiim Teksti renderdusrežiim Mängude kirjeldustes ei kasutata praegu teksti renderdamise ja vormindamise meetodeid. Eellaadi taustapildid Kui lubatud, laadib Playnite taustapildi alla metaandmete allalaadimise ajal, kasutades rohkem kettaruumi ja tehes kujutised võrguühenduseta kättesaadavaks. Kui see on keelatud, laaditakse taustapildid alla ainult esmavajadusel, kasutades vähem ruumi, kuid kujutiste kuvamisel võib tekkida viivitus ja mõned pildid ei pruugi olla võrguühenduseta saadaval. Pärast mängust väljumist sulge automaatselt kolmanda osapoole klient Kliendi sulgemise viivitus (sekundites) Ära sulge pärast mänguseansse, mis on lühemad kui (sekundites) Automaatselt sulge järgnevad kliendid: Autom. klientide sulgemine Impordi välistamiste loend Hoiata liiga suure mängumeedia määramisel Ava kaust käsk Eelistatud vanusepiirangu organisatsioon Värskenda mängude installi suurust teegi värskendamisel Skannib ja värskendab mängude installi suurust, kui tuvastatakse, et nende faile on pärast viimast skannimist muudetud Puudub Täida Säilita kuvasuhe Täida ja säilita kuvasuhe Vasakul Paremal Üleval All Viga importimisel Autentimine vajalik Autentimine ebaõnnestus Alternatiivne veebivaate kuvamisrežiim Kasuta, kui on probleeme veebivaadetega, näiteks integratsioonide autentimise dialoogiakendes. Suurte mängukirjelduste osaline laadimine Mahukad kirjeldused võivad mängude valimisel tekitada märgatavat viivitust. Kui lubatud, laaditakse esmalt ainult osa kirjelduse tekstist, ülejäänud saab laadida soovi korral. Metaandmete importimine Laadi alla metaandmed Määra valitud seadistus, et seda kasutataks kõigi tulevaste metaandmete allalaadimiste jaoks. Seda saab muuta ka rakenduse sätetes. Emulatsioonide importimise abiline See abiline juhendab sind läbi konsooli emulaatorite allalaadimise ja importimise ning emuleeritud mängude importimise protsessi. Pea meeles, et rohkem emulaatoreid ja/või mänge saab hiljem lisada peamenüü kaudu (emulaatori sätete jaoks menüü "Teek" ja emuleeritud mängude jaoks "Lisa mänge"). Allpool on loend emulaatoritest, mida Playnite suudab tuvastada ja automaatselt seadistada. Emulaatorite installijaid saab alla laadida nende veebisaitidelt. Kui oled emulaatorid (käsitsi) installinud, saad need importida emulaatori seadistamise dialoogiaknas. Mis tahes emulaatori, mis arvutis installitud, importimiseks klõpsa nupul 'Automaatselt tuvasta kaustast…'. Playnite otsib valitud kaustast tuntud emulaatoreid ja pakub võimalust neid importida. Emulaatorite importimiseks erinevatest kaustadest kasuta seda nuppu mitu korda. Emulaatorid lisatakse praeguse loendi lõppu. Sa saad mänge importida nupul 'Skanni kausta Emulaatorit kasutades' klõpsates. Sobiva emulaatori valimine ütleb Playnite'ile, milliseid failitüüpe tuleks skannida ja importida. Kui tahad mänge importida mitmest erinevast kaustast, võid seda nuppu kasutada mitu korda. Mängud lisatakse praeguse loendi lõppu. Importimiseks pole emulaatorit valitud. Enne emulaatorite seadistamist ei saa emuleeritud mänge automaatselt importida. Kas oled kindel, et tahad jätkata ja importimise protsessist väljuda? Playnite'is pole emulaatoreid seadistatud. Mänge ei saa importida, kui pole esmalt emulaatorit seadistanud ja sobivaid failitüüpe valinud. Kas tahad kohe emulaatoreid lisada? Skanni kausta Emulaatorit kasutades Vali failid Automaatselt tuvasta kaustast… Seadista emulaatoreid… Skannimine… Skannitakse: {0}… Esmakordne seadistamine See protsess juhendab väliste mänguteekide automaatsel importimisel ja konfigureerimisel. Playnite suudab mänge automaatselt importida mitmest mänguteenusest, näiteks Steam või GOG. Pea meeles, et hiljem saab peamenüüst mis tahes kohandatud või emuleeritud mängu mis tahes platvormi jaoks käsitsi lisada. Teegi integratsioon Järgnevalt on loetletud mõned Playnite'i teotatud teegi integratsioonid. Valige need, mida soovite paigaldada. Rohkem integratsioone saab hiljem paigaldada menüüst "Lisad". Seadistamine on lõpetatud Esmane seadistamine on lõpetatud. Pidage meeles, et kõiki sätteid saab hiljem muuta, samuti peamenüüst lisada täiendavaid integratsioone. Vähemalt ühe laienduse allalaadimine ebaõnnestus. Integratsioone saab lisade menüü kaudu alla laadida ka pärast abilise töö lõppu. {0} integratsiooni allalaadimine… Soovitatud integratsioonide loendi allalaadimine… Soovitatud integratsioonide loendi allalaadimine ebaõnnestus. Integratsioone saab hiljem alla laadida menüü Lisad kaudu. Platvormide ja emulaatorite seadistamine Emulaatorite seadistamine Platvormid Platvorm Emulaatorid Emulaator Lisa platvorm Vali ikoon Vali kaanepilt Pildi valimine Vali üksus Vali taustapilt Vali fail Vali URL Lisa emulaator Toetatud platvorm(id) Kas tahad platvormi muudatused salvestada? Kas tahad emulaatori muudatused salvestada? Täitmisprogramm Argumendid Töökaust Toetatud failitüübid Impordi emulaatoreid… Laadi emulaatorid alla… Laadi argumentide eelseadistus tuntud emulaatori profiilist Kas oled kindel, et tahad eemaldada emulaatori {0}? Seda kasutab hetkel {1} mäng(u). Kas oled kindel, et tahad eemaldada platvormi {0}? Seda kasutab hetkel {1} mäng(u) ja {2} emulaator(it). Sätete abi Sorteeri Sortimise suund Rühmita Kasvav Kahanev Ära rühmita Rühmita teegi järgi Rühmita kategooria järgi Rühmita platvormi järgi Vaate liik Vaade Exploreri paneel Filtreerimise paneel Ikoon Teegi ikoon Kaanepilt Taustapilt Sortimisnimi Teek Manuaal Nimi Installi ketas Konto nimi Platvorm Kategooria Žanr Väljalaske kuupäev Väljalaske aasta Arendaja Silt Väljaandja Installimise olek Sobitu kõikide filtritega Kui lubatud, kaasatakse vaates ainult mängud, mis kasutavad kõikide filtrite kõiki üksuseid. Kui keelatud, kaasatakse vaates mängud, mis kasutavad mistahes filtri mistahes üksust. Paigaldatud Paigaldatud Paigaldamata Peidetud Lemmik Luba HDR tugi Kui lubatud, lubatakse HDR põhiekraanil enne mängu käivitamist. Pane tähele, et sinu põhiekraan ei toeta HDR-i. Viimati mängitud Kategooria Kirjeldus Paigalduse asukoht Kaanepilt Viited Tõmmise, ROM-i või ISO rada Žanr Žanrid Ettevõte Ettevõtted Arendaja Arendajad Väljaandja Väljaandjad Kategooria Kategooriad Silt Sildid Funktsioon Funktsioonid Vanusepiirang Vanusepiirangud Piirkond Piirkonnad Allikas Allikad Viimatised tegevused Andmebaasi tõrge Teegi andmebaasi avamine ebaõnnestus. Andmebaas pole avatud. Ei pääse teegi andmebaasile ligi. Faili "{0}" kasutab mõni muu protsess või asub ligipääsetamatus kohas. Diagnostikapaki loomine ebaõnnestus. Diagnostikapaki automaatne üleslaadimine ebaõnnestus. Diagnostiline teave edukalt saadetud. Diagnostikapakk on edukalt loodud ja saadetud. Palun lisa oma probleemi raportile järgnev ID: Ei saanud importida mänge kohast {0}. Ei saanud importida emuleeritud mänge kohast {0}. Valitud emulaatori profiili järgi ei saa mänge otsida. Profiil ei sisalda faililaiendeid või platvorme. Playnite ei saanud käivituda. Palun sulge kõik muud programmi eksemplarid ja proovi uuesti. Ei saanud rakendada teemat "{0}", värviprofiil "{1}" {2} Ei saaviidet avada, URL on sobimatus vormingus. Ei saanud rakendust käivitada. Veebivaate komponendi käivitamine ebaõnnestus. Playnite ei saa käivitumise protsessiga edasi minna. Rohkem teavet lehel https://playnite.link/cefstartup Ei saa emulaatoreid importida puuduva või vigase definitsioonifaili tõttu. Menüü tegevuse käivitamine ebaõnnestus. Mängu üksikasjade muutmine Pildi URL Lisa viide Lisa ROM Salvesta muudatused Rakenda väljade muudatused hetkel muudetava(te)le mängu(de)le. Lisa tegevus Eemalda Eemalda mängimise tegevus Lisa mänge Skanni kausta… Tuvasta installitud Sirvi… Ava Playnite Profiili seaded Mängu nimi ei või olla tühi. Mängutegevuste jälgimise kaust ei tohi olla tühi. Mängu nimi ei tohi enne metaandmete otsimist olla tühi. Vigased mängu andmed Sisesta kehtiv veebi URL, mille alguses kas http:// või https:// Vali URL Metaandmete allalaadimine ebaõnnestus: {0} Allalaadimise viga Tühjenda filtrid Privaatne konto Avalik konto API võti Käivitamise viga Teema tõrge Tühjenda kõik Paigaldamine Desinstallimine Käivitamine Käivitamine Kehtetu URL Ära tee midagi Minimeeri Taasta aken Taasta aken ainult siis, kui käivitati kasutajaliidese kaudu Sulge Muuda Täpsem Mitte kunagi Lõpetamisolek Lõpetamisolekud Kasutaja skoor Kriitika skoor Kogukonna skoor Mängu skriptid Rakenduse skriptid Skriptid Pluginad Metaandmete allikad Laiendused Laienduse ID Taaslaadi skriptid Interaktiivne SDK PowerShell Kõik skriptid on uuesti laaditud. Mängud ei vastanud määratud otsingu-/filtrikriteeriumitele Ühtegi üksust ei leitud Lülitu töölaua režiimile Sulge Playnite Teegid Värskenda kõiki Autor: Versioon: Uuendatud: Moodul: Teek Statistika Kõik Puudub Teavitused Laius Kõrgus Suurus Väike Tavaline Suur Suurem Suurim Vaikimisi Vali Vali kõik Tühista kõik valikud Esimene Juhuslik Kasutaja valik Laadi rohkem Läbipaistev Ahenda Laienda Ahenda kõik Laienda kõik Muu Teemad Emulaatori argumendid Integreeritud argumendid Kohandatud argumendid Emulaatori lisaargumendid Kirjuta üle emulaatori argumendid Mängimise tegevus Metaandmete importimiseks valimine Mängude importimiseks valimine Metaandmete otsing Värskendus on saadaval Muudatused alates eelmisest värskendusest Laadi alla ja paigalda värskendus Otsi värskendusi Viga värskendamisel Uue versiooni olemasolu kontroll ebaõnnestus. Uut versiooni ei leitud, sul on ajakohane versioon. Värskenduse allalaadimine ja installimine ebaõnnestus. Mõni taustatöö on praegu pooleli. Kas tahad selle katkestada ja värskendamisega edasi minna? Mõni taustatöö on praegu pooleli. Kas tahad selle katkestada ja Playnite'i sulgeda? Mõni taustatöö on praegu pooleli. Režiimide lülitamisel see töö katkestatakse, kas tahad ikkagi režiimi lülitada? Playnite'ile on saadaval värskendus Laadi teemade loend uuesti Rakenda valitud teema Jälgi failide muudatusi Lähtefaili muutumisel rakenda teema automaatselt Skripti käivitumise aeg Teosta enne mängu käivitamist Teosta pärast mängu sulgemist Teosta pärast mängu käivitamist Teosta rakenduse käivitumisel Teosta rakenduse sulgumisel Mängu käivitumisaegne skript Mängu käivitumisjärgne skript Mängu lõpetamisaegne skript Teosta globaalne skript Globaalne Filtreeritud Käesolev Uus Katseta skripti Näita ainult valitud üksuseid. Salvesta vaikimisi Lisa lemmikutesse Eemalda lemmikutest Peida see mäng Eemalda peidust Luba HDR tugi Keela HDR tugi Muuda… Arvuta installi suurus Arvuta installi suurus (kõik mängud) Arvuta installi suurus (ainult puuduvad andmed) Installi suurus Määra kategooria… Määra lõpetamisolek Eemalda Mängi Paigalda Mängu suvandid Üksikasjad Desinstalli Ava paigalduse asukoht Loo töölaua otsetee Ava manuaal Rohkem Haldab teegi plugin Mängu käivitamise protsessi haldab selle mängu eest vastutav teegi plugin. Määratud lehelt ei leitud mängu '{0}' kohta asjakohast teavet. Vihje: Veel edasijõudnuma metaandmete allalaadimise protsessi saab kasutada, redigeerides üksikut mängu menüü "Muuda" kaudu. Pole saadaval, kui mõni tegevus on käimas. Kirjelduse tekst on HTML süntaksi tundlik Mänguaega loetakse sekundites. Installi suurus on näidatud baitides. Väljalaske kuupäev tuleb määrata 'aasta-kuu-päev' formaadis. Kuu ja päeva väärtused saab välja jätta. Skoori väärtus vahemikus 0 kuni 100 või tühi. Playnite'i arendamist on toetanud järgnevad patroonid ja Ko-fi liikmed: Koodi, lokaliseerimise ja muud kaasautorid mitte kindlas järjestuses: Kas tühistada mängu jälgimine? Installimise jälgimine on praegu käimas. Kas soovid protsessi tühistada ja mängu tagasi eelmisele olekule viia? Mängu käivitamise jälgimine on praegu käimas. Kas soovid protsessi tühistada ja mängu tagasi eelmisele olekule viia? Mängitud aeg Viimati mängitud {0}p {1}t {2}min {0}t {1}min {0} minutit {0} sekundit Pole mängitud Töölaua režiimi avamine… Täisekraani režiimi avamine… Mänguteegi laadimine… Installi suuruse arvutamine… Mängu {0} installi suuruse arvutamine… Skriptifaili installimine ebaõnnestus. Skript on edukalt paigaldatud. Installi skript Skripti viga Laienduse funktsiooni teostamine ebaõnnestus. Ava metaandmete kaust Arvuta Automaatselt arvutab installi suuruse, kasutades kas ROM-i, kui mängul see on, või installikausta, kui see on määratud Klient {0} pole installitud. Klient {0} nüüd avaneb. Palun logi sisse ja seejärel sulge see teade. Kasutaja sisselogimise ootel; palun sulge see teade, kui oled valmis… Mängu installimiskausta ei leitud. Vigane mängutegevuse seadistus. Konto sünkroonimise probleemide tõrkeotsing Probleemide tõrkeotsing Muuda nime Lisa uus üksus Sisestage nimetus Sisestage uus nimetus Vähem kui tund 1 kuni 10 tundi 10 kuni 100 tundi 100 kuni 500 tundi 500 kuni 1000 tundi 1000+ Installimise lõpuleviimiseks tuleb Playnite taaskäivitada. Kas tahad kohe taaskäivitada? Laiendus pole õigesti pakitud. Teema pole õigesti pakitud. Laienduse "{0}" laadimine ebaõnnestus. Ei saa laadida laiendust "{0}", see ei toeta praegust Playnite'i versiooni. Teema "{0}" laadimine ebaõnnestus. Ei saa laadida teemat "{0}", see ei toeta praegust Playnite'i versiooni. Laienduse laadimine ebaõnnestus. Teema laadimine ebaõnnestus. Teema/Laiendus kasutab mittetoetatud API versiooni. Paigaldamine oli edukas. Paigalda laiendus? Üldine Lisa "{0}" paigaldamine ebaõnnestus. Laiendust ei saanud paigaldada. {0} Kas tahad paigaldada uue laienduse? {0} Autor {1} Versioon {2} Kas tahad värskendada laienduse "{0}"? Praegune versioon: {1} Uus versioon: {2} Teemat ei saanud paigaldada. {0} Kas tahad paigaldada uue teema? {0} Autor {1} Versioon {2} Kas tahad värskendada teema "{0}"? Praegune versioon: {1} Uus versioon: {2} Sa oled Playnite'ist lahkumas ja avamas järgnevat veebiaadressi oma vaikimisi veebibrauseris. Kas tahad jätkata? {0} Valitud pilt/pildid võivad optimaalse jõudluse jaoks olla liiga suured. Väga suurte piltide kasutamine võib halvendada kasutajaliidese reageerimist ja suurendada mälukasutust. Suurimad soovitatavad resolutsioonid: Ikoonid: {0} megapikslit Kaanepildid: {1} megapikslit Taustapildid: {2} megapikslit Jõudluse hoiatus Ära enam näita Fail laiendiga {0} ei ühildu. Ühildumatu faililaiend Valitud pilt võib optimaalse jõudluse jaoks olla liiga suur. Kas oled kindel, et soovid valitud teema desinstallida? Desinstallimine teostatakse rakenduse järgmisel käivitumisel. Integreeritud teemasid ei saa desinstallida. See teema ei toeta seda Playnite'i versiooni. Kas oled kindel, et soovid valitud laienduse desinstallida? Desinstallimine teostatakse rakenduse järgmisel käivitumisel. Integreeritud laiendusi ei saa desinstallida. See laiendus ei toeta seda Playnite'i versiooni. Paigalduse asukoht Andmete kaust Diagnostikapaki loomine… Diagnostikapaki üleslaadimine… Impordi fail… Mis see on? Kindel, et soovid seda teha? Mänguaeg kokku Keskmine mänguaeg Kõrgeim mänguaeg Installi kogusuurus Ülevaade Külgriba Näita külgribal Lähtesta seaded Lähtestatakse kõik rakenduse sätted, välja arvatud: - Andmebaasi asukoht - Importimiste välistamiste loend - Laienduste sätte, k.a teekide integratsioonid Protsessi lõpetamiseks on vajalik rakenduse taaskäivitamine. Kas tahad sätete vaikimisi sätted taastada? Arendajale Välised laiendused Sisesta täielik kausta rada. Saavutused Foorum Uudised Poeleht Esmane seadistamine on lõpetamata. Playnite taaskäivitub Töölaua režiimis, et toiming lõpuni viia. Viimati mängitud Lemmikud Enim mängitud Kõik Rakendatud on filtreid. Rakendatud on lisafiltreid. Kuvatakse otsingutulemusi: Sellise nimega üksus on juba olemas. Piira valik praegusele filtrile Vali teine Lisad… Paigaldatud Laienduste seaded Sirvi Värskendused Värskendused ({0}) Paigaldatud laienduste ja teemade, k.a nende sätete, haldamine on liigutatud uude menüüsse "Lisad". Siin saab seadistada kõiki praegu installitud teegi integratsioonide laiendusi. Kui tahad integratsioone installida või desinstallida, kasuta menüüd "Lisad" peamenüüs. Töölaua teemad Täisekraani teemad Otsimine… Lisa ei ühildu Playnite'i selle versiooniga. Lisa installimispaki allalaadimine ebaõnnestus. Lisa installimismanifesti allalaadimine ebaõnnestus. Ootel muudatuste rakendamiseks on vaja rakendus taaskäivitada. See lisa on installimise järjekorras. Paigalda Desinstalli Juba installitud Lisade värskendusi ei leitud. Värskenda lisasid Muudatuste logi pole saadaval Installimiseks plaanitud Allalaadimine ebaõnnestus Litsentsist keelduti Allalaadimine {0}… Lisade värskenduste otsimine… Vähemalt ühele lisale on saadaval värskendus. Vali, mis värskendada Laienduse arendusversioon {0} litsentsileping Nõustu Keeldu Kaasa teegi integratsioonide mängutegevused Tegevuse valimine Jälgimise viis Jälgitav rada Algse jälgimise viivitus Jälgimise sagedus Link Fail Emulaator Skript Vaikimisi Protsess Kaust Esialgne protsess Logi jälgede teated Järgnevad muudatused kirjutavad üle kõigi hetkel valitud mängude andmed! Puudub Ühtne Ainult üksuste vahel Ainult alguses ja lõpus Kerimise tundlikkus Sujuv kerimine Animatsiooni kiirus Kas eemaldada üksus? Kas oled kindel, et tahad selle üksuse eemaldada? Näita ülemisel paneelil nuppe: Üldised vaate sätted Rühmitamise sätted Sortimise sätted Filtri eelseadistused Plugina üksuste asukoht Jaotiste eraldaja laius Liiguta peamenüü nupp külgribale Exploreri paneel Suvalise mängu valija Vaatest suvalise mängu valija Vali vaatest suvaline mäng Salvesta rühmitamise ja sortimise sätted Näita kiirfiltrina täisekraani režiimil Viimase 7 päeva jooksul Viimase 31 päeva jooksul Viimase 365 päeva jooksul Rohkem kui 365 päeva tagasi Seadista Salvesta eelseadistus Pärast mängu käivitamist minimeeri aken Pärast mängu käivitamist minimeeri Playnite. Selle keelamine võib kaasa tuua olukorra, kus mängud ei saa käivitumisel sisendeid kasutada! Fondi suurus Fondi suurus väike Juhtpuldi tugi Kui keelatud, ei võta Playnite vastu mitte mingisuguseid juhtpuldi sisendeid. Keela, kui kasutad vahendeid, mis tõlgivad juhtpuldi sisendeid hiire/klaviatuuri sisenditeks ja sa koged Playnite'is topelt sisendeid. Näita peamenüüs: Pööratud X/A nupu seostamine peamises vaates Vahetab mängu käivitamise ja mängu üksikasjade näitamise nuppude seostamised peamises vaates. Vahetab kinnitamise/tühistamise nuppude seostamise Pöörab A/B nuppude seostamise kinnitamiseks ja tühistamiseks. Ainult esmane juhtpult Kui lubatud, lubatakse ainult esmase juhtpuldi sisendid. Nupp Guide toob Playnite'i esile Liidese helitugevus Tausta helitugevus Vaigista, kui taustal Heliliidese käivitamine ebaõnnestus. Heliväljundi API Heliväljundi jaoks kasutatav API. Kui esineb probleeme heliga, muuda sätet. Üldine Väljanägemine Heli Paigutus Menüüd Sisend {0} on käivitumas… {0} töötab… Caps Tühik Piltide renderdamise skaleerija Alternatiivne Tasakaalustatud Kvaliteet Kvaliteet: Parim pildikvaliteet, aeglane, kõrge mälukasutus. Tasakaalustatud: Hea kvaliteet, kiire, madal mälukasutus. Alternatiivne: Parem kvaliteet, keskmine kiirus, madal mälukasutus. Vali fail… Vali kaust… Käivitusaegne skript Pane tähele, et nii laiendused kui ka teemad võivad Playnite'i jõudlust, stabiilsust ja turvalisust oluliselt mõjutada. Kui pärast teema või laienduse installimist ilmnevad probleemid, proovi need esmalt keelata/desinstallida, et näha, kas need on probleemi põhjustajaks. Vali käivitumisel Vali käivitumisel Integreeritud profiilid Integreeritud profiil Kohandatud profiilid Kohandatud profiil Käsitletud sisseehitatud skripti poolt Emulaatori määratlus Platvormi määratlus Regiooni määratlus Teosta enne emulaatori käivitamist Teosta pärast emulaatori käivitamist Teosta pärast emulaatori sulgemist Emulaatori täitmisprogrammi ei leitud. Emulaatori määratlust ei leitud. Emulaatori käivitusaegset skripti ei leitud. Eralda eri mängudeks Ühenda üheks mänguks Määra platvorm Määra regioon Skanni kausta Skannimise seadistused Kontrollsumma skannimise välistamise mustrid Määratud mustri(te)le vastavate failide kontrollsummat ei kontrollita ja need vastendatakse failinimega. Lisateabe saamiseks vaata emulaatori abilehte. Skanni emulaatoriga Uue seadistuse salvestamiseks on vaja määrata nimi. Emulaator või emulaatori profiil on määramata. Skannitav kaust on määratlemata või seda pole olemas. Skannimine pole õigesti seadistatud. Kaasa automaatses hulgiskannimises Ei saanud kaustast emulaatoreid skannida. Ei saanud kausta(de)st emuleeritud mänge skannida. Peida imporditud Profiilid, mis importida: Automaat-skannimise seadistused Salvesta automaat-skannimise seadistusena Salvesta seadistus hilisemaks kasutamiseks teegi värskendamisel. Salvestatud seadistusi saab hallata menüü "Seadista amulaatoreid" kaudu. Impordi suhtelisi radasid kasutades Võimalusel impordi mängufailid, kasutades Playnite'i installimiskausta või emulaatori installimiskausta suhtes suhtelisi radasid. Skanni alamkaustu Skanni arhiividest Ühenda seotud failid Ühenda seotud mängufailid, näiteks eraldi mänguplaadid, ühte mängukirjesse. Lisa skannija Lisa salvestatud skannija Käivita skannimine Kindlate kaustade skannimiseks lisa emulaatoritega skannimise seadistus(ed). Enne mängude importimist veendu, et emulaatorid oleks õigesti seadistatud (menüüs Teek -> Seadista emulaatoreid). Äsja lisatud mängudele määratud vaikeolek Esmakordselt mängitud mängudele määratud olek Ei saanud käivitada PowerShelli skripti. Kui kasutad Windows 7-t, proovi probleemi parandamiseks PowerShell 5.1 (uuesti) installida. Sellise nimega filtri eelseadistus on juba olemas. Kas uuendada eelseadistus uute sätetega? Need sõnad eemaldatakse automaatselt täidetud Sortimisnime algusest: Kasuta seda, et sortimise eesmärkidel eirata sõnede alguses teatud sõnu. Vaikimisi valikuteks on "The", "An" ja "A". Täida Sortimisnimi ilma selleta mängudel Sortimine Sortimisnime väärtuste täitmine… Sinu süsteemis on tuvastatud teenus Nahimic. On teada, et see teenus põhjustab Playnite'ile (ja muudele rakendustele) renderdamisprobleeme. Kui avastad Playnite'is vigase graafika või muid renderdusprobleeme, soovitame Nahimici teenuse keelata või täielikult desinstallida. Lisateavet aadressil https://playnite.link/nahimicsucks Playnite töötab kõrgendatud õigustega (administraatorina). See pole soovitatav, kuna see annab kõrgendatud õigused kõigile paigaldatud laiendustele ja Playnite'i kaudu käivitatud mängudele/rakendustele! Rohkem teavet lehel https://playnite.link/adminfaq Näita hoiatust, kui Playnite töötab administraatori õigustega Mängude suuruse arvutamisel hangi tegelik suurus kettal Kui lubatud, on skannimised aeglasemad ja hangitakse tegelik suurus, mida failid kettal enda alla võtavad. Kui keelatud, on skannimised kiiremad ja kasutatakse failide endi suurust. Järgnev(ad) lisa(d) on raporteeritud kui võimalikud probleemsed, kas stabiilsuse/jõudluse probleemide või turvalisuse tõttu. Soovitame tungivalt need desinstallida: {0} Ära skanni võrgus olevaid faile Pilves salvestatud faile ei skannita ega impordita, kui need pole kohalikult saadaval. Toetatud ainult teenustes: Google Drive, DropBox, OneDrive Skanni, aga kasutades lihtsustatud meetodit ilma faili sisuta Failid imporditakse, aga kasutatakse vähem täpset meetodit, mis ei nõua faili sisu allalaadimist ega kohalikku olemasolu. Rakenda kõigile Alista installituse olek Kui märgistatud, eirab Playnite mängu importiva integratsiooni plugina poolt määratud installituse olekut (sh. installikaust). See suvand ei pruugi täielikult töötada pluginatega, mis kasutavad kindlat mängude importimise meetodit, välja arvatud juhul kui need võtavad arvesse seda alistamise suvandit. Ainult käsitsi Kord päevas Kord nädalas Igal käivitumisel Kontrolli programmi värskendusi Kontrolli lisade värskendusi Värskenda teeke Skanni emulatsiooni kaustasid Kaasa peidetud mängud Muuda väljasid Vali kõik / tühista valik Ava Aktiveeri Määra Mängude otsimiseks alusta trükkimist… [F1] abi saamiseks Alustades märgiga #, kuvatakse saadaolevate käskude loend. Alustades märgiga /, kuvatakse saadaolevate otsingupakkujate/pluginate loend. Otsingu märksõna sisestamine ja TÜHIKuga lõpetamine lülitub kohe sellele otsingule. TAB: lülitustoiming ENTER: aktiveerib valitud toimingu SHIFT-ENTER: avab üksuse menüü Kaasa paigaldamata mängud Kaasa peidetud mängud Paigaldamata mängud kaasatud Paigaldamata mängud välistatud Peidetud mängud kaasatud Peidetud mängud välistatud Mängi või Installi Mine üksikasjade juurde Mängu menüü Muuda mängu üksikasju Ava otsing Otsingu kast Otsingu nupp Esmane mängu tegevus Teisene mängu tegevus CTRL-F avab otsingukasti fookustamise asemel otsingukasti Salvesta mängufiltri sätted otsinguseansside vahel Otsinguteenuse pakkujad Vaikimisi märksõna Kohandatud märksõna Süsteemiülene otseklahv Playnite'i otsing Laienduste sätted Välistamised Välistatud failid, skannimiskaustaga suhtelised Välistatud kaustad, skannimiskaustaga suhtelised Lisa fail välistamiste loendisse Lisa kaust välistamiste loendisse Välistamisi saab lisada ainult salvestatud skannijate seadistustele. Välistamised on lisatud skannijale "{0}". Kirjuta platvorm üle Kui märgistatud, määrab skannija selle platvormi kõigile mängudele, kirjutades üle automaatselt tuvastatud platvormid. Kaasa vaikimisi otsingusse käsud Kui keelatud, ei kaasata vaikimisi otsingusse käske ilma eesliidet # kasutamata. Nimefiltris kasuta hägusat vastendamist Kui märgistatud, vastendatakse nime filter mängude nimedega, sarnaselt globaalse otsinguga. Range vastendamise kasutamist saab sundida, lisades filtri ette ! tähemärgi. Mängude tulemuste jaoks näidatavad väljad: Peidetuse olek Andmete varundamine katkestati. Andmete varundamine ebaõnnestus. Viga andmete varundamisel Andmete varundamine käib… Andmete taastamine varukoopiast… Andmete varukoopiast taastamine ebaõnnestus. Sätted Mängude teek Mängude teegi meedia Paigaldatud laiendused Laienduste andmed Paigaldatud teemad Vali, millised andmed taastada määratletud varukoopiast. Playnite taaskäivitub automaatselt, et varukoopia taastamisega alustada. Vali, millised andmed varundamisse kaasata. Rakenduse sätted ja mängude teegi andmed kaasatakse vaikimisi. Playnite taaskäivitub automaatselt, et varundamisega alustada. Automaatne andmete varundamine Automaatsete varundamiste sagedus Varunduste kaust Aktiivseid varundamisi Kaasa lisaandmed: Kui automaatne varundamine on lubatud, tuleb määrata varundamiste kaust. Näita teavitusi ainult väiksemate värskenduspaikade korral Kui see on lubatud, näidatakse värskendusteadet ainult praegu installitud põhiväljaande jaoks saadaolevate värskenduste korral. Uued suuremad väljalasked ei too esile värskendusteatist. Eelmise nädala jaoks kasuta suhtelisi kuupäevi Kui kuupäev on vähem kui nädal vana, kasuta suhtelisi kuupäevi formaadis "Täna", "Eile" jne. Kõigi teiste kuupäevade puhul kasutatakse määratud kuupäeva formaati. Piltide otsing veebist Ikoonipildi otsingu sõne Kaanepildi otsingu sõne Taustpildi otsingu sõne Lisa teabe hankimine… Ühtki metaandmete allikat pole saadaval Mängimise tegevuse sätted Kasuta skannija sätteid Vali profiil käivitumisel Vali emulaator käivitumisel Automaatne Alati lubatud Alati keelatud Ligipääsetavuse (ekraanilugeja) tugi Rakenduse menüü Mängu menüü Programmi kaust Kasutaja andmete kaust Tuvastati rikutud teegifailid, Playnite nüüd sulgub. Ava Playnite'i GitHubi lehel uus vearaport taotlusega parandada oma rikutud failid. Kas tahad tehtud muudatused salvestada? Portatiivne paigaldus Juhtpulte ei tuvastatud ================================================ FILE: source/Playnite/Localization/fa_IR.xaml ================================================  فارسی زبان برنامه خروج فیلتر فعال فیلتر غیر فعال فیلتر های اضافی فیلترها فیلتر داده نامعتبر ذخیره تغییرات؟ صفحه اصلی در www.playnite.link کد منبع در گیت‌هاب ساخت بسته diag. ارسال اطلاعات diag. درباره Playnite ساخته شده توسط Josef Němec تعیین دسته تنظیم دسته‌بندی‌ها افزودن دسته‌بندی بررسی شده - اختصاص دادن دسته‌بندی انتخاب نشده - زدایش دسته‌بندی نامعین - بدون تغییر (هنگام ویرایش چندین بازی) بدون دسته‌بندی بدون سکو اوه! مشکلی پیش آمد... یک خطای غیرقابل بازگشت رخ داده است. اگر مایلید به ما در رفع این مشکل کمک کنید، لطفاً اقدامات انجام شده قبل از خرابی را به طور خلاصه توضیح دهید و سپس اطلاعات تشخیصی را ارسال کنید. اگر آنلاین هستید، بسته برای تجزیه و تحلیل به سرور برنامه ارسال می‌شود. در غیر این صورت، می‌توانید روی دکمه "گزارش خرابی" کلیک کنید تا یک مشکل جدید در گیت‌هاب ایجاد کرده و خرابی را به صورت دستی گزارش دهید. از همکاری شما متشکریم. افزونه "{0}" باعث ایجاد یک خطای غیرقابل بازگشت شد. توصیه می‌کنیم فایل گزارش را ذخیره کرده و مشکل را به توسعه‌دهنده افزونه گزارش دهید. اگر مشکل همچنان تکرار می‌شود، افزونه را غیرفعال کنید. افزونه‌ی "{0}" باعث خطایی بازیابی‌ناپذیر شد. ما سفارش می‌کنیم مشکل را به سازنده‌ی افزونه گزارش دهید. اگر مشکل همچنان رخ می‌دهد، افزونه را ازکاربیاندازید. افزونه یا زمینه‌ی ناشناخته باعث خطایی بازیابی‌ناپذیر شد. ما سفارش می‌کنیم افزونه‌ی شخص ثالث را ازکارانداخته، مشکل‌ساز را جدا کرده و مشکل را به سازنده‌ی افزونه گزارش دهید. خطایی بازیابی‌ناپذیر رخ داد. اگر میخواهید در رفع این مشکل کمک کنید، لطفاً اطلاعات تشخیصی را بفرستید. سپاسگزاریم. ازکارانداختن افزونه ذخیره پرونده‌ی گزارش‌ها فرستادن اطلاعات .diag گزارش خرابی بازراه‌اندازی Playnite بازراه‌اندازی در حالت امن ازکارانداختن همه‌ی افزونه‌های شخص ثالث و بکاربردن زمینه‌ی پیش‌فرض. خروج از Playnite اقدامات انجام شده قبل از اشکال (به انگلیسی): مدیر کتابخانه زدایش بازی(ها)؟ نمی‌توان زدود - بازی یا نصب‌کننده در حال اجرا است. نمی‌توان زدود - بازی در حال اجرا است. آیا مطمئن هستید که می‌خواهید {0} را بزدایید؟ آیا مطمئن‌ هستید که می‌خواهید {0} بازی را بزدایید؟ آیا مطمئن هستید که می‌خواهید {0} را بزدایید؟ گزینش گزینه‌ی "افزودن به فهرست استثناء" از درون‌برد شدن دوباره‌ی بازی پس از به‌روز شدن کتابخانه پیشگیری خواهد کرد. آیا مطمئن هستید که می‌خواهید {0} بازی را بزدایید؟ گزینش گزینه‌ی "افزودن به فهرست استثناء" از درون‌برد شدن دوباره‌ی بازی‌ها پس از به‌روز شدن کتابخانه پیشگیری خواهد کرد. مطمئن هستید که می‌خواهید {0} ورودی را که هم‌اکنون بکارنمی‌روند بزدایید؟ فیلد استفاده نشده یافت نشد. آره (افزودن به فهرست استثناء) در این بخش تغییرات ذخیره نشده وجود دارد به‌روزرسانی چارچوب کتابخانه‌ی بازی... ناکامی در به‌روزرسانی پایگاه داده. نمی‌توان کتابخانه‌ی بازی را به‌روز کرد. {0} مگابایت فضای آزاد نیاز است. خطا بازی نمی‌توان بازی را آغاز کرد. '{0}' در پایگاه داده یافت نشد. نمی‌توان بازی را آغاز کرد: {0} نمی‌توان کنش را آغاز کرد: {0} نمی‌توان مکان بازی را گشود: {0} ناتوانی در شناسایی اندازه‌ی نصب بازی: {0} خطای اسکن اندازه‌ی نصب هنگام اسکن اندازه‌ی نصب، {0} چند خطا وجود داشت ناکامی در ساخت میانبر: {0} دستی باز نشد: {0} نمی‌توان بازی را نصب کرد: {0} نمی‌توان بازی را زدود: {0} هیچ کنش راه‌اندازی بازی معتبری یافت نشد. هنگام بکاربری کنش‌های شبیه‌ساز، مطمئن شوید که تعاریف سکو میان بازی و پیکربندی شبیه‌ساز مطابقت دارند. پیاده‌سازی نصب در دسترس نیست. افزونه‌ی کتابخانه مسئول این بازی ازکارافتاده است یا نصب نشده است. بارگیری رسمی فراداده در دسترس نیست. هیچ بازی‌ای برگزیده نشده است. ناکامی در اجرای اسکریپت بازی. ناکامی در اجرای اسکریپت برنامه. ناکامی در اجرای اسکریپت سراسری. ناکامی در اجرای اسکریپت شبیه‌ساز. ناکامی در اجرای کنش اسکریپت بازی. PowerShell 3.0 یا تازه‌تر نصب نشده است. ناتوانی در مشخص کردن شیوه‌ی آغاز بازی. بکارافتاده ازکارافتاده زدایش زدایش بکارنبرده‌ها بازنامگذاری روگرفت افزودن آیکن پیش‌فرض تصویر کاور پیش‌فرض پس‌زمینه پیش‌فرض پایان بعدی قبلی انجامید بازگشت پاک کردن پاک کردن رد کردن رد کردن همه درون‌برد نام سازنده ماژول سری نسخه آخرین بازی شده بیشترین بازی شده شمار نشست بازی اندازه‌ی نصب پوشه یادداشت‌ها افزوده شد تاریخ افزوده شدن دگرگون شده تاریخ دگرگونی تارنما مسیر باشه ذخیره بستن لغو تأیید بازنشانی آره نه خوش آمدید کاربر بومی عمومی رسانه پیوند‌ها نصب کنش‌ها بارگيری... بارگیری رسانه… بارکردن… گونه نمایه نمایه‌ها زدایش بارگیری جستجو وضوح: هر بزرگنمايی نمای فهرست کاورها نمای توری نمای جزییات سفارشی URL سپاس ویژه پروانه مشارکت‌کنندگان خروج از Playnite... امروز دیروز دوشنبه سه‌شنبه چهارشنبه پنجشنبه جمعه شنبه یکشنبه هفته‌‌ی گذشته ماه گذشته سال گذشته بیش از یک سال پیش ۰ تا ۱۰۰ مگابایت ۱۰۰ مگابایت تا ۱ گیگابایت ۱ تا ۵ گیگابایت ۵ تا ۱۰ گیگابایت ۱۰ تا ۲۰ گیگابایت ۲۰ تا ۴۰ گیگابایت ۴۰ تا ۱۰۰ گیگابایت ۱۰۰ گیگابایت یا بیشتر درون‌برد با موفقیت انجام شد. همه‌ی بازی‌ها شناسه‌ی بازی شناسه‌ی پایگاه داده پیش‌نشانده‌ها ستون ستون‌ها ردیف ردیف‌ها نماد از کنش بازی دریافت نشد. کنشی از گونه‌ی پرونده وجود ندارد. تنها بارگیری فراداده‌های گمشده بکاراندازی این گزینه از بارگیری فراداده برای بخش‌های داده که هم‌اکنون حاوی اطلاعات هستند گذر خواهد کرد. گزینش بازی‌ها لطفاً انتخاب کنید کدام بازی را باید با ابرداده جدید به روز کنید: همه بازی ها از پایگاه داده همه بازی های فیلتر شده فعلی فقط بازی های انتخاب شده هیچ میدان فراداده‌ای برگزیده نشده است. هیچ میدان فراداده‌ای برای بارگیری برگزیده نشده است. خواهشمندیم دست‌کم یکی را برگزینید و دست‌کم یک فراهم‌آورندهٔ‌ فراداده برایش فعال کنید. فروشگاه رسمی IGDB لطفا برگزینید کدام بخش‌ها باید خودکار به دست playnite پر شوند و کدام بنمایه‌ها باید برای گردآوری داده بکارروند. لطفاً روی لوگو فوق کلیک کنید و به منظور بروزرسانی در استفاده از Playnite، به پایگاه داده igdb.com کمک کنید. در حال بارگیری ابرداده... درون‌بردن بازی‌های نصب شده… درون‌بردن {0} بازی… درون‌بردن بازی‌های شبیه‌سازی شده از {0}… بارگرفتن به‌روزرسانی‌های کتابخانه... اسکن اندازه‌ی بازی‌ها در کتابخانه… اسکن اندازه‌ی بازی‌های درون‌برده… بروزرسانی کتابخانه شد. رهش منابع... پیکربندی تنظیمات… سکوها و شبیه‌سازها پیکربندی شبیه‌سازها… مدیر کتابخانه… ابزارها بارگیری فراداده… ابزارهای نرم‌افزار… پیکربندی یکپارچگی‌ها… گشودن کلاینت شخص ثالث کلاینت‌های شخص ثالث به‌روزرسانی کتابخانه بازی لغو به‌روزرسانی کتابخانه به‌روزرسانی پوشه‌های شبیه‌سازی‌شده افزودن بازی دستی… اسکن خودکار… بازی شبیه‌سازی‌شده… برنامه فروشگاه مایکروسافت… درباره‌ی Playnite فرستادن بازخورد تعویض به حالت تمام‌صفحه پیوند‌ها کمک پشتیبانی در Patreon پشتیبانی در Ko-fi راهنمای کاربر مستندات SDK بازراه‌اندازی رایانه خاموش کردن رایانه تعلیق رایانه فرستادن رایانه به خواب زمستانی قفل کردن رایانه خروج کاربر گزینش یک بازی تصادفی بخش‌های بازی برای نمایش در پهنه‌ی جزئیات: فاصله آیتم کشیدن پس‌زمینه‌ی آیتم توری پهنای مرز آیتم توری منبع نماد بازی گمشده منبع کاور بازی گمشده منبع پس‌زمینه بازی گمشده فاصله‌ی عمودی تا جزئیات بازی جایگاه جزئیات نمای توری جایگاه نمای جزئیات فهرست بازی کشیدن میان پهنه‌های جداگر بلندای کاور بازی بلندای نماد فهرست بازی فونت برنامه فونت هم‌پهنا جایگاه پهنه‌ی پالایه جایگاه پهنه‌ی کاوشگر نماپردازش کاور هنری نسبت نما‌ی هدف گزینه‌های زیر به نماپردازش کاشی‌ها در حالت تمام‌صفحه نیز تاثیر می‌گذارند! حالت کشش جعبه‌ی DVD فروشگاه Epic Games GOG Galaxy ۲٫۰ IGDB مربع بنر Steam کاور عمودی Steam توییچ * برای بکاربستن نیازمند بازراه‌اندازی است تنظیمات عمومی پهنه‌ی بالایی ظاهر جزییات بازی چیدمان پیشرفته تمام‌صفحه درونداد کارایی فراداده به‌روزرسانیدن جستجو پشتیبان پشتیبان‌گیری از داده‌های کتابخانه بازیابی نسخهٔ پشتیبان داده درون‌برد خودکار تغییرات در کتابخانه مکان پرونده‌ی پایگاه داده نامعتبر است، مسیر پرونده‌ی درستی باید نشانده شود. نام حساب نمی‌تواند خالی باشد. پس از درون‌بردن بازی‌ها، فراداده را بارگیرید آغاز Playnite در حالت کمینه هنگام راه‌اندازی رایانه Playnite آغاز شود راه‌اندازی درون سینی ناکامی در ثبت Playnite برای آغاز هنگام راه‌اندازی رایانه. آغاز در حالت تمام‌صفحه بارکردن ناهمگام تصویر به ازای کندتر شدن زمان بارکنش تصویر، می‌تواند نرمی پیمایش فهرست‌های بازی را بهتر کند. اگر کاور هنری گمشده است نام بازی را نمایش بده نمایش نام بازی‌ها در نمای توری تاریک کردن بازی‌های نصب نشده نمایش نمادهای بازی در فهرست نمای جزئیات نمایش شمار آیتم‌ها در توضیحات گروه تنها نمایش بخش‌های واگذاشته در پالایه و پهنه‌های کاوشگر ازکارانداختن شتاب سخت‌افزاری بکاربردن هنگام تجربه‌ی تپغ یا مشکل‌های همانند آن در میانای کاربری نمایش بازی‌های پنهان در فهرست آغاز تند روی فهرست پرش و فهرست‌های گزینگان سینی تاثیر می‌گذارد. شمار آیتم‌های آغاز تند بکاربردن تصویر پس‌زمینه‌ی بازی بعنوان پس‌زمینه‌ی پنجره تاری پس‌زمینه کیفیت بالا تاریکی پس‌زمینه نمایش در نمای توری زمینه نمایه‌ی زمینه زمینه‌ی تمام‌صفحه نمایه‌ی زمینه‌ی تمام‌صفحه مکان پایگاه داده وضیعت ورود: تنظیمات Playnite پاک کردن حافظه پنهان وب شاید مشکلات مربوط به هنگام پیوند حساب‌ها را حل کند. نمایش نماد سینی رایانه کمینه کردن Playnite در سینی رایانه کمینه کردن Playnite در سینی رایانه هنگام بسته شدن پنجره‌ی برنامه هنگام راه‌اندازی بازی: پس از بسته شدن بازی: قالب بستن زمان بازی برای نمایش شمار روزهای بازی شده چارچوب‌های تاریخ: خروج از حساب شما را از همه‌ی خدمات پیوسته خارج می کند. برنامه به بازراه‌اندازی نیاز دارد، آیا می‌خواهید ادامه دهید؟ حافظه پنهان پاک شود؟ Playnite برای بکاربستن زمینه‌ی نو به بازراه‌اندازی نیاز دارد دریافت زمینه‌های بیشتر ساخت زمینه‌ی نو گرفتن افزونه‌های بیشتر ساخت افزونه‌ی نو در ترجمه‌ی Playnite به ما کمک کنید Playnite برای بکاربستن تنظیمات نو نیاز به بازراه‌اندازی دارد. اکنون بازراه‌اندازی شود؟ بازراه‌اندازی هرگونه کنش فعال (بارگیری‌ها) در حال انجام را لغو خواهد کرد. بازراه‌اندازی Playnite؟ Playnite نمی تواند پرونده‌های کتابخانه‌ی شما را خودکار جابه‌جا کند. شما باید پیش از دگرگون کردن مکان پرونده‌ها، آنها را دستی روگرفت یا جابه‌جا کنید. اگر در مکان هدف کتابخانه‌ای نباشد، یک کتابخانه‌ی نو ساخته خواهد شد. تا زمانی که Playnite بازراه‌اندازی نشود مکان پایگاه داده‌ی نو بکار نخواهد رفت. اگر کنش "بستن" نشانده باشد زمان بازی ضبط نخواهد شد. شمار ردیف‌ها شمار ستون‌ها شمار ردیف‌های نمای جزئیات نمایش تصویر پس‌زمینه در صفحه‌ی اصلی بدون بازبارگیری فراداده، پس‌نگرانه برای بازی‌های موجود بکاربسته نمی‌شود. درون‌برد زمان بازیِ بازی‌ها در کتابخانه: برنامه مشخص می‌کند چه زمانی باید زمان بازی گزارش شده توسط افزونه‌های کتابخانه برای بازی‌ها در پایگاه داده برنامه را وارد کند. برای استفاده از این ویژگی، پشتیبانی توسط افزونه‌های کتابخانه‌ای که مسئولیت مدیریت بازی‌ها را بر عهده دارند، نیاز است. همیشه: زمان بازی را برای بازی‌های جدید وارد شده و موجود در پایگاه داده برنامه وارد می‌کند. فقط برای بازی‌های تازه وارد شده: فقط زمان بازی برای بازی‌های جدید وارد شده وارد می‌شود. هرگز: هرگز زمان بازی را وارد نمی‌کند. همیشه تنها برای بازی‌های تازه درون‌برد شده هرگز بکاراندازی پشتیبانی دسته‌ی بازی در حالت دسکتاپ دکمه‌ی راهنما حالت تمام‌صفحه را می‌گشاید تنظیمات بارگیری خودکار فراداده برای بازی‌های تازه درون‌برد شده. نمایشگر هدف همیشه نمایشگر اصلی را بکارببر نمایش عنوان‌های بازی نمایش وضعیت باتری نمایش درصد باتری نمایش ساعت پنهان کردن نشانگر موشی نصب شده تنها در پالایه‌های تند پیامواره‌های دکمه چیدمان پیمایش افقی یکی از زیرمجموعه‌ها را برگزینید تنظیماتی در دسترس نیست ناکامی در بارکردن تنظیمات این اسکریپت‌ها برای هر بازی در کتابخانه اجرا می شوند. هنگام ویرایش جزئیات بازی می‌توان جداگانه برای هر بازی اسکریپتی منحصر به فرد واگذاشت. پویانماییدن تَرایِش تصویر پس‌زمینه اندازه‌ی فونت خودکار دندانه‌ای شده درجه‌ی خاکستری ClearType آرمانی نمایشگر حالت قالب‌بندی فونت حالت نماپردازش نوشتار روش‌های نماپردازش و قالب‌بندی نوشتار هم‌اکنون برای توضیحات بازی بکار نمی‌روند. پیش‌بارکردن تصویرهای پس‌زمینه در صورت فعال بودن، Playnite هنگام بارگیری متاداده، آثار هنری پس‌زمینه را نیز بارگیری می‌کند. این کار فضای دیسک بیشتری را مصرف می‌کند، اما آثار هنری در زمان عدم اتصال به اینترنت در دسترس خواهند بود. در صورت غیرفعال بودن، آثار هنری پس‌زمینه تنها زمانی که اولین بار مورد نیاز هستند بارگیری می‌شوند. این کار فضای کمتری را مصرف می‌کند، اما ممکن است باعث تأخیر در نمایش آثار هنری شود و برخی از تصاویر در حالت عدم اتصال به اینترنت در دسترس نباشند. پس از خروج از بازی، کلاینت شخص ثالث خودکار بسته شود دیرکرد خاموش کردن کلاینت (به ثانیه) پس از نشست‌های بازی کوتاه‌تر از این، نبند (به ثانیه) خودکار کلاینت‌های زیر را ببند: خودکار بستن کلاینت‌ها فهرست استثنای درون‌برد نمایش هشدار هنگام واگذاری رسانه‌ی بازی بیش از اندازه بزرگ فرمان گشودن پوشه سازمان رده‌بندی سنی ترجیحی اندازه‌ی نصب بازی‌ها را هنگام به‌روزرسانی کتابخانه به روز برسانید اگر یافت شود که پس از آخرین پویش پرونده‌هایشان دگرگون شده است، اندازه‌ی نصب بازی‌ها را پوییده و به‌روز می‌رساند. هیچ پر کردن یکنواخت یکنواخت برای پر کردن چپ راست بالا پایین خطا در درون‌برد نیاز به تایید هویت دارد ناکامی در تایید هویت حالت نماپردازش نمای وب دگرگزین هنگام بروز مشکل در نمای وب بکار ببرید، برای نمونه: دیالوگ‌های یکپارچگی تایید هویت. بارکردن پاره‌ای از توضیحات بزرگ بازی توضیحات بزرگ می‌تواند باعث تاخیر چشمگیری هنگام گزینش بازی شود. هنگامی که بکارافتاده باشد، تنها پاره‌ای از نوشتار توضیحات در آغاز بار خواهد شد با گزینه‌ای برای بارکردن بقیه هنگام درخواست. درون‌برد فراداده بارگیری فراداده پیکربندی برگزیده را برای بکاربردن در همه‌ی بارگیری‌های فراداده در آینده بنشانید. همچنین می‌تواند در تنظیمات برنامه دگرگون شود. جادوگر درون‌برد شبیه‌ساز این جادوگر شما را در فرآیند بارگیری و درون‌بردن شبیه سازهای کنسول و درون‌بردن بازی‌های شبیه‌سازی شده راهنمایی می‌کند. بدانید که همیشه می‌توانید شبیه‌سازهای بیشتر و/یا بازی‌ها را زمانی دیگر با فهرست اصلی (زیر فهرست "کتابخانه" برای تنظیمات شبیه‌ساز و فهرست "افزودن بازی‌ها" برای بازی‌های شبیه‌سازی شده) بیفزایید. در زیر فهرستی از شبیه‌سازهایی که Playnite می‌تواند خودکار بشناسد و پیکربندی کند می‌باشد. شما می‌توانید نصب‌کننده‌ی شبیه‌سازها را از تارنمای آنها بارگیری کنید. پس از نصب شبیه‌سازها (به شیوه‌ی دستی)، می‌توانید آنها را روی دیالوگ پیکربندی شبیه‌ساز درون‌برد کنید. شما می‌توانید هر شبیه‌سازی را که روی رایانه‌تان نصب شده است را با کلیک کردن بر دکمه‌ی 'شناسایی خودکار از پوشه…' درون‌برد کنید. Playnite پوشه‌ی برگزیده را برای هر شبیه‌ساز شناخته شده جستجو کرده و گزینه‌ای برای درون‌برد آنها فراهم خواهد کرد. شما می‌توانید این دکمه را چندین بار برای درون‌برد شبیه‌سازها از پوشه‌های متفاوت بکار ببرید. شبیه‌سازها به پایین فهرست کنونی افزوده خواهند شد. شما می‌توانید بازی‌ها را با کلیک کردن بر دکمه‌ی 'پویش پوشه با شبیه‌ساز' درون‌برد کنید. گزینش شبیه‌ساز مناسب به Playnite خواهد گفت که کدام گونه‌های پرونده باید پوییده و درون‌برد شوند. شما می‌توانید این دکمه را چندین بار برای درون‌برد بازی‌ها از پوشه‌های متفاوت بکار ببرید. بازی‌ها به پایین فهرست کنونی افزوده خواهند شد. هیچ شبیه‌سازی برای درون‌برد برگزیده نشده است. شما بدون آنکه نخست شبیه‌سازها را پیکربندی کنید هیچ بازی شبیه‌سازی شده‌ای را نمی‌توانید خودکار درون‌برد کنید. آیا مطمئنید که می‌خواهید ادامه دهید و از فرآیند درون‌برد بیرون بروید؟ هیچ شبیه‌سازی در Playnite پیکربندی نشده است. شما نمی‌توانید پیش از آنکه شبیه‌ساز را پیکربندی کرده و گونه‌ی پرونده‌ی مناسب را برگزینید، بازی‌ها را درون‌برد کنید. آیا می‌خواهید اکنون چند شبیه‌ساز بیفزایید؟ پویش پوشه با شبیه‌ساز گزینش پرونده‌ها شناسایی خودکار از پوشه… پیکربندی شبیه‌سازها… پوییدن… پوییدن {0}… نخستین پیکربندی این فرآیند شما را در درون‌برد خودکار و پیکربندی کتابخانه‌ها‌ی بازی بیرونی راهنمایی خواهد کرد. Playnite می‌تواند بازی‌ها را خودکار از چندین فروشگاه‌ بازی همچون Steam یا GOG درون‌برد کند. بدانید که می‌توانید به شیوه‌ی دستی نیز هر بازی سفارشی یا شبیه‌سازی شده را برای هر سکویی در زمانی دیگر از فهرست اصلی بیفزایید. یکپارچگی کتابخانه در زیر فهرستی از برخی کتابخانه‌های یکپارچگی که Playnite از آنها پشتیبانی می‌کند را می‌بینید. لطفا آنهایی که می‌خواهید نصب کنید را برگزینید. از فهرست "افزونه‌ها" می‌توان یکپارچگی‌های بیشتری نصب کرد. پیکربندی پایان یافت نشاندن آغازین پایان یافت. به یاد داشته باشید که می‌توانید در زمانی دیگر همهٔ تنظیمات را دگرگون کرده و همچنین یکپارچگی‌های بیشتری را از فهرست اصلی بیفزایید. ناکامی در بارگیری یک یا چند افزونه. پس از پایان نخستین اجرای جادوگر می‌توانید برای بازبارگیری یکپارچگی‌ها از فهرست افزونه‌ها بکوشید. در حال بارگیری {0} یکپارچگی… در حال بارگیری فهرست یکپارگی‌های پیشنهادی… ناکامی در بارگیری فهرست یکپارچگی‌های پیشنهادی. می‌توانید در زمانی دیگر از راه فهرست افزونه‌ها برای بازبارگیری یکپارچگی‌ها بکوشید. پیکربندی سکوها و شبیه‌سازها پیکربندی شبیه‌سازها سکوها سکو شبیه‌سازها شبیه‌ساز افزودن سکو گزینش نماد گزینش کاور گزینش تصویر گزینش آیتم گزینش پس‌زمینه گزینش پرونده گزینش URL افزودن شبیه‌ساز سکو(های) پشتیبانی شده آیا می‌خواهید دگرگونی‌های سکو را ذخیره کنید؟ آیا می‌خواهید دگرگونی‌های شبیه‌ساز را ذخیره کنید؟ اجراپذیر آرگومان‌ها مسیر در حال کار گونه پرونده‌های پشتیبانی شده درون‌برد شبیه‌سازها… بارگیری شبیه‌سازها… بار کردن آرگومان‌های پیش‌نشانده از نمایه‌ی شبیه‌ساز شناخته شده آیا از زدایش شبیه‌ساز {0} مطمئن‌اید؟ هم‌اکنون {1} بازی آن را بکار می‌برند. آیا از زدایش {0} سکو مطمئن‌اید؟ هم‌اکنون {1} بازی و {2} شبیه‌ساز آن را بکار می‌برند. تنظیمات راهنما چینش بر پایهٔ سوی چینش گروه‌بندی بر پایهٔ بالارونده پایین‌رونده گروه نکنید گروه‌بندی بر پایهٔ کتابخانه گروه‌بندی بر پایه‌ی دسته گروه‌بندی بر پایه‌ی سکو دیدن گونه دید پهنه‌ی کاوشگر پهنه‌ی پالایه نماد نماد کتابخانه تصویر کاور تصویر پس‌زمینه چینش بر پایه‌ی نام کتابخانه کتابچه راهنما نام درایو نصب نام حساب سکو دسته‌بندی ژانر تاریخ رهش سال رهش توسعه‌دهنده برچسب ناشر وضعیت نصب سازگار کردن همه‌ی پالایه‌ها اگر بکارافتاده باشد، تنها بازی‌هایی که همه‌ی آیتم‌های درون همه‌ی پالایه‌ها را بکار می‌برند در نما خواهند بود. اگر ازکارافتاده باشد، بازی‌هایی که هر آیتمی در هر پالایه‌ای را بکار می‌برند، در نما خواهند بود.   نصب شده   نصب شده نصب نشده پنهان دلخواه بکاراندازی پشتیبانی از HDR اگر بکارافتاده باشد، پیش از آغاز بازی، HDR روی نمایشگر اصلی بکار می‌افتد. به یاد داشته باشید که HDR در نمایشگر اصلی شما پشتیبانی نمی‌شود. آخرین بازی شده دسته‌بندی توضیحات پوشه‌ی نصب تصویر کاور پیوند‌ها تصویر، رام یا مسیر ISO ژانر ژانرها شرکت شرکت‌ها توسعه‌دهنده توسعه‌دهندگان ناشر ناشران دسته‌بندی دسته‌بندی‌ها برچسب برچسب‌ها ویژگی ویژگی‌ها رده سنی رده سنی‌ها منطقه مناطق منبع منابع کنش‌های اخیر خطا در پایگاه داده ناکامی در گشودن پایگاه داده‌ی کتابخانه. پایگاه داده گشوده نیست. دسترسی به پایگاه داده‌ی کتابخانه شدنی نیست. یا فرآیند دیگری دارد پرونده‌ی "{0}" را بکار می‌برد یا در مکانی دور از دسترس است. ناکامی در ساخت بسته‌ی تشخیصی. ناکامی در بارگذاری خودکار بسته‌ی تشخیصی. اطلاعات تشخیصی پیروزمندانه فرستاده شد. بسته‌ی تشخیصی پیروزمندانه ساخته و فرستاده شد. لطفا شناسه‌ی زیر را به گزارش مشکل خود بچسبانید: ناکامی در درون‌برد بازی‌ها از {0}. ناکامی در درون‌برد بازی‌های شبیه‌سازی شده از {0}. نمی‌توان با نمایه‌ی شبیه‌ساز برگزیده بازی‌ها را جستجو کرد. نمایه هیچگونه پسوند پرونده یا سکویی ندارد. Playnite در راه‌اندازی ناکام ماند. لطفا دیگر نمونه‌های آن را بسته و دوباره بکوشید. ناکامی در بکاربستن زمینه‌ی "{0}"، نمایه‌ی رنگ "{1}" {2} نمی‌توان پیوند را گشود، URL در چارچوبی معتبر نیست. ناکامی در راه‌اندازی برنامه. ناکامی در راه‌اندازی مولفه‌ی نمای وب. Playnite نمی‌تواند فرآیند راه‌اندازی را ادامه دهد. اطلاعات بیشتر در https://playnite.link/cefstartup به دلیل گمشدگی یا خرابی پرونده‌ی تعریفی، نمی‌توان شبیه‌سازها را درون‌برد کرد. ناکامی در اجرای کنش فهرست. ویرایش جزییات بازی URL تصویر افزودن پیوند افزودن ROM ذخیره دگرگونی‌ها بکاربستن دگرگونی‌ها میدان در بازی(های) در حال ویرایش. کنش افزودن زدایش کنش زدایش پخش افزودن بازی‌ها پویش پوشه… ردیابی نصب‌شده مرور… گشودن Playnite تنظیمات نمایه نام بازی نمی‌تواند تهی باشد. مسیر ردیابی کنش بازی نمی‌تواند تهی باشد. نام بازی نمی‌تواند پیش از جستجوی فراداده تهی باشد. داده‌ی بازی نامعتبر است URL وب معتبری که با http:// یا https:// آغاز می‌شود وارد کنید. گزینش URL ناکامی در بارگیری فراداده: {0} خطا در بارگیری پاک کردن پالایه‌ها حساب شخصی حساب همگانی کلید API خطا در راه‌اندازی خطای زمینه پاک کردن همه در حال نصب در حال حذف نصب در حال آغاز در حال اجرا URL نامعتبر است کاری نکن کمینه کردن بازیابی پنجره بازیابی پنجره تنها زمانی که از میانای کاربری راه‌اندازی شود بستن دگرگونی پیشرفته هرگز وضعیت تکمیلی وضعیت‌های تکمیل امتیاز کاربر امتیاز منتقدان امتیاز جامعه اسکریپت‌های بازی اسکریپت‌های برنامه اسکریپت‌ها افزایه‌ها منابع فراداده افزونه‌ها شناسه‌ی افزونه بازبارکردن اسکریپت‌ها SDK PowerShell تعاملی همه‌ی اسکریپت‌ها پیروزمندانه دوباره بار شدند. هیچ بازی برای معیارهای مشخص شده‌ی پالایه/جستجو یافت نشد آیتمی یافت نشد تعویض به حالت میز‌کار خروج از Playnite کتابخانه‌ها به‌روزرسانی همه ساخته به دست: نسخه: به‌روز‌رسیده: ماژول: کتابخانه آمارها همه هیچ آگهی‌ها پهنا بلندا اندازه‌ کوچک بهنجار بزرگ بزرگتر بزرگترین پیش‌فرض گزینش گزینش همه واگزینش همه نخستین بختوار گزینش کاربر بار کردن بیشتر زلال بستن گسترش بستن همه گسترش همه ديگر زمینه‌ها آرگومان‌های شبیه‌ساز آرگومان‌های درونی آرگومان‌های سفارشی آرگومان‌های بیشتر شبیه‌ساز بازنویسی آرگومان‌های شبیه‌ساز کنش بازی فراداده را برای درون‌برد برگزینید گزینش بازی‌ها برای درون‌برد جستجوی فراداده به روزرسانی در دسترس است دگرگونی‌ها از آخرین به‌روزرسانی تاکنون بارگیری و نصب به‌روزرسانی بررسی برای به‌روزرسانی خطا در به‌روزرسانی ناکامی در بررسی برای نسخه‌ی نو. نسخه نویی یافت نشد، شما به‌روزاید. ناکامی در بارگیری و نصب به‌روزرسانی. یک کار پس‌زمینه در جریان کار است. آیا می‌خواهید آن را لغو کنید و به‌روزرسانی را ادامه دهید؟ یک کار پس‌زمینه در جریان کار است. آیا می‌خواهید آن را لغو کنید و از Playnite بیرون بروید؟ یک کار پس‌زمینه در جریان کار است. دگرگون کردن حالت کار را لغو می‌کند، آیا می‌خواهید دگرگون کنید؟ یک به‌روزرسانی برای Playnite در دسترس است بازبار کردن فهرست زمینه بکار بستن زمینه‌ی برگزیده دیدن دگرگونی‌های پرونده هنگام دگرگونی پرونده‌ی منبع، زمینه را خودکارانه بکار ببندید اسکریپت زمان اجرا اجرا پیش از آغاز کردن بازی اجرا پس از بیرون شدن از بازی اجرا پس از آغاز شدن بازی اجرا هنگام راه‌اندازی برنامه اجرا هنگام خاموش کردن برنامه اسکریپت آغاز بازی اسکریپت بازی آغازشده اسکریپت بازی ایستانده اجرای اسکریپت سراسری سراسری پالاییده کنونی نو اسکریپت آزمایش تنها آیتم‌های برگزیده را نشان دهید. ذخیره بعنوان پیش‌فرض افزودن به دلخواه‌ها زدودن از دلخواه‌ها پنهان کردن این بازی زدودن از پنهانیده بکاراندازی پشتیبانی از HDR ازکاراندازی پشتیبانی از HDR ویرایش… برآورد اندازه‌ی نصب برآورد اندازه‌ی نصب (همه‌ی بازی‌ها) برآورد اندازه‌ی نصب (تنها داده‌های گمشده) اندازه‌ی نصب نشاندن دسته‌بندی‌ها… نشاندن وضعیت تکمیل زدایش آغاز نصب گزینه‌های بازی جزئیات حذف نصب گشودن مکان نصب ساخت میانبر در دسکتاپ گشودن راهنما بیشتر به دست افزایه‌ٔ کتابخانه مدیریت می‌شود فرایند راه‌اندازی بازی به دست افزایهٔ کتابخانه مسئول این بازی مدیریت خواهد شد. هیچ اطلاعات مرتبطی دربارهٔ بازی "{0}" در برگهٔ مشخص‌شده یافت نشد. نکته: می‌توانید هنگام ویرایش پیشرفتهٔ یک بازی از طریق گزینهٔ "ویرایش"، فرایند بارگیری فرادادهٔ پیشرفته‌تری را بکار ببرید. هنگام در جریان بودن یک کنش در دسترس نیست. نوشتار توضیحات به نحو HTML حساس است زمان بازی به ثانیه ثبت می‌شود. اندازهٔ نصب به بایت نشان داده شده است. تاریخ انتشار باید در چارچوب "سال-ماه-روز" نشانده شود. ارزش‌های ماه و روز را می‌توان زدود. ارزش‌ها از ۰ تا ۱۰۰ یا تهی از امتیاز. توسعهٔ Playnite به دست این پشتیبانان و عضو‌های Ko-fi پشتیبانی می‌شود: کد، بومی‌سازی و دیگر کمک‌کنندگان بدون چیدمان مشخص: لغو پایش بازی؟ پایش نصب هم‌اکنون دارد اجرا می‌شود. آیا می‌خواهید فرایند را لغو کرده و بازی را به حالت پیشین برگردانید؟ پایش اجرای بازی هم‌اکنون دارد اجرا می‌شود. آیا می‌خواهید فرایند را لغو کرده و بازی را به حالت پیشین برگردانید؟ زمان بازی‌شده آخرین بازی‌شده {0} روز {1} ساعت {2} دقیقه {0} ساعت {1} دقیقه {0} دقیقه {0} ثانیه بازی‌نشده گشودن حالت دسکتاپ... گشودن حالت تمام‌صفحه... بار کردن کتابخانهٔ بازی... برآورد اندازهٔ نصب... برآورد اندازهٔ نصب از {0}... ناکامی در نصب پروندهٔ اسکریپت. اسکریپت پیروزمندانه نصب شد. نصب اسکرپیت خطای اسکریپت ناکامی در اجرای کارکرد افزونه. گشودن پوشهٔ فراداده برآورد اگر بازی ROMی دارد یا مسیر نصبی نشانده شده است، خودکارانه اندازهٔ نصب را با به‌کارگیری آن برآورد می‌کند. {0} کلاینت نصب نشده است. {0} کلاینت اکنون گشوده خواهد شد. لطفا درون شوید و سپس این پیام را ببندید. در انتظار درون شدن کاربر، لطفا این را پس از انجام ببندید... مکان نصب بازی یافت نشد. پیکربندی کنش بازی نامعتبر است. عیب‌یابی مشکل‌های همگام‌سازی حساب عیب‌یابی مشکل‌ها بازنامگذاری آیتم افزودن آیتم نو نام را وارد کنید نام نو را وارد کنید کمتر از یک ساعت ۱ تا ۱۰ ساعت ۱۰ تا ۱۰۰ ساعت ۱۰۰ تا ۵۰۰ ساعت ۵۰۰ تا ۱۰۰۰ ساعت بیشتر از ۱۰۰۰ ساعت برای تکمیل کردن نصب Playnite باید بازراه‌اندازی شود. آیا می‌خواهید اکنون انجام شود؟ افزونه به درستی بسته‌بندی نشده است. زمینه به درستی بسته‌بندی نشده است. افزونهٔ «{0}» به درستی بار نشد. نمی‌توان افزونهٔ «{0}» را بار کرد، نسخهٔ کنونی Playnite پشتیبانی نمی‌شود. ناکامی در درست بار کردن زمینهٔ «{0}». نمی‌توان زمینهٔ «{0}» را بار کرد، نسخهٔ کنونی Playnite پشتیبانی نمی‌شود. ناکامی در درست بار کردن افزونه. ناکامی در درست بار کردن زمینه. زمینه/افزونه نسخهٔ اِی.پی.آی پشتیبانی‌نشده‌ای را بکار می‌برد. نصب پیروزمندانه بود. نصب برافزا؟ عمومی ناکامی در نصب برافزای «{0}». ناکامی در نصب افزونه. {0} آیا می‌خواهید افزونهٔ نویی نصب کنید؟ {0} توسط {1} نسخهٔ {2} آیا می‌خواهید افزونهٔ «{0}» را به‌روزرسانید؟ نسخهٔ کنونی: {1} نسخهٔ نو: {2} ناکامی در نصب زمینه. {0} آیا می‌خواهید زمینهٔ نویی نصب کنید؟ {0} توسط {1} نسخهٔ {2} آیا می‌خواهید زمینهٔ «{0}» را به‌روزرسانید؟ نسخهٔ کنونی: {1} نسخهٔ نو: {2} شما در حال بیرون رفتن از Playnite و رفتن به وبگاه زیر با مرورگر پیش‌فرض خود می‌باشید. آیا می‌خواهید ادامه دهید؟ {0} تصویر(های) برگزیده شاید برای کارایی بهینه بیش از اندازه بزرگ باشند. به‌کارگیری تصویرهای بسیار بزرگ می‌تواند باعث واکنش‌پذیری بدتر میانای کاربری و افزایش مصرف حافظه شود. بیشینه نسبت نمای پیشنهادی: نمادها: {0} مگاپیکسل کاورها: {1} مگاپیکسل پس‌زمینه‌ها: {2} مگاپیکسل هشدار کارایی دوباره نمایش نده پرونده با پسوند {0} سازگار نیست. پسوند پرونده ناسازگار است تصویر برگزیده شاید برای کارایی بهینه بیش از اندازه بزرگ باشد. آیا مطمئنید که می‌خواهید زمینهٔ برگزیده را حذف نصب کنید؟ حذف نصب در صف برای اجرا در راه‌اندازی بعدی برنامه گذاشته خواهد شد. زمینه‌های درون‌ساخته را نمی‌توان حذف نصب کرد. این زمینه نسخهٔ کنونی Playnite را پشتیبانی نمی‌کند. آیا مطمئنید که می‌خواهید افزونهٔ برگزیده را حذف نصب کنید؟ حذف نصب در صف برای اجرا در راه‌اندازی بعدی برنامه گذاشته خواهد شد. افزونه‌های درون‌ساخته را نمی‌توان حذف نصب کرد. این افزونه نسخهٔ کنونی Playnite را پشتیبانی نمی‌کند. پوشهٔ نصب پوشهٔ داده زایش بستهٔ تشخیصی… بارگذاری بستهٔ تشخیصی… درون‌برد پرونده… این چیست؟ آیا مطمئنید که می‌خواهید این کار را انجام دهید؟ زمان بازی کل میانگین زمان بازی بیشترین زمان بازی اندازهٔ کل نصب نمای کلی نوار کناری نمایش در نوار کناری بازنشاندن تنظیمات همهٔ تنظیمات برنامه به ارزش‌های پیش‌فرض بازگردانده می‌شوند، به جز: - مکان پایگاه داده - فهرست برون‌داشت درون‌برد - تنظیمات افزونه، همچون یکپارچه‌سازی کتابخانه برای پایان دادن به فرایند، برنامه به بازراه‌اندازی نیاز دارد. آیا می‌خواهید تنظیمات را بازگردانید؟ برای توسعه‌دهندگان افزونه‌های برونی مسیر کامل پوشه را وارد کنید دستاوردها انجمن خبرها برگهٔ فروشگاه تنظیمات آغازین کامل نیست. اکنون Playnite در حالت رایانه بازراه‌اندازی می‌شود تا رویه پایان یابد. اخیرا بازی‌شده دلخواه‌ها بیشترین بازی‌شده همه پالایه‌هایی به کار بسته شده‌اند. پالایه‌های بیشتری به کار بسته شده‌اند. نمایش نتیجه‌های جستجو برای: آیتمی با همین نام هم‌اکنون وجود دارد. محدود کردن گزینش به پالایهٔ کنونی گزینش دیگری برافزاها… نصب‌شده تنظیمات افزونه‌ها مرور به‌روزرسانی‌ها ({0}) به‌روزرسانی مدیریت افزونه‌ها و زمینه‌های نصب‌شده، همچون تنظیمات آنها، به گزینگان نوی «برافزاها» جابه‌جا شده است. همهٔ افزونه‌های یکپارچه‌سازی کتابخانه که هم‌اکنون نصب‌شده‌اند را می‌توان در اینجا پیکربندی کرد. اگر می‌خواهید یکپارچگی‌های بیشتری نصب یا حذف نصب کنید، گزینهٔ «برافزا» از گزینگان اصلی را به کار ببرید. زمینهٔ رایانه زمینه‌های تمام‌صفحه در حال جستجو… برافزا با این نسخه از Playnite سازگار نیست. ناکامی در بارگیری بستهٔ نصب برافزا. ناکامی در بارگیری اعلامیهٔ نصب برافزا. بازراه‌اندازی برنامه برای به‌کاربندی دگرش‌های در دست انجام نیاز است. این برافزا برای نصب زمان‌بندی شده است. نصب نصب مجدد حذف نصب هم‌اکنون نصب‌شده است هیچ به‌روزرسانی نویی برای برافزا یافت نشد. به‌روزرسانی برافزاها گزارش دگرش‌ها در دسترس نیست زمان‌بندی‌شده برای نصب ناکامی در بارگیری پروانه رد شد بارگیری {0}… به دنبال به‌روزرسانی‌های برافزا… جستجوی به‌روزرسانی‌های برنامه... یک یا چند به‌روزرسانی افزایه در دسترس است. موردها را برای به‌روزرسانی برگزینید نمونهٔ توسعهٔ افزونه {0} قرارداد پروانه پذیرفتن نپذیرفتن یکپارچه‌سازی کتابخانهٔ کنش‌های آغاز را در بر می‌گیرد گزینش کنش حالت ردیابی مسیر ردیابی دیرکرد ردیابی اولیه بسامد ردیابی پیوند پرونده شبیه‌ساز اسکریپت پیش‌فرض فرایند پوشه فرایند اصلی نام فرآیند ثبت پیام‌های ردیابی دگرش‌های زیر دادهٔ همهٔ بازی‌هایی که هم‌اکنون برگزیده‌اند را بازنویسی می‌کند. هیچ یکنواخت تنها آیتم‌ها تنها آغاز و پایان حساسیت پیمایش‌گر پیمایش نرم تندی پویانمایی برچیدن آیتم؟ از برچیدن این آیتم مطمئنید؟ نمایش دکمه‌ها در پهنهٔ بالایی: تنظیمات نمای کلی تنظیمات گروه‌بندی تنظیمات چینش پیش‌نشانده‌های پالایه موقعیت آیتم‌های افزایه پهنای جداکنندهٔ بخش بردن دکمهٔ فهرست اصلی به نوار کناری پهنهٔ کاوشگر گزینش‌گر بازی تصادفی نمایش گزینش‌گر بازی تصادفی بازی تصادفی را از نما برمی‌گزیند ذخیره‌سازی تنظیمات گروه‌بندی و چینش نمایش به‌عنوان پالایهٔ تند در حالت تمام‌صفحه در ۷ روز گذشته در ۳۱ روز گذشته در ۳۶۵ روز گذشته بیش از ۳۶۵ روز پیش پیکربندی ذخیره‌سازی پیش‌نشانده کمینه‌سازی پس از آغاز بازی Playnite را پس از آغاز بازی کمینه می‌کند. از کار انداختن این گزینه می‌تواند منجر به مشکل‌هایی شود که باعث می‌شوند بازی‌ها در زمان راه‌اندازی تمرکز درونداد را دریافت نکنند! اندازهٔ قلم اندازهٔ قلم کوچک فعال کردن پشتیبانی از API کنترلر بازی پشتیبانی از دستهٔ بازی اگر نافعال باشد، Playnite هیچ دستهٔ بازی‌ای را نمی‌پذیرد. اگر از ابزارهایی استفاده می‌کنید که دروندادهای دستهٔ بازی را به دروندادهای موس/تخته‌کلید تبدیل می‌کند و در Playnite دروندادهای دوتایی دریافت می‌کنید، از کار بیندازید. نمایش آیتم‌ها در فهرست اصلی: اتصال دکمهٔ نمای اصلی X/A وارونه اتصال‌های دکمه را برای راه‌اندازی بازی و نمایش صفحهٔ جزئیات بازی در نمای اصلی تعویض می‌کند. تعویض دکمهٔ تأیید/لغو اتصال‌های دکمه‌های A/B را برای تأیید و لغو وارونه می‌کند. تنها دستهٔ بازی اصلی تنها زمانی که فعال است از دستهٔ بازی اصلی درونداد بپذیرید. دکمهٔ راهنما Playnite را متمرکز می‌کند صدای میانا صدای پس‌زمینه بی‌صداسازی در پس‌زمینه ناکامی در راه‌اندازی میانای صوتی. اِی.پی.آی برونداد اِی.پی.آیی که برای برونداد صوت به کار می‌رود. اگر مشکلی با صدا دارید تغییر دهید. عمومی دیداری صدا چیدمان فهرست‌ها درونداد {0} در حال آغاز است… {0} در حال اجرا است… حرف‌های بزرگ فاصله مقیاس‌دهندهٔ پردازش تصویر دگرگزین ترازشده کیفیت کیفیت: بهترین کیفیت تصویر، کُند، مصرف حافظهٔ بالا. ترازشده: کیفیت خوب، تند، مصرف حافظهٔ کم. دگرگزین: کیفیت بهتر، تندی متوسط، مصرف حافظهٔ کم. گزینش پرونده… گزینش پوشه… اسکریپتِ راه‌اندازی لطفاً توجه داشته باشید که هم افزونه‌ها و هم زمینه‌ها می‌توانند تأثیر بسیاری بر کارایی، پایداری و امنیت Playnite داشته باشند. اگر پس از نصب زمینه یا افزونه‌ای با مشکلی روبه‌رو شدید، نخست برای فهمیدن اینکه آیا دلیل مشکل هستند یا نه آنها را از کار بیندازید/حذف نصب کنید. گزینش هنگام راه‌اندازی گزینش هنگام راه‌اندازی نمایه‌های درونی نمایهٔ درونی پروفایل های سفارشی نمایه سفارشی توسط یک اسکریپت داخلی مدیریت می شود مشخصات شبیه ساز مشخصات پلت فرم مشخصات منطقه قبل از شروع شبیه ساز اجرا کنید پس از شروع شبیه ساز اجرا کنید پس از خروج از شبیه ساز اجرا کنید پس از خروج از شبیه ساز اجرا کنید مشخصات شبیه ساز پیدا نشد. اسکریپت راه اندازی شبیه ساز پیدا نشد. تقسیم به عنوان بازی های جداگانه ادغام در یک بازی تنظیم پلتفرم تنظیم منطقه بررسی پوشه بررسی پیکربندی الگوها را از اسکن جمع‌آزما حذف کنید فایل‌های مطابق با الگو(های) مشخص شده برای چک‌سوم اسکن نمی‌شوند و با نام فایل مطابقت داده می‌شوند. برای اطلاعات بیشتر به صفحه راهنمای شبیه ساز مراجعه کنید. اسکن با شبیه ساز هنگام ذخیره پیکربندی جدید، نام باید تنظیم شود. شبیه ساز یا نمایه شبیه ساز تنظیم نشده است. مسیر برای اسکن مشخص نشده است یا وجود ندارد. پیکربندی اسکن به درستی تنظیم نشده است. شامل اسکن خودکار اسکن انبوه اسکن پوشه برای شبیه سازها انجام نشد. اسکن پوشه(های) بازی های شبیه سازی شده انجام نشد. پنهان کردن وارد شده پروفایل‌هایی برای وارد کردن: تنظیمات اسکن خودکار تنظیمات اسکن خودکار پیکربندی را برای استفاده بعدی در طول به روز رسانی کتابخانه ذخیره می کند. تنظیمات ذخیره شده را می توان از طریق منوی "پیکربندی شبیه‌ساز ها" مدیریت کرد. وارد کردن با استفاده از مسیرهای نسبی در صورت امکان فایل های بازی را با استفاده از مسیرهای مربوط به پوشه نصب Playnite یا پوشه نصب شبیه‌ساز وارد کنید. اسکن زیر پوشه ها اسکن داخل آرشیوها ادغام فایل های مرتبط فایل‌های بازی مرتبط، مانند دیسک‌های بازی جداگانه، را یکجا در بازی ادغام کنید. افزودن اسکنر اضافه کردن اسکنر ذخیره شده شروع بررسی برای اسکن پوشه های خاص، پیکربندی(های) اسکن را با شبیه سازها اضافه کنید. مطمئن شوید که شبیه سازها قبل از وارد کردن بازی ها به درستی پیکربندی شده اند (از طریق کتابخانه -> منوی پیکربندی شبیه سازها). وضعیت پیش‌فرض به بازی‌های تازه اضافه شده اختصاص داده شده است وضعیت اختصاص داده شده به بازی‌هایی که برای اولین بار انجام می شوند خطا در راه‌اندازی محیط اجرای اسکریپت PowerShell. اگر از کاربران ویندوز 7 هستید، سعی کنید PowerShell 5.1 را نصب یا مجدداً نصب کنید تا مشکل برطرف شود. فیلتر از پیش تعیین شده با نام مشخص از قبل موجود است. از پیش تنظیم شده با تنظیمات جدید به روز شود؟ نام‌های مرتب‌سازی از دست رفته را برای بازی‌های اضافه شده یا ویرایش شده به صورت دسته‌ای، به طور خودکار پر کنید وقتی یک بازی را ویرایش می‌کنید، از طریق به‌روزرسانی کتابخانه، اسکن پوشه شبیه‌ساز یا اسکن پوشه معمولی، بازی‌ها را اضافه می‌کنید، فیلد «نام مرتب‌سازی» به طور خودکار با نمایش مرتب‌سازی بهتری از نام بازی پر می‌شود. برای مثال، «The Witcher 3» نام مرتب‌سازی «Witcher 03» را دریافت می‌کند. این کار هرگز نام مرتب‌سازی‌ای را که با نام بازی متفاوت نیست، تنظیم نمی‌کند و فقط نام‌های مرتب‌سازی خالی را به طور خودکار به‌روزرسانی می‌کند. این کلمات از ابتدای مقدار نام مرتب‌سازی که به طور خودکار پر شده حذف می شوند: از این برای نادیده گرفتن کلمات در ابتدای رشته برای اهداف مرتب‌سازی استفاده کنید. پیش فرض "The"، "An" و "A" است. نام مرتب‌سازی را برای بازی‌های بدون آن پر کنید مرتب‌سازی پر کردن مرتب‌سازی مقادیر نام… سرویس Nahimic در سیستم شما شناسایی شده است. این سرویس باعث مشکلات رندر در Playnite (و برنامه‌های دیگر) می‌شود. اگر با هرگونه خرابی گرافیکی یا مشکلات رندر در Playnite روبرو شدید، توصیه می‌کنیم سرویس Nahimic را غیرفعال یا به طور کامل حذف نصب کنید. اطلاعات بیشتر در این لینک https://playnite.link/nahimicsucks Playnite با امتیازات بالا (به عنوان مدیر) در حال اجرا است. این توصیه نمی‌شود زیرا به تمام افزونه‌های نصب‌شده و تمامی بازی‌ها/برنامه‌هایی که از Playnite شروع می‌شوند امتیازات بالایی می‌دهد! اطلاعات بیشتر در https://playnite.link/adminfaq نمایش هشدار در صورت اجرای Playnite با امتیازات بالا هنگام محاسبه اندازه بازی ها، اندازه واقعی درایو را دریافت کنید اگر فعال باشد، اسکن‌ها کندتر می‌شوند و اندازه واقعی فایل‌ها در درایو را دریافت می‌کنند. اگر غیرفعال باشد، اسکن ها سریعتر انجام می شود و از اندازه خود فایل ها استفاده می شود. افزونه(های) بعدی به دلیل تأثیر پایداری/عملکرد بالا یا مسائل امنیتی به عنوان بالقوه مشکل ساز گزارش شده است. ما شدیداً توصیه می کنیم که آنها را حذف کنید: {0} حذف فایل های آنلاین از اسکن فایل‌های ذخیره‌شده در فضای ذخیره‌سازی ابری، اگر به صورت محلی در دسترس نباشند، اسکن و وارد نمی‌شوند. فقط برای: Google Drive، DropBox، OneDrive پشتیبانی می شود اسکن کنید اما با استفاده از روش ساده بدون محتوای فایل فایل‌ها وارد خواهند شد، اما با استفاده از روشی کمتر دقیق که نیاز به دانلود و حضور محلی محتوای فایل ندارد. اعمال بر همه لغو وضعیت نصب وقتی تنظیم شود، Playnite وضعیت نصب (از جمله دایرکتوری نصب) را که توسط افزونه یکپارچه‌سازی که این بازی را وارد می‌کند، نادیده می‌گیرد. این گزینه ممکن است به طور کامل با افزونه هایی که از روش واردات بازی خاصی استفاده می کنند کار نکند، مگر اینکه این گزینه لغو را نیز در نظر بگیرند. تنها دستی یک بار در روز یکبار در هفته در هر راه‌اندازی بررسی به روز رسانی برنامه بررسی بروزرسانی افزونه بروزرسانی کتابخانه ها اسکن پوشه های شبیه سازی شامل بازی های پنهان ویرایش فیلدها انتخاب / لغو انتخاب همه باز کن فعال اختصاص برای جستجوی بازی ها شروع به تایپ کنید… [F1] برای کمک شروع با # فهرستی از دستورها موجود را نشان می دهد. شروع با / فهرستی از ارائه دهندگان/افزونه های جستجوی موجود را نشان می دهد. تایپ کلمه کلیدی جستجو و پایان دادن با SPACE بلافاصله به آن جستجو تغییر می کند. TAB: عمل سوئیچ ENTER: فعال کردن عمل انتخاب شده SHIFT-ENTER: منوی مورد را باز کنید شامل بازی های حذف شده شامل بازی های پنهان بازی های حذف شده گنجانده شد بازی های حذف شده محروم شدند شامل بازی های مخفی بازی های پنهان محروم شدند اجرا یا نصب برو به جزئیات منو بازی ویرایش بازی باز شدن جستجو جعبه جستجو دکمه جستجو عملکرد اولیه بازی عملکرد ثانویه بازی CTRL-F جستجوی سراسری را به جای تمرکز کادر جستجو باز می کند تنظیمات فیلتر بازی را بین جلسات جستجو ذخیره کنید ارائه دهندگان جستجو کلمه کلیدی پیش فرض کلمه کلیدی سفارشی میانبر گسترده سیستم جستجو Playnite تنظیمات افزونه استثنائات فایل های استثناء نسبت به پوشه اسکن پوشه های استثناء نسبت به پوشه اسکن افزودن فایل به لیست استثناء افزودن پوشه به لیست استثناء موارد استثنا را فقط می توان به تنظیمات اسکنر ذخیره شده اضافه کرد. موارد استثنا به اسکنر "{0}" اضافه شده است. لغو پلت فرم هنگامی که اسکنر تنظیم شود، این پلتفرم را به همه بازی‌ها اختصاص می‌دهد و پلتفرم‌های شناسایی شده به‌طور خودکار را بازنویسی می‌کند. شامل دستورات در جستجوی پیش فرض وقتی غیرفعال است، تا زمانی که از پیشوند # استفاده نشود، دستورها در جستجوی پیش‌فرض گنجانده نمی‌شوند. از تطبیق فازی در فیلتر نام استفاده کنید وقتی فعال باشد، فیلتر نام با نام بازی‌ها مطابق با جستجوی جهانی مطابقت پیدا می‌کند. تطبیق دقیق را می توان با قرار دادن پیشوند فیلتر با کاراکتر ! انجام داد. فیلدهایی که برای نتایج بازی نمایش داده می شوند: وضعیت پنهان پشتیبان گیری از داده ها لغو شد. پشتیبان گیری از داده ها نشد. خطا پشتیبان گیری از داده ها پشتیبان گیری از داده ها... بازیابی داده از پشتیبان... بازیابی داده از پشتیبان نشد تنظیمات پوشه بازی پوشه رسانه بازی افزونه های نصب شده داده های افزونه ها پوسته‌های نصب شده داده هایی را برای بازیابی از فایل پشتیبان مشخص شده انتخاب کنید. Playnite به طور خودکار راه‌اندازی مجدد می شود تا فرآیند بازیابی نسخه پشتیبان شروع شود. مواردی را انتخاب کنید که با پشتیبان‌گیری داده‌ها همراه شوند. تنظیمات برنامه و داده های کتابخانه بازی به طور پیش فرض گنجانده شده است. برای شروع فرآیند پشتیبان گیری، Playnite به طور خودکار راه‌اندازی مجدد می شود. پشتیبان گیری خودکار اطلاعات فرکانس پشتیبان گیری خودکار پوشه پشتیبان پشتیبان گیری چرخشی شامل داده های اضافی: اگر پشتیبان‌گیری خودکار فعال باشد، باید پوشه پشتیبان‌گیری تنظیم شود. نمایش اعلان‌ها فقط برای انتشار پچ وقتی فعال باشد، فقط به‌روزرسانی‌های موجود برای نسخه اصلی نصب‌شده در حال حاضر منجر به اعلان به‌روزرسانی می‌شوند. نسخه های اصلی جدید منجر به اعلان به روز رسانی نمی شوند. از تاریخ های نسبی هفته گذشته استفاده کنید اگر تاریخ کمتر از یک هفته است، از تاریخ های نسبی در قالب «امروز»، «دیروز» و غیره استفاده کنید. قالب تاریخ مشخص شده برای تمام تاریخ های دیگر استفاده خواهد شد. جستجوی تصویر وب رشته جستجوی تصویر نماد رشته جستجوی تصویر جلد رشته جستجوی تصویر پس زمینه در حال دریافت اطلاعات افزونه… هیچ منبع ابرداده ای در دسترس نیست تنظیمات اجرا اکشن استفاده از تنظیمات اسکنر هنگام راه‌اندازی نمایه را انتخاب کنید انتخاب شبیه‌ساز در زمان راه‌اندازی خودکار همیشه روشن همیشه خاموش قابلیت دسترسی (خواننده صفحه نمایش) پشتیبانی می کند منوی برنامه منو بازی پوشه برنامه پوشه اطلاعات کاربر خرابی فایل کتابخانه شناسایی شده است، Playnite اکنون خاموش می‌شود. با درخواست رفع خرابی برای فایل‌های خود، مسئله جدیدی در صفحه گیت‌هاب Playnite باز کنید. آیا می خواهید تغییراتی را که ایجاد کرده اید ذخیره کنید؟ نصب قابل حمل کنترلری شناسایی نشد ================================================ FILE: source/Playnite/Localization/fi_FI.xaml ================================================  Suomi Playniten kieli Poistu Suodatin käytössä Suodatin pois käytöstä Muut suodattimet Suodattimet Suodatin Virheellinen data Tallenna muutokset? Kotisivut osoitteessa www.playnite.link Lähdekoodi GitHubissa Luo diag. paketti Lähetä diag. tiedot Tietoja Playnitesta Luonut Josef Němec Valitse kategoria Aseta kategoriat Lisää kategoria Valittu - Aseta kategoria Ei valittu - Poista kategoria Määrittelemätön - Ei muutoksia (kun muokataan useita pelejä) Ei kategoriaa Ei alustaa Hups, jokin meni pieleen… Tapahtui peruuttamaton virhe. Jos haluat auttaa meitä korjaamaan tämän ongelman, kirjoita lyhyt kuvaus siitä, mitä teit ennen virheen tapahtumista ja lähetä diagnostiikkatiedot. Jos olet yhdistettynä verkkoon, paketti lähetetään Playnite-palvelimelle tutkimusta varten. Vaihtoehtoisesti voit painaa "Ilmoita ohjelman kaatumisesta" -nappia ja luoda GitHubiin uuden korjausehdotuksen ja raportoida virheen suoraan sinne. Kiitos avustasi. Laajennus "{0}" aiheutti peruuttamattoman virheen. Suosittelemme, että tallennat lokitiedoston ja raportoit virheestä laajennuksen kehittäjälle. Jos ongelma toistuu, poista laajennus käytöstä. Laajennus "{0}" aiheutti peruuttamattoman virheen. Suosittelemme, että raportoit virheestä laajennuksen kehittäjälle. Jos ongelma toistuu, ota laajennus pois käytöstä. Peruuttamaton virhe tapahtui. Jos haluat auttaa meitä korjaamaan tämän ongelman, ole hyvä ja lähetä diagnostiikkatiedot. Kiitos. Poista laajennus käytöstä Tallenna lokitiedosto Lähetä diagnostiikkatiedot Ilmoita ohjelman kaatumisesta Uudelleenkäynnistä Playnite Käynnistä uudelleen vikasietotilassa Poistetaan kaikki kolmannen osapuolen laajennukset käytöstä ja käytetään oletusteemaa. Poistu Playnitesta Mitä tein ennen kuin ohjelma kaatui (Englanniksi): Kirjaston hallinta Poista peli(t)? Ei voi poistaa - peli tai asennusohjelma on käynnissä. Asennusta ei voi poistaa - peli on käynnissä. Oletko varma, että haluat poistaa {0}? Haluatko varmasti poistaa {0} peliä? Oletko varma, että haluat poistaa {0}? Valitsemalla "Lisää ohitus listalle" valinnan estää pelin lisäämisen uudestaan seuraavalla kerralla kun kirjastoa päivitetään. Haluatko varmasti poistaa {0} peliä? Jos valitset "lisää ohituslistalle" -valinnan näitä pelejä ei tuoda uudestaan seuraavan kerran kun kirjasto päivitetään. Haluatko varmasti poistaa {0} merkintää, jotka eivät tällä hetkellä ole käytössä? Käyttämättömiä kenttiä ei löytynyt. Kyllä (lisää ohituslistalle) Tässä osiossa on muutoksia, joita ei ole tallennettu Päivitetään pelikirjaston formaattia… Tietokannan päivitys epäonnistui. Pelikirjastoa ei voida päivittää. Vapaata tilaa tarvitaan {0} megatavua. Pelivirhe Peliä ei voida käynnistää. Tietokannasta ei löytynyt '{0}' peliä. Peliä ei voida käynnistää: {0} Toimintoa ei voida käynnistää: {0} Pelin sijaintia ei voitu avata: {0} Pelin asennuksen kokoa ei tunnistettu: {0} Asennuskoon hakuvirhe Asennuskoon haun aikana tapahtui {0} virhettä Virhe luotaessa pikakuvaketta: {0} Käyttöoppaan avaaminen epäonnistui: {0} Peliä ei voida asentaa: {0} Peliä ei voida poistaa: {0} Asennusratkaisu ei ole saatavilla. Tälle pelille ei ole asennettu kirjastoliitännäistä tai se ei ole käytössä. Virallista metatietoa ei ole saatavilla. Yhtään peliä ei ole valittu. Pelin skriptin suoritus epäonnistui. Yleisen skriptin suoritus epäonnistui. Emulaattori skriptin suoritus epäonnistui. Toistokomentosarjan suoritus epäonnistui. PowerShell 3.0 tai uudempi ei ole asennettuna. Pelin aloittaminen ei onnistunut. Käytössä Poista Poista käyttämättömät Nimeä uudelleen Kopioi Lisää Oletuskuvake Oletuskansikuva Oletustaustakuva Valmis Seuraava Takaisin VALMIS TAKAISIN TYHJENNÄ Tyhjennä Ohita Ohita kaikki Tuo Nimi Tekijä Moduuli Sarja Versio Viimeksi pelattu Eniten pelattu Pelikerrat Koko asennettuna Kansio Merkinnät Lisätty Lisäyspäivä Muokattu Muokkauspäivä Verkkosivusto Polku OK Tallenna Sulje Peruuta Vahvista Nollaa muutokset Kyllä Ei Tervetuloa Paikallinen käyttäjä Yleiset Media Linkit Asenna Toiminnot Ladataan… Ladataan mediaa… Ladataan… Tyyppi Profiili Profiilit Poista Lataa Hae Resoluutio: Mikä tahansa Zoomaa Listanäkymä Kannet Ruudukkonäkymä Lisätietonäkymä Mukautettu URL Erikois kiitos Lisenssi Avustajat Playnite suljetaan… Tänään Eilen Maanantai Tiistai Keskiviikko Torstai Perjantai Lauantai Sunnuntai Viime viikko Viime kuukausi Viime vuosi Yli vuosi sitten 0 – 100Mt 100Mt – 1Gt 1 Gt – 5 Gt 5 Gt – 10 Gt 10 Gt – 20 Gt 20 Gt – 40 Gt 40 Gt – 100 Gt 100Gt tai enemmän Tuonti onnistui. Kaikki pelit Pelin tunnus Tietokannan tunnus Esiasetukset Sarake Sarakkeet Rivi Rivit Lataa vain puuttuva metadata Ottamalla tämän valinnan käyttöön metadataa ei ladata niille kentille, jotka jo sisältävät tietoa. Pelikokoelma Valitse pelit jotka tulisi päivittää uudella metadatalla: Kaikki tietokannan pelit Kaikki suodatetut pelit Vain valitut pelit Virallinen kauppa IGDB Valitse kentät jotka Playniten tulisi automaattisesti täyttää ja mitä lähteitä datan hakemiseen tulisi käyttää. Parantaaksesi Playniten käyttämää dataa, harkitse yllä olevan logon klikkaamista ja osallistumista igdb.com-tietokannan päivittämiseen. Ladataan metadataa… Tuodaan asennettuja pelejä… Tuodaan {0} pelejä… Tuodaan emuloituja pelejä kohteesta {0} Ladataan kirjaston päivityksiä… Haetaan kirjaston pelien koot… Haetaan tuotujen pelien koot… Kirjaston päivitys suoritettu Vapautetaan resursseja… Määritys Asetukset… Alustat ja emulaattorit Määritä emulaattorit… Kirjaston hallinta… Työkalut Lataa metatieto… Ohjelmistotyökalut… Integrointien muokkaus Avaa kolmannen osapuolen sovellus Kolmannen osapuolen sovellukset Päivitä kirjasto Peruuta kirjaston päivitys Päivitä emuloitujen pelien kansiot Lisää peli Manuaalisesti… Etsi automaattisesti… Emuloitu peli… Microsoft Storen sovellus Tietoja Playnitesta Lähetä palautetta Vaihda televisiotilaan Linkit Ohjeet Tue Patreonissa SDK-dokumentaatio Käynnistä järjestelmä uudelleen Sammuta järjestelmä Aseta järjestelmä valmiustilaan Aseta järjestelmä horrostilaan Valitse satunnainen peli Lisätietopaneelissa näytettävät kentät: Kohteiden välistys Näytä ruudukkonäkymän kohteen taustakuva Ruudukon kohteen reunaviivan leveys Puuttuvan pelikuvakkeen lähde Puuttuvan kansikuvan lähde Puuttuvan taustakuvan lähde Pystysuora välistys pelin lisätietoihin Ruudukkonäkymän tietojen paikka Pelilistan asema lisätietonäkymässä Piirrä erotin paneelien väliin Pelin kansikuvan korkeus Pelilistan kuvakkeen korkeus Sovelluksen fontti Monospace fontti Suodatinpaneelin paikka Selainpaneelin paikka Kansitaiteen renderöinti Kuvasuhdetavoite Seuraavat asetukset vaikuttavat myös ruutujen esitykseen televisiotilassa! Sovitustapa DVD-laatikko Epic Games Store GOG Galaxy 2.0 IGDB Neliö Steam-juliste Steamin pystysuuntainen kansikuva Twitch * Käyttöönotto vaatii uudelleenkäynnistyksen Asetukset Yleiset Yläpalkki Ulkoasu Pelin lisätiedot Asettelu Lisäasetukset Televisiotila Syöte Suorituskyky Metadata Päivitetään Haku Varmuuskopio Varmuuskopioi Kirjaston Tiedot Palauta Tiedot Varmuuskopiosta Tuo kirjaston muutokset automaattisesti Virheellinen tietokannan sijainti, oikea polku vaaditaan. Tunnuksen nimi ei voi olla tyhjä. Lataa metadata pelien tuonnin jälkeen Käynnistä Playnite pienennettynä Käynnistä Playnite kun tietokone käynnistyy Aloita ilmoitusalueelle suljettuna Playniten asettaminen koneen käynnistykseen epäonnistui. Käynnistä kokoruututilassa Asynkroninen kuvan lataus Parantaa rullauksen sujuvuutta pelilistoissa mutta hidastaa hieman kuvien lataamista. Näytä pelin nimi jos kansitaide puuttuu Näytä pelien nimet ruudukkonäkymässä Himmennä pelit joita ei ole asennettu Näytä pelien kuvakkeet lisätietonäkymässä Näytä määrä ryhmien kuvauksissa Näytä vain käytössä olevat kentät suodatin- ja selainpaneeleissa Poista laitteistokiihdytys käytöstä Käytä jos havaitset nykimistä tai muita käyttöliittymän ongelmia Näytä piilotetut pelit pikakäynnistyslistoilla Vaikuttaa ohjelmaikonin listaan sekä ilmaisinalueen ikonin listaan. Pikakäynnistysnimikkeiden määrä Käytä pelin taustakuvaa ikkunan taustakuvana Sumenna tausta Korkea laatu Tummenna tausta Näytä ruudukkonäkymässä Teema Teemaprofiili Televisiotilan teema Televisiotilan teeman profiili Tietokannan sijainti Kirjautumisen tila: Playniten asetukset Tyhjennä välimuisti Saattaa ratkaista ongelmia tunnusten linkittämisessä. Näytä järjestelmäkuvake Pienennä Playnite ilmoitusalueelle Pienennä Playnite ilmoitusalueelle kun sovellusikkuna on suljettu Kun peli käynnistyy: Kun peli sulkeutuu: Päivämäärän esitystapa Tämä kirjaa sinut ulos kaikista linkitetyistä palveluista. Sovellus pitää käynnistää uudelleen, haluatko jatkaa? Tyhjennä välimuisti? Playnite pitää käynnistää uudelleen teeman käyttöönottamiseksi Hanki lisää teemoja Luo uusi teema Hanki lisää laajennuksia Luo uusi laajennus Auta meitä kääntämään Playnite muille kielille Playnite pitää käynnistää uudelleen, jotta uudet asetukset tulevat voimaan. Käynnistä uudelleen nyt? Uudelleenkäynnistys peruuttaa kaikki käynnissä olevat aktiiviset tehtävät (lataukset). Käynnistä Playnite uudelleen? Playnite ei voi siirtää kirjaston tiedostoja automaattisesti. Sinun pitää itse siirtää tai kopioida tiedostot ennen sijainnin vaihtamista. Jos kohdehakemistossa ei ole kirjastoa, sinne luodaan uusi kirjasto. Tietokannan uusi sijainti ei tule käyttöön ennen kuin Playnite on käynnistetty uudelleen. Peliaikaa ei kirjata jos "Sulje"-toiminto on asetettu. Rivien määrä Sarakkeiden määrä Lisätietonäkymän rivien määrä Näytä taustakuva pääruudussa Ei oteta takautuvasti käyttöön olemassa olevissa peleissä ilman että metadataa ladataan uudelleen. Tuo kirjaston pelien peliaika: Aina Vain äskettäin tuoduille peleille Ei koskaan Ota peliohjaintuki käyttöön työpöytätilassa Opas-näppäin avaa televisiotila Uusien tuotujen pelien metadatan automaattisen latauksen asetukset. Kohdenäyttö Käytä aina päänäyttöä Näytä pelien otsikot Näytä akun varaus Näytä akun latausprosentti Näytä kello Piilota kursori Asennettu vain pikasuodattimiin Painikekehotteet Asettelu Vaakasuuntainen vieritys Valitse yksi alakohdista Ei asetuksia Asetusten lataus epäonnistui Nämä skriptit suoritetaan jokaiselle kirjaston pelille. Jokaiseen peliin voi liittää yksilöityjä skriptejä muokkaamalla pelin tietoja. Animoi taustakuvien siirtymät Kirjasinkoot Automaattinen Pehmentämätön Harmaasävy ClearType Ihanteellinen Näyttö Tekstin muotoilutila Tekstin esitystila Tekstin esitys- ja muotoilutapoja ei tällä hetkellä käytetä pelien kuvausteksteihin. Esilataa taustakuvat Jos käytössä, Playnite lataa taustakuvat metatietoja ladatessaan, mikä vie enemmän levytilaa mutta kuvat voidaan näyttää myös offline-tilassa. Jos ei käytössä, taustakuvat ladataan vain kun niitä ensimmäisen kerran tarvitaan, mikä vie vähemmän levytilaa, mutta taustakuvat saatetaan näyttää viiveellä eikä kaikkia kuvia ole välttämättä saatavilla offline-tilassa. Sulje automaattisesti kolmannen osapuolen sovellukset pelin sammuttua Sovelluksen sammutuksen viive (sekunneissa) Älä sulje, jos pelisessio on lyhyempi kuin (sekunneissa) Sulje seuraavat sovellukset automaattisesti: Sulje automaattisesti sovellukset Tuo ohituslista Näytä varoitus kun ollaan liittämässä liian suurta peliä Hakemiston avauskomento Ensisijainen ikärajaorganisaatio Päivitä pelien koot kirjaston päivityksen yhteydessä Hae ja päivitä pelien asennuskoot, jos havaitaan, että niiden tiedostot ovat muuttuneet viimeisen skannauksen jälkeen Ei mitään Täytä Yhtenäinen Täytä säilyttäen kuvasuhde Vasen Oikea Yläreuna Alareuna Tuontivirhe Todennus vaaditaan Todennus epäonnistui Vaihtoehtoinen webnäkymän renderöintitila Metadatan tuonti Lataa metadata Aseta valittu määritys käytettäväksi jatkossa ladattaessa mitä tahansa metadataa. Voidaan muuttaa myös sovelluksen asetuksista. Emulaation tuontivelho Tämä ohjattu toiminto opastaa sinua konsoliemulaattorien lataamisessa ja tuomisessa sekä emuloitujen pelien tuomisessa. Pidä mielessä, että voit aina lisätä emulaattoreita ja/tai pelejä jälkeenpäin päävalikon kautta (Kirjasto valikkon alta Emulaattori-asetuksista sekä "Lisää peli" valikosta emulaattoripelejä). Alla on lista emulaattoreista, jotka Playnite voi tunnistaa ja konfiguroida automaattisesti. Voit ladata ja asentaa ne vierailemalla niiden verkkosivuilla. Kun olet asentanut emulaattorit, siirry seuraavaan ruutuun tuodaksesi ne Playniteen. Voit myös konfiguroida ja tuoda minkä tahansa emulaattorin myöhemmin konfiguraatiovalikon kautta. Voit tuoda minkä tahansa tietokoneellesi asennetun emulaattorin painamalla "Tunnista automaattisesti kansiosta…" -nappia. Playnite etsii valitusta kansiosta tuntemiaan emulaattoreita ja tarjoaa mahdollisuuden tuoda ne. Voit tuoda useista kansioista käyttämällä em. nappia useamman kerran. Emulaattorit lisätään listan loppuun. Voit tuoda pelejä painamalla "Skannaa hakemisto käyttäen emulaattoria" -nappia. Valitsemalla sopivan emulaattorin Playnite tietää minkä tyyppisiä tiedostoja sen pitää skannata ja tuoda. Voit tuoda useista kansiosta käyttämällä em. nappia useamman kerran. Pelit lisätään listan loppuun. Emulaattoreita ei ole valittu tuontia varten. Et voi automaattisesti tuoda emuloituja pelejä ilman että määrität emulaattorit ensin. Haluatko varmasti jatkaa ja lopettaa tuontiprosessin? Emulaattoreita ei ole määritetty Playnitessa. Et voi tuoda pelejä ilman että määrität emulaattorit ensin ja valitset sopivat tiedostotyypit. Haluatko lisätä joitakin emulaattoreita nyt? Skannaa hakemisto käyttäen emulaattoria Valitse tiedostot Tunnista automaattisesti kansiosta… Määritä emulaattorit… Skannaus… Skannataan {0} Ensimmäisen käynnistyksen määritys Tämä ohjattu toiminto opastaa sinua ulkopuolisten pelikirjastojen automaattisessa tuonnissa sekä määrittämisessä. Playnite voi automaattisesti tuoda pelejä useista pelipalveluista, kuten Steam tai GOG, sekä pitää kirjastosi ajan tasalla päivittämällä sen automaattisesti sovelluksen käynnistyksen yhteydessä. Muista, että voit aina manuaalisesti lisätä minkä tahansa pelin mille tahansa alustalle klikkaamalla päävalikon "Playnite"-nappia. Kirjastointegrointi Tuo pelejä automaattisesti oheisista palveluista. Myöhemmät muutokset peleihin (asennuksen tila) päivittyvät automaattisesti Playniten käynnistyessä tai manuaalisesti aloittaen. Valitut asetukset vaikuttavat ensimmäiseen ja kaikkiin myöhempiin tuonteihin. Määritys suoritettu Esiasennus on nyt valmis. Muista, että myöhemmin voit vaihtaa kaikkia asetuksia 'Asetukset' valikosta. Voit myöhemmin myös lisätä minkä tahansa pelin painamalla Playnite-logoa. Ladataan {0} integraatio Määritä alustat ja emulaattorit Määritä emulaattorit Alustat Alusta Emulaattorit Emulaattori Lisää alusta Valitse kuvake Valitse kansi Valitse kuva Valitse kohde Valitse tausta Valitse tiedosto Valitse URL Lisää emulaattori Tuetut alustat Haluatko tallentaa alustan muutokset? Haluatko tallentaa emulaattorin muutokset? Suoritettava ohjelma Muuttujat Työkansio Tuetut tiedostotyypit Tuo emulaattoreita… Lataa emulaattoreita… Lataa argumenttimallit tunnetusta emulaattoriprofiilista Haluatko varmasti poistaa {0} emulaattorin? Sitä käyttää tällä hetkellä {1} peli(ä). Haluatko varmasti poistaa {0} alustan? Sitä käyttää tällä hetkellä {1} peli(ä) and {2} emulaattori(a). Asetusten ohje Lajittele Järjestyssuunta Ryhmittele Nouseva Laskeva Älä ryhmittele Ryhmittele kirjaston mukaan Ryhmittele kategorioittain Ryhmittele alustan mukaan Näkymätyyppi Näkymä Selainpaneeli Suodatinpaneeli Kuvake Kirjaston Kuvake Kansikuva Taustakuva Järjestelynimi Kirjasto Käyttöopas Nimi Asenna Asemalle Tilin nimi Alusta Kategoria Lajityyppi Julkaisupäivä Julkaisuvuosi Kehittäjä Tunniste Julkaisija Asennuksen tila Käytä kaikkia suodattimia Asennettu Asennettu Asentamatta Piilotettu Suosikki Viimeksi pelattu Kategoria Kuvaus Asennushakemisto Kansikuva Linkit Levykuva-, ROM- tai ISO-polku Lajityyppi Lajityypit Yritys Yritykset Kehittäjä Kehittäjät Julkaisija Julkaisija Kategoria Kategoriat Tunniste Tunnisteet Ominaisuus Ominaisuudet Ikäluokitus Ikäluokitus Alue Alueet Lähde Lähteet Viimeaikainen toiminta Tietokantavirhe Ei voitu avata tietokantaa. Tietokantaa ei ole avattu. Pääsy kirjastotietokantaan ei onnistu. Tiedosto "{0}" on toisen prosessin käytössä tai pääsemättömässä sijainnissa. Diagnostiikkapaketin luominen epäonnistui. Automaattinen diagnostiikkapaketin lähettäminen epäonnistui. Diagnostiikkatietojen lähetys onnistui. Ongelmanmäärityspaketti on luotu ja lähetetty onnistuneesti. Lisää oheinen tunniste raporttiisi: Pelejä ei voitu tuoda sijainnista {0}. Emuloituja pelejä ei voitu tuoda sijainnista {0}. Ei voida etsiä pelejä valitun emulaattoriprofiilin mukaan. Profiili ei sisällä tiedostopäätteitä tai alustoja. Playniten käynnistys epäonnistui. Sulje ohjelman kaikki muut esiintymät ja yritä uudelleen. Teeman "{0}" käyttöönotto epäonnistui, väri profiili "{1}" {2} Linkkiä ei voida alita, URL ei ole oikeassa formaatissa. Sovelluksen käynnistys epäonnistui. Emulaattoreita ei voi tuoda puuttuvan tai viallisen määrittelytiedoston takia. Valikkotoiminnon suorittaminen epäonnistui. Muokkaa pelin tietoja Kuvan URL Lisää linkki Lisää ROM Tallenna muutokset Lisää toiminto Poista toiminto Poista Pelaa-toiminto Lisää pelejä Skannaa hakemisto… Havaitse asennetut Selaa… Avaa Playnite Profiilin asetukset Pelin nimi ei voi olla tyhjä. Pelin nimi ei voi olla tyhjä haettaessa metadataa. Virheellinen pelidata Anna oikea URL-muoto alkaen http:// tai https:// Valitse URL Tietojen lataaminen epäonnistui: {0} Latausvirhe Tyhjennä suodattimet Yksityinen tili Julkinen tili API-avain Käynnistyksen virhe Teeman virhe Tyhjennä kaikki Asennetaan Poistetaan Käynnistetään Suoritetaan Virheellinen osoite Älä tee mitään Pienennä Palauta ikkuna Sulje Muuta Edistynyt Ei koskaan Edistyminen Edistyminen Pisteesi Kriitikoiden pisteet Yhteisön pisteet Skriptit Liitännäiset Metadatan lähteet Laajennukset Laajennuksen tunnus Lataa skriptit uudelleen Kaikki skriptit on ladattu uudelleen onnistuneesti. Hakua/suodatinta vastaavia pelejä ei löytynyt Nimikkeitä ei löytynyt Vaihda työpöytätilaan Poistu Playnitesta Kirjastot Päivitä kaikki Luoja: Versio: Päivitetty: Moduuli: Kirjasto Tilastot Kaikki Ei mitään Ilmoitukset Leveys Korkeus Koko Pieni Normaali Suuri Suurempi Suurin Oletus Valitse Valitse kaikki Poista kaikki valinnat Ensimmäinen Satunnainen Käyttäjän valinta Lataa lisää Läpinäkyvä Kutista Laajenna Kutista kaikki Laajenna kaikki Muut Teemat Emulaattorien argumentit Sisäänrakennetut parametrit Mukautetut parametrit Emulaattorin lisä-argumentit Ohita emulaattorien argumentit Pelaa-toiminto Valitse tuotava metadata Valitse tuotavat pelit Metadatan haku Päivitys saatavilla Muutokset edellisen päivityksen jälkeen Lataa ja asenna päivitys Tarkista päivitykset Päivitysvirhe Uuden version tarkistaminen epäonnistui. Olet ajan tasalla. Päivityksen lataaminen ja asentaminen epäonnistui. Playniteen on saatavilla päivitys Lataa teemalista uudelleen Ota käyttöön valittu teema Seuraa tiedostomuutoksia Ota teema automaattisesti käyttöön kun lähdetiedosto muuttuu Skriptin suoritus Ennen pelin käynnistämistä suoritettava skripti Pelin sulkeutumisen jälkeen suoritettava skripti Pelin käynnistymisen jälkeen suoritettava skripti Suorita sovelluksen käynnistyessä Suorita sovellusta suljettaessa Suorita yleinen skripti Yleinen Suodatettu Ajantasainen Uusi Testaa komentosarja Tallenna oletukseksi Lisää suosikkeihin Poista suosikeista Piilota tämä peli Poista piilotetuista Muokkaa… Laske asennuksen koko Asennuksen koko Aseta kategoria… Aseta pelin edistymisen tila Poista Pelaa Asenna Pelin asetukset Lisätiedot Poista asennus Avaa asennuksen sijainti Luo pikakuvake työpöydälle Avaa käyttöopas Enemmän Kirjastoliitännäisen hallinnoima Pelin käynnistysprosessia hallinnoi pelistä vastaava kirjastoliitännäinen. Määritellyltä sivulta ei löytynyt oleellista tietoa pelistä '{0}'. Vihje: Voit käyttää edistyneempää metadatan latausprosessia muokatessasi yksittäisen pelin tietoja valikon "Muokkaa"-vaihtoehdon kautta. Ei saatavilla kun jokin toiminto on käynnissä. Kuvausteksti huomioi HTML-syntaksin Peliaika tallennetaan sekunneissa. Luku välillä 0 ja 100 tai tyhjä jos ei pisteytystä. Ohjelmoinnissa, lokalisoinnissa ja muussa avustaneet, ei missään erityisessä järjestyksessä: Peruuta pelin seuranta? Asennuksen seuranta on käynnissä. Haluatko peruuttaa prosessin ja palauttaa pelin aiempaan tilaan? Pelin suorittamisen seuranta on käynnissä. Haluatko peruuttaa prosessin ja palauttaa pelin aiempaan tilaan? Pelattu aika Viimeksi pelattu {0}h {1}m {0} minuuttia {0} sekuntia Ei pelattu Avataan työpöytätilaa… Avataan televisiotilaan… Ladataan pelikirjastoa… Lasketaan asennus kokoa... Lasketaan {0} asennus kokoa... Skriptiä ei voitu asentaa. Skriptin asentaminen onnistui. Asenna skripti Skriptivirhe Laajennuksen toiminnon suorittaminen epäonnistui. Avaa metatietokansio Laske {0} -sovellusta ei ole asennettu. {0} sovellus avautuu nyt. Kirjaudu sisään ja sulje sen jälkeen tämä viesti. Odotetaan käyttäjän sisäänkirjautumista, sulje tämä kun olet valmis… Pelin asennuskansiota ei löydy. Virheellinen pelitoiminnon määritys. Tilin synkronointiongelmien vianmääritys Ongelmien vianmääritys Nimeä uudelleen Lisää uusi kohde Anna nimi Syötä uusi nimi Alle tunti 1-10 tuntia 10-100 tuntia 100-500 tuntia 500-1000 tuntia 1000+ Playnite pitää käynnistää uudelleen asennuksen viimeistelyä varten. Haluatko käynnistää uudelleen nyt? Laajennusta ei ole paketoitu oikein. Teemaa ei ole paketoitu oikein. Laajennuksen "{0}" oikein lataaminen epäonnistui. Teeman "{0}" oikein lataaminen epäonnistui. Laajennuksen oikein latautuminen epäonnistui. Teeman oikein lataaminen epäonnistui. Teema/Laajennus käyttää API-versiota, jota ei tueta. Asennus onnistui. Asenna laajennus? Yleinen Lisäosan "{0}" asennus epäonnistui. Laajennuksen asennus epäonnistui. {0} Haluatko asentaa uuden laajennuksen? {0} Tekijä: {1} Versio: {2} Haluatko päivittää "{0}" -laajennuksen? Nykyinen versio: {1} Uusi versio: {2} Teeman asennus epäonnistui. {0} Haluatko asentaa uuden teeman? {0} Tekijä: {1} Versio: {2} Haluatko päivittää "{0}" -teeman? Nykyinen versio: {1} Uusi versio: {2} Olet poistumassa Playnitesta ja avaamassa seuraavaa verkkosivua oletussselaimellasi. Haluatko jatkaa? {0} Valitut kuvat ovat niin suuria, että ne saattavat heikentää suorituskykyä. Erittäin suurien kuvien käyttö voi hidastaa käyttöliittymän vasteaikaa sekä lisätä muistin käyttöä. Suurimmat suositellut mitat: Kuvakkeet: {0} megapikseliä Kansikuvat: {1} megapikseliä Taustakuvat: {2} megapikseliä Suorituskykyvaroitus Älä näytä uudelleen Tiedostopääte {0} ei ole yhteensopiva. Virheellinen tiedostomuoto Valittu kuvatiedosto saattaa olla liian suuri optimi tehokkuuteen. Haluatko varmasti poistaa valitun teeman asennuksen? Asennus poistetaan, kun sovellus käynnistyy seuraavan kerran. Sisäänrakennettuja teemoja ei voi poistaa. Tämä teema ei tue Playniten tämänhetkistä versiota. Haluatko varmasti poistaa valitun laajennuksen asennuksen? Asennus poistetaan, kun sovellus käynnistyy seuraavan kerran. Sisäänrakennettuja laajennuksia ei voi poistaa. Tämä laajennus ei tue Playniten tämänhetkistä versiota. Asennushakemisto Datahakemisto Luodaan diagnostiikkapakettia… Lähetetään diagnostiikkapakettia… Tuo tiedosto… Mikä tämä on? Haluatko varmasti tehdä tämän? Pelattu aika Keskimääräinen peliaika Suurin peliaika Yleiskatsaus Sivupalkki Näytä sivupalkissa Nollaa asetukset Kaikki sovelluksen asetukset palautetaan oletuksiin, poislukien: - Tietokannan sijainti - Tuotujen poikkeuslista - Laajennusten asetukset, mukaan lukien kirjastointegroinnit Sovelluksen uudelleenkäynnistys vaaditaan. Haluatko varmasti nollata asetukset? Kehittäjille Ulkoiset laajennukset Anna täysi kansiopolku. Saavutukset Keskustelupalsta Uutiset Kaupan sivu Alustava asennus ei ole valmis. Toiminnon viimeistelemiseksi Playnite käynnistyy nyt uudelleen työpöytätilassa. Viimeksi pelatut Suosikit Eniten pelatut Kaikki Suodattimia on käytössä. Suodattimia on käytössä. Tulokset haulle: Annettu nimi on jo käytössä. Rajoita valinta nykyiseen suodattimeen Valitse toinen Lisäosat... Asennettu Laajennusten asetukset Selaa Päivitykset Päivitykset {0} Työpöytä-tilan teemat Televisiotilan teemat Etsitään... Lisäosa ei ole yhteensopiva tämän Playnite-version kanssa Lisäosan asennuspaketin lataaminen epäonnistui Tämä lisäosa on ajoitettu asennukseen Asenna Poista asennus Jo asennettu Uusia lisäosien päivityksiä ei löytynyt. Päivitä lisäosat Muutosloki ei saatavilla Ajoitettu asennettavaksi Lataus epäonnistui Lisenssi hylätty Ladataan {0}... Haetaan lisäosien päivityksiä… Yksi tai useampi lisäosapäivitys saatavilla. Valitse päivitettävät kohteet {0} Käyttöoikeussopimus Hyväksy Hylkää Valitse toiminto Linkki Tiedosto Emulaattori Komentosarja Oletus Prosessi Kansio Alkuperäinen prosessi Ei mitään Yhtenäinen Vain kohteet Vain alku ja loppu Tasainen vieritys Poista kohde? Haluatko varmasti poistaa tämän kohteen? Näytä painikkeet yläpalkissa: Yleisasetukset Ryhmittelyasetukset Lajitteluasetukset Suodata esiasetuksia Osanerottimen leveys Siirrä päämenu-painike sivupalkkiin Selainpaneeli Satunnainen pelinvalitsin Näyttää satunnaisen pelinvalitsimen Valitse satunnainen peli näkymästä Näytä hakukenttä koko ruudun tilassa Viimeisen 7 päivän aikana Viimeisen 31 päivän aikana Viimeisen 365 päivän aikana Yli 365 päivää sitten Konfiguroi Tallenna esiasetus Pienennä pelin käynnistämisen jälkeen Pienennä Playnite pelin käynnistyksen jälkeen. Tämän asetuksen käytöstä pois ottaminen voi johtaa syötön ongelmiin peliä käynnistettäessä! Fonttikoko Pieni fonttikoko Näytä kohteet päävalikossa: Käännetty X/A pää näkymän nappuloiden sitominen Vaihda pelin käynnistys ja pelin tiedot sivun napit keskenään. Vain ensisijainen ohjain Opaspainike tuo Playniten esiin Käyttöliittymän äänenvoimakkuus Tausta-äänenvoimakkuus Mykistä kun taustalla Äänijärjestelmää ei voitu käynnistää. Ulostulon rajapinta Yleiset Ulkoasu Ääni Asettelu Valikot Syöte {0} käynnistyy… {0} on käynnissä… Välilyönti Kuvarenderöinnin skaalain Vaihtoehtoinen Tasapainotettu Laatu Laadukas: Paras kuvanlaatu, hidas, korkea muistikäyttö. Keskitaso: Hyvä kuvanlaatu, nopea, pieni muistikäyttö. Vaihtoehtoinen: Parempi kuvanlaatu, keskitason nopeus, pieni muistikäyttö. Valitse tiedosto… Valitse kansio... Aloituskomentosarja Huomioi että laajennukset ja teemat voivat vaikuttaa Playniten suorituskykyyn, vakauteen ja turvallisuuteen suuresti. Jos ongelmia ilmenee teeman tai laajennuksen asentamisen jälkeen, kokeile poistaa ne käytöstä/poistaa niiden asennus nähdäksesi johtuuko ongelma niistä. Valitse käynnistettäessä Valitse käynnistettäessä Sisäänrakennetut profiilit Sisäänrakennettu profiili Mukautetut profiilit Emulaattorin määritys Alustan määritys Alueen määritys Yhdistä yhdeksi peliksi Aseta alusta Aseta alue Skannaa kansio Skannaa emulaattorilla Kansion skannaus emulaattorille epäonnistui Kansioiden skannaus emuloiduille peleille epäonnistui Piilota tuodut Tuotavat profiilit: Skannaa kokoonpanot automaattisesti Tallenna autoskannaavana kokoonpanona Skannaa alihakemistoista Skannaa arkistojen sisältä Lisää skanneri Lisää tallennettu skanneri Käynnistä skannaus Järjestäminen Käytä kaikille Muokkaa kenttiä Valitse kaikki / poista kaikki valinnat Avaa Aktivoi Aloita kirjoittaminen etsiäksesi pelejä… [F1] saadaksesi apua Sisällytä asentamattomat pelit Asentamattomat pelit jätetty pois Piilotetut pelit mukana Piilotetut pelit jätetty pois Käynnistä tai asenna Mene lisätietoihin Pelivalikko Muokkaa peliä Avaa haku Hakukenttä Hakupainike Ensisijainen pelin toiminto Toissijainen pelin toiminto CTRL+F avaa yleisen haun hakukentän valinnan sijaan Haun tarjoajat Oletus avainsana Mukautettu avainsana Järjestelmän laajuinen pikanäppäin Playnite haku Laajennuksen asetukset Poikkeukset Asetukset Haetaan lisäosan tietoja… Metatietolähdettä ei ole saatavilla Pelivalikko ================================================ FILE: source/Playnite/Localization/fr_FR.xaml ================================================  Français Langue Quitter Filtre activé Filtre désactivé Autres filtres Filtres Filtre Données invalides Sauvegarder les modifications ? www.playnite.link Code source sur GitHub Créer un dossier de diagnostic Envoyer un dossier de diagnostic À propos de Playnite Créé par Josef Nemec Attribuer une catégorie Définir les catégories Ajouter une catégorie Sélectionné - Attribue une catégorie Non sélectionné - Retire une catégorie Indéterminé - Aucun changement Sans catégorie Sans plateforme Oh non, quelque chose s'est mal passé… Une erreur est survenue. Si vous souhaitez nous aider à corriger ce problème, vous pouvez brièvement décrire ce que vous faisiez au moment du crash puis créer un dossier de diagnostic. Si vous êtes connecté à internet, le rapport généré sera automatiquement envoyé aux serveurs de Playnite pour être analysé. Vous pouvez également cliquer sur le bouton 'Signaler un Crash' pour créer une nouvelle entrée dans GitHub et rédiger le rapport de crash manuellement. Merci de votre aide. L'extension "{0}" a causé une erreur critique. Il est recommandé de signaler ce problème au développeur de l'extension (via un fichier log) et de la désactiver si il devait persister. L'extension "{0}" a causé une erreur critique. Il est recommandé de signaler ce problème au développeur de l'extension et de la désactiver si il devait persister. Une extension ou un thème a provoqué une erreur irrécupérable. Nous vous recommandons de désactiver les modules complémentaires, d'isoler celui qui pose problème et de signaler le problème au développeur du module en question. Une erreur est survenue. Si vous souhaitez nous aider à corriger ce problème, veuillez créer un rapport de bug. Merci. Désactiver l'extension Enregistrer le fichier log Envoyer les informations de diagnostic Signaler un plantage Redémarrer Playnite Redémarrer en mode sans échec Désactivation de tous les extensions tierces et activation du thème par défaut. Quitter Playnite Activité au moment du crash (à rédiger en anglais) : Gestionnaire de bibliothèque Supprimer le(s) jeu(x) ? Suppression impossible - le jeu ou l'installateur est en cours d’exécution. Le jeu est en cours d'exécution et ne peut être désinstallé. Êtes-vous sûr de vouloir supprimer {0} ? Êtes-vous sûr de vouloir supprimer {0} jeux ? Êtes-vous sûr de vouloir supprimer {0} ? L'ajout à la liste d'exclusion empêchera l'importation automatique du jeu concerné lors des prochaines mises à jour de la bibliothèque. Êtes-vous sûr de vouloir supprimer {0} jeux ? Ajouter les jeux supprimés à la liste d'exclusion d'importation préviendra leur ajout lors de la prochaine mise à jour de la bibliothèque. Êtes-vous sûr de vouloir supprimer la ou les données ({0}) actuellement inutilisées ? Aucune donnée inutilisée n'a été trouvée. Oui (Ajouter à la liste d'exclusion) La section contient des modifications à enregistrer Mise à jour de la base de données... La mise à jour de la base de données a échoué. La mise à jour de la base de données a échoué. {0} Mo d'espace libre requis. Erreur de jeu Erreur : Impossible de démarrer le jeu. '{0}' n'a pas été trouvé dans la base de données. Impossible de démarrer le jeu : {0} Impossible d'exécuter l'action : {0} Erreur : Impossible d'ouvrir l'emplacement du jeu : {0} Impossible de détecter la taille de l'installation du jeu : {0} Erreur lors de l'analyse de la taille d'installation Il y a eu {0} erreurs lors de l'analyse de la taille de l'installation Impossible de créer un raccourci : {0} Impossible d'ouvrir le manuel : {0} Erreur : Impossible d'installer le jeu : {0} Impossible de désinstaller le jeu : {0} Aucune action de démarrage valide n'a été trouvée. Lorsque vous utilisez des actions d'émulateur, assurez-vous que les définitions de la plateforme sont compatibles entre le jeu et la configuration de l'émulateur. Fonction d'installation introuvable. Le plugin responsable de ce jeu a été désactivé ou n'est pas encore installé. Téléchargement des métadonnées officielles non disponible. Aucun jeu sélectionné. Échec de l'exécution du script de jeu. Échec de l'exécution du script de jeu. Échec de l'exécution du script global. Échec de l'exécution du script pour l'émulateur. Échec de l'exécution du script de jeu. PowerShell 3.0 ou plus récent n'est pas installé. Impossible de déterminer une action de lancement. Activé(e) Désactivé Supprimer Supprimer les données inutilisées Renommer Copier Ajouter Icône par défaut Jaquette par défaut Arrière-plan par défaut Terminer Suivant Retour TERMINÉ RET. EFF. Effacer Ignorer Tout ignorer Importer Nom Auteur Module Série Version Dernière session Les plus joués Nombre de sessions Taille de l'installation Dossier Notes Ajouté le Date d'ajout Modifié le Dernière modification Site internet Chemin OK Sauvegarder Fermer Annuler Confirmer Réinitialiser Oui Non Bienvenue Utilisateur local Général Média Liens Installation Actions Téléchargement... Téléchargement des données... Chargement… Type Profil Profils Supprimer Télécharger Chercher Résolution : Indifférent Zoom Affichage en liste Pochettes Affichage en grille Affichage détaillé Personnalisé Lien Remerciements Licence Contributeurs Fermeture de Playnite... Aujourd'hui Hier Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche Cette semaine Mois dernier Cette année Il y a plus d'un an 0Mo à 100Mo 100 Mo à 1 Go 1 Go à 5 Go 5 Go à 10 Go 10 Go à 20 Go 20 Go à 40 Go 40 Go à 100 Go 100Go ou plus Les informations ont été importées avec succès. Tous Id jeu Id base de données Préréglages Colonne Colonnes Rangée Rangées Impossible de générer une icône à partir de cette action de lancement. Le fichier nécessaire n'est pas présent. Télécharger uniquement les métadonnées manquantes Cette option ne téléchargera des métadonnées que pour les champs vides. Sélection de jeux Veuillez sélectionner les jeux qui doivent être mis à jour avec les nouvelles métadonnées : Tous les jeux de la base de données Tous les jeux actuellement filtrés Le ou les jeux actuellement sélectionnés Aucun champ de métadonnées sélectionné Aucun champ de métadonnées n'est sélectionné pour le téléchargement. Veuillez en sélectionner au moins un, et activer au moins un fournisseur de métadonnées. Magasin officiel IGDB Veuillez sélectionner les champs qui doivent être automatiquement remplis par Playnite et les sources à utiliser pour obtenir les données. Vous pouvez cliquer sur le logo ci-dessus et contribuer à la mise à jour de la base de données igdb.com afin d'améliorer les données utilisées par Playnite. Téléchargement des métadonnées... Importation des jeux installés… Importation depuis {0}… Importation des jeux émulés depuis {0}… Téléchargement des mises à jour de la bibliothèque… Analyse de la taille des jeux dans la bibliothèque… Analyse de la taille des jeux importés… Mise à jour de bibliothèque terminée Rechargement des ressources… Configuration Paramètres Plateformes et émulateurs Configurer les émulateurs Gestionnaire de bibliothèque Utilitaires Télécharger les métadonnées Logiciels utilitaires Configurer les intégrations Ouvrir un client tiers Client tiers Actualiser les jeux Annuler la mise à jour de la bibliothèque Mettre à jour les dossier d'émulation Ajouter un jeu Manuellement Importer depuis une installation Importer des jeux émulés Application Microsoft Store À propos de Playnite Envoyer des Commentaires Basculer en mode plein écran Liens Aide Soutenir Playnite Soutenir sur Ko-fi Manuel utilisateur Documentation Redémarrer l'ordinateur Arrêter l'ordinateur Mettre en veille Mettre en veille prolongée Verrouiller le système Déconnexion de l'utilisateur Jouer à un jeu aléatoire Sélectionner les éléments à afficher : Espacement Afficher le cadre Largeur des bordures en mode grille Type d'icône par défaut Type de jaquette par défaut Image de fond par défaut Espacement vertical par rapport aux détails du jeu Position des détails (affichage en grille) Position de la liste (affichage détaillé) Afficher un séparateur entre les panneaux Hauteur de la jaquette Hauteur des icônes (liste de jeux) Police Police monospace Position du panneau de filtre Position du panneau d'explorateur Propriétés de l'affichage en grille Format d'affichage Les options listées ci-dessous affectent également le mode Plein Écran Mise à l'échelle Boîtier DVD Epic Games Store GOG Galaxy 2.0 IGDB Carré Bannière Steam Pochettes verticales (Steam) Twitch * Redémarrage nécessaire Paramètres Général Panneau supérieur Apparence Détails Affichage Autres Plein écran Manette Performances Métadonnées Mises à jour Recherche Sauvegarde Sauvegarder les données de la bibliothèque Restaurer une sauvegarde existante Importer les modifications dans la bibliothèque automatiquement L'emplacement du fichier de base de données n'est pas valide, le chemin de fichier correct doit être défini. Le nom du profil ne peut pas être vide. Télécharger les métadonnées automatiquement Minimiser la fenêtre au démarrage de l'application Lancer Playnite au démarrage de l'ordinateur Lancer Playnite et le réduire dans la barre d'état Impossible de lancer Playnite au démarrage de l'ordinateur. Lancer Playnite en mode plein écran Chargement d'image asynchrone Améliore la fluidité du défilement des listes de jeux en chargeant les images plus lentement. Afficher le nom des jeux à la place des jaquettes manquantes Afficher le nom des jeux dans le mode d'affichage en grille Assombrir les jeux non installés Afficher les icônes des jeux dans le mode d'affichage détaillé Afficher le nombre d'items dans la description des groupes Afficher uniquement les champs utilisés dans l'explorateur/filtre Désactiver l'accélération matérielle Cocher cette option en cas de ralentissement ou de malfonctionnement de l'interface. Afficher les jeux masqués dans la liste de lancement rapide Affecte les listes en mode barre des tâches et minimisé. Nombre de jeux dans la liste de lancement rapide Utiliser les image de fond des jeux comme arrière-plan de fenêtre Flouter les images de fond HQ Assombrir les images de fond Utiliser en mode d'affichage en grille Thème Profil du thème Thème plein écran Thème plein écran Emplacement de la base de données Statut de connexion : Paramètres de Playnite Effacer le cache Web Peut résoudre les problèmes rencontrés lors de la liaison de profils. Réduire dans la barre d'état Lorsque la fenêtre est minimisée Lorsque la fenêtre est fermée Lorsque le jeu démarre : Lorsque le jeu se ferme : Formater l'indicateur de temps de jeu pour afficher le nombre de jours joués. Formats des dates : Cela vous déconnectera de tous les services liés. Le redémarrage de l'application est requis, voulez-vous continuer ? Vider le cache ? Redémarrage nécessaire pour appliquer un nouveau thème Obtenir plus de thèmes Créer un thème Obtenir plus d'extensions Créer une nouvelle extension Contribuer à la traduction Les paramètres modifiés nécessitent le redémarrage de l'application. Voulez-vous continuer ? (les tâches en cours d'exécution seront annulées) Redémarrer Playnite ? Playnite ne déplace pas automatiquement les fichiers de la bibliothèque, vous devez les déplacer ou les copier avant que l'emplacement ne soit modifié. Si aucune bibliothèque n'existe dans l'emplacement cible, une nouvelle sera créée. Le nouvel emplacement de la base de données ne sera utilisé qu'après le redémarrage de l'application. La durée de la session ne sera pas enregistrée si Playnite est fermé après le lancement du jeu. Nombre de rangées Nombre de colonnes Nbr. de rangées (Détails) Menu : Afficher les images de fond Ne peut s'appliquer rétroactivement (à moins de retélécharger les métadonnées). Importer le temps de jeu de la bibliothèque : Détermine la façon dont Playnite prend en compte le temps de jeu rapporté par les clients tiers. Le support de la récupération du temps de jeu par les extensions chargées de gérer les clients de jeu est nécessaire pour pouvoir utiliser cette fonctionnalité. Toujours : Importe le temps de jeu lors de l'ajout initial et pour tous les jeux existants dans la base de données Playnite. Initialement : N'importe le temps de jeu que lors de l'ajout initial. Jamais : N'importe jamais le temps de jeu. Toujours Initialement Jamais Activer le support des manettes Xinput en mode bureau Le bouton Guide ouvre le mode plein écran Choisir les paramètres de téléchargement automatique des métadonnées lors de l'importation d'un ou plusieurs jeux. Affichage utilisé Toujours utiliser l'affichage principal Afficher le nom des jeux Afficher l'état de la batterie Afficher le pourcentage de batterie Afficher l'heure du système Cacher le curseur Tri rapide : jeux installés uniquement Apparence des boutons Affichage Défilement Horizontal Sélectionner une sous-section Aucun paramètre n'est disponible Impossible de charger les paramètres Ces scripts s'appliquent à l'ensemble de la bibliothèque. Des scripts supplémentaires peuvent être assignés individuellement en modifiant les propriétés des jeux désirés. Animer les transitions des images de fond Taille du texte Automatique Aliasé Niveaux de gris ClearType Optimal Affichage Formatage du texte Mode de rendu du texte Les options de rendu et de formatage du texte ne s'appliquent pas actuellement aux textes de description des jeux. Précharger les images de fond Lorsque cette option est active, Playnite téléchargera les images de fond en même temps que le reste des métadonnées. Cela nécessitera davantage d'espace disque mais rendra les images de fond disponible lorsque vous êtes hors-ligne. Si cette option est désactivée, les images de fond ne seront téléchargées que lorsqu'elles sont sollicitées. Cela nécessitera moins d'espace disque mais les images de fond pourraient ne pas être disponibles lorsque vous êtes hors-ligne. Fermer automatiquement les clients après la fin d'une session de jeu Délai (en secondes) avant la fermeture du client : Ne pas fermer après une session dont la durée (en secondes) est inférieure à : Fermer automatiquement les clients suivants : Fermeture auto. Liste d'exclusion Notifier lorsqu'une image dépasse la taille recommandée Ouverture des répertoires par invite de commande Système d'évaluation d'âge requis par défaut Mettre à jour la taille d'installation des jeux lors de l'actualisation de la bibliothèque Lorsque cette option est activée, la taille des dossiers d'installation de jeux est mise à jour si des fichiers ont été modifiés depuis la dernière analyse. Aucune Remplir (étirement) Préserver le rapport largeur/hauteur Recadrer Gauche Droite Haut Bas Erreur d'importation Statut : Authentification requise Statut : Authentification échouée Mode de rendu web alternatif Peut résoudre certains problèmes avec les fenêtres web, telles que les fenêtres d'authentification. Chargement partiel des descriptions de jeu Les descriptions trop longues peuvent ralentir la sélection de certains jeux. Le chargement partiel permet d'alléger le chargement initial de la description, le reste étant chargé à la demande. Importation de Métadonnées Télécharger les Métadonnées Définit un profil de téléchargement des métadonnées. Peut également être modifié dans le menu de paramètres. Assistant d'importation d'émulation Cet assistant vous guidera à travers un processus de téléchargement et d'importation d'émulateurs de consoles de jeu. Il vous aidera également à importer les copies de sauvegarde de vos jeux à émuler. Gardez à l'esprit que vous pourrez toujours ajouter des émulateurs supplémentaires et/ou des jeux plus tard via le menu principal (sous le menu "Bibliothèque" pour les paramètres de l'émulateur et le menu "Ajouter des jeux" pour les jeux émulés). Voici la liste des émulateurs que Playnite peut reconnaître et configurer automatiquement. Vous pouvez les télécharger et les installer en visitant leur site Web. Une fois les émulateurs manuellement installés, vous pourrez les importer via le menu de configuration. Vous pouvez importer les émulateurs installés sur votre PC en cliquant sur le bouton "Scanner un dossier". Playnite analysera le dossier sélectionné ainsi que ses sous-dossiers à la recherche d'émulateurs reconnus par l'application et vous permettra de les importer automatiquement. Vous pouvez importer à partir de plusieurs dossiers différents. Les émulateurs seront triés puis ajoutés au bas de la liste actuelle. Vous pouvez importer des jeux en cliquant sur le bouton "Scanner un dossier à l'aide d'un émulateur...". La sélection de l'émulateur indique à Playnite quels types de fichiers doivent être analysés et importés. Vous pouvez importer à partir de plusieurs dossiers différents. Les jeux seront triés puis ajoutés au bas de la liste actuelle. Aucun émulateur n'est sélectionné pour l'importation. Vous ne pourrez pas importer automatiquement les jeux émulés sans d'abord configurer les émulateurs. Êtes-vous sûr de vouloir continuer et quitter le processus d'importation ? Aucun émulateur n'est configuré dans Playnite. Vous ne pouvez pas importer de jeux sans d'abord configurer l'émulateur et les types de fichiers appropriés. Voulez-vous ajouter des émulateurs maintenant ? Scanner un dossier à l'aide d'un émulateur... Sélectionner les fichiers Scanner un dossier... Configurer les émulateurs… Balayage… Analyse en cours {0}… Assistant de configuration Playnite peut importer des jeux à partir des clients que vous utilisez et mettre à jour votre bibliothèque de manière automatique. Vous pouvez également ajouter des jeux ne dépendant d'aucun client en cliquant sur 'Ajouter un jeu' à partir du menu principal. Les profils de jeu personnalisés peuvent ensuite être assignés à n'importe quelle plateforme. Configuration de la bibliothèque Importez automatiquement les jeux des services suivants. Tout changement ultérieur tel que l'installation ou l'activation d'un jeu sera automatiquement pris en compte au démarrage de Playnite ou lors d'une actualisation manuelle. Les paramètres sélectionnés affectent les importations initiales et ultérieures. Configuration terminée Vous avez terminé la configuration initiale. N'oubliez pas que vous pouvez modifier la configuration des clients à tout moment via le menu 'Paramètres' De nouveaux jeux peuvent être ajoutés en cliquant sur le logo de Playnite. Échec du téléchargement d'une ou plusieurs extensions. Vous pouvez réessayer via le menu des extensions une fois la configuration initiale achevée. Téléchargement de {0} intégration(s)… Téléchargement de la liste d'intégrations recommandées… Échec du téléchargement de la liste d'intégrations recommandées. Le téléchargement peut être réinitialisé via le gestionnaire d'extensions. Configurer les plateformes et émulateurs Configurer les émulateurs Plateformes Plateforme Émulateurs Émulateur Ajouter une plateforme Sélectionner Sélectionner Sélectionner Sélectionner Sélectionner Sélectionner le Fichier Ajouter l'URL Ajouter un émulateur Plateforme(s) compatible(s) Voulez-vous enregistrer les modifications de la plateforme ? Voulez-vous enregistrer les modifications de l'émulateur ? Exécutable Arguments Répertoire de travail Types de fichiers compatible Importer des émulateurs… Télécharger des émulateurs… Charger les arguments prédéfinis à partir du profil d'émulateur connu ?Êtes-vous sûr de vouloir supprimer l'émulateur {0} ? Il est actuellement utilisé par {1} jeu(x). Voulez-vous vraiment supprimer la plateforme {0} ? Elle est actuellement associée à {1} jeu(x) et {2} émulateur(s). Aide Tri Ordre de tri Regrouper par Ascendant Descendant Ne pas ordonner Ordonner par Bibliothèque Ordonner par Catégorie Ordonner par Plateforme "Type d'affichage" Affichage Explorateur Filtres Icône Icône de bibliothèque Jaquette Image de fond Nom de tri Bibliothèque Manuel Nom Disque d'installation Nom du compte Plateforme Catégorie(s) Genre(s) Date de sortie Année de sortie Développeur(s) Mot(s) clé(s) Éditeur(s) État de l'installation Filtrage strict Si activé, seuls les jeux correspondant à tous les éléments de filtres seront inclus. Si désactivé, les jeux correspondant à au moins un élément de filtres seront inclus. Installés Jeu installé Non installés Masqués Favoris Activer le support HDR Lorsque cette option est activée, le mode HDR sera activé sur l'affichage principal avant le démarrage d'un jeu. Veuillez noter que le mode HDR est uniquement prise en charge par l'affichage principal. Dernière activité Catégorie Description Répertoire d'installation Jaquette Liens Chemin de l'ISO/ROM Genre Genres Entreprise Entreprises Développeur Développeurs Éditeur Éditeurs Catégories Catégories Étiquettes Étiquette(s) Fonctionnalité Fonctionnalités PEGI PEGI Région Régions Source Sources Activité récente Erreur de la Base de Données Erreur : Impossible d'ouvrir la base de données de la bibliothèque. La base de données n'est pas ouverte. Erreur : Impossible d'accéder à la base de données de la bibliothèque. Le fichier "{0}" est utilisé par un autre processus ou dans un emplacement inaccessible. Erreur : Impossible de créer le dossier de diagnostic. Impossible d'envoyer le dossier de diagnostic automatiquement . Le dossier de diagnostic a été créé et mis en ligne avec succès. Le dossier de diagnostic a été créé et mis en ligne avec succès. Veuillez copier le numéro d'identification indiqué ci-dessous pour le communiquer lors de la rédaction de votre rapport de bug sur la page Github du projet (Catégorie : Issues). Echec de l'importation depuis {0}. Échec de l'importation depuis {0}. Impossible de rechercher des jeux par profil d'émulateur sélectionné. Le profil ne contient aucune extension de fichier ou plateformes. Playnite n'a pas pu démarrer. Veuillez fermer toutes les instances en cours et réessayer. Échec de l'application du thème "{0}", profil de couleur "{1}" {2} Impossible d'ouvrir le lien, l'URL n'est pas au format valide. Impossible d'ouvrir l'application. Échec de l'initialisation du composant de vue web. Playnite ne peut pas continuer le processus de démarrage. Plus d'informations sur https://playnite.link/cefstartup Impossible d'importer des émulateurs, le fichier de définition d'émulateur n'existe pas. Échec de l'exécution de l'action. Modifier les propriétés URL de l'image Ajouter un lien Ajouter une ROM Enregistrer les modifications Appliquez les changements de champ au(x) jeu(x) en cours d'édition. Ajouter une action Supprimer l'action Supprimer l'action Ajouter la sélection Scanner un dossier… Détection automatique Parcourir… Ouvrir Playnite Paramètres de profil Le nom du jeu ne peut pas être vide. Le répertoire de l'action de jeu ne peut pas être vide. Le nom du jeu ne peut pas être vide avant de rechercher des métadonnées... Données invalides Entrez une adresse URL commençant par http:// ou https:// Ajouter l'URL Échec du téléchargement des métadonnées : {0} Erreur de téléchargement Effacer les filtres Profil privé Profil public Clé API Erreur de démarrage Erreur de thème Tout effacer Installation en cours... Désinstallation en cours... Démarrage... En cours d'exécution URL invalide Ne rien faire Minimiser Restaurer Restaurer la fenêtre uniquement lorsque lancé depuis l'interface utilisateur Fermer Changer Avancé [Indéterminé] Progression Progression Ma note Note presse Note communauté Scripts de jeu Scripts d'application Scripts Extensions Sources de métadonnées Extensions ID de l'extension Actualiser les scripts Kit de développement interactif PowerShell Scripts chargés avec succès. Aucun jeu trouvé avec les critères spécifiés Aucun élément n'a été trouvé Retour au bureau Quitter Playnite Clients de jeu Tout mettre à jour Auteur : Version : Mis à jour le : Module : Bibliothèque Statistiques Tout Indéterminé Notifications Largeur Hauteur Taille Réduite Normale Grande Très grande Maximale Par défaut Sélectionner Tout sélectionner Tout désélectionner Premier Aléatoire Sélection de l'utilisateur Charger plus Transparent Réduire Développer Tout réduire Tout développer Autres Thèmes Arguments de l'émulateur Arguments par défaut Arguments personnalisés Arguments d'émulateur supplémentaires Outrepasser les paramètres de l'émulateur Action de lancement Sélectionner les métadonnées à importer Recherche de jeux Recherche de métadonnées Mise à jour disponible Nouveautés Installer la mise à jour Mises à jour Erreur lors de la mise à jour. Erreur lors de la recherche. Vous possédez déjà la dernière version de Playnite. Erreur lors de l'installation. Une tâche de fond est en cours d'exécution. Voulez-vous l'annuler et continuer la mise à jour ? Une tâche de fond est en cours d'exécution. Voulez-vous l'annuler et fermer Playnite ? Une tâche de fond est en cours d'exécution. Voulez-vous l'annuler et changer de mode ? Une nouvelle mise à jour est disponible Actualiser la liste des thèmes Appliquer le thème sélectionnée Actualiser les changements de fichiers Appliquer automatiquement le thème lorsque le fichier source change Exécution de script Exécuter avant le lancement du jeu Exécuter après avoir quitté le jeu Exécuter après le lancement du jeu Exécuter au démarrage de l'application Exécuter à l'arrêt de l'application Script de lancement Script post-lancement Script d'arrêt Exécuter le script global Général Par filtre Actuelles Nouvelles Tester le script N'afficher que les objets sélectionnés Enregistrer comme réglage par défaut Ajouter aux favoris Retirer des favoris Masquer la sélection Afficher dans la bibliothèque Activer le mode HDR Désactiver le mode HDR Modifier les propriétés… Calculer la taille de l'installation Calculer la taille d'installation (tous les jeux) Calculer la taille d'installation (uniquement les données manquantes) Taille d'installation Définir des catégories… Déterminer la progression Supprimer Jouer Installer Options Propriétés Désinstaller Ouvrir l'emplacement du jeu Créer un raccourci sur le bureau Ouvrir le manuel Détails Géré par le client Le client en charge de ce jeu est responsable du processus de lancement. Pas d'informations pertinentes trouvées pour le jeu '{0}' sur la page spécifiée. Conseil : Vous pouvez utiliser un processus de téléchargement des métadonnées plus poussé via l'option "Modifier les propriétés". Indisponible lorsqu'une action est en cours. Le texte de description est sensible à la syntaxe HTML Le temps de jeu est indiqué en secondes. La taille de l'installation est indiquée en octets. La date de sortie doit être indiquée selon le format "année-mois-jour". L'indication du mois et du jour ne sont pas nécessaires. Note entre 0 et 100. Le développement de Playnite est soutenu par ces Patreon et membres Ko-fi : Code, traduction et autres contributions sans ordre particulier : Annuler monitoring ? Le monitoring de l'installation est en cours, voulez-vous l'annuler ? Le monitoring de l'exécution du jeu est en cours, voulez-vous l'annuler ? Temps de jeu Dernière session {0}j {1}h {2}m {0}h {1}m {0} minutes {0} secondes Non joué Retour au bureau… Ouverture du mode plein écran… Chargement de la bibliothèque… Calcul de la taille d'installation… Calcul de la taille d'installation de {0}… Impossible d'installer le script. Le script a été installé avec succès. Installer un script Erreur de script Impossible d'exécuter la fonction. Ouvrir dossier de métadonnées Calculer Calcule automatiquement la taille de l'installation en utilisant les ROMs existantes ou le répertoire d'installation s'il a été indiqué. {0} le client n'est pas installé. {0} Le client va maintenant démarrer. Veuillez vous identifier avant de fermer la fenêtre de connexion. (Fermer cette fenêtre une fois la connexion établie) Dossier d'installation introuvable. Configuration invalide. Régler les problèmes de synchronisation Résolution des problèmes Renommer l'élément Ajouter un nouvel élément Saisir un nom Saisir un nouveau nom Moins d'une heure 1 à 10 heures 10 à 100 heures 100 à 500 heures 500 à 1000 heures 1000+ Playnite doit être redémarré pour compléter l'installation. Voulez-vous redémarrer maintenant ? L'extension n'a pas été archivée correctement. Le thème n'a pas été archivé correctement. L'extension "{0}" ne s'est pas chargée correctement. L'extension "{0}" n'est pas supportée par cette version de Playnite et ne peut pas être chargée. Le thème "{0}" ne s'est pas chargé correctement. Le thème "{0}" n'est pas supporté par cette version de Playnite et ne peut pas être chargé. L'extension ne s'est pas chargée correctement. Le thème ne s'est pas chargé correctement. Thème/Extension utilise une version d'API non supportée. L'installation a réussi. Installer l'extension ? Divers Échec d'installation de l'extension "{0}" L'installation de l'extension a échoué. {0} Souhaitez-vous installer une nouvelle extension ? {0} Par {1} Version {2} Souhaitez-vous mettre à jour l'extension "{0}" ? Version actuelle : {1} Nouvelle version : {2} L'installation du thème a échoué. {0} Souhaitez-vous installer un nouveau thème ? {0} Par {1} Version {2} Souhaitez-vous mettre à jour le thème "{0}" ? Version actuelle : {1} Nouvelle version : {2} Vous êtes sur le point de quitter Playnite pour ouvrir la page web suivante à l'aide de votre navigateur par défaut. Voulez-vous continuer ? {0} L'image ou les images sélectionnées pourraient être trop lourdes pour des performances optimales. Utiliser des images d'une trop grande résolution peut impacter la fluidité de l'interface et accroitre l'utilisation de la mémoire de manière significative. Les tailles maximales recommandées sont : Icônes : {0} px (hauteur) Jaquettes : {1} px (hauteur) Images de fond : {2} px (hauteur) Avertissement de performances Ne plus afficher Le fichier ayant l'extension {0} n'est pas compatible Extension de fichier incompatible L'image sélectionnée pourrait être trop lourde pour des performances optimales. Voulez-vous désinstaller le thème sélectionné ? La désinstallation sera planifiée pour le prochain lancement de l'application. Les thèmes par défaut ne peuvent être désinstallés. Le thème sélectionné est incompatible avec la version actuelle de Playnite. Voulez-vous désinstaller l'extension sélectionnée ? La désinstallation sera planifiée pour le prochain lancement de l'application. Les extensions par défaut ne peuvent être désinstallées. L'extension sélectionnée est incompatible avec la version actuelle de Playnite. Emplacement d'installation Emplacement des données Création du dossier de diagnostic... Envoi du dossier de diagnostic... Importer un fichier... Qu'est-ce que c'est ? Valider l'action en cours ? Temps de jeu global Temps de jeu moyen Temps de jeu le plus élevé Taille totale Vue d'ensemble Barre latérale Afficher dans la barre latérale Réinitialiser les paramètres Tous les paramètres de l'application vont être réinitialisés à l'exception de : - L'emplacement de la base de données - La liste d'exclusion d'importation - Les paramètres d'extension et des bibliothèques L'application va redémarrer pour achever ce processus. Voulez-vous réinitialiser les paramètres ? Développement Extension externes Indiquer le chemin complet du dossier Succès Forum Actualités Page du magasin La configuration initiale n'est pas achevée. Playnite va maintenant redémarrer en mode Bureau pour continuer la procédure. Joués récemment Favoris Les plus joués Tous Des filtres sont actifs Des filtres additionnels sont actifs Résultats de la recherche : Ce nom est déjà utilisé par un autre élément. Limiter la sélection au filtrage actuel Autre choix Gestionnaire d'extensions Installé(s) Paramètres des extensions Parcourir Mises à jour Mises à jour ({0}) La gestion des extensions et thèmes installés, y compris leurs paramètres, a été déplacée vers le gestionnaire d'extensions Les intégrations de bibliothèques actuellement installées peuvent être configurées ici. Pour installer ou désinstaller des intégrations, utilisez le gestionnaire d'extension depuis le menu principal. Thèmes (Bureau) Thèmes (Plein écran) Recherche en cours… Cette extension n'est pas compatible avec cette version de Playnite. Échec du téléchargement du paquet de l'extension. Échec du téléchargement du manifeste d'extension. Un redémarrage est requis pour appliquer les paramètres. Cette extension est en attente d'installation. Installer Réinstaller Désinstaller Déjà installé Aucune mise à jour disponible Mettre à jour les extensions Notes de version indisponibles Installation planifiée Échec du téléchargement Licence rejetée Téléchargement en cours {0}… Recherche de mises à jour d'extensions… Recherche de mises à jour du programme… Une ou plusieurs mises à jour sont disponibles. Sélectionner le ou les éléments à mettre à jour Instance de développement d'extension {0} Contrat d'utilisation Accepter Refuser Inclure les actions de lancement du client Sélectionner l'action Mode de suivi Chemin de suivi Délai de suivi initial Fréquence de suivi Lien Fichier Émulateur Script Défaut Processus Dossier Processus originel Nom du processus Log Les modifications suivantes vont écraser les données pour tous les jeux sélectionnés ! Aucun Uniforme Items uniquement Début et fin uniquement Sensibilité du défilement Défilement fluide Vitesse d'animation Supprimer l'élément ? Êtes-vous certain de vouloir supprimer cet item ? Boutons à afficher dans le panneau supérieur : Paramètres d'affichage Paramètres de regroupement Paramètres de tri Filtres rapides Position des plugins Largeur des séparateurs Déplacer le bouton du menu principal vers la barre latérale Explorateur Jeu aléatoire Jeu aléatoire (Liste) Sélectionner un jeu aléatoire dans la liste/grillle Enregistrer les paramètres de tri et de regroupement Afficher comme filtre rapide en mode Plein Écran Ces sept derniers jours Ces trente et un derniers jours Ces 365 derniers jours Il y a plus d'un an Configurer Enregistrer le Préréglage Réduire après le lancement du jeu Réduit Playnite après le lancement du jeu. Désactiver cette option peut créer des problèmes de lancement avec certains jeux. Taille de police Taille de police réduite Activer la prise en charge de l'API de la manette de jeu Prise en charge des manettes de jeu Si désactivé, Playnite n'acceptera aucune entrée de contrôleur de jeu. Désactivez-le si vous utilisez des outils qui convertissent les entrées des contrôleurs de jeu en entrées de souris/clavier et que vous obtenez des entrées doubles dans Playnite. Afficher les éléments dans le menu principal : Inverser les boutons X/A Inverse l'attribution des boutons de lancement et d'affichage des propriétés du jeu. Inverser les boutons A/B Inverse l'attribution des boutons de confirmation et d'annulation. N'autoriser que la manette principale Si cette option est activée, Playnite n'acceptera que les entrées de la manette principale. Le bouton guide rétablit le focus du mode plein écran Volume de l'interface Volume d'ambiance Rendre muet lorsque la fenêtre est en arrière-plan Échec de l'initialisation de l'interface audio. Sortie audio Déterminer l'API utilisée pour la sortie audio. Modifier l'API peut résoudre certains problèmes de son. Général Affichage Audio Disposition Menus Entrée {0} est en cours de lancement… {0} est en cours d'exécution… Majuscules Espace Mode de mise à l'échelle Alernatif Equilibré Haute qualité Haute qualité: Garantit la meilleur qualité mais peut être lent et utiliser beaucoup de mémoire. Équilibré: Bon compromis entre qualité et performances. Alternatif: Qualité améliorée avec une pénalité minimale en termes de performances. Sélectionner un fichier… Sélectionner un dossier… Script de lancement Veuillez noter que les thèmes et les extensions sont susceptibles d'affecter les performances, la sécurité et la stabilité de Playnite. Si vous rencontrez des problèmes après l'installation d'un thème ou d'une extension, désactivez-les de façon à déterminer si ils peuvent en être la cause. Choisir au démarrage Choisir au démarrage Profils intégrés Profil intégré Profils personnalisés Profil personnalisé Géré par un script pré-installé Spécifications de l'émulateur Spécifications de la plateforme Région Exécuter avant le lancement de l'émulateur Exécuter après le lancement de l'émulateur Exécuter après avoir quitté l'émulateur Exécutable de l'émulateur introuvable. Spécifications de l'émulateur introuvables. Le script lié au démarrage de l'émulateur est introuvable. Séparer en plusieurs jeux. Fusionner les jeux en un seul. Indiquer la plateforme Indiquer la région Dossier d'analyse Configurations d'analyse Exclure les modèles de l'analyse de somme de contrôle Les fichiers correspondant au(x) motif(s) spécifié(s) ne seront pas analysés pour la somme de contrôle et seront comparés au nom du fichier. Voir la page d'aide de l'émulateur pour plus d'informations. Émulateur d'analyse Un nom doit être indiqué pour enregistrer une nouvelle configuration. L'émulateur ou le profil d'émulateur n'est pas indiqué. Le dossier n'est pas spécifié ou n'existe pas. La configuration de l'analyse n'est pas correctement paramétrée. Inclure dans l'auto-scan global Échec de l'analyse des dossiers d'émulateurs. Échec de l'analyse des dossiers de jeux émulés. Ignorer les exécutables non reconnus Profils à importer : Configurations d'analyse Enregistrer le profil Enregistre la configuration pour une utilisation ultérieure. La configuration enregistrée peut être gérée depuis le menu de configuration des émulateurs. Importer en utilisant des chemins relatifs Si possible, importez les fichiers du jeu en utilisant des chemins relatifs au dossier d'installation de Playnite ou au dossier d'installation de l'émulateur. Analyser les sous-dossiers Analyser dans les archives Fusionner les fichiers liés Permet de fusionner les fichiers de jeu liés, tels que les disques multiples, dans une fiche de jeu unique. Créer un profil d'analyse Ajouter un profil préexistant Lancer l'analyse Ajoute des configurations d'analyse aux émulateurs pour des dossiers spécifique. Assurez-vous que les émulateurs sont correctement configurés (via le menu de configuration des émulateurs) avant de lancer l'importation. Statut par défaut des jeux nouvellement ajoutés Statut des jeux lancés pour la première fois Échec de l'initialisation du script PowerShell. Il est recommandé aux utilisateurs de Windows 7 de (ré)installer PowerShell 5.1 pour résoudre ce problème. Un préréglage portant ce nom existe déjà. Mettre à jour le préréglage avec les nouveaux paramètres ? Remplissage automatique des noms de tri pour les jeux ajoutés ou modifiés par lots Les mots suivants seront ignorés lors de la génération de noms de tri automatique : Cette liste permet d'indiquer les premiers mots et lettres d'un nom de jeu que vous souhaitez ignorer lors du tri. "The", "An" et "A" sont ignorés par défaut. Ajouter un nom de tri à tous les jeux Tri Remplissage des valeurs du nom de tri… Le service Nahimic a été détecté comme étant en cours d'exécution sur votre système. Ce service est connu pour causer de sérieux problèmes de rendu pour Playnite (et d'autres applications). Nous vous recommandons de le désactiver ou de le désinstaller complètement pour éviter ces problèmes. Plus d'informations sur https://playnite.link/nahimicsucks Playnite est actuellement exécuté en mode administrateur. Ce paramètre n'est pas recommandé pour cette application, car les privilèges administrateur attribués à Playnite s'appliquent à tous les jeux et applications lancés par son biais. Pour plus d'informations : https://playnite.link/adminfaq Alerter lorsque Playnite est exécuté en mode Administrateur Obtenir la taille réelle sur le disque lors du calcul de la taille des jeux Si activé, les scans seront plus lents et détemineront la taille réelle des fichiers sur le disque. Si désactivé, les scans seront plus rapides et utiliseront la taille indiquée par les fichiers. Les extensions suivantes ont été signalées comme potentiellement problématiques, soit en raison d'un fort impact sur la stabilité/les performances, soit en raison de problèmes de sécurité. Nous vous recommandons fortement de les désinstaller : {0} Exclure du scan les fichiers en ligne Les fichiers stockés sur le cloud ne seront pas analysés et importés s'ils ne sont pas disponibles localement. Pris en charge uniquement pour : Google Drive, DropBox, OneDrive Scanner mais en utilisant une méthode simplifiée sans le contenu du fichier Les fichiers seront importés mais en utilisant une méthode moins précise qui ne nécessite pas que le contenu du fichier soit téléchargé et présent localement. Appliquer à tout Ignorer l'état d'installation Indique à Playnite d'ignorer l'état d'installation (y compris le répertoire d'installation) défini par le plugin d'intégration responsable de l'importation du jeu. Cette option peut ne pas fonctionner correctement avec les plugins qui utilisent une méthode spécifique d'importation de jeu, à moins qu'ils ne prennent également en compte cette option de remplacement. Manuellement Une fois par jour Une fois par semaine A chaque démarrage Vérifier les mises à jour de Playnite Vérifier les mises à jour d'extensions Mettre à jour les bibliothèques Analyser les dossiers d'émulation Inclure les jeux masqués Modifier les propriétés Sélectionner / Désélectionner tout Ouvrir Activer Attribuer Tapez ici pour effectuer une recherche… [F1] : Aide Commencer par # permet d'afficher une liste des commandes disponibles. Commencer par / permet d'affiche une liste des fournisseurs de recherche / plugins disponibles. Taper le mot clé de recherche puis appuyer sur ENTRÉE focalise le premier résultat suggéré. TAB : bascule entre les actions disponibles ENTRÉE : active l'action en subrillance MAJ-ENTRÉE : ouvre le menu de l'élément sélectionné dans la liste ci-dessous Inclure les jeux non installés Inclure les jeux masqués Jeux non installés inclus Jeux non installés exclus Jeux cachés inclus Jeux cachés exclus Jouer ou installer Afficher Menu du jeu Modifier le jeu Lancer la recherche globale Barre de recherche Recherche globale Action de lancement principale Action de lancement secondaire CTRL-F lance la recherche globale au lieu de focaliser la barre de recherche Enregistrer les paramètres de filtre de jeu entre les sessions de recherche Moteurs de recherche Mots-clé par défaut Mot-clé personnalisé Raccourci pour l'ensemble du système Recherche Playnite Paramètres de l'extension Exclusions Fichiers exclus lors de l'analyse du dossier Sous-dossiers exclus lors de l'analyse du dossier Ajouter un fichier à la liste d'exclusion Ajouter un dossier à la liste d'exclusion Les exclusions ne peuvent être ajoutées qu'aux configurations sauvegardées du scanner. Les exclusions ont été ajoutées au scanner "{0}". Remplacer la plateforme Lorsque défini, le scanner assignera cette plate-forme à tous les jeux, écrasant toutes les plateformes détectées automatiquement. Inclure les commandes dans la recherche par défaut Lorsque cette option est désactivée, les commandes ne seront pas incluses dans la recherche par défaut jusqu'à ce que le préfixe # soit utilisé. Activer la correspondance approximative dans la barre de recherche Lorsque cette option est activée, la barre de recherche utilisera le même mode de correspondance que la recherche globale. Une correspondance stricte peut être forcée au cas par cas en préfixant la recherche avec le caractère "!". Informations à afficher pour les résultats de recherche : Statut affiché/masqué La sauvegarde des données a été annulée. La sauvegarde des données a échoué. Erreur lors de la sauvegarde des données Sauvegarde des données en cours… Restauration des données à partir de la sauvegarde… Impossible de restaurer les données de sauvegarde. Paramètres Bibliothèque de jeux Médias de la bibliothèque de jeux Extensions installées Données des extensions Thèmes installés Sélectionnez les données à restaurer à partir du fichier de sauvegarde spécifié. Playnite redémarrera automatiquement pour démarrer le processus de restauration de sauvegarde. Sélectionnez les éléments à inclure avec la sauvegarde des données. Les paramètres de l'application et les données de la bibliothèque de jeu sont inclus par défaut. Playnite redémarrera automatiquement pour démarrer le processus de sauvegarde. Sauvegarde automatique des données de l'application Fréquence de sauvegarde automatique Répertoire de sauvegarde Rotation des sauvegardes Inclure des données supplémentaires : Le répertoire de sauvegarde doit être défini si la sauvegarde automatique est activée. Afficher uniquement les notifications de correctifs Lorsque cette option est activée, seules les mises à jour de correctifs entraîneront une notification. Il n'y aura pas de notifications lorsqu'une mise à jour majeure est disponible. Utiliser les dates relatives pour la semaine passée Utilisez des dates relatives au format "Aujourd'hui", "Hier", etc. si la date est inférieure à une semaine. Le format de date spécifié sera utilisé pour toutes les autres dates. Recherche d'images Web Chaîne de recherche d'icône Chaîne de recherche de jaquette Chaîne de recherche d'image de fond Récupération des informations Aucune source de métadonnées n'est disponible Paramètres de l'action de lancement Utiliser les paramètres du scanner Sélectionner le profil au lancement Sélectionner l'émulateur au lancement Automatique Toujours activer Laisser désactivé Support de la fonction narrateur (lecteur d'écran) Menu d'applications Menu de jeu Dossier du programme Répertoire de l'utilisateur Une corruption a été détectée dans les fichiers de bibliothèque. Playnite va être fermé. Si vous voyez ce message, veuillez ouvrir un ticket sur la page Github de Playnite et demandez un correctif pour la corruption de vos fichiers. Voulez-vous enregistrer les modifications que vous avez apportées ? Installation portable Aucune manette détectée ================================================ FILE: source/Playnite/Localization/ga_IE.xaml ================================================  ================================================ FILE: source/Playnite/Localization/gl_ES.xaml ================================================  Galego Lingua do Playnite Saír Filtro Activo Filtro deshabilitado Filtros adicionáis Filtros Filtro: Datos non válidos Gardar os cambios Páxina web en www.playnite.link Código fonte en GitHub Crear paquete diag. Enviar información de diag. Sobre Playnite Feito por Josef Němec Adxudicar categoría Establecer categorías Engadir categoría Sinalado- Adxudicar Categoría Non Sinalado - Eliminar Categoría Indeterminado - Sen Trocos (ó editar múltiples xogos) Sen categoría Sen plataforma ¡Ups! Algunha cousa foi mal… Sucedeu un erro irrecuperábel. Se desexa axudarnos a solucionar este problema, expoña brevemente as accións feitas antes do error e logo envíe a información de diagnose. Se está en liña, o paquete cargarase no servidor de Playnite para a súa análisise. Doutro xeito, pode premer no botón 'Informar do Erro' para crear unha nova versión de GitHub e informar do erro a man. Gracias pola súa axuda. A extensión "{0}" causou un erro irrecuperábel. Aconsellamos gardar o arquivo de rexistro e informar do erro ó desenvolvedor da extensión. Se o erro segue a repetirse, deshabilite a extensión. A extensión "{0}" causou un erro irrecuperábel. Aconsellamos que informe do erro ó desenvolvedor da extensión. Se o erro segue a repetirse, deshabilite a extensión. Unha extensión descoñecida ou un tema causou un erro irrecuperable. Producíuse un erro irrecuperábel. Se desexa axudarnos a corexir este erro, por favor envíe a información de diagnose. Moitas grazas. Deshabilitar extensión Gardar arquivo de rexistro Enviar información de diagnose Reportar erro Reiniciar Playnite Reiniciar no Modo Seguro Deshabilitando todas as extensións de terceiros e empregando o tema predeterminado. Saír do Playnite Accións feitas antes do erro (en Inglés) Administrador da biblioteca Eliminar Xogo(s)? Non se pode eliminar - O xogo ou o instalador estase a executar Non se pode desinstalar - O xogo estase a executar Confirmas que queres quitar {0}? ¿Seguro que queres eliminar estos {0} xogos? Confirmas que queres quitar {0}? Se seleccionas a opción "engadir á lista de exclusións", o xogo non se volverá importar de novo a vindeira vez que se actualice a biblioteca. ¿Seguro que queres eliminar estos {0} xogos? Se escolles a opción "Engadir á lista de exclusións", evitarase que o xogo se volva a importar a vindeira vez que se actualice a biblioteca. ¿Seguro que queres eliminar estas {0} entradas que non se están a usar? Non se atoparon campos sen uso Sí (engadir á lista de exclusións) Hai cambios sen gardar nesta sección. Actualizando o formato da biblioteca dos xogos Fallou o actualizar a base de datos Non se pode actualizar a biblioteca. Necesítanse {0} MBs de espazo libre no disco. Erro do xogo Non foi posíbel comezar co xogo. '{0}' non foi atopado na nosa base de datos. Non foi posíbel comezar o xogo: {0} Non foi posíbel comezar a acción: '{0}' Non foi posíbel atopar o xogo: {0} Non se puido detectar o tamaño da instalación do xogo: {0} Erro ao analizar o tamaño da instalación Producíronse {0} erros durante a análise do tamaño da instalación Erro ó crear o atallo: {0} Erro ó abrir o manual: {0} Non foi posíbel instalar o xogo: {0} Non foi posíbel desinstalar o xogo: {0} Non se atoparon accións de comezo de xogo válidas. Cando empregues accións do emulador, asegúrate de que as definicións da plataforma encaixan cas do xogo e a configuración do emulador. A implementación da instalación no está dispoñible O complemento da biblioteca responsable deste xogo está deshabilitado ou non instalado. A descárrega de metadatos oficial non está dispoñíbel. Ningún xogo foi seleccionado. Fallou a execución do script de accións do xogo. Fallou a execución do script de accións do xogo. Fallou a execución do script de accións global. Fallou a execución do script do emulador. Fallou a execución do script de xogo. O PowerShell 3.0 ou máis novo non foi instalado. Non se puido determinar como comezar o xogo. Habilitado Desactivado Eliminar Eliminar non empregados. Renomear Copiar Engadir Iconas por defecto Imaxe da cuberta por defecto Imaxe de fondo por defecto Rematar Seguinte Voltar Feito Voltar LIMPAR Limpar Rexeitar Rexeitar todo Importar Nome Autor/a Módulo Series Versión Último xogado Máis xogados Número de sesións de xogo Tamaño da instalación Cartafol Notas Engadido Engadido na data Modificado Modificado na data Páxina web Camiño OK Gardar Pechar Cancelar Confirmar Restablecer Non Benvido/a Usuario Local Xeral Medios Ligazóns Instalación Accións Descarregando... Descarregando medios Cargando... Tipo Perfil Perfís Eliminar Descarregar Procurar Resolución Calquera Zoom Vista de lista Cuberta Vista de Grella Vista de Detalles Personalizado Ligazón Agradecementos especiais Licenza Contribuíntes Saíndo do Playnite... Hoxe Onte Luns Martes Mércores Xoves Venres Sábado Domingo A semana pasada O mes pasado O ano pasado Fai máis dun ano De 0 a 100 MB De 100 MB a 1 GB De 1 GB a 5 GB De 5 GB a 10 GB De 10 GB a 20 GB De 20 GB a 40 GB De 40 GB a 100 GB 100 GB ou máis O proceso de importación completouse axeitadamente Todos os xogos Id do Xogo Id. da base de datos Axustes predefinidos Columna Columnas Ringleira Ringleiras Non se puido obter a icona de acción de Play. Non hai ningunha acción de tipo de ficheiro presente. Descarregar só os metadatos que fallan Activar esta opción evitará que os metadatos descarguen campos de datos que xa teñen esta información Selección de xogos Por favor, escolle qué xogos han de actualizarse con novos metadatos: Todos os xogos da base de datos Todos os xogos filtrados na actualidade Só os xogos escollidos Tenda Oficial IGDB Por favor, escolle qué campos debe encher automáticamente Playnite y qué fontes empregar para obter os datos. Por favor, considera premer no logo de enriba e contribuír con actualizacións á base de datos de igdb.com de xeito que se melloren os datos que usa Playnite. Descargando metadatos… Importando xogos instalados Importando xogos de {0}… Importando xogos emulados de {0} ... Descargando actualizacións da biblioteca… Analizando o tamaño dos xogos da biblioteca… Analizando o tamaño dos xogos importados… Actualización da biblio rematada Liberando recursos... Configuración Configuración... Plataformas e emuladores Configurar emuladores... Xestor de biblio... Ferramentas Descargar metadatos... Ferramentas de software… Configurar integracións... Abre o cliente de terceiro Clientes de terceiros Actualizar a biblioteca de xogos Cancelar a actualización da biblio Actualizar cartafois emulados Engadir xogo Manualmente… Escanear automaticamente... Xogo Emulado... Aplicación Microsoft Store ... Sobre Playnite Enviar suxestións Cambia ao modo de pantalla completa Ligazóns Axuda Apoia en Patreon Apoyame en Ko-fi Manual do usuario Documentación do SDK Reiniciar o sistema Apagar o sistema Suspender o sistema Hibernar o sistema Sistema de bloqueo Pechar sesión de usuario Escolle un xogo ao chou Os campos do xogo que se amosarán no panel de detalles Espazo entre elementos Debuxa o fondo do elemento da reixa Ancho do bordo do elemento da reixa Falta unha fonte de icona do xogo Falta unha fonte da careta do xogo Falta a fonte de fondo do xogo Espazamento vertical dos detalles do xogo Posición de detalles da vista da reixa Posición da lista de xogos á vista dos detalles Debuxa separador entre paneis Altura da imaxe da portada do xogo Altura da icona da lista de xogos Fonte da aplicación Fonte monoespazada Posición do panel de filtro Posición do panel do explorador Renderizado da arte da portada Relación de aspecto obxectivo As seguintes opcións tamén afectan á representación de mosaicos no modo de pantalla completa Modo de estiramento Caixa de DVD Epic Games Store GOG Galaxy 2.0 IGDB Cadrado Banner de Steam Portada vertical de Steam Twitch * Require reinicio para aplicar Axustes Xeral Panel superior Aparencia Detalles do xogo Disposición Avanzado Pantalla completa Entrada Rendemento Metadatos Actualizando Procurar Copia de seguridade Copiar de seguridade da biblioteca Restaurar copia de seguridade Importar cambios na biblio automaticamente A localización do ficheiro de base de datos non é válida, debe establecerse unha ruta adecuada. O nome da conta non pode estar baleiro. Descargar metadatos despois de importar xogos Inicia Playnite minimizado Inicia Playnite cando inicies o teu ordenador Comezar pechado na bandexa Non se puido rexistrar Playnite para iniciarse cando se inicie o ordenador. Inicie no modo de pantalla completa Carga de imaxes asíncrona Pode mellorar a fluidez do desprazamento das listas de xogos a cambio de tempos de carga de imaxes máis lentos. Mostra o nome do xogo se falta a portada Mostra os nomes dos xogos na vista de cuadrícula Escurecer os xogos non instalados Mostra as iconas do xogo na lista de vista de detalles Mostra o reconto de elementos nas descricións do grupo Mostra só os campos seleccionados nos paneis de filtro e explorador Desactivar a aceleración de hardware Utilízao cando teñas problemas de rendemento ou de interface Mostra os xogos ocultos en listas de inicio rápido Afecta á barra de tarefas e ás listas minimizadas. Número de elementos de inicio rápido Usa a imaxe de fondo do xogo como fondo da xanela Desenfoque de fondo Alta calidade Escurecer o fondo Mostrar na vista de cuadrícula Tema Perfil do tema Tema de pantalla completa Perfil do tema de pantalla completa Localización da base de datos Estado da sesión: Configuración de Playnite Borrar a caché web Pode resolver os problemas que se atopan ao ligar contas. Mostrar a icona da bandexa do sistema Minimizar Playnite á bandexa do sistema Minimiza Playnite á bandexa do sistema cando a xanela da aplicación estea pechada Ao comezar un xogo: Despois de pechar o xogo: Dar formato ao tempo de xogo par indicar a cantidade de días xogados Formatos de datas: Isto pechará sesión en todos os servizos vinculados. é necesario reiniciar a aplicación, queres continuar? Borrar a caché? Requírese reiniciar Playnite cando cambie o tema Obtén máis temas Crea un novo tema Obtén máis extensións Crear unha nova extensión Axúdanos a traducir Playnite Playnite debe reiniciarse para aplicar a nova configuración. Reiniciar agora? Ao reiniciar, cancelarase todas as tarefas activas (descargas) en curso. Reiniciar Playnite? Playnite non pode mover os ficheiros da biblio automaticamente. Debes mover/copiar manualmente os ficheiros antes de cambiar a localización. Se non hai ningunha biblio na localización de destino, crearase unha nova. A nova localización da base de datos non se utilizará ata que se reinicie Playnite. O tempo de xogo non se rexistrará se se establece a acción "Pechar". Número de ringleiras Número de columnas Número de filas na vista de detalle Mostra a imaxe de fondo na pantalla principal Non se aplica aos xogos existentes sen volver descargar os metadatos. Importar tempo de xogo dos xogos da biblioteca Configura cando debe importar Playnite o tempo de xogo reportado pólos plugins da biblioteca para xogos na base de datos de Playnite. Para poderes utilizar esta característica é necesario o soporte dos plugins da biblioteca a cargo de manexar os xogos. Sempre: Importa o tempo de xogo para novos xogos importados e existentes na base de datos de Playnite. Só para os xogos recén importados: Importa tempo de xogo só para os novos xogos importados. Nunca: Non se importa o tempo de xogo baixo ningunha circunstancia. Sempre Só para os xogos recén importados Nunca Activa a compatibilidade con controis no modo de fiestra O botón guía abre o modo Pantalla completa Configuración de descarga automática de metadatos para xogos recentemente importados. Pantalla de destino Use sempre a pantalla de principal Mostrar títulos dos xogos Mostra o estado da batería Mostrar a porcentaxe de batería Mostrar reloxo Ocultar o cursor do rato Instalado só en filtros rápidos Indicacións de botón Disposición Desprazamento horizontal Seleccione unha das subseccións Sen axustes dispoñibles Errou o cargar os axustes Estos scripts son executados por cada xogo da biblioteca. Os scripts individuais poden ser atribuidos a cada xogo por separado mentras se editan os detalles do xogo. Animar transicións das imaxes de fondo Tamaños de letra Auto Suavizado Escala de grises ClearType Ideal Pantalla Modo de formato de texto Modo de renderizado de texto Os métodos de renderización de texto e formato non se empregan actualmente para as descripcións dos xogos. Precargar imaxes de fondo Se está activado, Playnite transferirá as ilustracións de fondo cando descarga os metadados, empregando máis espazo de disco e tornando as ilustracións dispoñíbles sen conexión. Se está desactivado, as ilustracións de fondo serán descargadas só cando se necesiten a primeira vez, usando menos espazo, máis pode ocorrer que haxa atrasos antes de que a ilustración sexa mostrada e algunhas imaxes poden non estar dispoñibles cando non haxa conexión. Pechar automáticamente o cliente de terceiros despóis de saír do xogo Retardo do peche do cliente (en segundos) Non pechar despóis de sesións de xogo máis curtas que (en segundos) Pechar automáticamente os seguintes clientes: Pechar clientes automáticamente Importar Lista de Exclusión Mostra aviso cando se asignen datos de xogos moi grandes Comando de apertura de cartafol Organización de clasificación por edade preferida Actualizar o tamaño da instalación dos xogos ao actualizar a biblioteca Analiza e actualiza o tamaño da instalación dos xogos se se detecta que se modificaron os ficheiros desde a última análise Ningún Encher Uniforme Uniforme para encher Esquerda Dereita Arriba Inferior Erro de importación Requírese autenticación Errou na autenticación Modo de redenderizado da vista web alternativo Usar cando se teñan problemas cas vistas web, por exemplo, na integración dos diálogos de autenticación. Carga parcial de descricións de xogos longas As descricións longas poden producir atrasos notables ao seleccionar xogos. Se se activa esta opción, só se cargará parte do texto da descrición cunha opción para cargar o resto se o escolles. Importación de Metadatos Descargar metadatos... Usar configuración seleccionada para calquer descarga de metadatos futura. Tamén pode cambiarse nas opcións da aplicación. Asistente de Importación da Emulación Este asistente axudarache no proceso de descargar e importar emuladores de consolas e importar xogos emulados. Ten conta de que sempre podes engadir despóis emuladores adicionáis e/ou xogos a través do menú principal (debaixo do menú "Bilbioteca" nos axustes do Emulador e no menú "Engadir Xogos" para sogos emulados). A continuación móstrase unha lista de emuladores que Playnite pode recoñecer e configurar automaticamente. Podes descargar instaladores de emuladores dos seus sitios web. Unha vez que teñas os emuladores instalados (manualmente), podes importalos no diálogo de configuración do emulador. Podes importar calquera emulador que estea instalado no teu PC facendo clic no botón "Detectar automaticamente desde o cartafol...". Playnite buscará no cartafol seleccionado calquera emulador coñecido e ofrecerá a opción de importalos. Podes usar este botón varias veces para importar emuladores de diferentes cartafois. Os emuladores engadiranse ao final da lista actual. Podes importar xogos facendo clic no botón "Escanear cartafol usando emulador". Ao seleccionar o emulador axeitado, Playnite saberá que tipos de ficheiros quere escanear e importar. Podes usar este botón varias veces para importar xogos de diferentes cartafois. Os xogos engadiranse ao final da lista actual. Non hai emuladores seleccionados para importar. Non poderás importar automaticamente ningún xogo emulado sen configurar primeiro os emuladores. Estás seguro de que queres continuar e saír do proceso de importación? Non hai emuladores configurados en Playnite. Non podes importar xogos sen antes configurar o emulador e seleccionar os tipos de ficheiro adecuados. Queres engadir algúns emuladores agora? Escanear cartafol usando Emulador Seleccionar ficheiro Detecta automaticamente desde o cartafol... Configurar emuladores... Escaneando… Escaneando {0}… Primeira configuración Este proceso guiarache a través dunha importación e configuración automáticas de bibliotecas de xogos externas. Playnite pode importar automaticamente xogos de varios servizos de xogos, como Steam ou GOG. Teña en conta que tamén pode engadir manualmente calquera xogo personalizado ou emulado para calquera plataforma máis tarde desde o menú principal. Integración de bibliotecas De seguido móstranse algunhas das integracións de bibliotecas que Playnite soporta. Por favor, escolle as que queres instalar. Podes instalar máis integracións despóis dende o menú de "Complementos". Configuración finalizada A configuración inicial completouse. Lembra que podes cambiar todas as opcións máis tarde, así como engadir integracións adicionais desde o menú principal. Errou ó descargar unha ou máis extensións. Podes tentar de descargar as integración dende o menú de complementos despóis de que o asistente de inicio remate. Descargando a integración de {0}... A descargar a lista de integracións recomendadas… Erro ao descargar a lista de integracións recomendadas. Podes tentar e redescargar as integracións máis adiante no menú de complementos. Configurar plataformas e emuladores Configurar emuladores Plataformas Plataforma Emuladores Emulador Engadir plataforma Seleccionar icona Seleccione Portada Seleccionar imaxe Seleccionar elemento Seleccionar imaxe de fondo Seleccionar ficheiro Seleccionar unha URL Engadir emulador Plataforma(s) compatibles Queres gardar os cambios de plataforma? Queres gardar os cambios do emulador? Executábel Argumentos Directorio de traballo Tipos de ficheiros admitidos Importar emuladores... Descargar emuladores... Cargar argumentos predefinidos do perfil do emulador coñecido Estás seguro de que queres eliminar o emulador de {0}? Actualmente está a ser usado por {1} xogos. Estás seguro de que queres eliminar a plataforma {0}? Actualmente está a ser usado por {1} xogos e {2} emuladores. Axuda de configuración Ordenar por Dirección da orde Agrupar por Ascendente Descendente Non agrupar Agrupar por biblioteca Agrupar por categoría Agrupar por Plataforma Tipo de vista Ver Panel de exploración Panel de filtros Icona Icona da biblioteca Imaxe da portada Imaxe de fondo Nome para Ordenación Biblioteca Manual Nome Disco de instalación Nome da conta Plataforma Categoría Xénero Data de lanzamento Ano de lanzamento Desenvolvedor Etiqueta Editora Estado da instalación Combina todos os filtros Se está activado, só se incluirán na vista os xogos que utilicen todos os elementos en todos os filtros. Se está desactivado, incluiranse na vista os xogos que utilicen calquera elemento de calquera filtro. Instalado Instalado Non instalado Oculto Favorito Activar compatibilidade con HDR Se se activa esta opción, activarase o HDR na pantalla principal antes de iniciar o xogo. Ten en conta que a túa pantalla principal non admite HDR. Última vez xogada Categoría Descrición Cartafol de Instalación Imaxe da portada Ligazóns Imaxe, ROM ou ruta ISO Xénero Xéneros Compañía Compañias Desenvolvedor Desenvolvedores Editora Editoras Categoría Categorías Etiqueta Etiquetas Característica Características Clasificación por idade Clasificación por idade Rexión Rexións Orixe Orixes Actividade recente Erro na base de datos Fallou ó abrir a base de datos Non se puido abrir a base de datos. Non se pode acceder á base de datos. O arquivo "{0}" está sendo usado por outro proceso ou atópase nunha locación inaccesible. Erro ó crear o paquete de diagnose. Erro ó enviar automáticamente o paquete de diagnose. A información de diagnose foi enviada con éxito. O paquete de diagnose creouse e foi enviado correctamente. Por favor, achegue o seguinte ID ó seu informe de erros: Erro ó importar os xogos de {0}. Erro ó importar xogos emulados de {0}. Non se poden procurar xogos co perfil do emulador seleccionado. O perfil non contén extensións de arquivos ou plataformas. Playnite non puido iniciarse. Peche todas as demáis instancias e ténteo de novo. Erro ó aplicar otema "{0}", perfil de cor "{1}" {2} Non se conseguiu abrir a ligazón, a URL non está nun formato válido. Non se puido iniciar a aplicación. Errou ó inicializar o compoñente da vista web. Playnite non pode continuar co proceso de inicio. Máis información en https://playnite.link/cefstartup Non se poden importar emuladores porque falta ou está danado un arquivo de definición. Non se puido executar a acción do menú. Editar detalles do xogo URL da imaxe Engadir ligazón Engadir ROM Gardar os cambios Aplica as cambios ao (s) xogo (s) que están a ser editado (s). Engadir acción Eliminar Eliminar Acción de Xogar Engadir xogos Escanear Carpeta… Detectar Instalados Navegar… Abrir Playnite Axustes do perfil O nome da conta non pode estar baleiro. O directorio de seguemento de acción de xogo non pode estar valeiro. O nome do xogo non pode estar valeiro antes de procurar metadatos. Información de xogo non válida Introduce unha URL válida que comece por http:// ou https:// Escolle unha URL Fallou ó descargar os metadatos: {0} Erro ao descargar Limpar filtros Conta privada Conta pública Chave API Erro de inicio Erro de tema Borrar todo Instalando Desinstalando Lanzando En execución URL non válido Non facer nada Minimizar Restaurar xanela Restaurar a xanela só cando se inicia desde a IU Pechar Cambiar Avanzado Nunca Estado de finalización Estados de finalización Puntuación do usuario Puntuación da crítica Puntuación da comunidade Scripts de xogo Scripts da aplicación Scripts Complementos Fontes de metadatos Extensións ID de extensión Recargar scripts PowerShell SDK Interactivo Todos os scripts recargaronse correctamente. Non se atoparon xogos para os criterios de busca/filtro especificados Ningún elemento atopado Cambia ao modo de escritorio Pechar Playnite Bibliotecas Actualizar todo Creado por: Versión: Actualización: Módulo: Biblioteca Estatísticas Todos Ningún Notificacións Largura Altura Tamaño Pequeno Normal Grande Máis grande O máis grande Predefinido Escoller Seleccionalo todo Deseleccionar todo Primeiro Ao chou Selección de usuario Cargar máis Transparente Agochar Despregar Recollelo todo Expandilo todo Outro Temas Parámetros do Emulador Parámetros integrados Parámetros personalizados Parámteros do Emulador Adicionáis Sobreescribe os Parámetros do Emulador Acción de Xogar Selecciona os metadatos para importar Seleccionar os Xogos para importar Procura de metadatos Actualización dispoñible Cambios dende a última actualización Descargar e instalar a actualización Buscar actualizacións Houbo un erro ao actualizar Error ó comprobar actualizacións. Non se atoparon novas versións, estás ó día Errou ó descargar e instalar a actualización. Algunha tarefa en segundo plano está en curso. Queres cancelalo e continuar coa actualización? Algunha tarefa en segundo plano está en curso. Queres cancelalo e saír de Playnite? Algunha tarefa en segundo plano está en curso. Cambiar de modo cancelará a tarefa, queres cambiala igualmente? Hai dispoñible unha actualización para Playnite. Volve a cargar a lista dos temas Aplica o tema escollido Ver os cambios dos arquivos Aplica o tema automáticamente cando o arquivo fonte cambie Tempo de execución do Script Executar antes de iniciar un xogo Executar despóis de saír dun xogo Executar despóis de iniciar un xogo Executar ó iniciar a aplicación Executar ó pechar a aplicación Script de iniciando xogo Script de xogo iniciado Script de xogo pechado Executar script global Global Filtrado Actual Novo Probar script Mostra só os artigos escollidos Gardar como predeterminado Engadir a favoritos Eliminar dos favoritos Ocultar este xogo Eliminar de Oculto Activar compatibilidade con HDR Desactivar compatibilidade con HDR Editar… Calcular tamaño da instalación Calcular tamaño da instalación (tódolos xogos) Calcular tamaño da instalación (só datos restantes) Tamaño da instalación Establecer categoría… Establecer estado de finalización Eliminar Reproducir Instalar Opcións do xogo Detalles Desinstalar Abre a localización de instalación Crear atallo no escritorio Abrir manual Máis Xestionado polo complemento da biblioteca O proceso de inicio do xogo será administrado polo complemento da biblioteca responsable deste xogo. Non se atopou ningunha información relevante sobre o xogo '{0}' na páxina especificada. Consello: podes usar un proceso de descarga de metadatos máis avanzado mentres editas un único xogo mediante a opción de menú "Editar". Non dispoñible cando unha acción está en curso. O texto da descrición é sensible á sintaxe HTML O tempo do xogo rexístrase en segundos. O tamaño da instalación está indicado en bytes. A data de lanzamento debe establecerse no formato "ano-mes-día". Pódense omitir os valores de mes e día. Valores entre 0 e 100 ou deixa en branco para indicar que non hai puntuación. O desenvolvemento de Playnite conta co apoio destes patrocinadores e membros de Ko-fi: Código, tradutores e outros colaboradores sen orde en particular: Queres cancelar o seguimento do xogo? A supervisión da instalación está en execución. Queres cancelar o proceso e devolver o xogo ao estado anterior? O seguimento da execución do xogo está en execución. Queres cancelar o proceso e devolver o xogo ao estado anterior? Tempo xogado Última vez xogada {0} d {1} h {2} min {0}h {1}min {0} Minutos {0} Segundos Non xogado Abrindo o modo de escritorio... Abrindo o modo de pantalla completa... Cargando biblioteca de xogos... A calcular o tamaño da instalación… A calcular o tamaño da instalación de {0}… Erro ao instalar o arquivo script. O script instalouse correctamente. Instalar Script Script con erro Produciuse un erro ao executar a función de extensión. Abrir o cartafol de metadatos Calcular Calcula automaticamente o tamaño da instalación usando as ROM, se o xogo as ten, ou o directorio de instalación se está configurado O cliente {0} non está instalado. O cliente {0} abrirase. Inicia sesión e pecha esta mensaxe. Agardando pola verificación do usuario, por favor pecha esta xanela cando remates Non se atopou o cartafol de instalación do xogo. Configuración de acción de xogo inválido. Solución de problemas de sincronización da conta Solución de problemas Renomear o elemento Engadir novo elemento Introducir nome Introduce un novo nome Menos dunha hora 1 a 10 horas 10 a 100 horas 100 a 500 horas 500 a 1000 horas 1000+ Playnite debe reiniciarse para completar a instalación. Queres reiniciar agora? A extensión non está empaquetada correctamente. O tema non está empaquetado correctamente. A extensión "{0}" non se puido cargar correctamente. Non se pode cargar a extensión "{0}", a versión actual de Playnite non é compatible. O tema "{0}" non se puido cargar correctamente. Non se pode cargar o tema "{0}", a versión actual de Playnite non é compatible. A extensión non se puido cargar correctamente. O tema non se puido cargar correctamente. O tema/extensión está a usar unha versión da API non compatible. A instalación foi exitosa. ¿Instalar un complemento? Genérica Erro ó instalar o complemento "{0}". Non se puido instalar a extensión. {0} Queres instalar unha nova extensión? {0} Por {1} Versión {2} Queres actualizar a extensión "{0}"? Versión actual: {1} Nova versión: {2} Non se puido instalar o tema. {0} Queres instalar un novo tema? {0} Por {1} Versión {2} Queres actualizar o tema "{0}"? Versión actual: {1} Nova versión: {2} Estás a piques de saír de Playnite e navegar ata a seguinte páxina web usando o teu navegador web predeterminado. Queres continuar? {0} As imaxes seleccionadas poden ser demasiado grandes para un rendemento óptimo. O uso de imaxes moi grandes pode provocar unha mala resposta da interface de usuario e un aumento do uso da memoria. Resolucións máximas recomendadas: Iconas: {0} megapíxeles Placas frontales: {1} megapíxeles Fondos: {2} megapíxeles Aviso de rendemento Non mostrar de novo O ficheiro coa extensión {0} non é compatible. Extensión de ficheiro incompatible O arquivo da imaxe seleccionada pode ser demasiado grande para un rendemento óptimo. ¿Está seguro de que desexa desinstalar o tema seleccionado? A desinstalación se pondrá en cola para o próximo inicio da aplicación. Os temas predeterminados non se poden desinstalar. Este tema non é compatible con esta versión de Playnite. Estás seguro de que queres desinstalar a extensión seleccionada? A desinstalación quedará en cola para o próximo inicio da aplicación. Non se poden desinstalar as extensións predeterminadas. Esta extensión non é compatible con esta versión de Playnite. Cartafol de Instalación Directorio de datos Xerando paquete de diagnóstico... Cargando paquete de diagnóstico... Importar ficheiros Que é isto? Estás seguro de que queres facelo? Tempo total de xogo Tempo medio de xogo Tempo máximo de xogo Tamaño total da instalación Vista xeral Barra lateral Mostrar na barra lateral Restablecer a configuración Toda a configuración da aplicación restablecerase á configuración predeterminada, excepto: - Localización da base de datos - Importar lista de exclusións - Configuración de extensións, incluíndo integracións de bibliotecas. É necesario reiniciar a aplicación para rematar o proceso. Queres restablecer a configuración? Para desenvolvedores Extensións externas Introduza a ruta completa do cartafol. Logros Foro Novas Páxina da tenda A configuración inicial non está completa. Playnite reiniciarase no modo de escritorio para rematar o procedemento. Xogado recentemente Favoritos Máis xogados Todos Hai filtros aplicados. Hai filtros adicionais aplicados. Mostrando os resultados da busca de: Xa existe un artigo co mesmo nome. Limitar a selección ao filtro actual Escoller outro Complementos… Instalado Configuración das extensións Explorar Actualizacións Actualizacións ({0}) A xestión das extensións e temas instalados, incluíndo as súas configuracións, moveuse ó novo menú "Complementos". Todas as extensións de integración de biblioteca instaladas actualmente poden ser configuradas aquí. Se queres instalar ou desinstalar integracións adicionáis, vai a opción "Complementos" do menú principal. Temas de escritorio Temas de pantalla completa A buscar… O complemento non é compatible con esta versión do Playnite. Errou ó descargar o paquete de instalación do complemento. Errou ó descargar o manifesto de instalación do complemento. Cómpre reiniciar a aplicación para aplicar as modificacións pendentes. Este complemento está programado para a súa instalación. Instalar Desinstalar Xa instalado Non se atoparon novas actualizacións de complementos. Actualizar complementos Rexistro de cambios non dispoñible Instalación programada Erro na descarga Licenza rexeitada A descargar {0}… Procurando actualizacións de complementos ... Unha ou máis actualizacións de complementos están dispoñibles. Selecciona artigos para actualizalos Instancia de desenvolvemento da extensión Acordo da licenza de {0} Aceptar Rexeitar Incluír as accións de xogo da integración da biblioteca Seleccionar acción Modo de rexistro Ruta do rexistro Atraso de seguimento inicial Frecuencia de seguimento Ligazón Ficheiro Emulador Script Predefinido Proceso Cartafol Proceso orixinal Rexistrar mensaxes de seguimento As seguintes modificacións sobrescribirán os datos de todos os xogos seleccionados actualmente Ningún Uniforme Só artigos Só inicio e fin Sensibilidade do desprazamento Desprazamento suave Velocidade de animación Queres eliminar o elemento? Estás seguro de que queres eliminar este elemento? Mostra os botóns no panel superior: Configuración xeral Configuración de agrupación Configuración de ordenación Filtros predefinidos Posición dos elementos do complemento Ancho divisor de sección Move o botón do menú principal á barra lateral. Panel de navegación Selector de xogos aleatorios Selector de xogo aleatorio de vistas Seleccionar xogo aleatorio da vista Garda a configuración de agrupación e clasificación Mostrar como filtro rápido no modo de pantalla completa Nos últimos 7 días Nos últimos 31 días Nos últimos 365 días Hai máis de 365 días Configurar Gardar preaxuste Minimizar despois de comezar o xogo. Minimizar Playnite despois de comezar un xogo. Desactivar isto pode provocar problemas cos xogos que non reciban información de entrada ao inicio. Tamaño da fonte tamaño de fonte pequeno Soporte de controlador de xogo Se está desactivado, Playnite non aceptará ningunha entrada da interface XInput. Desactívao se estás a usar ferramentas que traducen XInput a entrada de rato/teclado e estás recibindo entrada dobre en Playnite. Mostrar elementos no menú principal: Botóns X/A invertidos na vista principal. Cambia os botóns para iniciar un xogo e mostrar a páxina de detalles do xogo na vista principal. Intercambiar a ligazón do botón de confirmación/cancelar Inverte as ligazóns dos botóns A/B para confirmación e cancelación. Só control primario Só acepta entradas do control principal cando estea activado. O botón Guía enfoca Playnite Volume de interface Volume de fondo Silenciar cando está en segundo plano Produciuse un erro ao inicializar a interface de audio. API de saída API utilizada para a saída de audio. Cambia se tes problemas de son. Xeral Visuais Audio Disposición Menús Entrada {0} está comezando... {0} está en execución... Maiúsculas Espazo Escalador de representación da imaxe Alternativo Equilibrado Calidade Calidade: Mellor calidade de imaxe, lento e alto consumo de memoria. Equilibrado: Boa calidade, rápido e baixo consumo de memoria. Alternativo: Mellor calidade, velocidade media e baixo consumo de memoria. Selecciona o ficheiro… Selecciona o cartafol… Script de inicio Tanto as extensións coma os temas poden afectar notablemente ao desempeño, a estabilidade e a seguranza de Playnite. Se empezas a ter problemas tras instalar un tema ou unha extensión, tenta desactivalo ou desinstalalo primeiro para ver se son a causa do problema. Escoller ao inicio Escoller ao inicio Perfís integrados Perfil integrado Perfís personalizados Perfil personalizado Controlado por un script integrado Especificación do emulador Especificación da plataforma Especificación da rexión Executar antes de iniciar o emulador Executar tras iniciar o emulador Executar tras saír do emulador Executador do emulador non atopado. Especificación do emulador non atopada. Script de inicio do emulador non atopado. Dividir como xogos distintos Combinar nun só xogo Establecer plataforma Establecer rexión Analizar cartafol Analizar configuración Padróns de exclusión da análise da suma de verificación Os ficheiros que coinciden cos padróns especificados non se analizarán para a suma de comprobación e combinaranse por nome de ficheiro. Consulta a páxina de axuda do emulador, onde atoparás máis información. Analizar co emulador Cómpre asignar un nome ao gardar unha configuración nova. Non se estableceu o emulador ou o perfil do emulador. Non se especificou o directorio para a análise ou non existe. A configuración da análise non se establecer correctamente. Incluír na análise automática en masa Erro ao analizar o cartafol para emuladores. Erro ao analizar o cartafol de xogos emulados. Ocultar importados Perfiles a importar: Configuracións de análises automáticas Gardar como configuración de análise automática Garda a configuración para poder usala máis adiante durante a actualización de biblioteca. Podes xestionar as configuracións gardadas no menú "Configurar emuladores". Importar con rutas relativas Se é posible, importa os ficheiros dos xogos con rutas relativas ao cartafol de instalación de Playnite ou ao cartafol de instalación do emulador. Analizar subcartafois Analizar dentro de ficheiros Combinar ficheiros relacionados Combina os ficheiros de xogos rlacionados, como discos dun mesmo xogo, dentro dunha soa entrada. Engadir analizador Engadir analizador gardado Iniciar análise Engadir as configuracións de análise con emuladores para analizar cartafois específicos. Asegúrate de que os emuladores están configurados correctamente antes de importar xogos (mediante o menú Biblioteca -> Configurar emuladores). Estado predefinido asignado a xogos recén engadidos Estado asignado aos xogos xogados por primeira vez Erro ao inicializar o tempo de execución do script de PowerShell. Se usas Windows 7, tenta (re)instalar PowerShell 5.1 para corrixir o problema. O filtro predefinido co nome especificado xa existe. Queres actualizar a predefinición con novos axustes? Eliminaranse estas palabras do inicio do valor de nome de orde enchido automaticamente: Usa isto para ignorar palabras ao inicio dunha cadea para a orde. Os valores predeterminados son "The", "An" e "A". Enche o nome de orde para xogos que non teñan un Orde A encher os valores de nomes de orde… Detectouse que o servizo Nahimic estase a executar no teu sistema. Este servizo é coñecido por causar problemas de renderizado no Playnite (e outras aplicacións). Se atopas algunha corrupción nos gráficos ou outros problemas de renderizado no Playnite, recoméndase deshabilitar ous desinstalar completamente o servizo de Nahimic. Máis información en https://playnite.link/nahimicsucks Playnite está a executarse con privilexios elevados (como administrador). Non é recomendábel porque outorga privilexios elevados a todas as extensións instaladas e a todos os xogos e aplicacións iniciados desde Playnite. Amosar advertencia se Playnite está a executarse con privilexios elevados Obter o tamaño real no disco ao calcular o tamaño dos xogos Se se activa esta opción, as análises serán máis lentas e obterase o tamaño real dos ficheiros no disco. Se se desactiva, as análises serán máis rápidas e usarase o tamaño dos propios ficheiros. Os seguintes complementos foron reportados como potencialmente problemáticos, ben por un alto impacto na estabilidade ou rendemento, ben por problemas de seguridade. Recomendamos fortemente a súa desinstalación: {0} Excluír ficheiros de internet da análise Os ficheiros almacenados en almacenamento na nube non se analizaran e importarán se non están dispoñíbeis localmente. Só se admite para Google Drive, Dropbox e OneDrive. Analizar mais cun método simplificado sen o contido do ficheiro Os ficheiros importaranse mais cun método menos preciso que non require que se descargue o contido dos ficheiros para que estea presente localmente. Aplicar a todo Sobrescribir o estado da instalación Ao establecer esta opción, Playnite ignorará o estado da instalación (incluído o directorio da instalación) establecido polo complemento de integración que importa o xogo. É posible que esta opción non funcione completamente con complementos que usan métodos de importación de xogos específicos a menos que tamén teñan en conta esta opción de sobrescritura. Só manualmente Diariamente Semanalmente En cada inicio Comprobar actualizacións do programa Comprobar actualizacións de complementos Actualizar bibliotecas Analizar cartafois de emulación Incluír xogos ocultos Editar campos Seleccionar todo ou cancelar a selección de todo Abrir Activar Asignar Comeza a escribir para buscar xogos… Preme [F1] para ver a axuda Se escribes #, mostrarase unha lista de comandos dispoñibles. Se escribes /, mostrarase unha lista de complementos ou fornecedores de busca dispoñibles. Se escribes unha palabra clave de busca e terminas cun espazo, cambiarase automaticamente a esa busca. Incluír xogos desinstalados Incluír xogos ocultos Xogos desinstalados incluídos Xogos desinstalados excluídos Xogos ocultos incluídos Xogos ocultos excluídos Xogar ou instalar Ir aos detalles Menú do xogo Editar xogo Abrir a busca Cadro de busca Botón de busca Acción principal do xogo Acción secundaria do xogo Ctrl+F abre unha busca global en lugar de pasar o foco ao cadro de busca Gardar a configuración de filtro de xogos entre sesións de busca Fornecedores de busca Palabra clave predefinida Palabra clave personalizada Atallo global do sistema Busca de Playnite Axustes da extensión Exclusións Ficheiros excluídos relacionados co cartafol da análise Cartafois excluídos relacionados co cartafol da análise Engadir ficheiro á lista de exclusión Engadir cartafol á lista de exclusión Só se poden engadir exclusións a configuracións do analizador gardadas. Engadíronse exclusións ao analizador "{0}". Sobrescribir plataforma Ao establecer esta opción, o analizador asignará esta plataforma a todos os xogos e sobrescribirá as plataformas detectadas automaticamente. Incluír comandos na busca predefinida Se se desactiva esta opción, non se mostrarán comandos na busca predefinida a menos que se use o prefixo #. Usar coincidencias parciais no filtro de nomes Se se activa esta opción, o filtro de nomes atopará nomes de xogos do mesmo modo que a busca global. A coincidencia estrita pode aplicarse en casos individuais se comezas o filtro co carácter !. Campos que se amosarán para resultados de xogos: Estado oculto Cancelouse a copia de seguranza dos datos. Erro na copia de seguranza dos datos. Erro na copia de seguranza dos datos Copia de seguranza dos datos en curso… A restaurar datos dunha copia de seguranza… Erro ao restaurar datos dunha copia de seguranza. Axustes Biblioteca de xogos Ficheiros multimedia da biblioteca de xogos Extensións instaladas Datos de extensións Temas instalados Selecciona os datos que queres restaurar do ficheiro de copia de seguranza especificado. Playnite reiniciarase automaticamente para iniciar o proceso de restauración da copia de seguranza. Selecciona artigos que se incluirán na copia de seguranza dos datos. Os axustes de aplicacións e os datos da biblioteca de xogos están incluídos de modo predefinido. Playnite reiniciarase automaticamente para iniciar o proceso de copia de seguranza. Copia de seguranza automática dos datos Frecuencia da copia de seguranza automática Cartafol da copia de seguranza Copias de seguranza rotativas Incluír datos adicionais: Cómpre establecer o cartafol da copia de seguranza se está activada a copia de seguranza automática. Mostrar notificacións só para a publicación de correccións Se se activa esta opción, só se notificará a publicación de actualizacións para a versión principal instalada actualmente. As novas versións principais non xerarán unha notificación de actualización. Usar datas relativas para a semana anterior Usar datas relativas en formato de "hoxe", "onte", etc. se a data é de hai menos dunha semana. O formato de data especificado usarase para todas as outras datas. Busca de imaxes en internet Cadea de busca de imaxes de iconas Cadena de busca de imaxes de portadas Cadea de busca de imaxes de fondo A obter información do complemento… Non hai ningunha fonte de metadatos dispoñible Axustes da acción de xogo Usar axustes do analizador Seleccionar perfil ao inicio Seleccionar emulador ao inicio Automático Sempre activado Sempre desactivado Compatibilidade coa accesibilidade (lector de pantalla) Menú da aplicación Menú do xogo Cartafol do programa Directorio dos datos de usuario Detectouse un ficheiro da biblioteca danado, polo que Playnite vai pecharse. Abre un problema na páxina de GitHub de Playnite e solicita que se arranxe o problema cos teus ficheiros. Queres gardar os cambios realizados? Instalación portátil Non se detectaron controladores ================================================ FILE: source/Playnite/Localization/he_IL.xaml ================================================  עברית שפת התצוגה של Playnite יציאה סינון פעיל מסנן כבוי מסננים נוספים מסננים מסנן מידע שגוי לשמור שינויים? אתר הבית ב www.playnite.link קוד המקור ב github צור חבילה דיאגנוסטית שלח מידע דיאגנוסטי אודות פליינייט נוצר על ידי Josef Němec הצמד קטגוריה הגדר קטגוריות הוסף קטגוריה מסומן - קטגוריה מוקצת לא מסומן - הסר קטגוריה ביניים - ללא שינוי (בעריכה של מספר משחקים) אין קטגוריה אין פלטפורמה אופס! משהו השתבש… קרתה שגיאה שלא ניתנת לתיקון. אם ברצונך לעזור לנו לתקן את הבעיה, אנא תאר בקצרה את הפעולות שנעשו לפני הקריסה ולאחר מכן שלח מידע אבחוני. אם אתם מקוונים, האבחון יועלה לשרתי פליינייט לבדיקה. ניתן גם להשתמש בכפתור "Report Crash" ליצירת באג חדש בGitHub ולדווח על הקריסה באופן ידני. תודה על העזרה. ההרחבה "{0}" גרמה לשגיאה שלא ניתנת לתיקון. אנו ממליצים לשמור את קובץ הלוג ולדווח על הבעיה למפתח ההרחבה. אם הבעיה ממשיכה להופיע שוב, השבת את ההרחבה. ההרחבה "{0}" גרמה לשגיאה שלא ניתנת לתיקון. אנו ממליצים לדווח על הבעיה למפתח ההרחבה . אם הבעיה ממשיכה להופיע שוב, השבת את ההרחבה. הרחבה לא ידועה או ערכת נושא גרמו לשגיאה שלא ניתנת לתיקון. אנו ממליצים להשבית הרחבות של צד שלישי, לבודד את הבעייתית ולדווח על הבעיה למפתח ההרחבה. אירעה שגיאה שלא ניתנת לתיקון. אם ברצונך לעזור לנו לתקן את הבעיה, אנא אבחן את התקלה ושלח לנו את מידע האבחון. תודה. השבתת ההרחבה שמור קובץ יומן (log) שלח מידע אבחון דווח על קריסה הפעל מחדש את פליינייט התחל מחדש במצב בטוח משבית את כל הרחבות הצד השלישי ומשתמש בערכת נושא ברירת מחדל צא מפליינייט פעולות שקרו לפני הקריסה (באנגלית): מנהל ספרייה הסר משחק(ים)? לא ניתן למחוק משום שהמשחק או תוכנת ההתקנה רצים כרגע לא ניתן להסיר התקנה של משחק שרץ אתה בטוח שברצונך להסיר את {0}? אתה בטוח שברצונך להסיר {0} משחקים? האם אתה בטוח שאתה רוצה להסיר את {0}? בחירת "הוסף לרשימת יוצאים מהכלל" תמנע ייבוא מחדש של המשחק בפעם הבאה שהספריה תעודכן. האם אתה בטוח שאתה רוצה להסיר את המשחקים {0}? בחירה באפשרות "הוסף לרשימת יוצאים מהכלל" תמנע את ייבוא המשחקים שוב בפעם הבאה שהספריה תעודכן. האם אתה בטוח שברצונך להסיר {0} פריטים שאינם בשימוש כרגע? לא נמצאו שדות שאינם בשימוש. כן (הוסף לרשימת יוצאים מהכלל) קיימים שינויים שלא נשמרו באיזור זה מעדכן את מבנה ספריית המשחקים… עדכון בסיס הנתונים נכשל. עדכון ספריית המשחקים נכשל. {0} MBs של מקום פנוי דרושים. שגיאת משחק לא ניתן להפעיל את המשחק. '{0}' לא נמצא בבסיס הנתונים. אי אפשר להתחיל משחק: {0} לא ניתן להחיל את הפעולה: {0} לא ניתן לפתוח את מיקום המשחק: {0} לא ניתן לזהות את גודל ההתקנה של המשחק: {0} סריקת גודל ההתקנה נכשלה היו {0} שגיאות במהלך סריקת גודל ההתקנה נכשל ביצירת קיצור דרך: {0} פתיחת המדריך נכשלה: {0} לא ניתן להתקין את המשחק: {0} לא ניתן להסיר את המשחק: {0} לא נמצאו פעולות הפעלה חוקיות של המשחק. בעת שימוש בפעולות אמולטור, ודא שהגדרות הפלטפורמה תואמות בין תצורת המשחק לתצורת האמולטור. מימוש ההתקנה אינו זמין. תוסף הספריה האחראי למשחק זה מושבת או אינו מותקן. הורדת נתונים רשמית אינו זמין. אף משחק אינו בחור. נכשל הניסיון לביצוע פעולת הסקריפט של המשחק. נכשל ניסיון לבצע את פעולת הסקריפט של הישום. נכשל ניסיון לבצע את פעולת הסקריפט הכללית. נכשל ניסיון לבצע את פעולת הסקריפט של האמולטור. נכשל הניסיון לביצוע פעולת הסקריפט של המשחק. PowerShell 3.0 או חדשה יותר אינה מותקנת. לא הצלחתי לקבוע איך להתחיל את המשחק. פעיל כבוי הסר הסר את מה שלא שבשימוש שנה שם העתק הוסף סמל ברירת מחדל תמונת עטיפה ברירת מחדל תמונת רקע ברירת מחדל סיים הבא הקודם סיים הקודם נקה נקה דחה בטל הכל יבוא שם יוצר מודול סדרה גירסה שוחק לאחרונה הכי משוחקים כמות משחקים גודל ההתקנה תיקיה הערות נוסף תאריך הוספה נערך נערך לאחרונה אתר נתיב אישור שמור סגור ביטול לאשר אפס כן לא ברוך הבא משתמש מקומי כללי מדיה קישורים התקנה פעולות מוריד… מוריד נתונים… טוען… סוג פרופיל פרופילים הסר הורדה חיפוש רזולוציה: כלשהו זום תצוגת רשימה עטיפות תצוגת רשת תצוגה מפורטת התאמה אישית URL תודה מיוחדת רישיון תורמים יוצא מפליינייט… היום אתמול שני שלישי רביעי חמישי שישי שבת ראשון בשבוע האחרון בחודש האחרון בשנה האחרונה לפני יותר משנה 0 עד 100 מגה 100 מגה עד 1 ג'יגה 1 ג'יגה עד 5 ג'יגה 5 ג'יגה עד 10 ג'יגה 10 ג'יגה עד 20 ג'יגה 20 ג'יגה עד 40 ג'יגה 40 ג'יגה עד 100 ג'יגה 100ג'יגה ומעלה ייבוא הושלם בהצלחה. כל המשחקים מזהה המשחק מזהה בסיס הנתונים הגדרות קבועות מראש עמודה עמודות שורה שורות לא הצליח לקבל סמל מפעולת המשחק. פעולה לסוג הקובץ אינה קיימת. הורד רק נתונים חסרים הפעלת אפשרות זו תדלג על הורדת נתונים עבור שדות אשר כבר מכילים מידע. בחירת משקים אנא בחר אילו משחקים צריכים להתעדכן עם נתונים חדשים: כל המשחקים מבסיס הנתונים כל המשחקים שמסוננים כעת משחקים נבחרים בלבד לא נבחרו שדות מטא נתונים לא נבחרו שדות מטא נתונים להורדה. צריך לבחור לפחות שדה אחד ולאפשר לפחות מקור מטא נתונים עבורו. חנות רשמית IGDB אנא בחרו אילו שדות יאוכלסו אוטומטית על ידי פליינייט ובאילו מקורות להשתמש כדי להשיג את הנתונים. אנא שיקלו ללחוץ על הלוגו מעל ולתרום עדכונים לבסיס הנתונים של igdb.com כדי לשפר את המידע שבשימוש פליינייט. מוריד נתונים… מייבא משחקים מותקנים… מייבא {0} משחקים… מיבא משחקי אמולטורים מ {0}... מוריד עדכוני ספריה… סורק את גודל המשחקים בספריה סורק את גודל המשחקים מיוובאים עדכון הספריה הושלם משחרר משאבים… הגדרות הגדרות… פלטפורמות ואמולטורים הגדר אמולטורים… מנהל הספריה… כלים מוריד נתונים… כלי תכנה… קביעת תצורה של אינטגרציה... פתח תוכנת לקוח צד שלישי לקוחות צד שלישי עדכון ספריית המשחקים ביטול עדכון ספריה עדכון תיקיות אמולטורים הוסף משחק ידנית… סרוק אוטומטית… משחק אמולטור… אפליקציה ממייקרוסופט סטור… אודות פליינייט שלח משוב עבור למצב מסך מלא קישורים עזרה תמוך באמצעות Patreon תמיכה דרך Ko-fi מדריך למשתמש תיעוד SDK איתחול מערכת כבה מערכת השהה מערכת מצב שינה נעל מערכת נתק משתמש בחר משחק אקראי שדות משחק להצגה בפאנל הפרטים: ריווח פריטים צייר רקע פריט רשת עובי מסגרת פריט רשת מקור סמל משחק חסר מקור עטיפת משחק חסר מקור רקע משחק חסר ריווח אנכי לפרטי משחק מיקום פרטי פריט רשת מיקום רשימת משחקים בתצוגה מפורטת צייר הפרדה בין פאנלים גובה תמונת עטיפת משחק גובה סמל רשימת משחקים גופן יישום פונט בעל רוחב קבוע מיקום פאנל מסנן מיקום פאנל סייר עיבוד עטיפת משחק יחס גובה-רוחב יעד האפשרויות הבאות משפיעות גם על רינדור גרפיקה במצב מסך מלא! מצב מתיחה קופסת DVD Epic Games Store GOG Galaxy 2.0 IGDB ריבוע Steam Banner כריכה אנכית של Steam Twitch * דורש הפעלה מחדש הגדרות כללי פאנל עליון תצוגה פרטי משחק פריסה מתקדם מסך מלא קלט ביצועים נתונים מעדכן חיפוש גיבוי נתוני ספריית גיבוי שחזר גיבוי ייבא אוטומטית שינויים בספרייה מיקום קובץ בסיס נתונים לא תקין, חובה לספק מיקום קובץ מתאים. שם חשבון אינו יכול להיות ריק. הורד נתונים לאחר יבוא משחקים התחל את פליינייט בצורה ממוזערת הפעל את פליינייט בצורה אוטומטית כאשר המחשב יופעל התחיל במצב סוגר לTray נכשל ברישום פליינייט לרוץ בהפעלת המחשב. התחל במצב מסך מלא טעינת תמונות ברקע משפר החלקת גלילה של רשימת משחקים בתמורה לטעינה איטית יותר של תמונות. הצג את שם המשחק אם העטיפה אינה זמינה הצג שמות משחקים בתצוגת רשת החשך משחקים שלא מותקנים הצג סמלי משחקים בתצוגה מפורטת הצג כמות פריטים בתיאור קבוצות הצגת שדות שהוקצו רק בחלוניות מסנן וסייר בטל האצת חומרה השתמש כשנחווה גמגום או בעיות ממשק דומות הצג משחקים מוסתרים ברשימות הפעלה מהירה משפיע על רשימת קפיצות ותפריטי Tray מספר פריטי הפעלה מהירה השתמש בתמונת רקע המשחק כרקע החלון טשטוש הרקע איכות גבוהה השחרת הרקע הצג בתצוגת רשת ערכת נושא פרופיל ערכת נושא ערכת נושא למסך מלא פרופיל ערכת נושא מסך מלא מיקום בסיס הנתונים סטטוס חיבור: הגדרות פליינייט ניקוי מטמון יכול לפתור בעיות הנגרמות בעת חיבור חשבונות. הצג אייקון ממוזער מזער לאייקון בשורת המשימות מזער לאייקון בשורת המשימות כאשר חלון האפליקציה נסגר כשהמשחק מתחיל: אחרי שהמשחק נסגר: פורמט "זמן שוחק" כדי לציין כמה ימים שוחקו פורמט תאריך פעולה זו תנתק אותך מכל החשבונות המחוברים. נדרשת הפעלה מחדש של האפליקציה, האם ברצונך להמשיך? לנקות מטמון? אתחול פליינייט דרוש להחלת ערכת נושא חדשה קבל ערכות נושא נוספות צור ערכת נושא חדשה השג הרחבות נוספות צור הרחבה חדשה עזור לנו לתרגם את פליינייט יש להפעיל מחדש את פליינייט כדי להחיל הגדרות חדשות. הפעל מחדש עכשיו? הפעלה מחדש תבטל את כל המשימות הפעילות (הורדות) המתבצעות כעת. הפעל מחדש את פליינייט? פליינייט אינו מזיז קבצי ספריות אוטומטית, נדרש להעתיק\להזיז את הקבצים לפני שהמיקום משתנה. אם שום ספריה לא קיימת במיקום היעד, ספריה חדשה תיווצר. ייעשה שימוש במיקום מסד נתונים חדש רק לאחר אתחול האפליקציה. משך זמן משחק לא יירשם אם פעולת "סגירה" נקבעה. מספר שורות מספר עמודות מספר שורות תצוגה מפורטת הצג את תמונת הרקע במסך הראשי אינו מוכל רטרואקטיבית על משחקים קיימים ללא הורדת נתונים מחדש. ייבוא זמן משחק של משחקים בספריה: מגדיר מתי פליינייט מיהא את זמן המשחק שמגווח ע"י הספריות המשחקים במסד הנתונים של פליינייט. נדרשת תמיכה של הפלאגין של הספריה על מנת להשתמש באפשרות זו. תמיד: מיבא את הזמן המשוחק עבור משחקים חדשים וקיימים רק עבור משחקים מיובאים חדשים: מייבא את הסמן המשוחק עבור משחקים חדשים אף פעם: לא מעדכן זמן משוחק בשום מצב תמיד רק עבור משחקים מיובאים חדשים אף פעם אפשר תמיכה בשלט כאשר נמצא במצב חלון כפתור מנחה פותח מצב מסך מלא הורד נתוני משחקים באופן אוטומטי תצוגת יעד תמיד השתמש בתצוגה ראשית הצג כותרות משחקים הצג מצב סוללה הצג אחוז סוללה הצג שעון החבא סמן עכבר מותקן רק במסננים מהירים הנחיות כפתורים פריסה גלילה אופקית בחר אחד מתתי הסעיפים אין אפשרויות זמינות נכשל בטעינת ההגדרות סקריפטים אלה מבוצעים עבור כל משחק בספריה. ניתן להקצות סקריפטים בודדים לכל משחק בנפרד תוך עריכת פרטי המשחק. הנפשת מעברי תמונת רקע גודלי גופנים אוטומטי מוחלק גווני אפור ClearType אידיאלי תצוגה מצב עיצוב טקסט מצב עיבוד טקסט אפשרויות עיבוד ועיצוב טקסט לא מופעלות על טקסט תיאור המשחק כעת. טען מראש תמונות רקע אם פונקציה זו מופעלת, פליינייט תוריד תמונות רקע בעת הורדת נתונים. הפעלת הפונקציה תגרום לצריכת שטח נוסף ותאפשר לך לגלוש בתמונות במצב לא מקוון.   אם תכונה זו אינה זמינה, פליינייט תוריד תמונות רקע רק בגישה ראשונה. אי הפעלת הפונקציה תחסוך בשטח אך עלולה לגרום לקריאה איטית של תמונות, ושלא יהיה ניתן להציג תמונות מסוימות במצב לא מקוון. סגור אוטומטית את תוכנת הפתיחה של המשחק בעת היציאה מהמשחק השהיית זמן כיבוי הלקוח (בשניות) אל תיסגר לאחר הפעלות במשחק קצר מ- (בשניות) סגור אוטומטית את התוכנות הבאות: סגור קליינטים אוטומטית ייבא רשימת יוצאים מהכלל הצג אזהרה בעת הקצאת מדיה גדולה מדי למשחק פעולת פתיחת תיקיה ארגון מועדף לדרוג גיל מתאים עדכן נפח התקנה של משחקים בעת עדכון סיפריה סורק ומעדכן את נפח ההתקנה אם זוהה שהקבצים השתנו מאז הסריקה האחרונה ללא מילוי אחיד אחיד למילוי שמאלה ימינה למעלה למטה שגיאה בייבוא נדרש אימות האימות נכשל מצב אלטרנטיבי לעיבוד תצוגת רשת השתמש כאשר ישנן בעיות בתצוגת הרשת, לדוגמא חלונות אימות של אינטגרציות טעינה חלקית של תאורי משחק ארוכים תאורים ארוכים עלולים לגרום ללאג כאשר בוחרים משחקים. כאשר מופעל, רק חלק מהתיאור יטען תחילה עם אפשרות לטעון את ההמשך לפי דרישה יבוא נתונים הורד נתונים קבע הגדרה נבחרת לשימוש עבור כל הורדת נתונים עתידית. ניתן לשנות גם דרך הגדרות האפליקציה. אשף ייבוא האמולציה האשף ינחה אתכם בתהליך ההורדה והיבוא של אמולטורים לקונסולות ויבוא משחקים זכור שתמיד באפשרותך להוסיף אמולטורים ו\או משחקים נוספים מאוחר יותר דרך התפריט הראשי (תחת תפריט "כלים" של הגדרות אמולטורים ותפריט "הוסף משחקים" עבור משחקי אמולציה). להלן רשימת אמולטורים שפליינייט יכול לזהות ולהגדיר אוטומטית. אפשר להוריד את האמולטורים מהאתרים שלהם. ברגע שהאמולטורים מותקנים (ידנית), אפשר ליבא אותם בחלון הגדרת האמולטורים ניתן לייבא כל אמולטור המותקן על המחשב על ידי לחיצה על כפתור "זיהוי אוטומטי מתיקיה…". פליינייט יחפש בתיקיה הנבחרת עבור אמולטורים ידועים ויספק אפשרות לייבא אותם. באפשרותך להשתמש בלחצן זה מספר פעמים כדי לייבא אמולטורים מתיקיות שונות. אמולטורים יתווספו לתחתית הרשימה הנוכחית. ניתן לייבא משחקים על ידי לחיצה על כפתור "סרוק תיקיה על ידי אמולטור". בחירת אמולטור נכון יאמר ל פליינייט, אילו סוגי קבצים צריכים להיסרק ולייבא. באפשרותך להשתמש בלחצן זה מספר פעמים כדי לייבא משחקים מתיקיות שונות. משחקים יתווספו לתחתית הרשימה הנוכחית. אין אמולטורים שנבחרו לייבוא. לא תוכלו לייבא אוטומטית משחקים בלי להגדיר קודם אמולטורים. האם אתה בטוח שברצונך להמשיך ולצאת מתהליך הייבוא? אין אמולטורים המוגדרים בפליינייט. לא ניתן לייבא משחקים מבלי להגדיר תחילה את האמולטור ולבחור את סוגי הקבצים המתאימים. האם אתה רוצה להוסיף כמה אמולטורים עכשיו? סרוק תיקיה באמצעות אמולטור בחר קבצים זיהוי אוטומטי מתיקיה… הגדר אמולטורים… סורק… סורק {0}... הגדרת הפעם הראשונה תהליך זה ינחה אותך בייבוא ותצורה אוטומטיים של ספריות משחקים חיצוניות. פליינייט יכול לייבא באופן אוטומטי משחקים משירותי משחקים מרובים, כגון Steam או GOG. זכור שאתה יכול גם להוסיף באופן ידני כל משחק מותאם אישית או אמולטור עבור כל פלטפורמה מאוחר יותר מהתפריט הראשי. אינטגרציית ספריה להלן הרשימה של כמה שילובי ספריות שנאספו שפליינייט תומך בהן. אנא בחר את אלה שברצונך להתקין. ניתן להתקין אינטגרציות נוספות מאוחר יותר מתפריט "הרחבות". הסתיימה ההגדרה ההתקנה הראשונית הושלמה. זכור שתוכל לשנות את כל ההגדרות בהמשך בתפריט 'הגדרות'. אתה יכול גם להוסיף כל משחק אחר בהמשך על ידי לחיצה על תפריט הלוגו של פליינייט. נכשל בהורדה של הרחבה אחת או יותר. אפשר לנסות להוריד את ההרחבה מחדש מתפריט ההרחבות אחרי שהאשף יסיים. מוריד אינטגרציה {0} מוריד רשימה של אינטגרציות מומלצות… הורדת רשימה של אינטגרציות מומלצות. אפשר לנסות ולהוריד מחדש יותר מאוחר דרך תפריט התוספים. הגדר פלטפורמות ואמולטורים הגדר אמולטורים פלטפורמות פלטפורמה אמולטורים אמולטור הוסף פלטפורמה בחר סמל בחר עטיפה בחר תמונה בחר פריט בחר רקע בחר קובץ בחר URL הוסף אמולטור פלטפורמות נתמכות האם ברצונך לשמור שינויי הפלטפורמות? האם ברצונך לשמור שינויי האמולטורים? ניתן להרצה ארגומנטים תיקיית עבודה סוגי קבצים נתמכים ייבא אמולטורים… הורד אמולטורים… טען ערכי ארגומנטים קבועים מראש מפרופיל אמולטור ידוע האם אתה בטוח שברצונך להסיר את אמולטור {0}? הוא כרגע בשימוש על ידי {1} משחק(ים). האם אתה בטוח שברצונך להסיר את הפלטפורמה {0}? היא כרגע בשימוש על ידי {1} משחק(ים) ו {2} אמולטור(ים). עזרה בהגדרות מיין לפי כיוון המיון קבץ לפי סדר עולה סדר יורד אל תקבץ קבץ לפי ספריה קבץ לפי קטגוריה קבץ לפי פלטפורמה סוג תצוגה תצוגה פאנל סייר פאנל מסנן סמל איקון סיפרייה תמונת עטיפה תמונת רקע שם מיון ספריה ידני שם קונן התקנה שם חשבון פלטפורמה קטגוריה ז'אנר תאריך שחרור שנת שחרור מפתח תג מפרסם מצב ההתקנה התאם את כל המסננים במידה ומאופשר, רק משחקים המשתמשים בכל הפריטים בסינון יהיו כלולים בתצוגה. במידה וכבוי, משחקים אשר משתמשים אפילו רק בפריט אחד בסינון יהיו כלולים בתצוגה. מותקן מותקן לא מותקן מוסתר מועדף הפעל תמיכה בHDR אם מופעל, HDR יופעל בצג הראשי לפני התחלת משחק. שים לב שHDR לא נתמך בצג הראשי שוחק לאחרונה קטגוריה תיאור תיקיית התקנה תמונת עטיפה קישורים נתיב קובץ תמונה, ROM או ISO ז'אנר ז'אנרים חברה חברות מפתח מפתחים מפרסם מפרסמים קטגוריה קטגוריות תג תגיות תכונה תכונות דירוג גיל דירוג גיל אזור אזורים מקור מקורות פעילות אחרונה שגיאת בסיס נתונים כשלון בטעינת בסיס נתוני הספריה. בסיס הנתונים אינו פתוח. לא ניתן לגשת לקובץ בסיס נתוני הספריה. הקובץ "{0}" נמצא בשימוש על ידי תהליך אחר או נמצא במקום לא נגיש. נכשל ביצירת חבילת אבחון. נכשל בהעלאה אוטומטית של חבילת אבחון. מידע אבחון נשלח בהצלחה. חבילת האבחון נוצרה והוגשה בהצלחה. אנא צרף את המזהה הבא לדוח הקריסה: נכשל ביבוא משחקים מ {0}. נכשל ביבוא משחקי אמולטור מ {0}. לא ניתן לחפש עבור משחקים על ידי פרופיל האמולטור הנבחר. הפרופיל לא מכיל סיומות קבצים או פלטפורמות. פליינייט נכשל להתחיל. אנא סגרו את כל המופעים הרצים של האפליקציה ונסו שוב. כישלון בהחלת ערכת נושא "{0}", פרופיל צבע "{1}" {2} לא ניתן לפתוח קישור, URL לא תקין. נכשלה הפעלת האפליקציה. נכשל באתחול רכיב תצוגת הרשת. פליינייט לא יכול להמשיך עם תהליך האתחול למידע נוסף: https://playnite.link/cefstartup לא ניתן לייבא אמולטורים כתוצאה מקובץ הגדרות חסר ו/או פגום ביצוע פעולת תפריט נכשלה ערוך פרטי משחק כתובת תמונה הוסף קישור הוסף ROM שמור שינויים הפעל שינוי שדה לכלל המשחקים הנערכים. הוסף פעולה מחק פעולה הסר פעולת משחק הוסף משחקים סרוק תיקיה… גלה מותקנים עיון… פתח את פליינייט הגדרות פרופיל שם משחק אינו יכול להיות ריק. תיקיית מעקב התקדמות במשחק לא יכולה להיות ריקה. שם משחק לא יכול להיות ריק לפני חיפוש נתונים. מידע משחק לא תקין הזן כתובת URL חוקית המתחילה ב http:// או https:// בחר URL נכשלה הורדת נתונים: {0} שגיאת הורדה נקה מסננים חשבון פרטי חשבון ציבורי מפתח API שגיאת הפעלה שגיאה בערכת נושא נקה הכל מתקין מסיר התקנה מפעיל מריץ URL לא חוקי אל תעשה דבר מזער שחזר חלון שחזור החלון רק כאשר מופעל דרך ה-UI סגור שינוי מתקדם אף פעם סטטוס השלמה סטטוסי השלמה ציון משתמש ציון מבקרים ציון קהילה תסריטי משחק תסריטי אפליקציה סקריפטים תוספים מקורות נתונים הרחבות זיהוי הרחבה טען מחדש סקריפטים Interactive SDK PowerShell כל הסקריפטים נטענו מחדש בהצלחה. לא נמצאו משחקים עבור קריטריון חיפוש\מסנן לא נמצאו פריטים עבור למצב שולחן עבודה צא מפליינייט ספריות עדכן הכל נוצר על-ידי: גירסה: עודכן: מודול: ספריה סטטיסטיקה הכל ללא התראות רוחב גובה גודל קטן רגיל גדול גדול יותר הכי גדול ברירת מחדל בחר בחר הכל בטל בחירה של הכל ראשון אקראי בחירת משתמש טען עוד שקיפות כווץ הרחב כווץ הכל הרחב הכל אחר ערכות נושא ארגומנטים לאמולטור ארגומנטים מובנים ארגומנטים מותאמים אישית ארגומנטים נוספים לאמולטור דרוס ארגומנטים לאמולטור פעולת משחק בחר מידע מטא לייבוא בחר משחקים לייבא חיפוש נתונים עדכון זמין שינויים מהעדכון האחרון הורד והתקן עדכון חפש עדכונים שגיאת עדכון נכשל בחיפוש גרסה חדשה. לא נמצאה גירסה חדשה, אתה מעודכן. נכשל בהורדה והתקנת עדכון. מספר פעולות רצות ברקע. האם לבטל ולהמשיך עם העדכון? מספר פעולות רצות ברקע. האם לבטל ולצאת מפליינייט? מספר פעולות רצות ברקע. שינוי מצב יבטל את הפעולות. האם להחליף בכל זאת? עדכון זמין לפליינייט טען רשימת ערכות נושא מחדש הפעל ערכת נושא נבחרת האזן לשינויי קבצים החל ערכת נושא אוטומטית כאשר קובץ המקור משתנה סביבת סקריפטים סקריפט להרצה לפני תחילת המשחק סקריפט להרצה לאחר יציאה מהמשחק סקריפט להרצה אחרי שמשחק מתחיל הרץ בעת הפעלת האפליקציה הפעל בעת כיבוי האפליקציה סקריפט תחילת משחק סקריפט לאחר תחילת משחק סקריפט לאחר עצירת משחק הרץ סקריפט גלובלי גלובלי מסונן נוכחי חדש סקריפט בדיקה הצג רק פרטים שנבחרו שמור כברירת המחדל הוסף למעודפים הסר ממועדפים הסתר משחק זה הסר מהסתרה הפעל תמיכה בHDR בטל תמיכה בHDR ערוך… חשב נפח התקנה חשב נפח התקנה (לכל המשחקים) חשב נפח התקנה (רק עבור מידע חסר) גודל ההתקנה הגדר קטגוריה… הגדר סטטוס השלמה הסר שחק התקן אפשרויות משחק פרטים הסר התקנה פתח מיקום התקנה צור קיצור דרך בשולחן העבודה פתח את המדריך עוד מנוהל על ידי תוסף הספריה תהליך הרצת המשחק ינוהל על ידי התוסף תיקיה האחראי על משחק זה. לא נמצא מידע רלוונטי עבור המשחק '{0}' בעמוד הבוקש. טיפ: ניתן להשתמש בתהליך הורדת נתונים מתקדם יותר בעת עריכת משחק בודד על ידי התפריט "עריכה". לא זמין כאשר פעולה כלשהי בתהליך. טקסט התיאור הינו רגיש לתחביר HTML זמן המשחק מוקלט לפי שניות נפח ההתקנה מוצג בביטים תאריך השחרור חייב להיות בתצורה 'שנה-חודש-יום'. ערכי החודש והיום יכולים להיות מושמטים. ערכים בין 0 ל 100 או ריק לללא ציון. פיתוח Playnite מתאפשר באמצעות תמיכה של הפטרונים וחברי Ko-fi הבאים: תורמים לקוד, תרגום ואחרים לא בסדר כלשהו: בטל ניטור משחקים? ניתור התקנה כרגע רץ, הרם ברצונך לבטל את התהליך ולהחזיר את המשחק למצבו הקודם? ניתור הרצת משחק כרגע רץ, הרם ברצונך לבטל את התהליך ולהחזיר את המשחק למצבו הקודם? משך המשחק שוחק לאחרונה {0} ימים {1} שעות ו-{2} דקות {0}ש {1}ד {0} דקות {0} שניות לא שוחקו פותח מצב שולחן עבודה… פותח מצב מסך מלא… טוען ספרית משחקים… מחשב נפח התקנה... מחשב נפח התקנה של {0}… נכשל בהתקנת קובץ סקריפט. הסקריפט הותקן בהצלחה. סקריפט התקנה שגיאת סקריפט ביצוע פעולת תוסף נכשלה. פתח תיקיית נתונים חשב חישוב אוטומטי של גודל ההתקנה באמצעות רום המשחק, במידה ולמשחק יש אחד או תיקיית ההתקנה אם הוגדרה כזו תוכנת {0} אינה מותקנת. תוכנת {0} תפתח כעת. אנא התחבר ואז סגור את הודעה זו. מחכה להתחברות משתמש, אנא סגרו את זה בסיום… תיקיית התקנת משחק לא נמצאה. תצורת פעולת משחק לא תקינה. טיפול בתקלות סנכרון חשבון פתרון בעיות שנה שם פריט הוסף פריט חדש הכנס שם הזן שם חדש פחות משעה שעה עד 10 שעות 10 עד 100 שעות 100 עד 500 שעות 500 עד 1000 שעות מעל 1000 פליינייט צריך לאתחל כדי להשלים התקנה. האם את/ה רוצה לבצע אתחול עכשיו? הרחבה אינה ארוזה כראוי. ערכת נושא אינה ארוזה כראוי. הרחבה "{0}" נכשלה להיטען כראוי. אין אפשרת לטעון את ההרחבה "{0}", הגרסה הנוכחית של פליינייט לא נתמכת. ערכת נושא "{0}" נכשלה להיטען כראוי. אין אפשרת לטעון את ערכת הנושא "{0}", הגרסה הנוכחית של פליינייט לא נתמכת. הרחבה נכשלה להיטען כראוי. ערכת נושא נכשלה להיטען כראוי. ערכת נושא\הרחבה משתמשת בגירסת API שאינה נתמכת. ההתקנה הסתיימה בהצלחה. להתקין הרחבה? כללי נכשל להתקין את התוסף "{0}" נכשלה התקנת הרחבה. {0} האם את/ה רוצה להתקין הרחבה חדשה? {0} מאת {1} גרסה {2} האם את/ה רוצה לעדכן את ההרחבה "{0}"? גרסה נוכחית: {1} גרסה חדשה: {2} נכשלה התקנת ערכת נושא. {0} האם את/ה רוצה להתקין ערכת נושא חדשה? {0} מאת {1} גרסה {2} האם את/ה רוצה לעדכן את ערכת הנושא "{0}"? גרסה נוכחית: {1} גרסה חדשה: {2} אתם עומדים לעזוב את Playnite ולנווט לדף אינטרנט על ידי דפדפן ברירת המחדל. האם ברצונך להמשיך? {0} תמונה/תמונות שנבחרו עשויות להיות גדולות מדי לביצוע מיטבי. שימוש בתמונות גדולות מאוד עלול לגרום לתגובה הרבה פחות טובה של ממשק המשתמש ולשיפור השימוש בזיכרון. מקסימום החלטות מומלצות: סמלים: {0} מגה פיקסלים מכסה: {1} מגה פיקסלים רקעים: {2} מגה פיקסלים אזהרת ביצועים אל תציג שוב קובץ עם סיומת {0} אינו נתמך. סיומת קובץ לא מתאימה קובץ תמונה שנבחר עשוי להיות גדול מדי לביצועים מיטביים. האם ברצונך להסיר את ערכת הרקע? ההתקנה תתבצע בפעם הבאה שהתוכנה תופעל מחדש. ערכות נושא מובנות לא ניתנות להסרה. ערכת נושא זו אינה תומכת בגירסא זו של פליינייט. האם ברצונך להסיר את התוסף? הסרת ההתקנה תתבצע בפעם הבאה שהתוכנה תופעל מחדש. הרחבות מובנות לא ניתנות להסרה. הרחבה זו אינה תומכת בגירסא זו של פליינייט. תיקיית התקנה תיקיית מידע מייצר חבילת דיאגנוסטיקה... מעלה חבילת דיאגנוסטיקה... ייבא קובץ מה זה? אתה בטוח שברצונך לעשות את זה? זמן משחק כולל זמן משחק ממוצע זמן משחק מירבי נפח התקנה כולל סקירה כללית סרגל צד הצג בסרגל הצדדי אפס הגדרות כל הגדרות התוכנה יחזרו להיות ברירת המחדל, כולל: - מיקום מסד הנתונים - רשימת החרגות ייבוא - הגדרות התוספים, כולל אינטגרציות לספריה הפעלת התוכנה נדרשת על מנת להשלים את התהליך. האם לבצע שחזור להגדרות ברירת מחדל? עבור מפתחים תוספים חיצוניים הכנס נתיב תיקיה מלא הישגים פורום חדשות דף חנות הגדרה ראשונית לא הושלמה. פליינייט כעת יאותחל למצב שולחן עבודה לסיום התהליך. שוחק לאחרונה מועדפים הכי משוחקים הכל יש מסננים מופעלים. ישנם מסננים נוספים פעילים תוצאות חיפוש עבור: פריט בעל שם זהה כבר קיים. הגבל בחירה למסנן הנוכחי בחר נוסף תוספים... מותקן הגדרות הרחבות עיון עדכונים עדכונים ({0}) ניהול תוספים וערכות רקע מותקנים, כולל ההגדות, הועבר לתפריט חדש - "תוספים" ניתן להגדיר את כל התוספים האפשרים חיבור לספריות קיימות כאן. במידה וברצונך להתקין או להסיר תוספי קישור נוספים, יש להשתמש בתפריט "תוספים" תחת התפריט הראשי. ערכות נושא שולחן עבודה ערכות נושא מסך מלא מחפש... התוסף אינו תואם לגרסה זו של פליינייט נכשל בהורדת חבילת התקנה של הרחבה הורדת חבילת ההתקנה של התוסף נכשלה. אתחול האפליקציה נדרש על מנת להחיל שינויים שבהמתנה. הרחבה זו מתוכננת להתקנה. התקן התקנה מחדש הסר התקנה כבר מותקן לא נמצאו עדכוני הרחבות חדשים. עדכן תוספים יומן שינויים לא זמין מתוכנן להתקנה הורדה נכשלה רישיון נדחה מוריד {0}… מחפש עדכוני הרחבות… מחפש עדכוני תוכנה… ישנו עדכון הרחבה אחד או יותר זמין. בחר פריטים לעדכון תוסף בפיתוח מקומי {0} הסכם רשיון קבל דחה כלול פעולת ניגון של אינטגרצית ספריות בחר פעולה מצב מעקב מיקום מעקב עיכוב בתחילת המעקב תדירות מעקב קישור קובץ אמולטור סקריפט ברירת מחדל תהליך תיקיה תהליך מקורי שם התהליך תעד הודעות מעקב השינויים הבאים יגרמו לשינוי נתונים בכל המשחקים שנבחרו! ללא אחיד פריטים בלבד התחלה וסיום בלבד רגישות גלילה גלילה חלקה מהירות אנימציה הסר פריט? האם אתה בטוח שברצונך להסיר פריט זה? הצג כפתורים בפאנל העליון: הגדרות תצוגה כלליות הגדרות קיבוץ הגדרות מיון סנן ערכות מוגדרות מראש מיקום פריטי פלאגין רוחב מפריד אזורים הזז כפתור תפריט ראשי לסרגל הצד פאנל סייר בוחר משחק אקראי בחירת משחק באופן אקראי בחר משחק אקראי מהתצוגה שמור הגדרת קבוצות ומיון הצג כמסנן מהיר במצב מסך מלא ב 7 הימים האחרונים ב 31 הימים האחרונים ב 365 הימים האחרונים לפני יותר מ365 ימים הגדר שמור תצורה צמצם לאחר הפעלת משחק צמצם את פליינייט אחרי שמשחק מתחיל גודל גופן גודל גופן קטן תמיכה בבקר משחק אם כבוי, פליינייט לא יקבל שום קלט מאף בקר משחק הצג פריטים בתפריט הראשי: הפוך את פעולת הכפתורים X/A בתצוגה הראשית החלף את שיוך הכפתורים עבור התחלת משחק והצגת מידע נוסף בתצוגה הראשית החלף את שיוך כפתורי אישור/ביטול החלף את שיוך הכפתורים A/B עבור אישור או ביטול בקר ראשי בלבד כאשר מופעל, יתקבלו פקודות מהבקר הראשי בלבד כפתור Guide ממקד על פליינייט עוצמת ממשק עוצמת רקע השתק כאשר ברקע נכשל באתחול ממשק קול API פלט API לשימוש עבור פלט קול. במידה ונתקלת בבעיות שמע, אפשר לשנות את הערך הזה. כללי חזותי שמע פריסה תפריטים קלט {0} מתחיל… {0} רץ… רישיות רווח מגדיל עיבוד תמונה חלופי מאוזן איכות איכות גבוהה: איכות תמונה הכי טובה, איטי, צריכת זיכרון גבוהה. מאוזן: איכות טובה מאוד, מהיר, צריכת זיכרון נמוכה. חלופי: איכות טובה, מהירות בינונית, צריכת זיכרון נמוכה. בחר קובץ... בחר תיקיה... תסריט אתחול חשוב לשים לב ששימוש בתוספים וערכות רקע עלול להשפיע משמעותית על הביצועים של פליינייט, יציבות ואבטחת המידע. במידה ונתקלת בבעיות כלשהן לאחר התקנת תוסף או ערכת רקע. יש קודם לנסות להסיר / לבטל כדי לוודא האם זה מקור הבעיה. בחר בעת הפעלה בחר בעת הפעלה פרופילים מובנים פרופיל מובנה פרופילים מותאמים אישית פרופיל מותאמים אישית מנוהל ע"י סקריפט מובנה בחירת אמולטור בחירת פלטפורמה מפרט אזור הרץ לפני תחילת אמולטור הרץ אחרי הפעלת אמולטור הרץ לאחר יציאה מאמולטור קובץ הפעלה של אמולטור לא נמצא לא נמצאה בחירת אמולטור. סריפט אתחול של אמולטור לא נמצא פצל למשחקים נפרדים אחד למשחק אחד הגדר פלטפורמה הגדר אזור סרוק תיקיה סרוק קונפיגורציות לא לכלול דפוסים מסריקת checksum לא יבדק checksum עבור הקבצים המתאימים לדפוסים ותבוצע התאמה לפי שם הקובץ. פירוט נוסף נמצא בדף העזרה של האמולטור. סרוק עם אמולטור נדרש לקבוע שם כאשר שומרים קונפיגורציה חדשה לא הוגדר אמולטור או פרופיל לאמולטור לא הוגדרה ספריה לסריקה או שהיא אינה קיימת סריקת ההגדרות לא הוגדרה כמו שצריך. לכלול בצבר סריקה אוטומטית סריקת תיקיה של אמולטורים נכשלה. סריקת תיקיה של משחקים לאמולטורים נכשלה. החבא מיובאים פרופילים לייבוא: הגדרות סריקה אוטומטית שמור בתור הגדרת סריקה אוטומטית שמירת ההגדרה לשימוש מאוחר יותר בעת עדכון הספריה. ניתן לנהל הגדרות שמורות דרך התפריט "הגדרת אמולטורים" ייבוא על ידי שימוש בנתיב יחסי שימוש בנתיב יחסי להתקנת פליינייט או לתיקיית התקנת האמולטור על מנת לייבא משחקים. סרוק תקיות משנה סרוק בתוך קבצים מכווצים אחד קבצים קשורים איחוד כל הקבצים הקשורים למשחק הוסף סורק הוסף סורק שנשמר התחלה בסריקה הוספת הגדרות סריקה לתיקיות מסויימות. יש לוודא שהאמולטורים מוגדרים כמו שצריך לפני ייבוא משחקים (דרך התפריט "ספרייה" -> "הגדרת אמולטורים") סטטוס ברירת מחדל המשוייך למשחקים חדשים שנוספו סטטוס המשוייך למשחקים אשר הופעלו בפעם הראשונה נכשלה הפעלת סקריפט PowerShell. במידה ומערכת ההפעלה היא Windows 7, יש לנסות להתקין מחדש PowerShell 5.1. פילטר עם שם זה כבר קיים, האם לעדכן אותו עם ההגדרות החדשות? מילוי אוטומטי של שמות מיון חסרים עבור משחקים שנוספו בבת אחת או נערכו כאשר מתבצעת עריכת משחק, הוספת משחק באמצעות סריקת תיקיה או סריקת תיקיית אמולטור, יתווסף אוטומטית שם המתאים למיון עבור המשחק, למשל, "The Witcher 3" יקבל את שם המיון "Witcher 03". פעולה זו תמיד תיצור שם שונה מאשר שם המשחק המקורי ותעדכן שמות מיון ריקים בלבד. המילים האלו יוסרו מתחילת ערך שם המיון האוטומטי: השתמש בערך האלו כדי להתעלם ממילים בתחילת שם המשחק למטרות מיון. ברירת המחדל היא "The", "An", "A" הכנסת ערך שם מיון עבור משחקים שאין להם כזה מיון הכנסת ערכי שם מיון… במערכת ההפעלה שלך קיים שירות בשם Nahimic. שירות זה ידוע כגורם בעיות תצוגה בפליינייט (ותוכנות נוספות). במידה ונתקלת בתקלות גרפיות או תקלות תצוגה כלשהן בפליינייט, אנחנו ממליצים על כיבוי או הסרה לחלוטין של Nahimic. מידע נוסף זמין בכתובת https://playnite.link/nahimicsucksPlaynite (מידע באנגלית) פליינייט רץ עם הרשאות חזקות (כמו למשל administrator). דבר זה אינו מומלץ מכיוון שכל התוספים, המשחקים והתוכנות אפשר יופעלו דרך פליינייט יקבלו הרשאות כאלו גם כן! מידע נוסף זמין בכתובת https://playnite.link/adminfaq (מידע באנגלית) הצג אזהרה אם פליינייט רץ עם זכויות יתר חישוב הערך האמיתי של גודל התיקייה המותקנת בכונן כאשר מחשבים את גודל המשחקים במידה ומופעל, סריקות יהיה איטיות יותר ויחשבו את הגודל האמיתי של הקבצים בכונן הקשיח. במידה וכבוי, סריקות יהיו מהירות יותר וישתמשו בגודל הקבצים עצמם. התוספים הבאים דווחו כבעלי בעיות אפשריות, או כתוצאה מתקלות ביצועים/יציבות רבות או כתוצאה מבעיות אבטחת מידע. אנחנו מציעים להסיר את התוספים הבאים: {0} אל תכלול קבצי אונליין מהסריקה קבצים אשר נמצאים באחסון ענן לא יסרקו ולא יתבצע ייבוא שלהם אם אינם זמינים בכונן מקומי. נתמך עבור השירותים הבאים בלבד: Google Drive, DropBox, OneDrive ביצוע סריקה תוך התעלמות מתוכן הקובץ יתבצע ייבוא של קבצים אלו אך בצורה מדוייקת פחות בשיטה שלא מצריכה הורדת הקובץ מהענן. החל על הכל הגדרה מחדש של מצב ההתקנה כאשר מוגדר, פליינייט יתעלם ממצב ההתקנה (כולל תיקיית ההתקנה) אשר הוגדר באמצעות התוסף שייבא את המשחק. אפשרות זו עלולה שלא לעבוד עם תוספים מסויימים שאינים לוקחים בחשבון אפשרות זו. רק באופן ידני פעם ביום פעם בשבוע בכל הפעלה בדוק עדכוני תוכנה בדוק עדכוני הרחבות עדכן ספריות סריקת תיקיית אמולטורים כולל משחקים מוסתרים עריכת שדות בחר/בטל בחירה של הכל פתח הפעל להקצות החל להקליד על מנת לחפש משחקים… [F1] לעזרה # יביא את רשימת כל הפקודות האפשריות / יביא את רשימת כלל אפשרויות החיפוש הקלדת מילת חיפוש המסתיימת ברחח תבצע את החיפוש. TAB: החלפת פעולה ENTER: הפעלת פעולה נבחרת SHIFT-ENTER: פתיחת תפריט כלול משחקים לא מותקנים כולל משחקים מוסתרים משחקים לא מותקנים כלולים משחקים לא מותקנים לא כלולים משחקים מוסתרים כלולים למעט משחקים מוסתרים הפעל או התקן עבור לפרטים נוספים תפריט משחק ערוך משחק פתח חיפוש תיבת חיפוש מקש החיפוש פעולת משחק עיקרית פעולת משחק משנית CTRL-F פותח אפשרות חיפוש רחבה שמור הגדרות סינון משחק ספקי חיפוש מילת מפתח ברירת מחדל מילת מפתח מותאמת אישית קיצור דרך גלובלי חיפוש פליינייט הגדרת הרחבות החרגות אל תכלול קבצים יחסיים לסריקת תיקייה אל תכלול תיקיות היחסיות לתייקיית הסריקה הוסף קובץ לרשימת יוצאים מהכלל הוסף תיקיה לרשימת יוצאים מהכלל ניתן להוסיף החרגות רק להגדרות סריקה שמורות. החרגות נוספו לסורק "{0}" שכתוב ערך פלטפורמה אם מאופשר, הסורק יחיל את ערך הפלטפורמה שהוזן לכלל המשחקים, גם אם נמצא ערך פלטפורמה אחר. כלול פקודות בחיפוש ברירת מחדל אם מבוטל, פקודות לא תהיינה חלק מחיפוש ברירת מחדל עד הוספת # כתחילית. שימוש בחיפוש מעורפל בשדה השם כאשר מאופשר, סינון לפי שם המשחק יבצע התאמה כמו בחיפוש הכללי. ניתן לבצע התאמה מסויימת במקרה בודד על ידי הוספת התו ! שדות אשר יוצגו עבור תוצאות חיפוש משחק: סטטוס חבוי גיבוי נתונים בוטל. גיבוי נתונים נכשל. שגיאה בגיבוי נתונים גיבוי נתונים מתבצע… משחזר נתונים מגיבוי… שחזור נתונים מגיבוי נכשל. הגדרות ספרית משחקים נתוני משחק בספרייה הרחבות מותקנות מידע הרחבות ערכות נושא מותקנות יש לבחור את המידע לשחזור מקובץ הגיבוי שנבחר. פליינייט יבצע הפעלה מחדש ויתחיל את תהליך השחזור באופן אוטומטי. יש לבחור את הפריטים לגיבוי. נתוני אפליקציה ונתוני ספריית המשחקים יגובו כברירת מחדל. פליינייט יבצע הפעלה מחדש ויתחיל את תהליך הגיבוי באופן אוטומטי גיבוי נתונים אוטומטי תדירות גיבוי אוטומטי ספריית גיבוי גיבויים מתחלפים כלול מידע נוסף: יש להגדיר תיקיית גיבוי במידה והופעלה אפשרות הגיבוי האוטומטי. הצגת התראות לשחרור עדכונים בלבד כאשר מופעל, רק עדכוני גרסה עיקריים הקשורים לגרסה המותקנת יופיעו בהודעת העדכון. עדכוני גרסה חדשים לא יופיעו בהודעת העדכון. השתמש בתאריכים יחסיים עבור השבוע האחרון יש להשתמש בערכים יחסיים כמו "היום", "אתמול" במידה ומדובר בפחות משבוע. ערך זה ישמש עבור כלל התאריכים. חפש תמונה ברשת טקסט לחיפוש איקון טקסט לחיפוש תמונת כיסוי טקסט לחיפוש תמונת רקע מביא מידע על הרחבה… אין מקור מטא נתונים זמין הגדרות פעולת הפעלה השתמש בהגדרות סורק בחר פרופיל בהפעלה בחר אמולטור בהפעלה אוטומטי תמיד מופעל תמיד כבוי תמיכת נגישות (קורא מסך) תפריט האפליקציה תפריט משחק תיקיית התוכנה תיקיית קבצי משתמש שגיאה בקובץ ספריה, פליינייט ייסגר. יש לפתוח בקשה בעמוד ה-GitHub של פליינייט על מנת לבקש תיקון לשגיאה בקבצים אלו. האם לשמור את השינויים שביצעת? התקנה ניידת לא נמצאו בקרים ================================================ FILE: source/Playnite/Localization/hr_HR.xaml ================================================  Hrvatski Playnite jezik Izlaz Filtar je aktivan Filter isključen Dodatni filteri Filteri Filter Nevažeći podaci Sačuvaj promjene? Website: www.playnite.link Izvorni kod na GitHubu Napravi novi dijagnostički paket Pošalji dijagnostičke informacije O Playniteu Napravio Josef Němec Dodeli kategoriju Postavi kategorije Dodaj kategoriju Čekirano - Dodaj kategoriju Nečekirano - Obriši kategoriju Neodređeno - Bez izmjena (kada izmjenjujete više igara) Bez kategorije Bez platforme Ups! Nešto je pošlo po krivom... Neoporavljiva greška se dogodila. Ako nam želite pomoći popraviti situaciju, molimo opis poduzetih akcija prije pada, te slanje dijagnostičkih podataka. Ako ste priključeni na internet, paket će se učitati na Playnite poslužitelje za analizu. Alternativno, možete kliknuti na 'Prijavi Pad' tipku da napravite novi GitHub slučaj i prijavite pad ručno. Hvala ti na tvojoj pomoći. Dodatak "{0}" uzrokovao je neoporavljivu grešku. Preporučamo da spremite datoteku zapisa i prijavite slučaj razvijatelju dodatka. Ako se slučaj ponovi, onemogućite dodatak. Dodatak "{0}" uzrokovao je neoporavljivu grešku. Preporučamo da prijavite slučaj razvijatelju dodatka. Ako se slučaj ponovi, onemogućite dodatak. Nepoznati dodatak ili tema su uzrokovali nepopravljivu grešku. Preporučujemo da isključite vanjske dodatke, izolirate problematični dodatak i prijavite problem razvojnom timu dodatka. Nepopravljiva greška se dogodila. Ako nam želite pomoći da riješimo ovaj problem, molimo da nam pošaljete dijagnostičke informacije. Hvala vam. Onemogući dodatak Spremi datoteku zapisa Pošalji dijag. info Prijavi problem Restartujte Playnite Ponovno pokreni u sigurnom načinu rada Onemogućavanje svih dodataka trećih strana i korištenje zadane teme. Izađite iz Playnitea Stvari koje ste radili prije greške (na engleskom): Menadžer biblioteke igara Obriši igru(e)? Nemoguće ukloniti - Igra ili instalacija je pokrenuta. Nemoguće deinstalirati - Igra je pokrenuta. Želiš li sigurno ukloniti {0}? Sigurno želite ukloniti {0} igre? Sigurno želite ukloniti {0} igre? Odabir opcije "dodaj na listu izuzetaka" će spriječiti da igre budu ponovno uvedene kada se knjižnica bude ponovno ažurirala. Jeste li sigurni da hoćete da obrišete {0} odrednica koje nisu u korišćenju? Nema pronađenih nekorišćenih polja. Da (dodaj na listu izuzetaka) Postoje promjene koje nisu spremljene u ovom dijelu Ažuriranje formata biblioteke igara… Neuspjelo ažuriranje baze podataka. Ne može da se ažurira biblioteka igara. {0} MB-a slobodnog prostora je potrebno za to. GreškaIgre Ne može da se uđe u igru. '{0}' nije pronađeno u bazi podataka. Ne može da se uđe u igru: {0} Ne može da se započne radnja: {0} Ne može da se otvori lokacija igre: {0} Nije moguće detektirati veličinu instalacije igrice: {0} Greška prilikom skeniranja veličine instalacije Pojavilo se {0} grešaka prilikom skeniranja veličine instalacije Neuspjelo pravljenje prečaca: {0} Nemoguće je otvoriti priručnik:{0} Ne može da se instalira igra: {0} Ne može da se deinstalira igra: {0} Instalacija implementacije nije dostupna Knjižnični dodatak odgovoran za ovu igru je onemogućen ili nije instaliran. Preuzimanje zvaničnih metapodataka nije dostupno. Nije odabrana nijedna igra. Neuspjelo pokretanje skriptirane radnje igre. Neuspjelo pokretanje skripte za aplikaciju Neuspjelo pokretanje globalne skriptirane radnje. Neuspjelo pokretanje emulatorske skripte Neuspjelo pokretanje skriptirane radnje igre. PowerShell 3.0 ili noviji nije instaliran. Nije moguće utvrditi kako pokrenuti igru Uključeno Obriši Obriši nekorišćeno Preimenuj Kopiraj Dodaj Zadana ikona Zadana naslovnica Zadana pozadina Završi Dalje Natrag ZAVRŠENO NATRAG OČISTI Očisti Odbaci Odbaci sve Uvezi Naziv Autor Modul Serija Verzija Posljednji put igrano Najviše igrano Vreme igranja Veličina instalacije Mapa Zabilješke Dodato Datum dodavanja Promijenjeno Datum izmjene Website Putanja OK Sačuvaj Zatvori Poništi Potvrdi Ponovno postavi Da Ne Dobrodošli Lokalni korisnik Općenito Media Linkovi Instalacija Radnje Preuzimanje… Preuzimanje medija... Učitavanje… Vrsta Profil Profili Obriši Preuzmi Pretraži Rezolucija: Bilo koja Uvećaj Prikaz liste Naslovnice Prikaz rešetke Prikaz detalja Prilagođeno URL Posebna zahvala Licenca Suradnici Izlaženje iz Playnitea… Danas Jučer Ponedjeljak Utorak Srijeda Četvrtak Petak Subota Nedjelja Prošli tjedan Prošli mjesec Prošla godina Prije više od godinu dana 0 do 100MB 100MB do 1GB 1GB do 5GB 5GB do 10GB 10GB do 20GB 20GB do 40GB 40GB do 100GB 100GB ili više Uvoz je uspješno završen. Sve igre ID Igara ID baze podataka Preseti Stupac Stupci Red Redovi Samo preuzmi metapodatke koji nedostaju Uključivanje ove opcije će da preskoči preuzimanje metapodataka za polja podataka koja već sadrže neku informaciju. Odabir igara Molimo izaberite koje igre treba da se ažuriraju novim metapodacima: Sve igre iz baze podataka Sve trenutno filtrirane igre Samo odabrane igre Zvanična radnja IGDB Molimo odaberite koja polja trebaju automatsko popunjavanje od strane Playnitea i koji izvori treba da se koriste da bi se iz njih uzeli podaci. Molimo da ako želite kliknite na logo iznad i doprinesite nadopune na websiteu igdb.com da biste poboljšali podatke koje Playnite koristi. Preuzimanje metapodataka… Uvođenje instaliranih igara… Uvođenje {0} igara… Uvozim emulirane igre iz {0}... Preuzimanje ažuriranja biblioteke igara… Pretraživanje zauzeća igara u biblioteci... Pretraživanje zauzeća uvezenih igara... Ažuriranje biblioteke igara završeno Odpuštanje resursa… Konfiguracija Postavke... Platforme i emulatori Konfiguriraj emulatore… Menadžer biblioteke igara… Alati Preuzmi metapodatke… Programski Alati... Podesi Integracije... Otvori klijent Klijenti Treće Strane Ažuriraj biblioteku igara Zaustavi ažuriranje biblioteke Dodaj igru Ručno… Skeniraj automatski… Emulirana igra… Microsoft Store aplikacija… O Playniteu Pošalji povratne informacije Zameni u mod cijelog zaslona Linkovi Pomoć Podržite na Patreonu Korisnički priručnik SDK dokumentacija Ponovno pokreni sustav Isključi sustav Suspendiraj sustav Hibernacija sustava Zaključaj sustav Odjavi korisnika Izaberi igru nasumice Polja igara koja će se prikazivati na detaljnom panelu: Razmak između stvari Nacrtaj pozadinu za rešetke Širina granice rešetki Izvor za igre koje nedostaju ikonu Izvor za igre koje nedostaju naslovnicu Izvor za igre koje nedostaju pozadinu Vertikalni razmak detalja igre Pozicija detalja na rešetkastom prikazu Pozicija liste igara na detaljnom prikazu Nacrtaj separator između panela Visina naslovnice igre Visina ikone igre Aplikacijski font Pozicija filterskog panela Pozicija istraživačkog panela Predstavljanje naslovnice Omjer slike Slijedeće opcije također utječu na prikaz punog zaslona! Mod rastezanja DVD kutija Epic Games Store GOG Galaxy 2.0 IGDB Steam baner Steam vertikalna naslovnica Twitch * Zahtjeva restartovanje da bi se apliciralo Postavke Općenito Izgled Detalji igara Raspored Napredno Cijeli zaslon Unos Performanse Metapodaci Ažuriranje Pretraga Sigurnosna kopija Uvezi promjene u biblioteku igara automatski Pogrešna lokacija fajla baze podataka, ispravna putanja se mora podesiti. Naziv računa ne može biti prazan. Preuzmi metapodatke nakon uvoženja igara Pokreni Playnite smanjen Pokreni Playnite pri pokretanju računala Pokreni spušteno u programskoj traci Neuspjelo registriranje pokretanja Playnitea prilikom pokretanja kompjutera. Pokreni u Prikazu Punog Zaslona Asinkrono učitavanje slika Poboljšava glatkost skrolovanja listi igara u zamjenu za sporije učitavanje slika. Prikaži naziv igre ako naslovnica nedostaje Prikaži nazive igara na prikazu rešetke Zatamni igre koje nisu instalirane Prikaži ikone igara na listi prikaza detalja Prikaži broj stavki na grupnim opisima Pokaži samo dodjeljena polja na filterima i panelima preglednika Isključi hardversko ubrzanje Koristite samo kada imate probleme s mucanjem ili sličnim UI problemima Prikaži skrivene igre u popisima brzog pokretanja Broj stvari brzog pokretanja Koristi pozadinu igre kao pozadinu prozora Zamagli pozadinu Visoka kvaliteta Potamni pozadinu Prikaži na prikazu rešetke Tema Profil teme Tema za cijeli zaslon Profil teme za cijeli zaslon Lokacija baze podataka Status prijave: Playnite postavke Očisti web predmemoriju Možda popravi probleme pri spajanju računa. Prikaz sistemske ikone Smanji Playnite u sistemsku traku Smanji Playnite u sistemsku traku pri zatvaranju prozora aplikacije Kada se igra pokrene: Nakon što se igra zatvori: Oblikovanje nadnevka: Ovo će vas odjaviti sa svih povezanih servisa. Restartovanje aplikacije je potrebno za ovo, hoćete li da nastavite dalje? Očisti predmemoriju? Restartovanje Playnitea je potrebno da bi se aplicirala nova tema Uzmi više tema Kreiraj novu temu Nabavite više proširenja Napravi novo proširenje Pomozi nam prevesti Playnite Restartujte Playnite? Vreme igranja se neće brojati ako je "Zatvori" radnja postavljena. Broj redova Broj stupaca Broj redova u detaljnom prikazu Prikaži pozadinu na glavnom zaslonu Ne aplicira se retrospektivno već postojećim igrama bez ponovnog preuzimanja metapodataka. Uvezi vrijeme igranja igara u biblioteci Uvijek Samo za svježe uvezene igre Nikad Uključi podršku za kontroler u desktop načinu Guide dugme otvara mod cijelog zaslona Uvijek koristi glavni zaslon Prikaži nazive igara Prikaži status baterije Prikaži postotak baterije Prikaži sat Skrij pokazivač miša Samo instalirano u brzim filterima Stil tipki Raspored Vodoravno skrolovanje Odaberite jedan od pododjeljaka Nema dostupnih postavki Učitavanje postavki neuspjelo Ove skripte se pokreću za svaku igru u biblioteci igara. Pojedinačne skripte mogu se dodeliti svakoj igri odvojeno tokom izmjene igrinih detalja. Animiraj izmjene pozadine Veličine fonta Automatski Ima drugi naziv Sivi tonovi OčistiVrstu Idealno Zaslon Mod formatiranja teksta Mod predstavljanja teksta Učitaj pozadinske slike unaprijed Ako je uključeno, Playnite će da preuzima pozadine tokom preuzimanja metapodataka, samim time koristiće i više prostora na hard disku i omogućiće da slike budu dostupne i kada niste na mreži. Ako je isključeno, pozadine će se preuzimati samo kada su potrebne, samim time koristiće se i manje prostora, što će možda rezultirati zakašnjenjem prije nego što su slike prikazane i neke slike također neće biti dostupne kada niste na mreži. Automatski zatvori klijent nakon što se izađe iz igre Ne zatvaraj nakon sesije igranja kraće od (u sekundama) Automatski zatvori sljedeće klijente: Automatski zatvori klijente Prikaži upozorenje kada dodjeljuješ prevelike medijske podatke za igru Ništa Popuni Uniforma Uniforma koja se popunjava Lijevo Desno Vrh Dno Greška prilikom uvoza Autentifikacija potrebna Autentifikacija neuspjela Uvoz metapodataka Preuzimanje metapodataka Namestite da se odabrana konfiguracija koristi za bilo koja buduća preuzimanja metapodataka. Ovo takođe može da se promjeni i u postavkama aplikacije. Čarobnjak za uvoz emulatora Uvek možete da dodate dodatne emulatore i/ili igre kasnije koristeći glavni meni (ispod "Biblioteka igara" menija za emulatorske postavke i "Dodaj igre" menija za emulirane igre). Nijedan emulator nije odabran za uvođenje. Nećete moći da automatski uvodite bilo kakve emulirane igre bez prvo konfiguriranja emulatora. Jeste li sigurni da želite da nastavite i izađete iz procesa uvođenja? Skeniraj folder koristeći emulator Odaberi datoteke Pronađi u mapi... Konfiguriraj emulatore… Skeniranje… Pretraživanje {0}… Konfiguriranje pri prvom ulasku u Playnite Ovaj postupak će te voditi kroz automatski uvoz i postavljanje vanjskih biblioteka igara. Playnite može automatski uvesti igre od više vanjskih biblioteka, kao što su Steam ili GOG. Imaj na umu da također možeš ručno dodati bilo koju po volji, ili emuliranu igru s bilo koje platforme kasnije kroz glavni izbornik. Integracija biblioteke igara Sljedeće predstavlja popis provjerenih integracija biblioteka koje Playnite podržava. Molimo odaberite koje želite instalirati. Moguće je instalirati više integracija kasnije kroz izbornik "Dodatci". Konfiguriranje završeno Inicijalno konfiguriranje je sada završeno. Zapamtite da možete uvek da promijenite sve postavke kasnije u "Postavke" meniju. Možete također i da dodati bilo koju drugu igru kasnije tako što kliknete na Playnite logo. Preuzimanje {0} integracije… Preuzimanje popisa preporučenih integracija… Neuspješno preuzimanje popisa preporučenih integracija. Možete pokušati preuzeti kasnije putem izbornika "Dodatci". Konfiguriraj platforme i emulatore Konfiguriraj emulatore Platforme Platforma Emulatori Emulator Dodaj platformu Odaberite ikonu Odaberite naslovnicu Odaberite sliku Odaberite stavku Odaberi pozadinu Odaberi datoteku Odaberi URL Dodaj emulator Podržane platforme Želite li sačuvati izmjene za platformu? Želite li sačuvati izmjene za emulator? Fajl za pokretanje Argumenti Radni direktorij Podržane vrste datoteka Uvezi emulatore… Preuzmi emulatore… Učitaj presete argumenata iz poznatih emulatorskih profila Jeste li sigurni da hoćete da obrišete {0} emulator(a)? Upravo ga(ih) koristi {1} igra(e). Jeste li sigurni da hoćete da obrišete {0} platformu(i)? Upravo je(ih) koristi {1} igra(e) i {2} emulator(a). Pomoć oko postavki Sortiraj prema Smjer poretka Grupiraj po Uzlazno Silazno Ne grupiraj Grupiraj po biblioteci igara Grupiraj po kategoriji Grupiraj po platformi Vrsta prikaza Prikaz Istraživački panel Filterski panel Ikona Naslovnica Pozadina Sortirajući naziv Biblioteka igara Priručnik Naziv Naziv računa Platforma Kategorija Žanr Datum izdavanja Godina objavljivanja Developer Oznaka Izdavač Status instalacije Instalirano Instalirano Nije instalirano Skriveno Favorit Posljednji put igrano Kategorija Opis Lokacija instalacije Naslovnica Linkovi Image, ROM ili ISO putanja Žanr Žanrovi Tvrtka Tvrtke Developer Developeri Izdavač Izdavači Kategorija Kategorije Oznaka Oznake Značajka Značajke Dobna ocjena Dobne ocjene Regija Regije Izvor Izvori Nedavna aktivnost Greška u bazi podataka Neuspjelo otvaranje baze podataka biblioteke igara. Baza podataka nije otvorena. Ne možemo da pristupimo bazi podataka biblioteke igara. Datoteka "{0}" je već korišćena od strane drugog procesa ili je to nepristupačna lokacija. Neuspjelo pravljenje dijagnostičkog paketa. Neuspjelo automatsko uploadovanje dijagnostičkog paketa. Dijagnostičke informacije su uspješno poslate. Neuspjeli uvod igara iz {0}. Ne može da se odradi pretraga igara sa odabranim emulatorskim profilom. Profil ne sadrži nikakve ekstenzije datoteka ili platforme. Playnite se nije uspio pokrenuti. Molimo zatvorite ostale instance i pokušajte ponovno. Neuspjelo apliciranje teme "{0}", profil boje "{1}" {2} Ne može da se otvori link, URL nije u validnom formatu. Neuspjelo pokretanje aplikacije Uredite detalje igara URL slike Dodaj link Dodaj ROM Sačuvaj promjene Dodaj radnju Obriši radnju Ukloni radnju za igru Dodaj igre Skeniraj folder… Detektiraj instalirane igre Istraži… Otvori Playnite Postavke profila Naziv igre ne može biti prazan. Naziv igre ne može biti prazan prije pretrage metapodataka. Nevažeći podaci o igri Napišite validni URL koji počinje sa https:// ili http:// Odaberi URL Neuspjelo preuzimanje metapodataka: {0} Greška pri preuzimanju Očistite filtere Privatni račun Javni račun API ključ Greška prilikom pokretanja Greška teme Počisti sve Instalacija Deinstaliranje Pokretanje Pokrenuto Neispravan URL Ne čini ništa Umanji Povrati prozor Zatvori Promjeni Napredno Nikad Status završenosti Ocene korisnika Ocene kritičara Ocene zajednice Skripte Pluginovi Izvori metapodataka Proširenja ID proširenja Osvježi skripte Sve skripte uspješno osvježene. Nijedna igra nije pronađena koristeći filtere i unos koje ste zadali i pretražili Promjeni na desktop mod Izađite iz Playnitea Biblioteke igara Ažuriraj sve Napravio: Verzija: Modul: Biblioteka igara Sve Ništa Obavijesti Širina Visina Veličina Malo Normalno Veliko Veće Najveće Zadano Odaberi Označi sve Odznači sve Prvo Nasumice Korisnički odabir Učitaj još Transparentno Sažmi Proširi Sažmi sve Proširi sve Ostalo Teme Emulatorski argumenti Dodatni emulatorski argumenti Nadglasaj emulatorske argumente Odaberite igre koje hoćete da uvedete Pretraga metapodataka Dostupna su ažuriranja Promjene od poslednjeg ažuriranja Preuzmi i instaliraj ažuriranje Provjeri ima li ažuriranja Greška pri ažuriranju Nesupjelo provjeravanje ima li nove verzije. Nema nove verzije, već imate najnoviju. Neuspjelo preuzimanje i instaliranje ažuriranja. Ponovo učitaj listu tema Apliciraj odabranu temu Nadgledaj promene datoteka Automatski apliciraj temu kada se izvorna datoteka promjeni Dužina trajanja skripte Sačuvaj kao zadano Dodaj u favorite Ukloni iz favorita Skrij igru Izmjeni… Veličina instalacije Postavi kategoriju… Postavi status završenosti Obriši Igraj Instaliraj Opcije igre Detalji Deinstaliraj Otvori lokaciju instalacije Napravi prečac na desktopu Otvori priručnik Još Savjet: Možete koristiti još napredniji proces za preuzimanje metapodataka ako izmjenjujete jednu igru koristeći "Izmjeni" opciju u meniju. Nije dostupno kada je neka radnja u toku. Opisni tekst je HTML sintaksa-senzitivan Instalacijska veličina je iskazana u bajtovima Brojevi od 0 do 100 ili prazno su za bez bodova. Programiranje, lokalizacija i drugi suradnici bez posebnog redosleda: Prekinite praćenje igre? Vreme igranja Posljednji put igrano {0}h {1}m {0} minuta {0} sekundi Nije igrano Otvaranje desktop moda… Otvaranje moda cijelog zaslona… Učitavanje biblioteke igara… Računanje instalacijske veličine... Neuspjela instalacija skript fajla. Skripta uspješno instalirana. Instaliraj skriptu Greška u skripti Neuspjelo pokretanje funkcije proširenja. Otvori folder metapodataka {0} klijent nije instaliran. {0} klijent je sada otvoren. Molimo prijavite se i onda zatvorite ovu poruku. Čekanje da se korisnik prijavi, molimo zatvorite ovo kad ste gotovi… Lokacija instalacije igre nije pronađena. Pogrešna konfiguracija radnje igre. Rješavanje problema s sinkronizacijom računa Rješavanje problema Preimenuj stavku Dodaj novu stavku Unesite naziv Unesite novi naziv Manje od sat vremena Od 1 do 10 sati Od 10 do 100 sati Od 100 do 500 sati Od 500 do 1000 sati 1000+ Proširenje nije ispravno zapakirano Tema nije ispravno zapakirana Proširenje "{0}" se nije ispravno učitalo Nije moguće učitati proširenje "{0}", trenutna verzija Playnite-a nije podržana Tema "{0}" se nije uspjela ispravno učitati Nije moguće učitati temu "{0}", trenutna verzija Playnite-a nije podržana Proširenje se nije ispravno učitalo Tema se nije ispravno učitala Tema/proširenje koristi nepodržanu API verziju. Instalacija je uspješna. Instaliraj proširenje? Instalacija dodatka "{0}" nije uspjela Neuspjela instalacija proširenja. {0} Instalacija teme nije uspjela. {0} Želite li instalirati novu temu? {0} Od {1} Verzija {2} Želite li ažurirati temu "{0}"? Trenutna verzija: {1} Nova verzija: {2} Izaći ćete iz Playnite-a i uputiti se k navedenoj web stranici koristeći svoj zadani web preglednik. Želite li nastaviti? {0} Upozorenje za performanse Ne prikazuj ponovo Odabrana slika je možda prevelika za optimalne performanse. Instalacijska mapa Uvezi datoteku Pregled Bočna traka Prikaži na bočnoj traci Ponovno postavljanje postavki Za razvojne programere Vanjska proširenja Unesite potpuni put do mape Postignuća Forum Vijesti Stranica u radnji Nedavno igrano Favoriti Najviše igrano Sve Neki filteri su aplicirani. Rezultati pretrage za: Stavka s istim imenom već postoji. Limitiraj odabir na trenutni filter. Izaberi drugu Dodatci… Instalirano Ažuriranja Instaliraj Već instalirano Prihvati Odbij Poveznica Datoteka Zadano Premjesti tipku glavnog izbornika na površinu stranice U zadnjih 7 dana U zadnjih 31 dan U zadnjih 365 dana Prije više od 365 dana Postavi Spremi predložak Smanji nakon pokretanja igre Smanji Playnite nakon pokretanja igre. Isključivanje ove postavke može dovesti do poteškoća s igrama koje ne dobivaju fokus pri pokretanju! Veličina znakovlja Mala veličina znakovlja Odaberi datoteku… Odaberi mapu… Nije pronađena skripta pri pokretanju. Postavke Instalirane teme Mapa sigurnosne kopije ================================================ FILE: source/Playnite/Localization/hu_HU.xaml ================================================  Magyar Nyelv Kilépés Szűrő bekapcsolva Szűrő kikapcsolva További szűrők Szűrők Szűrés Érvénytelen adat Módosítások mentése? Oldalunk címe: www.playnite.link Forráskód a GitHub-on Diagnosztikai csomag létrehozása Diagnosztika küldése Névjegy Készítette: Josef Němec Hozzáadás kategóriához Kategóriákhoz adás Új kategória létrehozása Pipálva - Kategóriához adás Üres - Kategória eltávolítása Négyzet - Ne változzon (több játék állításakor) Nincs kategória Ismeretlen platform Hoppá! Valami hiba történt… Helyrehozhatatlan hiba történt. Ha segíteni szeretnél a hiba elhárításában, röviden foglald össze, mit csináltál az összeomlás előtt és hozz létre egy diagnosztikai csomagot. Ha online vagy, a csomagot elküldöm a Playnite szerverre, elemzésre. Más esetben használhatod a 'Hibajelentés küldése' gombot, hogy új hibajegyet hozz létre a GitHub-on és kézzel készítsd el a hibáról az összefoglalót. Angol nyelven kommunikálj, ha kérhetem. Köszönöm a segítséged! "{0}" bővítmény összeomlott. Javasoljuk, hogy mentsd el a naplófájlt, és jelentsd a problémát a bővítmény készítőjének. Ha ismétlődik a probléma, tiltsd le a bővítményt. "{0}" bővítmény összeomlott. Javasoljuk, hogy jelentsd a problémát a bővítmény készítőjének. Ha ismétlődik a probléma, tiltsd le a bővítményt. Ismeretlen bővítmény vagy téma miatti váratlan összeomlás. Azt javasoljuk, hogy tiltsd le a harmadik féltől származó bővítményeket, hogy kizárhasd a hibásat, és utána jelentsd a fejlesztőjének a problémát. Helyrehozhatatlan hiba történt. Ha segíteni szeretnél a hiba elhárításában, küldj hibajelentést. Köszönöm! Bővítmény tiltása Naplófájl mentése Diagnosztika küldése Hibajelentés küldése Playnite újraindítása Újraindítás csökkentett módban Minden harmadik féltől származó bővítmény letiltása és az alap téma használata. Kilépés a Playnite-ból. Röviden foglald össze, mit csináltál az összeomlás előtt (angolul): Könyvtárkezelő Listából való törlés megerősítése Nem tudom törölni, a játék vagy a telepítője még fut. Nem tudom eltávolítani, a játék még fut. Biztos eltávolítod {0}-t? Biztos, hogy eltávolítasz {0} játékot? Biztosan eltávolítod {0} játékot? A "hozzáadás a kivételekhez" beállítást választva megakadályozhatod, hogy a következő könyvtárfrissítéskor hozzáadja ismét a játékot. Biztosan eltávolítasz {0} játékot? A "hozzáadás a kivételekhez" beállítást választva megakadályozhatod, hogy a következő könyvtárfrissítéskor hozzáadja ismét a játékot. Biztosan eltávolitasz {0} nem használt játékot? Nem található üres mező. Igen (hozzáadás a kivételekhez) Nem mentett változások vannak Játékkönyvtár formátumának frissítése... Adatbázis frissítése sikertelen. Játékkönyvtár frissítése nem sikerült. {0} MB üres hely kell hozzá. Játék hiba Nem lehet elindítani a játékot. '{0}' nem található az adatbázisban. Nem lehet elindítani a játékot: {0} Művelet nem elvégezhető: {0} Játék helye nem megnyitható: {0} Nem tudtam megállapítani a játék méretét: {0} Hiba a játék méret megállapítása közben {0} hiba történt a játékok méretének megállapítása közben Nem sikerült {0} parancsikonját létrehozni Nem sikerült megnyitni {0} kézikönyvét. {0} játék nem telepíthető {0} játék nem eltávolítható Nem találtam indítási műveletet. Amikor emulátor műveleteket használsz, győződj meg róla, hogy a platform definíciók azonosak a játéknál és az emulátor beállításaiban. A telepítés funkció nem elérhető. Az ehhez a játékhoz tartozó könyvtár beépülők nincsenek bekapcsolva / engedélyezve. Hivatalos metaadat letöltés nem elérhető. Nincs játék kiválasztva. A játék indítószkript végrehajtása sikertelen. Hiba a programszkript futtatásában. Globális indítószkript végrehajtása sikertelen. Hiba az emulátorszkript futtatásában. Indítószkript végrehajtása sikertelen. PowerShell 3.0 vagy újabb nincs telepítve. Nem lehet hogy elindítani a játékot. Bekapcsolva Letiltva Eltávolítás Nem használtak törlése Átnevez Másolás Hozzáadás Alapértelmezett ikon Alapértelmezett borítókép Alapértelmezett háttér Kész Tovább Vissza OK VISSZA TÖRÖL Szűrők törlése Mégsem Inkább mégsem Importálás Név Szerző Modul Sorozat Verzió Utoljára játszott Legtöbbet játszott Indítások száma Telepített méret Mappa Jegyzetek Hozzáadva Hozzáadás dátuma Módosítva Módosítás dátuma Weboldal Elérési út OK Mentés Bezárás Mégse Megerősít Visszaállít Igen Nem Üdvözlünk Helyi felhasználó Általános Média Linkek Telepítés Műveletek Letöltés... Médiaadatok letöltése... Betöltés… Típus Profil Profilok Törlés Letöltés Keresés Felbontás: Bármely Nagyítás Listanézet Borítók Rácsnézet Részletes nézet Egyéni URL Külön köszönet Licensz Közreműködők Kilépés a Playnite-ból... Ma Tegnap Hétfő Kedd Szerda Csütörtök Péntek Szombat Vasárnap Múlt Héten Múlt Hónapban Múlt Évben Több, mint egy éve 0 és 100MB között 100MB és 1GB között 1 és 5GB között 5 és 10GB között 10 és 20GB között 20 és 40GB között 40 és 100GB között 100GB és nagyobb Importálás sikeres Összes játék Játék azonosító Adatbázis azonosító Sémák Oszlop Oszlopok Sor Sorok Nem sikerült ikont kinyerni az indítási műveletből. Nincs futtatható fájl kiválasztva. Csak a hiányzó metaadatok letöltése Ha bekapcsolod, metaadat letöltésekor átugorja a már meglévő adatokkal teli mezőket. Játék választás Kérlek válaszd ki mely játékok metaadatait szeretnéd frissíteni: Az adatbázis összes játéka A jelenleg szűrt összes játék Csak a kiválasztott játékok Nincs metaadat mező kiválasztva Nincs letöltendő metaadat mező kiválasztva. Válassz ki legalább egyet, és jelölj ki egy metaadat szolgáltatót hozzá. Hivatalos áruházból IGDB Kérlek válaszd ki mely mezőket frissítse a Playnite automatikusan, és milyen forrásból. Támogasd az igdb.com-ot azzal, hogy az adatbázisukhoz hozzájárulsz saját anyaggal. Metaadatok importálása... Telepített játékok importálása... {0} játék importálása… {0} Emulált játékok importálása … Könyvtár frissítések letöltése... Játékok méretének megállapłtása… Importált játékok méretének megállapítása… Könyvtár frissítve Erőforrások felszabadítása... Beállítások Beállítások… Platformok és emulátorok Emulátorok beállítása... Könyvtárkezelő... Eszközök Metaadatok importálása... Szoftvereszközök… Integrációk beállításai… Külső kliens megnyitása Harmadik féltől származó alkalmazások Játékkönyvtár frissítése Könyvtár frissítés leállítása Emulátorok mappáinak frissítése Játék hozzáadása Manuálisan... Automatikus beolvasással... Emulált játék... Microsoft Store alkalmazásból… Névjegy Visszajelzés küldése Teljesképernyős mód Linkek Súgó Támogass a Patreon-on Támogass a Ko-Fi-n Kézikönyv SDK Dokumentáció Újraindítás Leállítás Készenléti állapot Hibernálás Zárolás Kijelentkezés Véletlenszerű játék kiválasztása Megjelenítendő mezők a részletes nézetnél: Borítótávolság Kép keret megjelenítése Keret szélessége Helykitöltő ikon Helykitöltő borítókép Helykitöltő háttérkép Függőleges távolság a játékinfók felett Rácsnézet részletek helye Részletes nézet játéklista helye Elválasztó a panelek közé Borítókép magassága Játéklista ikon magassága A program betűtípusa Fix szélességű betű Szűrőpanel helye Platformpanel helye Borítókép minőség Célzott képarány Az alábbi beállítások a teljesképernyős mód bélyegkép megjelenítését is változtatják! Nyújtás módja DVD borító Epic Games Store-féle GOG Galaxy-féle IGDB-féle Négyzetes Steam széles borítók Steam magas borítók Twitch-féle * Újraindítás szükséges az alkalmazáshoz Beállítások Általános Felső panel Megjelenés Játék részletek Kinézet Speciális beállítások Teljesképernyős mód Bevitel Teljesítmény Metaadatok Frissítés Keresés Biztonsági mentés Könyvtáradatok mentése Könyvtáradatok visszaállítása Könyvtár változások automatikus követése Hibás adatbázis útvonal, helyes fájl útvonal kell legyen megadva. Fióknév nem lehet üres Metaadatok importálása a játékok hozzáadása után Playnite indítása kis méretben Playnite indítása a Windows indításakor Indítás tálcaikonként Nem sikerült beállítani a Windows indításakori programindítást. Indítás teljesképernyős módban Nem egyidejű kép betöltés Simább játéklista görgetés, de lassabb képbetöltés. Hiányzó borítókép helyett a játék neve látszódjon Játéknevek a borítók alatt Nem telepített játékok sötétítése Játékikonok kijelzése a részletes nézetben Csoportok leírásánál az elemek számának megjelenítése Csak a kijelölt mezőket mutassa a szűrőknél és platformpanelnél Hardveres gyorsítás tiltása Kapcsold be, ha szaggat, vagy más hibát produkál a program Elrejtett játékok megjelenítése a gyorsindító listákban Az ugráslistára és tálcamenüre van hatással. Gyorsindító lista elemeinek száma Használja a játék hátterét az ablak hátterének Háttérkép életlenítése Jó minőségű életlenítés Háttérkép sötétítése Háttérkép rácsnézetben is Téma Téma szín Teljesképernyős téma Teljesképernyős téma profil Adatbázis helye Bejelentkezési állapot Playnite beállítások Gyorsítótár ürítése Megoldhatja a fiókok összekapcsolási problémáit. Rendszertálca-ikon megjelenítése Playnite rendszertálcára kicsinyítése Playnite rendszertálcára kicsinyítése, amikor bezárod a programot Játék indítása után: Játék bezárása után: Időformátum a játékidő meghatározására Dátum formátum: Ez kiléptet az összes szolgáltatásból. Ehhez újra kell indítani az alkalmazást, folytatod? Gyorsítótár ürítése? A Playnite újraindítása szükséges a téma alkalmazásához Még több téma Új téma? Bővítmények letöltése Új bővítmény létrehozása? Segíts a Playnite fordításában A beállítások alkamazásához újra kell indítanod a Playnite-ot. Újraindítsam most? Az újraindítás minden futó feladatot (letöltést) megszakít. Újraindítod a Playnite-ot? A Playnite nem helyezi át automatikusan a könyvtárak fájljait, azt neked kell átmásolni/átmozgatni, mielőtt itt módosítod a helyüket. Ha nincs könyvtár a célhelyen, akkor újat fog létrehozni. Az új adatbázis helye csak újraindítás után lesz alkalmazva. A játékidő nem lesz rögzítve ha a "Bezárás" van kiválasztva. Sorok száma Oszlopok száma Sorok száma részletes nézetben Háttérkép megjelenítése a főképernyőn Visszamenőleg nem vonatkozik a meglévő játékokra a metaadatok letöltése nélkül. Játékidő importálása az alábbi játékokhoz: Azt állítja be, hogy a Playnite mikor importálja be a könyvtárbeépülők által megadott játékidőket az adatbázisába. Ehhez a játékokhoz tartozó könyvtárbeépülők támogatása szükséges. Mindig: mindig beimportálja az új és meglévő játékok játékidejét a Playnite adatbázisba. Csak új importált játékokat: csak az újonnan importált játékok idejét adja hozzá. Sosem: semmilyen esetben sem importál játékidőt. Mindig Csak új importált játékokat Sosem Játékvezérlő támogatása ablakos módban Guide/PS gomb teljesképernyős módra vált Automatikus metaadat beállítások új játékok hozzáadásánál. Megjelenítő kiválasztása Mindig az elsődleges képernyő használata. Játékcímek megjelenítése Akkumulátor szintjének megjelenítése Százalékos töltöttség megjelenítése Óra megjelenítése Egérkurzor elrejtése Csak telepítettek a gyorsszűrésnél Kontroller gombfeliratok Kinézet Vízszintes görgetés Válassz az almenük közül Nincsenek elérhető beállítások Nem sikerült a beállítások betöltése Ezek a szkriptek minden játéknál lefutnak. Egyéni szkripteket a játékoknál lehet a szerkesztés menüben definiálni. Háttérkép váltások animálása Betűméretek Automatikus Élsimított Szürkeárnyalatos ClearType Ideális Kijelzőhöz igazított Szöveg formázás Szöveg megjelenítés A szövegformázási és megjelenítési módok jelenleg nincsenek hatással a játékok leírásaira. Háttérképek előtöltése Ha engedélyezve van, az összes háttérkép a metaadatokkal együtt töltődik le, ezáltal több tárhelyet használ, de a képek elérhetőek lesznek offline is. Ha ki van kapcsolva, a háttérképek első megtekintésnél töltődnek le, ezáltal kevesebb tárhelyet használnak, de késleltetve fognak megjelenni, és nem lesznek elérhető offline. Játék bezárásakor automatikusan zárja be a klienseket is Kliens bezárásának késleltetése (másodpercben) Ne zárja be, ha a játékidő rövidebb volt, mint (másodpercben) Az alábbi kliensek automatikus bezárása: Kliensek auto. bezárása Kivétellista importálása Figyelmeztessen túl nagy méretű médiafájlok importálásakor Parancs egyéni mappa nyitáshoz Korhatár-besorolás rendezési módja Frissítésnél újra olvassa-e be a játékok méretét is Frissíti a játékok méretét, ha az utolsó beolvasás óta változtak Nincs Kitöltés Arányos Arányos kitöltött Balra Jobbra Fent Lent Hiba importálásnál Hitelesítés szükséges Nem sikerült bejelentkezni Alternatív webes megjelenítési mód Akkor használd, ha problémás a weboldalak megjelenítése, pl. felugró párbeszédablakok nem látszanak. Hosszú játékleírások részleges betöltése Játékkiválasztásnál a hosszú leírások akadáshoz vezethetnek. Ha bekapcsolod, csak a leírás eleje fog betöltődni egy lehetőséggel, hogy betöltse az egészet. Metaadatok importálása Metaadatok letöltése Beállítja a konfigurációt alapértelmezettnek az elkövetkező metaadat letöltések számára. A programbeállításoknál is megváltoztatható. Emulátor Importálás varázsló Ez a varázsló segít a játékkonzol emulátorok letöltésének és importálásának folyamatában, valamint az emulált játékromok hozzáadásában. Ne felejtsd, hogy bármikor adhatsz hozzá emulátort és emulált játékot kézzel a főmenüből (a "Könyvtár" menüpontból eléred az emulátor beállításokat és a "Játék hozzáadása" menüpontból az emulált játékokat). Az alábbi listában található emulátorokat a Playnite automatikusan felismeri és beállítja. Le tudod tölteni és telepíteni őket a sor végén található linkre kattintva. Ha feltelepítetted az emulátorokat (kézzel), lépj a következő képernyőre hogy beimportálhasd a Playnite-ba őket. A gépeden telepített emulátorokat tudod a "Automatikus felismerés mappából..." gombbal importálni. A Playnite végigkeresi a mappákat az általa ismert emulátorok után és felajánlja az importálás lehetőségét. Több mappából is importálhatsz a már említett gombbal; az újonnan felismert emulátorok a lista aljára kerülnek. A játékokat a "Játék hozzáadása emulátorhoz" gombbal tudod beimportálni. A megfelelő emulátor kiválasztásával a Playnite tudni fogja, hogy mely fájltípusok legyenek beolvasva és importálva. Több mappából is importálhatsz a már említett gombbal; az újonnan felismert játékok a lista aljára kerülnek. Nincs kiválasztva emulátor az importáláshoz. Nem tudsz automatikusan importálni egy emulált játékot sem anélkül, hogy először beállítanál egy emulátort. Biztosan folytatod és kilépsz az importálásból? Nincs egy emulátor sem beállítva a Playnite-ban. Nem tudsz importálni játékokat, ha nem állítod be az emulátorát és választod ki a fájltípusát. Szeretnél most új emulátort beállítani? Mappa beolvasása az emulátorral Fájlok kiválasztása Automatikus észlelés mappából... Emulátorok beállítása… Beolvasás... {0} beolvasása… Kezdeti lépések Ez a varázsló segít beállítani az automatikus játék importálást, és a külső könyvtárak beállítását. A Playnite automatikusan beimportálja a játékszolgáltatóktól (pl. Steam, GOG, Epic games) a játékokat, és frissíti az esetleges változásokat indításkor. Ne felejtsd, hogy bármikor adhatsz hozzá játékot bármelyik platformhoz kézzel a főmenüből, ha rákattintasz a 'Playnite' gombra bal fent. Játékkönyvtár integráció Automatikusan importálja a játékokat a következő szolgáltatóktól. Bármilyen későbbi változások automatikusan frissülnek a Playnite indulásakor, vagy ha kézzel elindítod a frissítést. A most kiválasztott szolgáltatók befolyásolják a kezdeti és későbbi importálást. Készen vagyunk a beállításokkal A kezdeti lépésekkel készen vagyunk. Ne feledd, hogy az összes beállítást eléred a 'Beállítások' menüpontból. Máshonnan származó játékot hozzáadhatsz a 'Playnite' logós főmenüből (bal felső sarokban). Nem sikerült letölteni egy vagy több bővítményt. Megpróbálhatod újra letölteni őket a bővítmények menüből, miután lefutott a varázsló. {0} integráció letöltése… Az ajánlott integrációk listájának letöltése… Nem sikerült az ajánlott integrációkat letölteni. Később újrapróbálhatod a Bővítmények menüből. Platformok és emulátorok beállítása Emulátorok beállításai Platformok Platform Emulátorok Emulátor Platform hozzáadása Ikon kiválasztása Borító kiválasztása Kép kiválasztása Elem kiválasztása Háttér kiválaszása Fájl kiválasztása URL kiválasztása Emulátor hozzáadása Támogatott platform(ok) Mented a platform változásokat? Mented a nem mentett változásokat? Program Paraméterek Munkamappa Támogatott fájltípusok Emulátorok importálása... Emulátorok letöltése... Paraméterek betöltése az ismert emulátor profilból Biztosan eltávolítod {0} emulátort? Jelenleg {1} játék használja. Biztosan eltávolítod {0} platformot? Jelenleg {1} játék és {2} emulátor használja. Beállítások súgó Sorbarendezés Sorbarendezés Csoportosítás Növekvő Csökkenő Nincs csoportosítás Könyvtár szerinti csoportosítás Kategória szerinti csoportosítás Platform szerinti csoportosítás Nézettípus Nézet Platformpanel Szűrőpanel Ikon Könyvtár ikon Borítókép Háttérkép Rendezési név Könyvtár Egyéni Név Meghajtó betűjele Fiók neve Platform Kategóriák Műfajok Megjelenési dátum Kiadás éve Fejlesztők Címkék Kiadó Telepítési állapot Szűrők egyeztetése Ha engedélyezve van, csak az összes szűrővel szűrt játék fog látszódni. Ha kikapcsolod, bármelyik szűrővel szűrt játék fog látszódni. Telepítve Telepített Nincs telepítve Rejtett Kedvencek HDR engedélyezése Bekapcsolása esetén az elsődleges kijelzőre engedélyezi a HDR-t a játék indítása előtt. A HDR nem támogatott az elsődleges kijelződön. Utoljára játszva Kategória Leírás Telepítési hely Borítókép Linkek ROM/ISO mappája Műfaj Műfajok Kiadó Kiadók Fejlesztő Fejlesztők Kiadó Kiadók Kategória Kategóriák Címke Címkék Jellemző Jellemzők Korhatár Korhatárok Régió Régiók Forrás Források Aktivitás Adatbázis hiba Hiba a könyvtár adatbázis megnyitásakor. Az adatbázist nem lehet megnyitni. Nem lehet hozzáférni a könyvtár adatbázishoz. A fájl "{0}" egy másik művelet által van használatban, vagy elérhetetlen helyen van. Hiba történt a diagnosztika csomag létrehozásakor. Nem sikerült a diagnosztikai csomag automatikus frissítése. A diagnosztika csomag sikeresen elküldve. A diagnosztika csomag sikeresen létrehozva és feltöltve. Kérlek csatold hozzá a a következő ID-t a hibajegyhez: Nem sikerült a {0} játékok importálása. Nem sikerült a {0} emulált játékok importálása. Nem lehet keresni játékot a kiválasztott emulátor profil alapján. Nincs beállítva platform vagy kiterjesztés. A Playnite már fut. Zárd be az összes futó folyamatát, majd próbáld meg újra. "{0}" téma és "{1}" színbeállítás nem sikerült {2} Nem sikerült a linket megnyitni, az URL nem megfelelő formátumú. Nem sikerült az alkalmazás elindítása. Nem sikerült a webes elem elindítása, a Playnite nem indult el. Több információt a https://playnite.link/cefstartup oldalon találsz. Nem sikerült az emulátorok importálása, az emulátorok definíciói hiányoznak vagy sérültek. Menü művelet futtatása nem sikerült. játék részletek szerkesztése Kép URL Link hozzáadása Játék ROM hozzáadása Változások mentése Szerkesztett játék(ok) módosított mezőinek mentése. Müvelet hozzáadása Művelet eltávolítása Indítási művelet eltávolítása Játékok hozzáadása Mappa beolvasása... Telepített játékok importálása Tallózás… Playnite megnyitása Profil beállítások A játék neve nem lehet üres. A játékművelet rögzítő mappája nem lehet kitöltetlen. Metaadatok keresése előtt a játék neve nem lehet üres. Hibás játékadat Adj meg helyes URL-t, ami http:// -vel VAGY https:// -sel kezdődik URL kiválasztása Hiba a metaadatok letöltésekor: {0} Hiba a letöltésben Szűrök törlése Nem publikus fiók Publikus fiók API Kulcs Indítási hiba Téma hiba Az összes törlése Telepítés folyamatban Eltávolítás Indítás Fut Helytelen URL Ne tegyen semmit Tálcára kicsinyítés Ablak visszaállítása Ablak visszaállítása csak akkor, ha a felhasználói felületről lett indítva Bezárás Változtatás Speciális Soha Befejezettség Befejezettség Felhasználók értékelése Kritikusok értékelése Közösség értékelése Játékszkriptek Alkalmazásszkriptek Szkriptek Beépülők Metaadat források Bővítmények Bővítményazonosító Szkriptek újratöltése Interaktív SDK PowerShell Minden szkript újratöltve. Nem találtam játékot a megadott adatok alapján Nincs találat Ablakos módra váltás Kilépés a Playnite-ból Könytárak Az összes frissítése Készítő: Verzió: Frissítve: Modul: Könyvtár Statisztikák Az összes játék Nincs Értesítések Szélesség Magasság Méret Kicsi Normál Nagy Nagyobb Legnagyobb Alapértelmezés Kiválaszt Az összes kiválasztása Kijelölés törlése az elsőt véletlenszerűen kérdezzen rá Továbbiak betöltése Átlátszóság Összecsukás Kibontás Az összes összecsukása Az összes kibontása Egyéb Témák Emulátor paraméterek Beépített paraméterek Egyéni paraméterek További emulátor paraméterek Emulátor paraméterek felülírása Indítási művelet Metaadatok kiválasztása importáláshoz Importálandó játékok kiválasztása Metaadatok keresése Frissítés elérhető Változások az utolsó frissítés óta Frissítés letöltése és telepítése Frissítések keresése Frissítési hiba Nem sikerült ellenőrizni a frissítéseket. Az aktuális verzió naprakész. Frissítés letöltése és telepítése sikertelen. Egy háttérfolyamat jelenleg is fut. Megszakítod, és folytatod a frissítés telepítését? Egy háttérfolyamat jelenleg is fut. Megszakítod, és bezárod a Playnite-ot? Egy háttérfolyamat jelenleg is fut. A módok közötti váltás befejezi a háttérfolyamatot. Szeretnéd folytatni? Playnite frissítés elérhető Témalista újratöltése Kiválasztott téma használata Témafrissítések figyelése Frissített téma automatikus alkalmazása Szkript futtatás Játék indítása előtt futtatott szkript Játékból kilépés után futtatott szkript Játék indítása után futtatott szkript Alkalmazás indításakor futtatandó Alkalmazás leállításakor futtatandó Játék indítása előtti szkript Játék futása alatti szkript Játékból kilépés utáni szkript Globális szkript futtatása Globális Szűrt Jelenlegi Új Szkript tesztelése Csak a kijelöltek látszódjanak. Mentés alapértelmezettként Kedvencekhez ad Törlés a kedvencekből Játék elrejtése Törlés a rejtettekből HDR bekapcsolása HDR kikapcsolása Szerkesztés… Játékméret kiszámolása Játékméret kiszámolása (minden játéknál) Játékméret kiszámolása (csak ahol nincs adat) Játék mérete Kategóriához adás… Befejezettség beállítása Törlés Indítás Telepítés Játékbeállítások Részletek Eltávolítás Tartalmazó mappa megnyitása Parancsikon az asztalra Kézikönyv megnyitása Tovább Könyvtár beépülő kezeli A játékindítás az ehhez a játékhoz tartozó könyvtár beépülőből lesz kezelve. '{0}' játékról nem található használható információ a megadott oldalon. Protip: Precízebb metaadat importálást lehet használni a játék "Szerkesztés" menüpontját választva. Nem elérhető néhány folyamat alatt. A játékleírásban használhatsz HTML kódot Másodperc alapú játékidő követés és rögzítés Játékméret, byte-ban. A megjelenési dátumnak az 'év-hónap-nap' formátumot kell követnie (a hónap és nap értékek opcionálisak). Pontérték 0-100-ig, vagy üres mező, ha nincs értékelve. A Playnite fejlődésének támogatói és Ko-Fi adományozói: Kód, fordítás és egyéb közreműködők: Játékfigyelés megszüntetése? A játék telepítés figyelő jelenleg is fut. Meg akarod szakítani a folyamatot és visszatérsz a játék előző állapotához? A játékfigyelő jelenleg is fut. Meg akarod szakítani a folyamatot és visszaállitani a játék előző állapotát? Játékidő Utoljára játszott {0} nap {1} óra {2} perc {0}:{1} {0} perc {0} másodperc Sosem játszott Ablakos módba lépés… Teljesképernyős mód indítása… Játékkönyvtár megnyitása… Játékméret kiszámolása… {0} méretének kiszámolása… Szkriptfájl telepítése nem sikerült. Szkriptfájl sikeresen telepítve. Szkript telepítése Szkript hiba A bővítmény hibát jelzett. Metaadat mappa megnyitása Kiszámol Automatikusan kiszámolja a játék méretét a ROM-ok alapján, ha be lett állítva könyvtár a játéknak {0} kliens nincs telepítve. A megnyíló {0} kliens ablakában jelentkezz be, és zárd be ezt az üzenet. Bejelentkezésre vár, zárd be ezt, ha készen vagy… A játék telepítési mappája nem található. Helytelen a művelet konfigurációja Fiók szinkronizálási hibaelhárító Hibaelhárítás Átnevezés Új hozzáadása Írj be nevet Írj be új nevet Kevesebb, mint egy óra 1-től 10 óráig 10-től 100 óráig 100-tól 500 óráig 500-tól 1000 óráig 1000 óra felett A Playnite-ot újra kell indítani a telepítés befejezéséhez. Szeretnéd újraindítani most? A bővítmény helytelenül csomagolt. A téma helytelenül csomagolt. {0} bővítményt nem sikerült betölteni. {0} bővítmény betöltése nem sikerült, a Playnite verziója nem támogatott. {0} témát nem sikerült betölteni. {0} téma betöltése nem sikerült, a Playnite verziója nem támogatott. A bővítményt nem sikerült betölteni. A témát nem sikerült betölteni. A Téma/Bővítmény nem támogatott API-t használ. Sikeres telepítés. Telepítsem a bővítményt? Általános "{0}" bővítmény telepítése nem sikerült. {0} bővítmény telepítése nem sikerült. Telepíted az új bővítményt? {0} Készítő: {1} Verziószám: {2} Frissíted {0} bővítményt? Jelenlegi: {1}, új verziószám: {2}. {0} téma telepítése nem sikerült. Telepíted az új témát? {0} Készítő: {1} Verziószám: {2} Frissíted {0} témát? Jelenlegi: {1}, új verziószám: {2}. Elhagyod a Playnite-ot és megnyitod a {0} weboldalt a böngésződdel. Folytatod? A választott kép(ek) túl nagy méretűek az optimális működéshez. A nagy képek miatt több memória lesz használatban és lassabb lesz a felület reakcióideje. Maximális ajánlott méretek: Ikonok: {0} megapixel Borítók: {1} megapixel Hátterek: {2} megapixel Csökkent teljesítmény Ne mutasd többször {0} fájlkiterjesztés nem támogatott. Nem támogatott fájl kiterjesztés A kiválasztott kép túl nagy méretű az optimális működéshez. Biztos eltávolítod a kijelölt témát? A következő indításnál fog törlődni. Beépitett témát nem lehet eltávolítani. Ez a téma nem támogatja ezt a Playnite verziót. Biztos eltávolítod a kijelölt bővítményt? A következő indításnál fog törlődni. Beépitett bővítményt nem lehet eltávolítani. Ez a bővítmény nem támogatja ezt a Playnite verziót. Telepítési hely Adatmappa Diagnosztikai csomag létrehozása… Diagnosztikai csomag feltöltése… Fájl importálás… Mi ez? Biztosan ezt szeretnéd tenni? Teljes játékidő Átlagos játékidő Leghosszabb játékidő Teljes játékméret Áttekintés Oldalsáv Megjelenítés az oldalsávban Beállítások visszaállítása Minden beállítás visszaáll alapállapotba, kivéve:: - adatbázis helye - kivételek listája exportáláshoz - bővítmény beállítások, többek között a játékkönyvtár integrációk Újra kell indítani az alkalmazást a véglegesítéshez. Visszaállítod a beállításokat? Fejlesztőknek Külső bővítmények A mappa elérési útja Eredmények Fórum Újdonságok Áruházbeli oldala A kezdeti lépések nem lettek befejezve. A Playnite újraindul Ablakos módban, hogy befejezhesd. Nemrég játszott Kedvencek Legtöbbet játszott Az összes játék Szűrt lista. Több szűrő van beállítva. Találatok: Már van ilyen név. Jeleneg szűrtekből válasszon Még egy véletlenszerűen Bővítmények… Telepített bővítmények Bővítmény beállítások Bővítmények letöltése Frissítések {0} frissítés A telepített bővítmények és témák kezelése és a beállításaik az új "Bővítmények" menübe lettek áthelyezve. Az összes játékkönyvtár integrációs bővítményt lehet itt állítani. Ha telepíteni vagy eltávolítani szeretnéd őket, használd a főmenü "bővítmények" menüpontját. Asztali nézet témák Teljes képernyős témák Keresés… Ez a bővítmény nem támogatja ezt a Playnite verziót. Bővítmény telepítőcsomag letöltése sikertelen. Bővítmény telepítőcsomag lista letöltése sikertelen. A Playnite újraindítása szükséges a változtatások alkalmazásához. A bővítmény telepítése ütemezve. Telepítés Újratelepítés Eltávolítás Már telepített Nem találtam frissítést a bővítményekhez. Bővítmények frissítése Változásnapló nem elérhető A telepítés ütemezve Sikertelen letöltés Licensz elutasítva {0} letöltése… Bővítmények frissítésének keresése… Frissítések keresése… Egy vagy több frissítés elérhető. Válaszd ki a frissítendőket Bővítmény fejlesztői változat {0} licenszfeltételek Elfogad Elutasít Játékkönyvtár integrációkat is beleértve Művelet kiválasztása Rögzítés módja Rögzítés elérési útja Játékidő indítás késleltetése Játékidő követés gyakorisága Link Fájl Emulátor Szkript Alapértelmezés Folyamat Mappa Eredeti folyamat Folyamat neve Nyomonkövetési üzenetek naplózása A módosítások felülírják az összes kijelölt játék szerkesztett adatát! Nincs Arányos Csak az elemek Csak kezdés és befejezés Görgetési érzékenység Sima görgetés Görgetés sebessége Elem eltávolítása? Biztos, hogy eltávolítod ezt az elemet? Gombok a felső panelen: Nézet beállítások Csoportosítási beállítások Sorba rendezési beállítások Szűrősémák Beépülők helye Elválasztó szélessége Főmenü gomb az oldalsávon Platformpanel Véletlenszerű játék Véletlenszerű játék Véletlenszerű játék kiválasztása a listából Sorba rendezési és csoportosítási beállítások mentése Mutassa gyorsszűrőként teljes képernyős módban Az elmúlt 7 napban Az elmúlt 31 napban Az elmúlt évben Több, mint egy éve Beállítás Szűrőséma mentése Játék indítása után tálcára kicsinyítés Játék indítása után a Playnite tálcára kicsinyítése. Ha kikapcsolod, bizonyos játékoknál hibát okozhat, hogy nem kapnak indításkor beviteli elsőbbséget. Betűméret Kis betűméret Játékvezérlő API támogatás XInput támogatás Ha ki van kapcsolva, a Playnite nem fogja használni a játékvezérlőket. Akkor kapcsold ki, ha olyan programokat használsz, ami játékvezérlő gombnyomásokat billentyűleütésekké és kurzormozgássá alakítja, és duplázza az inputot. Elemek mutatása a főmenüben: X/A gombok funkciójának felcserélése A játékindító X és a játék részletei A gomb funkcionalitását felcseréli. OK/mégsem gombok funkciójának felcserélése Megfordítja az A/B gombok funkcionailtását, így az alapértelmezett A: OK és B: vissza épp ellenkezőleg fog működni. Csak az elsődleges játékvezérlő Csak az első játékvezérlővel tudod irányítani a Playnite-ot. Guide/PS gombbal előtérbe kerül a Playnite Felület hangerő Háttérzene hangerő Háttérben némít Hangkezelő indítása nem sikerült. Kimeneti API Hangkimeneti illesztő. Akkor változtasd meg, ha problémák vannak a hanggal. Általános Kép Hang Kinézet Menük Bevitel {0} indul… {0} fut… Nagybetűs Szóköz Képskálázás módja Alternatív Kiegyensúlyozott Minőségi Minőségi: Legjobb képminőség, lassú, nagy memóriahasználat. Kiegyensúlyozott: Jó képminőség, gyors, alacsony memóriahasználat. Alternatív: Jobb képminőség, közepes sebesség, alacsony memóriahasználat. Fájl kiválasztása… Mappa kiválasztása… Indítószkript Érdemes tudnod, hogy a bővítmények és a témák nagyban befolyásolhatják a Playnite teljesítményét, stabilitását és biztonságosságát. Ha problémákat észlelsz egy téma vagy bővítmény telepítése után, érdemes azt lekapcsolni/eltávolítani, hátha az volt az okozója. Indításkor választható Indításkor választható Beépített profilok Beépített profil Egyéni profilok Egyéni profil A beépített szkript kezeli Emulátor adatok Platform adatok Régió adatok Emulátor indítása előtt futtatott szkript Emulátor indítása után futtatott szkript Emulátorból kilépés után futtatott szkript Emulátorprogram nem található. Emulátor beállítások nem találhatóak. Emulátor indító szkript nem található. Külön játékokként kezelje Egy játékként kezelje Platform beállítása Régió beállítása Mappa beolvasása Beolvasás konfigurációk Keresési minták kihagyása az ellenőrző keresésből Egyéni keresőmintákkal egyező fájlok nem lesznek ellenőrizve, csak fájlnév alapján. Több infóért nézd meg a súgó emulátor oldalát. Beolvasás az emulátorral Add meg az új konfigurációs fájl nevét. Emulátor vagy a profilja nincs beállítva. Nincs megadva beolvasandó mappa, vagy nem létezik. Hibásak a beolvasási beállítások. Olvassa be automatikusan a tömeges beolvasásnál Nem sikerült a mappa beolvasása az emulátorhoz. Emulált játékok mappájának (mappáinak) beolvasása sikertelen. Importáltak elrejtése Importálandó profilok: Automatikus beolvasás beállítások Mentse, mint automatikus beolvasás beállítást Mentse el a beállítást későbbi használatra könyvtárfrissítéskor. A mentett beállítások később kezelhetők az "Emulátorok beállítása" menüpontból. Relatív útvonalak használata importáláskor Ha lehet, akkor a Playnite-ot vagy emulátort tartalmazó mappák relatív útvonalait használja a teljes útvonal helyett. Almappák átvizsgálása Tömörített fájlok tartalmát is vizsgálja Összetartozó fájlok egyesítése Összefésüli az eggyé tartozó játékfájlokat, például iso-kat, egy játékcím alá. Beolvasó hozzáadása Mentett beolvasó hozzáadása Beolvasás indítása Beolvasási beállításokat állíthatsz be az emulátorokhoz, hogy megadott mappákon futtathasd. Győződj meg importálás előtt, hogy az emulátor helyesen be van állítva (Könyvtár / Emulátorok beállítása menüpont) Újonnan hozzáadott játékok automatikus állapota Elsőnek játszott játékok automatikus állapota PowerShell szkript futtatási hiba. Ha Windows 7 alól tolod, próbáld meg (újra)telepíteni a PowerShell 5.1-et. Már van ilyen névvel szűrőséma. Felülírod az új beállításokkal? Töltse ki automatikusan a rendezési nevet a tömegesen hozzáadott vagy szerkesztett játékoknál Ha könyvtárfrissítéssel, emulátor mappa beolvasással, vagy mappabeolvasással adsz hozzá játékokat, automatikusan töltse ki a "Rendezési név" mezőt egy könnyebben sorba rendezhető játéknévvel. Például a "The Witcher 3" a "Witcher 03"-as rendezési nevet fogja kapni. Nem fog az eredetitől különböző nevet adni, és csak az üres rendezési neveket tölti ki. Ezeket a szavakat automatikusan eltávolítja a rendezési név elejéről: Így lehet egyszerűen eltávolítani a rendezési nevek elejéről az extra szavakat. Alapértelmezésben a "The", az "An" és az "A" kerül eltávolításra. Rendezési név mező automatikus kitöltése Sorbarendezés Rendezési név értékek kitöltése… Nahimic észlelve a gépeden. Ez komoly megjelenítésbeli problémákkal járhat a Playnite és más programok esetében. Azt ajánljuk, hogy tiltsd le vagy távolítsd el a problémák megelőzésére. Több információt (angolul) az alábbi linken találsz: https://playnite.link/nahimicsucks A Playnite rendszergazdai jogokkal fut. Nem ajánlott így futtatni, mert az összes bövítménynek és innen indított játéknak is teljes hozzáférése lesz mindenhez. Javasoljuk, hogy rendszergazdai jogok nélkül futtasd, ugyanis a Playnite nem ígényli ezt a működéséhez. Több infót a https://playnite.link/adminfaq linken találsz. Figyelmeztessen, ha rendszergazdaként futtatnám a Playnite-ot. A háttértáron elfoglalt helyet használja a játék méretének megállapításához Ha bekapcsolod, lassabb lesz a beolvasás, mert a fájlok által elfoglalt tárhelyet számolja ki. Ha kikapcsolod, akkor a fájlok méretét számolja és adja össze. Az alábbi bővítmények lehetségesen problémásnak voltak jelentve, instabilitás, teljesítménycsökkenés vagy biztonsági problémák miatt. Erősen javasoljuk az eltávolításukat: {0} Az online fájlok kihagyása a keresésből A felhőben tárolt játékok nem lesznek beolvasva és importálva, ha nem elérhetőek a gépen. Támogatott platformok: Google Drive, DropBox, OneDrive Fájltartalom vizsgálat nélküli, egyszerű beolvasás Nem pontos módja a beolvasásnak, ahol a fájlok nem kellenek a gépen letöltve legyenek. Alkalmazás mindre Telepítési állapot felülbírálása Ha be van kapcsolva, a Playnite figyelmen kívül hagyja a beépülő által jelentett telepítési állapotot. Bizonyos beépülőknél nem működik teljesen, ahol speciális módon történik a játékimportálás. Csak kézzel Naponta egyszer Hetente egyszer Minden indításkor Frissítések keresése Bővítmények frissítésének keresése Játékkönyvtárak frissítése Emulátorok mappáinak frissítése Rejtett játékokat is Mezők szerkesztése Összes kijelölése / kijelölés megszüntetése Megnyitás Aktiválás Hozzáadás Kezdj el gépelni a kereséshez… Segítség: [F1] A # megmutatja az elérhető parancsok listáját. A / megmutatja a beépülőket/keresőklienseket. A keresőszavak utáni szóköz azonnal arra a keresésre áll át. TAB: művelet váltása ENTER: művelet kiválasztása SHIFT+ENTER: elem menü megnyitása Nem telepített játékokat is Rejtett játékokat is Eltávolított játékokat is Eltávolított játékokat ne Rejtett játékokat is Rejtett játékokat ne Indít vagy telepít Részletek Játék menüje Játék szerkesztése Keresés megnyitása Keresődoboz Keresőgomb Elsődleges indítási művelet Másodlagos indítási művelet CTRL+F a keresést nyitja meg a keresődobozra ugrás helyett Elmenti a szűrőbeállításokat a keresések közt Keresőszolgáltatók Alapértelmezett kulcsszó Egyéni kulcsszó Rendszerszintű gyorsbillentyű Playnite keresés Bővítmény Beállítások Kivételek Fájlok kizárása a beolvasott mappából Mappák kizárása a beolvasott mappából Fájl hozzáadása a kivételekhez Mappa hozzáadása a kivételekhez A kivételeket csak elmentett beolvasó beállításokhoz lehet hozzáadni. Kivételek hozzáadva a/az {0} beolcasóhoz. Platform felülbírálása A beolvasó ezt a platformot fogja hozzáadni minden játékhoz, felülírva az automatikusan felismert platformot. Alapértelmezett kereséshez hozzáadott parancsok Ha ki van kapcsolva, az alapértelmezett keresésben nem fog parancsokat használni, csak ha a #-et használod. Névszűrésnél részbeni egyezés is Bekapcsolásakor a globális kereséshez hasonlóan, szövegbeni találatokra is keres. Pontos keresést a ! karakterrel kezdődő kereséssel lehet indítani. Minden egyes találatnál megjelenítendő információk kiválasztása Rejtett Biztonsági mentés megszakítva. Biztonsági mentés sikertelen. Biztonsági mentési hiba Biztonsági mentés folyamatban… Mentés visszaállítása folyamatban… Mentés visszaállítása nem sikerült. Beállításokat Játékkönyvtárat Játékkönyvtárak médiatartalmát Telepített bővítményeket Bővítmények adatait Telepített témákat Válaszd ki a mentés fájlból a visszaállítandó adatokat. A Playnite automatikusan újraindul, hogy elkezdhesse a visszaállítást. Válaszd ki a biztonsági mentésbe kerülő adatokat. A program beállításai és a játékkönyvtár alapértelmezésben belekerül. A Playnite automatikusan újraindul, hogy elkezdhesse a visszaállítást. Automatikus biztonsági mentés Automatikus biztonsági mentés gyakorisága Biztonsági mentés célmappája Biztonsági mentések számának limitálása Az alábbi adatok is mentésre kerülnek: Egy mappa megadása kötelező az automatikus biztonsági mentések tárolására. Csak a hibajavításokról küld értesítést Bekapcsolása esetén az új nagy verziójú frissítések nem kerülnek telepítésre, csak a jelenlegi hibajavításai. Az elmúlt hetek dátumait a mai naphoz viszonyítja Ha egy hétnél korábbi a dátum, akkor "ma", "holnap" tegnapelőtt" lesz használatban. A már megadott dátumformátum lesz érvényes az összes többire. Webes képkeresés Ikonkép keresési módja Borítókép keresési módja Háttérkép keresési módja Bővítmény információk letöltése… Metaadat forrás nem elérhető Indítási művelet beállítások Beolvasó beállítások alkalmazása Profil kiválasztása indításkor Emulátor kiválasztása indításkor Automatikus Mindig bekapcsolva Mindig kikapcsolva Könnyű hozzáférés (képernyőolvasó) Alkalmazásmenü Játékmenü Program mappája Felhasználói adat mappája Adatbázisfájl sérülést észleltem, a Playnite le fog állni. Kérlek nyiss egy új jegyet a Playnite GitHub oldalán angolul, kérve, hogy javítsák meg a sérült könyvtárfájlt. Szeretnéd menteni változtatásaidat? Hordozható (portable) telepítés Nem található játékvezérlő ================================================ FILE: source/Playnite/Localization/id_ID.xaml ================================================  Bahasa Indonesia Bahasa Playnite Keluar Filter aktif Filter dinonaktifkan Filter tambahan Filter Filter Data Tidak Valid Simpan Perubahan? Beranda di www.playnite.link Lihat Source Code di GitHub Buat Paket Diagnosa Kirim informasi diagnosis Tentang Playnite Dibuat Oleh Josef Němec Beri Kategori Atur Kategori Tambah Kategori Dicek - Beri kategori Tidak dicek - Hilangkan kategori Tidak tentu - Tidak ada perubahan (ketika mengedit banyak game) Tidak ada kategori Tidak ada Platform Ups! ada kesalahan… Kesalahan permanen telah terjadi. Jika butuh bantuan kami, mohon deskripsikan tindakan sebelum kegagalan, lalu kirim informasi diagnostik. Jika online, paket akan di unggah ke server Playnite untuk di analisa. Alternatifnya, anda bisa ketuk tombol 'Lapor Kegagalan' untuk membuat isu GitHub baru dan melaporkan kegagalan secara manual. Terimakasih atas bantuannya. Ekstensi "{0}" menyebabkan masalah yang tidak dapat dipulihkan. Sebaiknya simpan file log dan laporkan masalah tersebut ke pengembang ekstensi. Jika masalah terus berulang, nonaktifkan ekstensi. Ekstensi "{0}" menyebabkan masalah yang tidak dapat dipulihkan. Sebaiknya laporkan masalah ini ke pengembang ekstensi. Jika masalah terus berulang, nonaktifkan ekstensi. Ekstensi atau tema yang tidak dikenal menyebabkan kesalahan yang tidak dapat diperbaiki. Kami merekomendasikan untuk menonaktifkan add-on pihak ketiga, serta memisahkan add-on yang bermasalah dan melaporkan masalah kepada pengembang add-on. Terjadi kesalahan yang tidak dapat dipulihkan. Jika kamu ingin membantu kami memperbaiki issue ini, mohon kirim informasi diagnostik. Terima kasih. Nonaktifkan ekstensi Simpan file log Kirim info diag. Laporkan Masalah Muat Ulang Playnite Mulai ulang dalam Safe Mode Menonaktifkan semua ekstensi pihak ke-3 dan menggunakan tema default. Keluar Playnite Aksi yang dilakukan sebelum terjadi kesalahan (dalam bahasa Inggris): Pengelola Pustaka Hapus Game yang dipilih? Tidak dapat menghapus - Game atau penginstal sedang berjalan. Tidak bisa mencopot - Game sedang berjalan. Apakah Anda yakin ingin menghapus {0}? Anda yakin ingin membuang game {0}? Apakah Anda yakin ingin menghapus {0}? Dengan memilih opsi "tambahkan ke daftar pengecualian" akan mencegah game diimpor lagi pada saat perpustakaan diperbarui. Kamu yakin ingin menghapus game {0}? Pilih opsi "tambahkan ke daftar pengecualian" akan mencegah game diimpor lagi saat perpustakaan diperbarui. Kamu yakin ingin menghapus entri {0} yang saat ini sedang tidak digunakan? Tidak ada field yang sedang tidak digunakan Ya (tambahkan ke daftar pengecualian) Ada perubahan yang belum disimpan di bagian ini Memperbarui format pustaka gim… Update database gagal, gagal membuka file database. Tidak dapat memperbaharui pustaka gim. {0} MB ruang bebas diperlukan. Game Error Gagal memulai game. '{0}' tidak ditemukan di database. Gagal memulai game: {0} Gagal memulai aksi: {0} Gagal membuka lokasi game: {0} Tidak dapat menemukan besar instalasi permainan: {0} Kesalahan pemasangan pemindaian ukuran Ada {0} kesalahan selama pemindaian ukuran pemasangan Gagal membuat shortcut: {0} Gagal untuk membuka manual: {0} Gagal menginstal game: {0} Gagal menguninstall game: {0} Tidak ditemukan tindakan startup game yang valid. Ketika menggunakan emulator, pastikan definisi platform cocok antara konfigurasi game dan emulator. Implementasi instalasi tidak tersedia. Plugin yang diperlukan untuk game ini tidak terpasang atau diaktifkan. Unduhan metadata resmi tidak tersedia. Tidak ada permainan yang terpilih. Eksekusi skrip game gagal. Eksekusi skrip aplikasi gagal. Eksekusi skrip global gagal. Eksekusi skrip emulator gagal Eksekusi aksi skrip Play gagal. PowerShell versi 3.0 atau lebih belum terinstal. Tidak dapat menentukan cara memulai game. Aktifkan Dinonaktifkan Hapus Hapus yang tidak digunakan Ubah Nama Salin Tambah Ikon Default Gambar Sampul Default Gambar latar belakang default Selesai Selanjutnya Sebelumnya SELESAI KEMBALI BERSIHKAN Bersihkan Abaikan Abaikan Semua Impor Nama Pembuat Modul Seri Versi Terakhir dimainkan Paling sering dimainkan Jumlah Main Besar Instalasi Folder Catatan Ditambahkan Tanggal Ditambahkan Dimodifikasi Tanggal Diubah Situs web Jalur OK Simpan Tutup Batal Konfirmasi Setel ulang Ya Tidak Selamat Datang User Lokal Umum Media Link Instalasi Aksi Mengunduh… Mengunduh media... Memuat... Tipe Profil Profil Hapus Unduh Cari Resolusi: Setiap Perbesaran Tampilan Daftar Sampul Tampilan Grid Tampilan Detail Kustom URL Terima kasih khusus Lisensi Kontributor Keluar Playnite… Hari Ini Kemarin Senin Selasa Rabu Kamis Jum'at Sabtu Minggu Minggu lalu Bulan lalu Tahun Terakhir Lebih dari 1 tahun yang lalu 0 hingga 100MB 100MB hingga 1GB 1GB hingga 5GB 5GB hingga 10GB 10GB hingga 20GB 20GB hingga 40GB 40GB hingga 100GB 100GB atau lebih Import berhasil dilakukan. Semua gim ID Game ID Database Prasetel Kolom Kolom Baris Baris Tidak dapat mengambil ikon dari aksi "Play". Tidak ada aksi untuk tipe berkas ini. Download metadata yang hilang Mengaktifkan opsi ini akan melewatkan unduhan metadata pada bidang data yang sudah berisi informasi. Pilih Game Pilih game mana yang akan diupdate dengan metadata baru: Semua game di database Semua game yang difilter Hanya game yang dipilih Situs Resmi IGDB Silahkan pilih baris mana yang akan otomatis diisi oleh Playnite dan source mana yang akan digunakan sebagai sumber data. Klik logo di atas untuk berkontribusi di igdb.com agar meningkatkan data yang bisa digunakan oleh Playnite. Mendownload metadata... Mengimport game yang terinstal... Mengimport {0} game... Mengimpor game emulasi dari {0}… Mengunduh pembaruan pustaka… Memindai ukuran game di perpustakaan game… Mengecek ukuran dari games yang diimpor ... Update library selesai Melepaskan sumber daya... Konfigurasi Pengaturan… Platform dan Emulator Setel Emulator… Pengelola Pustaka… Alat Mendownload metadata... Alat Perangkat Halus... Konfigurasi Integrasi... Buka Client Pihak Ketiga Klien Pihak Ketiga Muat Ulang List Game Batalkan Update Pustaka Perbarui Folder Emulasi Tambah Game Cari... Scan otomatis... Game Emulasi… Aplikasi Microsoft Store... Tentang Playnite Kirim Timbal Balik Ganti ke mode Fullscreen Tautan Bantuan Dukung lewat Patreon Dukungan Ko-fi Panduan pengguna Dokumentasi SDK Restart Sistem Matikan Sistem Suspend System Hibernasikan Sistem Kunci Sistem Pengguna Keluar Pilih Game Secara Acak Kolom game untuk ditampilkan pada bilah detail: Spasi antar item Gambar latar belakang item grid Lebar batas item grid Sumber icon game tidak ditemukan Sumber cover game tidak ditemukan Sumber background game tidak ditemukan Spasi vertikal dalam detail game Posisi detail Grid View Posisi daftar game di Detail view Tambah separator di antara panel Tinggi gambar cover game Tinggi ikon di daftar game Fon aplikasi Huruf monospace Posisi panel filter Posisi panel Explorer Rendering gambar cover Target rasio aspek Opsi berikut ini juga memengaruhi rendering ubin dalam mode Layar Penuh! Regang gambar Boks DVD Epic Game Store GOG Galaxy 2.0 IGDB Kotak Banner Steam Cover vertikal Steam Twitch Restart untuk melihat perubahan Pengaturan Umum Panel atas Penampilan Detail Gim Tata letak Opsi Lanjut Fullscreen Input Performa Metadata Memperbarui Cari Cadangkan Cadangkan Data Pustaka Pulihkan Cadangan Data Import perubahan library secara otomatis Lokasi file database invalid, silahkan set path yang benar. Nama akun tidak boleh kosong Download metadata setelah mengimpor game Luncurkan Playnite diperkecil. Luncurkan Playnite ketika menyala. Mulai dengan ditutup ke baki Gagal menambahkan Playnite ke daftar start up Luncurkan dalam Mode Layar Penuh Muat gambar secara Asinkron Menambah kehalusan scroll list game. Lebih lambat memuat gambar. Tampilkan nama gim jika sampul tidak ditemukan Perlihatkan nama game di Grid View Gelapkan game yang belum terpasang Tampilkan ikon game di Tampilan List Perlihatkan jumlah dalam deskripsi group Hanya menampilkan bidang yang ditetapkan pada panel filter dan explorer Matikan Hardware Acceleration Gunakan ketika bereksperimen dengan lag atau masalah UI Menampilkan game tersembunyi di daftar quick launch Mempengaruhi Jump List (Daftar Langsung) dan daftar menu baki. Jumlah objek mulai cepat Gunakan gambar background terpilih sebagai background window Blurkan background Kualitas Tinggi Gelapkan background Tampilkan di Grid View Tema Tema Profil Tema Fullscreen Warna Tema Fullscreen Lokasi Database Status login: Setting Playnite Bersihkan cache web Memungkinkan solusi untuk masalah yang berkaitan dengan akun. Tampilkan ikon system tray Perkecil Playnite ke system tray Sembunyikan Playnite ke bilah sistem ketika jendela aplikasi ditutup Ketika game dimulai: Setelah gim ditutup: Format waktu yang dimainkan untuk menunjukkan jumlah hari yang dimainkan Format tanggal: Semua akun akan dilogout. Aplikasi harus dimuat ulang. Lanjutkan? Bersihkan Cache? Playnite harus dimuat ulang untuk mengganti tema. Cari tema baru Buat tema baru Dapatkan ekstensi lain... Buat ekstensi baru Bantu kami menerjemahkan Playnite Playnite perlu dijalankan ulang untuk menerapkan pengaturan baru. Mulai ulang sekarang? Mulai Ulang Playnite? Playnite tidak dapat memindahkan file pustaka anda secara otomatis. Anda harus memindahkan/menyalin file secara manual sebelum mengubah lokasi. Jika tidak ada pustaka di lokasi target, pustaka baru akan dibuat. Waktu bermain tidak akan direkam jika aksi "Tutup" diatur. Jumlah baris Jumlah kolom Jumlah baris tampilan detail Tampilkan Gambar Background di Layar Utama Tidak berlaku retrospektif untuk game yang sudah ada tanpa mengunduh ulang metadata. Impor waktu bermain game di pustaka: Mengonfigurasi kapan Playnite harus mengimpor waktu bermain yang dilaporkan oleh plugin perpustakaan untuk game di database Playnite. Dukungan dari plugin perpustakaan yang bertanggung jawab menangani game diperlukan untuk dapat menggunakan fitur ini. Selalu: Mengimpor waktu bermain untuk game yang baru diimpor dan game yang sudah ada di basis data Playnite. Hanya untuk game yang baru diimpor: Mengimpor waktu bermain hanya untuk game yang baru diimpor. Tidak pernah: Tidak pernah mengimpor waktu bermain dalam keadaan apa pun. Selalu Hanya untuk game yang baru saja diimpor Tidak pernah Hidupkan dukungan controller di mode Desktop Tombol "Guide" membuka mode Layar Penuh Pengaturan unduhan Metadata otomatis untuk game yang baru diimpor. Layar tujuan Selalu gunakan layar utama Tampilkan Judul Game Tampilkan Status Baterai Tampilkan Persentase Baterai Tampilkan Jam Sembunyikan kursor Hanya yang terinstal di Quick Filter Tipe Tombol Kontroler Tata letak Scrollbar Horizontal Pilih salah satu sub-bagian Tidak ada setelan tersedia Gagal memuat pengaturan Skrip ini dijalankan untuk setiap game di perpustakaan. Skrip individual dapat ditetapkan ke setiap game secara terpisah saat mengedit detail game. Animasi transisi gambar latar belakang Ukuran font Otomatis Tajam Hitam putih ClearType Ideal Monitor Mode pemformatan teks Mode render teks Metode rendering dan pemformatan teks saat ini tidak digunakan untuk deskripsi game. Memuat gambar latar belakang terlebih dahulu Jika diaktifkan, Playnite akan mengunduh artwork latar belakang saat mengunduh metadata, menggunakan lebih banyak ruang disk dan membuat artwork tersedia saat offline. Jika dinonaktifkan, artwork latar belakang hanya akan diunduh saat pertama kali dibutuhkan, menggunakan lebih sedikit ruang, tetapi dapat mengakibatkan penundaan sebelum artwork ditampilkan dan beberapa gambar mungkin tidak tersedia saat offline. Secara otomatis menutup client pihak ketiga setelah game keluar Penundaan penutupan client (dalam hitungan detik) Jangan tutup setelah sesi permainan lebih pendek dari (dalam detik) Otomatis matikan klien berikut: Tutup klien secara otomatis Impor Daftar Pengecualian Menampilkan peringatan saat menetapkan media game yang terlalu besar Perintah buka folder Organisasi pemeringkatan usia yang diutamakan Perbarui ukuran penginstalan game pada pembaruan perpustakaan Memindai dan memperbarui ukuran penginstalan game jika terdeteksi bahwa file-file tersebut telah dimodifikasi sejak pemindaian terakhir Tidak ada Isi Seragam Menyesuaikan desain pada bagian yang akan diisi Kiri Kanan Atas Bawah Error Import Login dibutuhkan Login gagal Mode rendering tampilan web alternatif Gunakan saat mengalami masalah dengan tampilan web, misalnya dialog autentikasi integrasi. Memuat sebagian dari deskripsi game yang panjang Deskripsi yang panjang dapat menyebabkan lag saat memilih game. Ketika diaktifkan, hanya sebagian teks deskripsi yang akan dimuat pada awal dengan opsi untuk memuat sisanya sesuai permintaan. Import Metadata Download Metadata Mengatur konfigurasi yang dipilih untuk digunakan pada unduhan metadata di masa mendatang. Dapat juga diubah dalam pengaturan aplikasi Panduan Import Emulator Wizard ini akan memandu Anda melalui proses mengunduh dan mengimpor emulator konsol dan mengimpor game emulasi. Ingatlah bahwa Anda selalu dapat menambahkan emulator dan/atau game tambahan di kemudian hari melalui menu utama (di bawah menu "Library" untuk pengaturan Emulator dan menu " Tambah Game" untuk game emulasi). Di bawah ini adalah daftar emulator yang dapat dikenali dan dikonfigurasikan secara otomatis oleh Playnite. Anda dapat mengunduh penginstal emulator dari situs web mereka. Setelah emulator terinstal (secara manual), Anda dapat mengimpornya pada dialog konfigurasi emulator. Anda dapat mengimpor emulator apa pun yang terinstal di PC Anda dengan mengeklik tombol 'Autodetect Dari Folder...'. Playnite akan mencari folder yang dipilih untuk emulator yang dikenal dan memberikan opsi untuk mengimpornya. Anda dapat menggunakan tombol ini beberapa kali untuk mengimpor emulator dari folder yang berbeda. Emulator akan ditambahkan ke bagian bawah daftar saat ini. Anda dapat mengimpor game dengan mengeklik tombol 'Pindai Folder Menggunakan Emulator'. Memilih emulator yang sesuai akan memberi tahu Playnite jenis file mana yang harus dipindai dan diimpor. Anda dapat menggunakan tombol ini beberapa kali untuk mengimpor game dari folder yang berbeda. Game akan ditambahkan ke bagian bawah daftar saat ini. Tidak ada emulator dipilih untuk diimpor. Anda tidak bisa mengimport game emulasi secara otomatis tanpa memilih emulator terlebih dahulu. Apa anda yakin untuk melanjutkan dan tutup proses impor game? Tidak ada emulator yang dikonfigurasi di Playnite. Anda tidak dapat mengimpor game tanpa mengonfigurasi emulator terlebih dahulu dan memilih jenis file yang sesuai. Apakah Anda ingin menambahkan beberapa emulator sekarang? Cari folder menggunakan Emulator Pilih file Deteksi Automatis dari Berkas... Konfigurasi Emulator… Memindai… Memindai {0}… Konfigurasi Awal Proses ini akan memandu Anda melalui impor otomatis dan konfigurasi perpustakaan game eksternal. Playnite dapat mengimpor game secara otomatis dari beberapa layanan game, seperti Steam atau GOG. Ingatlah bahwa Anda juga dapat menambahkan game kustom atau game emulasi secara manual untuk platform apa pun nanti dari menu utama. Integrasi Library Berikut ini adalah daftar beberapa integrasi perpustakaan yang didukung Playnite. Pilih salah satu yang ingin Anda instal. Integrasi lainnya dapat diinstal nanti dari menu " Add-ons". Konfigurasi telah Selesai Setelan awal sudah selesai. Ingat bahwa Anda bisa mengubah kembali semua setelan kapan saja di menu 'Setelan' Kamu juga bisa menambah game lainnya kapanpun dengan mengklik logo Playnite. Gagal mengunduh satu atau beberapa ekstensi. Anda dapat mencoba mengunduh ulang integrasi dari menu add-ons setelah wizard pertama kali selesai. Mengunduh integrasi {0}… Mengunduh daftar integrasi yang direkomendasikan… Gagal mengunduh daftar integrasi yang direkomendasikan. Anda dapat mencoba dan mengunduh ulang integrasi nanti melalui menu Addons. Konfigurasi Platform dan Emulator Setel Emulator Platform Platform Emulator Emulator Tambah Pilih Ikon Pilih Sampul Pilih Gambar Pilih Item Pilih Background Pilih File Dari URL Tambah Emulator Platform yang didukung Simpan perubahan platform? Simpan perubahan emulator? File program (file .exe/.sh/.bat, dll..) Argumen Direktory Kerja Tipe File yang Didukung Impor Emulator... Download Emulator... muat argumen preset dari profil emulator yang tersedia Apakah Anda yakin ingin menghapus emulator {0}? Emulator ini sedang digunakan oleh game {1}. Apakah Anda yakin ingin menghapus platform {0}? Platform ini sedang digunakan oleh {1} game dan {2} emulator. Bantuan pengaturan Sortir Menyortir Arah Kelompokkan Berdasarkan Menaik Menurun Jangan buat grup Kelompokan berdasarkan Pustaka Grup berdasarkan Kategori Grup berdasarkan Platform Tipe Tampilan Tampil Panel Penjelajah Panel Filter Ikon Ikon Pustaka Gambar Sampul Gambar Background Nama Sortir Pustaka Petunjuk Nama Instal Drive Nama Akun Platform Kategori Genre Tanggal Rilis Tahun Rilis Developer Tag Publisher Status Instalasi Cocokkan semua filter Jika diaktifkan, hanya permainan yang menggunakan semua item di semua filter yang akan disertakan dalam tampilan. Jika dinonaktifkan, permainan yang menggunakan item apa pun dalam filter apa pun akan disertakan dalam tampilan. Terinstal Terinstal Belum terpasang Tersembunyi Favorit Aktifkan Dukungan HDR Jika diaktifkan, HDR akan diaktifkan pada tampilan utama sebelum memulai permainan. Perlu diperhatikan bahwa HDR tidak didukung di layar utama Anda. Aktivitas terakhir Kategori Deskripsi Direktori Instalasi Gambar Sampul Link Path untuk Image/ISO Genre Genre Perusahaan Perusahaan Pengembang Developer Penerbit Publisher Kategori Kategori Tag Tag Fitur Fitur Rating Umur Rating Umur Kawasan Region Sumber Sumber Aktivitas Terbaru Error Database Gagal membuka library database. Database tidak terbuka. Tidak dapat mengakses database perpustakaan. File "{0}" sedang digunakan oleh proses lain atau berada di lokasi yang tidak dapat diakses. Gagal membuat paket diagnosa. Gagal mengunggah paket diagnostik secara otomatis. Informasi diagnostik berhasil dikirim. Paket diagnostik telah berhasil dibuat dan dikirimkan. Harap lampirkan ID berikut ke laporan masalah Anda: Gagal mengimpor game dari {0} Gagal mengimpor game emulasi dari {0}. Tidak bisa mencari game dengan profil emulator yang dipilih. Profil tidak memiliki tipe file atau emulator apapun. Playnite gagal memulai. Harap tutup semua instance lainnya dan coba lagi. Gagal menerapkan tema "{0}", profil warna "{1}" Tidak bisa membuka link, URL format tidak valid. Gagal dalam menjalankan aplikasi. Gagal menginisialisasi komponen tampilan web. Playnite tidak dapat melanjutkan proses startup. Informasi lebih lanjut klik di https://playnite.link/cefstartup Tidak dapat mengimpor emulator karena file definisi hilang atau rusak. Gagal mengeksekusi aksi menu. Edit Rincian Game URL Gambar Tambah Link Tambahkan ROM Simpan Perubahan Terapkan perubahan bidang pada permainan yang sedang diedit. Tambah Aksi Hapus Aksi Hilangkan Tombol Mainkan Tambah Game Pindai Folder... Deteksi yang Terinstal Telusuri... Buka Playnite Setting Profil Nama game tidak boleh kosong. Direktori pelacakan aksi permainan tidak boleh kosong. Nama game tidak boleh kosong sebelum mencari metadata. Data game Invalid Masukan URL web yang valid. Sertakan http:// atau https:// Pilih URL Gagal mendownload metadata: {0} Download Error Bersihkan Filter Akun Privat Akun Publik Kunci API Error saat memuat Tema Error Bersihkan semua Menginstal... Menguninstal Meluncurkan... Sedang beroperasi URL invalid Diamkan aplikasi Minimalkan Kembalikan jendela ini Pulihkan jendela hanya ketika diluncurkan dari UI Tutup aplikasi Ubah Opsi Lanjut Jangan pernah Penyelesaian Status Penyelesaian Skor Pemain Skor Kritik Skor Komunitas Skrip permainan Skrip aplikasi Skrip Plugin Sumber Metadata Ekstensi ID Ekstensi Muat Ulang Skrip SDK PowerShell Interaktif Semua skrip berhasil dimuat ulang. Tidak ada permainan yang ditemukan untuk kriteria pencarian/filter tertentu Item tidak ditemukan Beralih ke Mode Desktop Keluar dari Playnite Koleksi Perbarui Semua Dibuat Oleh: Versi: Diperbarui: Modul: Pustaka Statistik Semua Tidak ada Notifikasi Lebar Panjang Ukuran Kecil Normal Besar Lebiih Besar Paling Besar Bawaan Pilih Pilih Semua Batalkan Semua Pilihan Pertama Acak Pilih Pengguna Muat lebih banyak Transparan Runtuhkan Perluas Runtuhkan Semua Perluas Semua Lainnya Tema Argumen untuk Emulator Argumen bawaan Argumen Khusus Argumen tambahan untuk Emulator Tindih argumen Emulator dengan... Mainkan aksi Pilih metadata untuk dimuat Pilih game untuk diimport Cari metadata Update Tersedia Perubahan sejak update terakhir Install Update Periksa Pembaruan Update Error Gagal memeriksa pembaruan. Versi baru tidak ditemukan Gagal mengunduh dan memasang pembaruan. Beberapa tugas latar belakang sedang berlangsung. Apakah Anda ingin membatalkannya dan melanjutkan pembaruan? Beberapa tugas latar belakang sedang berlangsung. Apakah Anda ingin membatalkannya dan keluar dari Playnite? Beberapa tugas latar belakang sedang berlangsung. Beralih mode akan membatalkan tugas, apakah Anda tetap ingin beralih? Update untuk Playnite tidak tersedia Muat ulang list tema Gunakan tema yang dipilih Saksikan perubahan file Gunakan tema secara otomatis ketika source berubah Waktu proses skrip Jalankan sebelum memulai permainan Jalankan setelah keluar dari permainan Jalankan setelah permainan dimulai Jalankan saat aplikasi dimulai Jalankan saat aplikasi dimatikan Skrip awal permainan Skrip permainan dimulai Skrip permainan berhenti Eksekusi Skrip Global Global Disaring Saat ini Baru Uji skrip Hanya tampilkan item yang dipilih. Simpan sebagai standar Tambah ke Favorit Hapus dari Favorit Sembunyikan game Hapus dari Sembunyi Aktifkan Dukungan HDR Nonaktifkan Dukungan HDR Edit Hitung ukuran pemasangan Hitung ukuran pemasangan (Semua game) Hitung ukuran pemasangan (Hanya data yang hilang) Ukuran Instalasi Tentukan Kategori... Tetapkan Status Penyelesaian Hapus Mainkan Instal Opsi Game Rincian Uninstal Buka folder game Buat Shortcut di Desktop Buka Manual Lainnya... Dikelola oleh plugin pustaka Proses memulai permainan akan dikelola oleh plugin perpustakaan yang bertanggung jawab untuk permainan ini. Tidak ada informasi relevan tentang permainan '{0}' yang ditemukan pada laman yang ditentukan. Tip: Kamu bisa menggunakan metadata editor yang lebih lengkap di menu "Edit" per game. Tidak tersedia. Ada yang masih diproses. Teks deskripsi ini menggunakan format HTML. waktu bermain di rekam dalam detik. Ukuran instalasi ditunjukkan dalam byte. Tanggal rilis harus ditetapkan dalam format 'tahun-bulan-hari'. Nilai Bulan dan Hari dapat dihilangkan. Nilai dari 0 sampai 100 atau kosong untuk tidak menilai. Perkembangan Playnite didukung oleh pelanggan dan anggota Ko-fi berikut: Kode, lokalisasi, dan kontribusi lainnya, tanpa urut: Batalkan pemantauan permainan? Pemantauan instalasi sedang berjalan. Apakah Anda ingin membatalkan proses dan mengembalikan game ke keadaan sebelumnya? Pemantauan eksekusi permainan sedang berjalan. Apakah Anda ingin membatalkan proses dan mengembalikan game ke keadaan sebelumnya? Waktu Dimainkan Terakhir dimainkan {0} hari {1} jam {2} menit {0}j {1}m {0} menit {0} detik Belum Dimainkan Buka dalam mode Desktop... Buka mode layar penuh… Memuat pustaka game… Menghitung ukuran instalasi… Menghitung ukuran pemasangan {0}… Gagal memasang file skrip. Modul berhasil diinstall Pasang Skrip Modul gagal Gagal menjalankan fungsi ekstensi. Buka folder metadata Hitung Menghitung ukuran penginstalan secara otomatis menggunakan ROM jika game tersebut memiliki ROM atau direktori penginstalan jika telah ditetapkan {0} klien belum terpasang. {0} akan dibuka. Silahkan masuk lalu tutup pesan ini. Menunggu pengguna untuk masuk, silahkan tutup ini setelah Anda selesai... Folder instalasi game tidak ditemukan Konfigurasi tindakan game tidak valid. Memecahkan masalah sinkronisasi akun Memecahkan masalah Ganti nama Tambah Beri nama Beri nama baru Kurang dari satu jam 1 sampai 10 jam 10 sampai 100 jam 100 sampai 500 jam 500 sampai 1000 jam 1000+ Playnite wajib di restart untuk menyelesaikan instalasi. Apakah Anda ingin untuk merestart sekarang? Ekstensi tidak dikemas dengan benar. Tema tidak dikemas dengan benar. Ekstensi "{0}" gagal untuk dimuat dengan sempurna. Tidak dapat memuat ekstensi "{0}", versi Playnite saat ini tidak didukung. Tema "{0}" gagal dimuat dengan sempurna Tidak dapat memuat tema "{0}", versi Playnite saat ini tidak mendukung. Ekstensi gagal dimuat dengan sempurna. Tema gagal dimuat dengan benar. Tema/Ekstensi menggunakan versi API yang tidak didukung. Instalasi berhasil. Instalasi tambahan? Umum Gagal memasang add-on "{0}". Gagal menginstal ekstensi. {0} Apakah anda ingin memasang ekstensi baru? {0} Oleh {1} Versi {2} Apakah Anda ingin memperbaharui ekstensi "{0}"? Versi saat ini: {1} Versi baru: {2} Gagal memasang tema. {0} Apakah Anda ingin menginstalasi tema baru? {0} Dibuat oleh {1} Versi {2} Apakah anda ingin meng-update tema "{0}"? Versi saat ini: {1} Versi terbaru: {2} Anda akan meninggalkan Playnite dan pergi ke halaman web dengan browser defaut anda. Apakah anda ingin melanjutkan? {0} Gambar yang dipilih mungkin terlalu besar untuk performa yang optimal. Gambar dengan ukuran yang sangat besar dapat memperburuk respons UI dan menambah penggunaan memori. Saran resolusi maksimal: Ikon: {0} megapiksel Cover: {1} megapiksel Latar belakang: {2} megapiksel Peringatan Performa Jangan Tampilkan Lagi File dengan ekstensi {0} tidak kompatibel. Ekstensi file tidak kompatibel File gambar yang dipilih mungkin terlalu besar untuk kinerja yang optimal. Apakah anda yakin ingin menghapus tema yang dipilih? Penghapusan instalasi akan menunggu hingga aplikasi dimulai kembali Tema bawaan tidak dapat di-uninstall Tema ini tidak mendukung Playnite versi ini Apakah anda yakin ingin menghapus ekstensi yang dipilih? Penghapusan instalasi akan menunggu hingga aplikasi dimulai kembali Ekstensi bawaan tidak dapat di-uninstall Ekstensi ini tidak mendukung Playnite versi ini Folder instalasi: Folder data Membuat paket diagnostik... Mengupload paket diagnostik... Muat berkas... Apa ini? Apakah Anda yakin ingin melakukan ini? Total waktu bermain Rata-rata waktu bermain Waktu bermain teratas Total ukuran instalasi Ringkasan Bilah Sisi Tampilkan Bilah Sisi Reset pengaturan Semua pengaturan aplikasi akan diatur ulang ke default, termasuk: - Lokasi database - Daftar import yang dikecualikan - Pengaturan ekstensi, termasuk integritas pustaka Aplikasi harus dimulai ulang untuk menyelesaikan proses. Apakah anda ingin mengatur ulang pengaturan? Untuk pengembang Ekstensi eksternal Masukkan path folder lengkap Pencapaian Forum Berita Laman Toko Penyiapan awal belum selesai. Playnite akan mulai ulang ke mode Desktop untuk menyelesaikan prosedur Terakhir Dimainkan Favorit Paling sering dimainkan Semua Ada filter yang diterapkan. Ada filter tambahan yang diterapkan. Hasil pencarian : Item dengan nama yang sama telah ada. Batasi pilihan ke filter saat ini Pilih lain Tambahan... Terinstal Pengaturan ekstensi Telusuri Pembaruan Perbarui ({0}) Pengurusan dari ekstensi dan tema yang sudah terinstall, termasuk settingnya, telah dipindah ke menu "Tambahan". Semua ekstensi integrasi pustaka yang saat ini terinstal dapat dikonfigurasi di sini. Jika anda ingin menginstal atau meng-uninstal integrasi tambahan, gunakan menu "Add-ons" di menu utama. Tema Desktop Tema mode layar penuh Mencari… Add-on tidak mendukung Playnite versi ini. Gagal mendownload paket instalasi add-on. Gagal mendownload manifest penginstalan add-on. Mulai ulang aplikasi diperlukan untuk menerapkan perubahan yang tertunda. Add-on ini dijadwalkan untuk penginstalan. Pasang Hapus Telah dipasang Tidak ditemukan pembaruan add-on baru. Perbaharui tambahan Catatan perubahan tidak tersedia Dijadwalkan untuk penginstalan Gagal diunduh Lisensi ditolak Mengunduh {0}… Mencari pembaruan add-on... Terdapat satu atau lebih add-on tersedia untuk di-update. Pilih item untuk di-update Sesi pengembangan ekstensi Persetujuan lisensi {0} Terima Tolak Sertakan tindakan permainan integrasi pustaka Pilih aksi Mode Pelacakan Jalur Pelacakan Penundaan pelacakan awal Kecepatan pelacakan Tautan File Emulator Script Default Dalam Proses Folder Proses awal Log pesan jejak Perubahan berikut ini akan menimpa data untuk semua game yang sedang dipilih! Tidak ada Proporsional Hanya item Hanya di awal dan akhir Sensitivitas scroll Scroll halus Kecepatan animasi Hapus item? Apakah Anda yakin ingin menghapus item ini? Tampilkan tombol di panel atas: Pengaturan tampilan umum Pengaturan pengelompokan Pengaturan pengurutan Filter bawaan Posisi item plugin Lebar pemisah bagian Memindahkan tombol menu utama ke samping Panel penjelajah Pemilih permainan acak Pemilih permainan acak dari tampilan Pilih permainan acak dari tampilan Simpan pengaturan pengelompokan dan pengurutan Tampilkan sebagai filter cepat dalam mode Layar Penuh Dalam 7 hari terakhir Dalam 31 hari terakhir Dalam 365 hari terakhir Lebih dari 30 hari yang lalu Konfigurasi Simpan Preset Minimalkan setelah memulai permainan Minimalkan Playnite setelah permainan dimulai. Menonaktifkan ini dapat menyebabkan masalah dengan permainan yang tidak mendapatkan fokus input saat startup! Ukuran font Ukuran Font Kecil Dukungan kontroler game Jika dinonaktifkan, Playnite tidak akan menerima input kontroler game apa pun. Nonaktifkan jika Anda menggunakan alat yang menerjemahkan input pengontrol game ke input mouse/keyboard dan Anda mendapatkan input ganda di Playnite. Tampilkan item pada menu utama: Balik binding tombol tampilan utama X/A Tukar binding tombol untuk memulai permainan dan menampilkan halaman detail permainan pada tampilan utama Tukar binding tombol konfirmasi/pembatalan Balik binding tombol A/B untuk konfirmasi dan pembatalan Kontroler utama saja Hanya menerima input dari kontroler utama bila diaktifkan. Tombol Guide memfokuskan Playnite Volume antarmuka Volume latar belakang Bisukan saat di latar belakang Gagal melakukan inisialisasi antarmuka audio. API Output API yang digunakan untuk output audio. Ubah jika Anda mengalami masalah dengan suara. Umum Visual Audio Tata Letak Menu Input {0} sedang memulai... {0} sedang berjalan... Kapitalisasi Spasi Penskalaan rendering gambar Alternatif Seimbang Kualitas Kualitas: Kualitas gambar terbaik, lambat, penggunaan memori tinggi. Seimbang: Kualitas bagus, cepat, penggunaan memori rendah. Alternatif: Kualitas yang lebih baik, kecepatan menengah, penggunaan memori rendah Pilih file… Pilih folder… Skrip startup Harap diperhatikan bahwa ekstensi dan tema dapat sangat memengaruhi kinerja, stabilitas, dan keamanan Playnite. Jika Anda mulai mengalami masalah setelah menginstal tema atau ekstensi, coba nonaktifkan/hapus instalannya terlebih dahulu untuk mengetahui penyebab masalahnya. Pilih saat startup Pilih saat memulai Profil bawaan Profil bawaan Profil kustom Profil kustom Diatur oleh script bawaan Spesifikasi emulator Spesifikasi platform Spesifikasi negara Jalankan sebelum memulai emulator Jalankan setelah emulator dimulai Jalankan setelah keluar dari emulator Emulator yang dapat dieksekusi tidak ditemukan. Spesifikasi emulator tidak ditemukan. Script pemulaian emulator tidak ditemukan. Bagi sebagai game terpisah Gabung menjadi satu game Atur platform Atur wilayah Folder pemindaian Pindai konfigurasi Kecualikan pola dari pemindaian checksum File yang sesuai dengan pola tertentu tidak akan dipindai untuk checksum dan akan dicocokkan dengan nama file. Lihat halaman bantuan emulator untuk informasi lebih lanjut Pindai dengan emulator Nama harus ditetapkan ketika menyimpan konfigurasi baru Emulator atau profil emulator tidak diatur. Direktori yang akan dipindai tidak ditentukan atau tidak ada Konfigurasi pemindaian tidak diatur dengan baik. Termasuk dalam pemindaian otomatis secara massal Gagal memindai folder untuk emulator. Gagal memindai folder untuk game emulasi. Sembunyikan yang diimpor Profil yang akan diimpor: Konfigurasi pemindaian otomatis Simpan sebagai konfigurasi pemindaian otomatis Menyimpan konfigurasi untuk digunakan nanti selama pembaruan pustaka. Konfigurasi yang disimpan dapat diatur melalui menu “Konfigurasi Emulator”. Impor menggunakan jalur relatif Jika memungkinkan impor file game menggunakan jalur relatif ke folder instalasi Playnite atau folder instalasi emulator. Pindai subfolder Pindai di dalam arsip Gabungkan file terkait Gabung file game yang serupa, seperti disk game individual, di bawah satu entri game. Tambahkan pemindai Tambahkan pemindai tersimpan Mulai memindai Tambahkan konfigurasi pemindaian dengan emulator untuk memindai folder tertentu. Pastikan emulator dikonfigurasi dengan benar sebelum mengimpor permainan (melalui menu Perpustakaan -> Konfigurasi Emulator). Status default diterapkan untuk game yang baru ditambahkan Status diterapkan untuk game yang dimainkan untuk pertamakalinya Gagal meninisialisasi PowerShell script runtime. Jika anda pengguna Windows 7, coba install (ulang) PowerShell 5.1 untuk memperbaiki isu ini. Filter preset dengan nama yang ditentukan telah tersedia. Perbarui preset dengan pengaturan yang baru? Kata-kata ini akan dihapus dari awal nilai Nama Pengurutan yang diisi secara otomatis: Gunakan ini untuk mengabaikan kata yang berada di awal untuk kepentingan pengurutan. Secara defaultnya "The", "An", dan "A". Isi Nama Pengurutan untuk game yang tidak memiliki nama Penyortiran Mengisi data Nama Pengurutan... Layanan Nahimic telah terdeteksi berjalan di sistem Anda. Layanan ini diketahui menyebabkan masalah rendering pada Playnite (dan aplikasi lainnya). Jika Anda mengalami kerusakan grafis atau masalah rendering lainnya di Playnite, kami sarankan untuk menonaktifkan atau menghapus layanan Nahimic sepenuhnya. Informasi lebih lanjut di https://playnite.link/nahimicsucks Playnite berjalan dengan akses yang lebih tinggi (sebagai administrator). Hal ini tidak disarankan karena memberikan akses yang lebih tinggi ke semua ekstensi yang terpasang dan semua game/aplikasi yang dimulai dari Playnite! Informasi lebih lanjut di https://playnite.link/adminfaq Tampilkan peringatan jika Playnite berjalan dengan akses tingkat tinggi Dapatkan ukuran aktual pada penyimpanan saat menghitung ukuran permainan Jika diaktifkan, pemindaian akan lebih lambat dan akan mendapatkan ukuran sebenarnya yang digunakan file dalam penyimpanan. Jika dinonaktifkan, pemindaian akan lebih cepat dan akan menggunakan ukuran file itu sendiri. Pengaya berikut ini telah dilaporkan berpotensi bermasalah, baik karena dampak stabilitas/kinerja yang tinggi atau masalah keamanan. Kami sangat menyarankan Anda untuk menghapus pengaya tersebut: {0} Kecualikan file online dari pemindaian File yang disimpan di penyimpanan cloud tidak akan dipindai dan diimpor jika tidak tersedia secara lokal. Dukungan hanya untuk: Google Drive, DropBox, OneDrive Pindai tetapi menggunakan metode yang disederhanakan tanpa konten file File akan diimpor tetapi menggunakan metode yang kurang akurat sehingga tidak memerlukan konten file untuk diunduh dan ditampilkan secara lokal. Terapkan ke semua Timpa status instalasi Ketika diatur, Playnite akan mengabaikan status instalasi (termasuk direktori instalasi) yang ditetapkan oleh plugin integrasi yang mengimpor permainan ini. Opsi ini mungkin tidak sepenuhnya berfungsi dengan plugin yang menggunakan metode impor permainan tertentu kecuali jika plugin tersebut juga mempertimbangkan opsi pengabaian ini. Hanya secara manual Sekali sehari Sekali seminggu Di setiap startup Periksa pembaruan program Periksa pembaruan pengaya Perbarui pustaka Pindai folder emulasi Sertakan permainan tersembunyi Edit isian Pilih / Batalkan semua Buka Aktivasi Setel Mulai mengetik untuk mencari permainan... [F1] untuk bantuan Dimulai dengan # akan menampilkan daftar perintah yang tersedia. Memulai dengan / akan menampilkan daftar penyedia/plugin pencarian yang tersedia. Mengetik kata kunci pencarian dan diakhiri dengan SPASI akan langsung beralih ke pencarian tersebut. TAB: beralih aksi ENTER: mengaktifkan aksi yang dipilih Shift-ENTER: membuka menu item Sertakan permainan yang tidak terpasang Sertakan permainan tersembunyi Termasuk permainan yang tidak terinstal Kecuali permainan yang tidak terinstal Termasuk permainan tersembunyi Kecuali permainan tersembunyi Mainkan atau Pasang Buka detail Menu permainan Edit game Buka pencarian Kotak pencarian Tombol pencarian Aksi permainan utama Aksi permainan kedua CTRL-F membuka pencarian global daripada memfokuskan kotak pencarian Simpan pengaturan filter permainan di antara sesi pencarian Penyedia pencarian Kata kunci bawaan Kata kunci kustom Pintasan di seluruh sistem Pencarian Playnite Pengaturan Ekstensi Pengecualian File yang dikecualikan relatif terhadap folder untuk pemindaian Folder yang dikecualikan relatif terhadap folder untuk pemindaian Tambahkan file ke daftar pengecualian Tambahkan folder ke daftar pengecualian Pengecualian hanya dapat ditambahkan ke konfigurasi pemindai yang disimpan. Pengecualian telah ditambahkan ke pemindai “{0}”. Pilih Platform Apabila diatur, pemindai akan menetapkan platform ini ke semua game, menimpa platform yang terdeteksi secara otomatis. Sertakan perintah dalam pencarian bawaan Jika dinonaktifkan, perintah tidak akan disertakan dalam pencarian bawaan hingga awalan # digunakan. Gunakan pencocokan samar pada filter nama Jika diaktifkan, filter nama akan mencocokkan nama permainan dengan cara yang sama seperti pencarian global. Pencocokan ketat dapat diberlakukan pada setiap huruf dengan mengawali filter dengan tanda ! Kolom yang akan ditampilkan untuk hasil permainan: Status Tersembunyi Pencadangan data dibatalkan. Pencadangan data gagal dilakukan. Kesalahan pencadangan data Pencadangan data dalam proses... Memulihkan data dari cadangan... Gagal memulihkan data dari cadangan. Pengaturan Pustaka Game Media pustaka game Ekstensi terpasang Data ekstensi Tema terpasang Pilih data yang akan dipulihkan dari file cadangan yang ditentukan. Playnite akan secara otomatis dimulai ulang untuk memulai proses pemulihan cadangan. Pilih item yang akan disertakan dengan cadangan data. Pengaturan aplikasi dan data perpustakaan game disertakan secara bawaan. Playnite akan dimulai ulang secara otomatis untuk memulai proses pencadangan. Pencadangan data otomatis Periode pencadangan otomatis Folder cadangan Rotasi cadangan Sertakan data tambahan: Folder pencadangan perlu diatur jika pencadangan otomatis diaktifkan. Tampilkan pemberitahuan hanya untuk rilis patch saja Ketika diaktifkan, hanya pembaruan yang tersedia untuk rilis utama yang sedang diinstal yang akan menampilkan notifikasi pembaruan. Rilis utama baru tidak akan menampilkan pemberitahuan pembaruan. Gunakan tanggal relatif selama pekan lalu Gunakan tanggal relatif dalam format “Hari ini”, “Kemarin”, dsb. jika tanggalnya kurang dari seminggu. Format tanggal yang ditentukan akan digunakan untuk semua tanggal lainnya. Pencarian gambar di web Teks pencarian gambar ikon Teks pencarian gambar sampul Teks pencarian gambar latar belakang Mendapatkan informasi pengaya... Tidak ada sumber metadata yang tersedia Pengaturan tindakan bermain Gunakan pengaturan pemindai Pilih profil saat memulai Pilih emulator saat memulai Otomatis Selalu nyala Selalu mati Dukungan aksesibilitas (pembaca layar) Menu aplikasi Menu Game Folder program Direktori data pengguna Kerusakan file perpustakaan telah terdeteksi, Playnite sekarang akan ditutup. Buka masalah baru di halaman GitHub Playnite dengan permintaan untuk memperbaiki kerusakan pada file Anda. Apakah Anda ingin menyimpan perubahan yang Anda buat? Pemasangan portabel Tidak ada kontroler yang terdeteksi ================================================ FILE: source/Playnite/Localization/it_IT.xaml ================================================  Italiano Lingua Playnite Esci Filtro attivo Filtro disattivato Filtri aggiuntivi Filtri Filtro Dati non validi Salvare le modifiche? Pagina principale su www.playnite.link Codice sorgente su GitHub Crea pacchetto diagnostica Invia le informazioni diagnostica Informazioni su Playnite Creato da Josef Němec Assegna una categoria Imposta categorie Aggiungi una categoria Selezionato - Assegna categoria Non selezionato - Rimuovi categoria Indeterminato - Nessuna modifica (mentre si modificano più giochi) Nessuna categoria Nessuna piattaforma Ops! Qualcosa è andato storto... Si è verificato un errore irrecuperabile. Se vuoi aiutarci a risolvere questo problema, per favore descrivi brevemente le azioni intraprese prima del crash e poi invia le informazioni diagnostiche. Se sei online, il pacchetto sarà caricato sul server di Playnite per l'analisi. In alternativa, puoi cliccare sul pulsante "Segnala Crash" per creare un nuovo problema su GitHub e segnalare il crash manualmente. Grazie per il tuo aiuto. L'Estensione "{0}" ha causato un errore irreparabile. Consigliamo di salvare il file di registro e di riportare il problema allo sviluppatore dell'estensione. Se il problema persiste, disattivare l'estensione. Estensione "{0}" ha causato un errore irreparabile. Consigliamo di riportare il problema allo sviluppatore dell'estensione. Se il problema persiste, disattivare l'estensione. Estensione o un tema sconosciuto hanno causato un errore irrecuperabile. Consigliamo di disattivare le estensioni di terze parti, isolando quello problematico e segnalando il problema allo sviluppatore dell'estensione. Si è verificato un errore fatale. Se vuoi aiutarci a risolvere questo problema, invia informazioni diagnostiche. Grazie. Disattiva l'estensione Salva il file di registro Invia info di diagnostica Segnala crash Riavvia Playnite Riavvia in modalità sicura Disabilita tutte le estensioni di terze parti e usa il tema predefinito Esci da Playnite Azioni eseguite prima dell'arresto (in inglese): Gestione libreria Rimuovere gioco/i? Impossibile rimuovere - Un gioco o l'installazione è in esecuzione. Impossibile disinstallare - Il gioco è in esecuzione Sei sicuro di voler eliminare {0}? Sei sicuro di voler rimuovere {0} giochi? Sei sicuro di voler rimuovere {0}? Selezionando "Aggiungi alla lista delle esclusioni" i giochi non saranno importati nuovamente quando la libreria sarà aggiornata. Sei sicuro di voler rimuovere {0} giochi? Selezionando l'opzione "Aggiungi alla lista delle esclusioni" i giochi non saranno importati nuovamente quando la libreria sarà aggiornata. Sicuro di voler rimuovere {0} voci al momento inutilizzate? Nessun campo inutilizzato trovato. Sì (aggiungi alla lista delle esclusioni) Ci sono modifiche non salvate in questa sezione Aggiornamento del database a versione… Aggiornamento del database fallito. Aggiornamento del database fallito. {0} MB di spazio libero richiesti. Errore di gioco Impossibile avviare il gioco. '{0}' non è stato trovato nel database. Impossibile avviare gioco: {0} Impossibile avviare l'azione: {0} Impossibile aprire il percorso del gioco: {0} Impossibile rilevare la dimensione di installazione del gioco: {0} Errore di scansione dimensione installazione Si sono verificati {0} errori durante la scansione delle dimensioni di installazione Impossibile creare il collegamento: {0} Impossibile aprire il manuale: {0} Impossibile installare il gioco: {0} Impossibile disinstallare il gioco: {0} Nessuna azione di avvio del gioco valida trovata. Quando utilizzi le azioni dell'emulatore, assicurati che le definizioni della piattaforma corrispondano tra il gioco e la configurazione dell'emulatore. Implementazione dell'installazione non è disponibile. Il plugin della libreria responsabile per questo gioco non è installato o abilitato. Il download ufficiale dei metadati non è disponibile. Nessun gioco selezionato. Avvio dello script di gioco fallito. Esecuzione dello script dell'applicazione non riuscita. Avvio dello script globale fallito. Avvio dello script dell'emulatore fallito. Avvio dello script d'azione fallito. PowerShell 3.0 o superiore non installato. Impossibile determinare come avviare il gioco. Abilitato Disattivato Rimuovi Rimuovere i titoli inutilizzati Rinomina Copia Aggiungi Icona di base Copertina di base Sfondo predefinito Finito Avanti Indietro FINITO INDIETRO PULITO Elimina Scarta Scarta tutto Importa Nome Autore Modulo Serie Versione Giocato l'ultima volta Più giocato Numero di partite Dimensione Installazione Cartella Note Aggiunto Data di aggiunta Modificato Data di modifica Sito Percorso OK Salva Chiudi Cancella Confermare Azzera No Benvenuto Utente locale Generale Media Collegamenti Installazione Azioni Download… Scaricamento dei metadati… Caricamento… Tipo Profilo Profili Rimuovi Scarica Cerca Risoluzione Qualsiasi Zoom Visualizzazione elenco Copertine Visualizzazione griglia Visualizzazione dettagliata Personalizzato URL Ringraziamenti speciali Licenza Contributori Chiusura di Playnite… Oggi Ieri Lunedì Martedì Mercoledì Giovedì Venerdì Sabato Domenica Settimana scorsa Mese scorso Anno scorso Più di un anno fa 0 a 100MB da 100MB a 1GB da 1GB a 5GB da 5GB a 10GB da 10GB a 20GB da 20GB a 40GB da 40GB a 100GB 100GB o più Importazione completata con successo. Tutti i giochi Id del gioco Id del database Preimpostazioni Colonna Colonne Riga Righe Impossibile ottenere l'icona dall'azione Play. Non è presente alcuna azione di tipo File. Scarica soltanto il metadata mancante Abilitando questa opzione verrà saltato il download dei metadati per i campi dati che contengono già informazioni. Selezione Giochi Seleziona quali giochi dovrebbero essere aggiornati con nuovi metadati: Tutti i giochi dalla banca dati Tutti i giochi al momento filtrati Solo i giochi selezionati Nessun campo di metadati selezionato Nessun campo di metadati selezionato per il download. Si prega di selezionarne almeno uno e abilitare almeno un provider di metadati. Negozio ufficiale IGDB Selezionare quali campi dovrebbero essere automaticamente popolati da Playnite e da quali fonti dovrebbero essere usati per ricavare i dati. Si prega di considerare di cliccare sul logo sopra e di contribuire agli aggiornamenti al database igdb.com per migliorare l'uso dei dati da Playnite. Scaricamento dei metadati… Importazione dei giochi installati… Importazione giochi di {0}… Importa giochi emulati da {0}… Scaricamento degli aggiornamenti della libreria… Scansione della dimensione dei giochi nella libreria… Dimensione della scansione dei giochi importati… Aggiornamento libreria completato Rilascio delle risorse… Configurazione Impostazioni… Piattaforme ed emulatori Configura gli emulatori… Gestione libreria… Strumenti Scaricamento metadata… Strumenti software... Configurare l'integrazione Aprire Client di terze parti Client di terze parti Aggiorna la lista dei giochi Annulla l'aggiornamento della libreria Aggiorna cartelle emulate Aggiungere gioco Manualmente… Scansiona automaticamente… Gioco emulato… Applicazione di Microsoft Store... Informazioni su Playnite Invia commento Imposta a schermo intero Collegamenti Aiuto Supporta su Patreon Supportaci su Ko-Fi Manuale utente Documentazione SDK Riavvia il sistema Arresta il sistema Sospendi il sistema Ibernare il sistema Sistema Di Blocco Disconnetti utente Scegli un gioco casuale Campi del gioco da visualizzare nel pannello dettagli: Spaziatura oggetti Disegna sfondo elemento griglia Larghezza del bordo dell'oggetto nella griglia Fonte dell'icona del gioco mancante Fonte della copertina del gioco mancante Fonte dello sfondo di gioco mancante Spaziatura verticale ai dettagli del gioco Posizione dettagli a griglia Posizione lista dettagli Disegna separatore tra i pannelli Altezza immagine della copertina gioco Altezza icona lista giochi Carattere dell'applicazione Carattere monospaziato Posizione pannello filtro Posizione pannello esplora Rendering copertina Rapporto d'aspetto dell'oggetto Le seguenti opzioni avranno effetto anche sulla visualizzazione a griglia in modalità schermo intero Modalità ampliata Confezione DVD Epic Game Store GOG Galaxy 2.0 IGDB Quadrato Banner Steam Cover verticali Steam Twitch * Richiede il riavvio per applicare Impostazioni Generale Pannello superiore Aspetto Dettagli del gioco Layout Avanzate Schermo intero Input Prestazioni Metadati In aggiornamento Ricerca Backup Backup dei dati della libreria Ripristina il backup dei dati Importa automaticamente le modifiche alla libreria Il percorso del file della banca dati non valido, dev'essere impostato un percorso file corretto. Il campo dell'account non può essere vuoto. Scaricare i metadati dopo l'importazione dei giochi Avvia Playnite minimizzato Avvia Playnite all'avvio del computer Avvia ridotto nella barra delle applicazioni Impossibile registrare l'avvio di Playnite all'avvio del computer. Avvia in modalità schermo Intero Caricamento asincrono dell'immagine Migliora la fluidità dello scorrimento delle liste giochi a discapito dei tempi di caricamento dell'immagine. Mostra il nome del gioco quando la copertina risulta mancante Mostra i nomi dei giochi in copertina Oscura i giochi non installati Mostra le icone del gioco nella visualizzazione a lista Mostra il conto degli oggetti nelle descrizioni del gruppo Mostra solo i campi assegnati nei pannelli di filtro e di esplorazione Disattiva l'accelerazione hardware Utilizzare quando si presentano scatti o problemi simili nell'interfaccia grafica Mostra i giochi nascosti nella lista avvio rapido Ha effetto sulla Jumplist e sulle liste dell'area di notifica. Numero di oggetti in avvio veloce Usa l'immagine di sfondo del gioco come sfondo della finestra Sfocatura dello sfondo Qualità elevata Sfondo scuro Visualizza griglia Tema Profilo del tema Tema Schermo Intero Colore del tema Schermo Intero Posizione del database Stato accesso: Impostazioni di Playnite Cancella cache web Potrebbe risolvere i problemi riscontrati durante il collegamento degli account. Mostra l'icona nell'area di notifica Minimizza Playnite nell'area di notifica Minimizza Playnite nell'area di notifica quando la finestra viene chiusa Quando si avvia il gioco: Dopo la chiusura del gioco: Formato tempo di gioco per indicare il numero di giorni giocati Formato delle date: Questo ti farà uscire da tutti i servizi collegati. E' richiesto il riavvio dell'applicazione, desideri procedere? Ripulire la cache? E' richiesto il riavvio di Playnite per applicare il nuovo tema Ottieni più temi Crea un nuovo tema Ottieni più estensioni Crea una nuova estensione Aiutaci nella traduzione di Playnite Playnite deve essere riavviato per applicare le nuove impostazioni. Riavviare ora? Riavviare annullerà tutte le attività attualmente in corso (download) Riavviare Playnite? Playnite non può spostare automaticamente i file della libreria. Devi spostarli/copiarli prima di cambiare la posizione. Se nessuna libreria è presente nella posizione di destinazione, ne verrà creata una nuova. La nuova posizione del database verrà utilizzata solo dopo il riavvio dell'applicazione. Il tempo di gioco non sarà registrato se l'azione "Chiudi" è impostata. Numero di righe Numero di colonne Numero di dettagli delle righe Mostra immagine sfondo nello Schermo Principale Non si applica retroattivamente ai giochi esistenti senza dover riscaricare i metadati. Importa il tempo di riproduzione dei giochi nella libreria: Configura quando Playnite deve importare il tempo di riproduzione riportato dai plug-in della libreria per i giochi nel database Playnite. Per poter utilizzare questa funzione è necessario il supporto dei plugin della libreria incaricati della gestione dei giochi. Sempre: importa il tempo di gioco per i nuovi giochi importati ed esistenti nel database di Playnite. Solo per i nuovi giochi importati: importa il tempo di gioco solo per i nuovi giochi importati. Mai: non importa mai il tempo di riproduzione in nessuna circostanza. Sempre Solo per i giochi appena importati Mai Abilità il supporto per il controller in modalità desktop Il pulsante guida apre la modalità a schermo intero Impostazioni per lo scaricamento automatico dei Metadata per i giochi appena importati Schermata utilizzata Usare sempre lo schermo principale Visualizza titoli dei giochi Mostra lo stato della batteria Mostra la percentuale della batteria Visualizza orologio Nascondi il cursore Solo Installati nei Filtri Rapidi Funzioni dei pulsanti Layout Scorrimento orizzontale Seleziona una delle sottosezioni Nessuna impostazione disponibile Caricamento delle impostazioni non riuscito Questi script sono eseguiti per ogni gioco nella libreria. I singoli script possono essere assegnati a ogni gioco separatamente mentre si modificano i dettagli del gioco. Transizione per le immagini di sfondo animate Dimensione caratteri Automatico Con alias Scala di grigi ClearType Ideale Schermo Modalità della formattazione del testo Modalità di rendering del testo I metodi di rendering e di formattazione non sono al momento utilizzati per le descrizioni del gioco. Precarica le immagini di sfondo Se disattivato, Playnite scaricherà l'immagine dello sfondo durante lo scaricamento del metadata. Utilizzando più spazio del disco e rendendo l'immagine disponibile quando non è connesso. Se disattivato, l'immagine dello sfondo sarà scaricata soltanto quando sarà richiesto. Utilizza meno spazio, ma potrebbe causare un ritardo prima di mostrare lo sfondo e alcune immagini potrebbero essere non disponibili quando non si è connessi. Chiudere automaticamente il launcher dopo essere usciti dal gioco Ritardo della chiusura del client (in secondi) Non chiudere dopo delle sessioni di gioco più corte di (in secondi) Chiudere automaticamente i seguenti client: Chiudere i client automaticamente Importa la lista delle esclusioni Mostra un avvertimento quando si assegna un'immagine troppo grande del gioco Comando di apertura cartella Organizza per la classificazione d'età Aggiorna la dimensione di installazione dei giochi nell'aggiornamento della libreria Scansiona e aggiorna la dimensione di installazione dei giochi se viene rilevato che i loro file sono stati modificati dall'ultima scansione Niente Riempi Uniforma Uniforma per riempire A sinistra A destra In alto In basso Errore di importazione Autenticazione richiesta Autenticazione fallita Modalità di rendering web alternativo Usa quando si presentano problemi con finestre web, per esempio l'integrazione dei dialoghi di autentificazione. Caricamento parziale delle grandi descrizioni dei giochi Le grandi descrizioni possono causare lag notevole quando si selezionano i giochi. Se abilitato, solo una parte del testo della descrizione verrà inizialmente caricata con un'opzione per caricare il resto su richiesta. Importazione dei metadati Scaricamento dei metadati Imposta la configurazione selezionata da utilizzare per qualsiasi download futuro dei metadati. Può essere modificata anche nelle impostazioni dell'applicazione. Wizard dell'importazione dell'emulazione Lo strumento ti guiderà attraverso il processo di scaricamento e importazione degli emulatori console e dei giochi emulati Ricorda che puoi sempre aggiungere emulatori aggiuntivi e/o giochi più tardi tramite menu principale (sotto il menu "Strumenti" per le impostazioni Emulator e il menu "Aggiungi giochi" per i giochi emulati). Di seguito la lista di emulatori che Playnite può riconoscere e configurare automaticamente. Può scaricarli e installarli visitando i loro siti. Una volta che hai installato gli emulatori, procedi nella schermata successiva per importarli in Playnite. Puoi anche configurare e importare ogni emulatore personalizzato attraverso il menù di configurazione. Puoi importare qualsiasi emulatore installato sul PC premendo sul tasto "Scansione automatica dalla cartella...". Playnite cercherà la cartella selezionata per ogni emulatore conosciuto e fornirà l'opzione di importarli. Puoi importare da più cartelle usando tale tasto più volte, gli emulatori saranno aggiunti sul fondo della lista attuale. Puoi importare i giochi premendo il tasto "Scansiona la cartella usando l'emulatore" . Selezionando l'emulatore appropriato, sarà comunicato a Playnite quali tipi di tile dovrebbero essere scansionati e importati. Puoi anche importare da più cartelle usando tale tasto più volte, i giochi saranno aggiunti sul fondo della lista attuale. Non ci sono emulatori selezionati per l'importazione. Non sarai in grado di importare automaticamente ogni gioco emulato senza configurare per prima gli emulatori. Sicuro di voler continuare e uscire dal processo di importazione? Non ci sono emulatori configurati in Playnite. Non puoi importare i giochi senza configurare l'emulatore e selezionando i tipi di file appropriati. Desideri aggiungere ora qualche emulatore? Scansiona la cartella usando l'emulatore Seleziona i file Scansione automatica dalla cartella... Configura gli emulatori… Scansione… Analizzando {0}… Configurazione iniziale Questa procedura guidata ti guiderà attraverso il processo di importazione e di configurazione automatica delle librerie di gioco esterne. Playnite può importare automaticamente i giochi da più servizi di gioco come Steam o GOG, e può inoltre mantenere aggiornata la libreria aggiornandola automaticamente durante l'avvio dell'applicazione. Ricorda che puoi sempre aggiungere manualmente qualunque gioco personalizzato da qualsiasi piattaforma cliccando sul tasto "Playnite" nel menù principale. Integrazione della libreria Di seguito è riportato l'elenco di alcune integrazioni di librerie curate che Playnite supporta. Seleziona quelle che vuoi installare. Altre integrazioni possono essere installate in seguito dal menu "Componenti aggiuntivi". Configurazione completata La configurazione iniziale è completata. Ricorda che puoi cambiare le impostazioni in seguito dal menu 'Impostazioni'. Puoi anche aggiungere altri giochi cliccalndo sul menu 'Playnite'. Fallito il download di una o più estensioni. Puoi provare a riscaricare le integrazioni dal menu dei componenti aggiuntivi dopo che la prima esecuzione della procedura guidata è terminata. Scaricamento di {0} integrazione/ni… Scaricamento elenco delle integrazioni raccomandate… Impossibile scaricare l'elenco delle integrazioni consigliate. Puoi provare a scaricare nuovamente le integrazioni in seguito tramite il menu Addons. Configurazione delle piattaforme e degli emulatori Configurazione degli emulatori Piattaforme Piattaforma Emulatori Emulatore Aggiungi piattaforma Seleziona icona Seleziona copertina Seleziona Immagine Seleziona elemento Seleziona Sfondo Seleziona file Seleziona URL Aggiungi emulatore Piattaforma/e supportata/e Desidera salvare le modifiche della piattaforma? Desidera salvare le modifiche dell'emulatore? Eseguibili Parametri Cartella di lavoro Tipologie di file supportati Importa emulatori… Scarica emulatori… Caricare parametri di base da profilo emulatore conosciuto Sicuro di voler rimuovere l'emulatore {0} ? E'al momento utilizzato da {1} gioco/chi. Sicuro di voler rimuovere la piattaforma {0} ? E' al momento utilizzata da {1} gioco/chi e {2} emulatore/i. Aiuto impostazioni Ordina per Ordina Direzione Raggruppa per Crescente Decrescente Non raggruppare Raggruppa per distributore Raggruppa per categoria Raggruppa per piattaforma Tipo di visualizzazione Visualizza Pannello Esplora Pannello dei filtri Icona Icona Libreria Immagine di copertina Immagine dello sfondo Nome ordinamento Libreria Manuale Nome Installa Drive Nome Account Piattaforma Categoria Genere Data di uscita Anno di uscita Sviluppatore Etichetta Editore Stato dell'installazione Abbina tutti i filtri Se abilitato, solo i giochi che usano tutti gli elementi in tutti i filtri saranno inclusi nella vista. Se disabilitato, i giochi che usano qualsiasi elemento in qualsiasi filtro saranno inclusi nella vista. Installati Installati Non installato Nascosti Preferiti Abilita supporto HDR Se abilitato, HDR sarà attivato sul display principale prima di iniziare il gioco. Nota che l'HDR non è supportato sul tuo display principale. Giocato l'ultima volta Categoria Descrizione Cartella d'installazione Immagine di copertina Collegamenti Percorso Immagine/ISO Genere Generi Azienda Compagnie Sviluppatore Sviluppatori Editore Editori Categoria Categorie Etichetta Etichette Funzionalità Funzioni Classificazione di età Classificazioni di età Regione Regioni Sorgente Sorgenti Attività recenti Errore della banca dati Impossibile aprire la banca dati della libreria Il database non è aperto. Impossibile accedere la banca dati della libreria. Il file "{0}" è stato utilizzato da un'altro processo o è in un percorso inaccessibile. Creazione del pacchetto diagnostica fallito. Caricamento automatico del pacchetto diagnostica fallito. Le informazioni del modulo di diagnostica sono state inviate con successo. Pacchetto di diagnostica è stato creato e caricato con successo. La prego di allegare il seguente codice ID al seguente resoconto del crash: Importazione giochi da {0} fallita. Importazione giochi da {0} fallita. Impossibile cercare giochi per profilo di emulatore selezionato. Il profilo non contiene estensioni di file o piattaforme. Avvio di Playnite fallito. Chiudere tutti i processi attivi e riprovare. L'applicazione del tema "{0}", profilo colore "{1}" {2} fallita Impossibile aprire il collegamento, l'URL non è in un formato valido. Impossibile avviare l'applicazione. Impossibile inizializzare il componente di visualizzazione Web. Playnite non può continuare con il processo di avvio. Maggiori informazioni su https://playnite.link/cefstartup Impossibile importare gli emulatori a causa del file di definizione mancante o corrotto. Impossibile eseguire la funzione di estensione. Modifica i dettagli del gioco URL immagine Aggiungi Link Aggiungi ROM Salva modifiche Applica modifica del campo al gioco/chi modificati. Aggiungi Azione Cancella Azione Rimuovi Azione di avvio Aggiungi giochi Scansione cartella… Rilevazione automatica Sfoglia… Apri Playnite Impostazioni del profilo Il nome del gioco non può essere vuoto. La directory di tracciamento delle azioni di gioco non può essere vuota. Il nome del gioco non può essere vuoto prima della ricerca del metadata. Dati di gioco invalidi Inserire un URL web valido che inizia con http:// o https:// Seleziona URL Scaricamento del metadata fallito: {0} Errore di scaricamento Ripulire i filtri Account privato Account pubblico Chiave API Errore all'avvio Errore tema Ripulire tutto Impostazione in corso... Disinstallazione... Avvio Esecuzione URL non corretto Non fare niente Minimizza Ripristina finestra Ripristina finestra solo quando lanciata dall'interfaccia utente Chiudi Cambia Avanzate Mai Stato di completamento Stato di completamento Voto dell'utenza Voto della critica Voto della comunità Script di gioco Script dell'applicazione Script Plugin Fonti dei metadati Estensioni ID dell'estensione Ricarica gli script SDK interattivo PowerShell Tutti gli script sono stati ricaricati con successo. Nessun gioco trovato secondo i criteri di ricerca/filtri specifici Nessun elemento trovato Imposta in modalità desktop Esci da Playnite Librerie Aggiorna tutto Creato Da: Versione: Aggiornato: Modulo: Libreria Statistiche Tutti Nessuno Notifiche Larghezza Altezza Dimensioni Piccolo Normale Grande Più grande Massimo Predefinito Seleziona Seleziona tutto Deseleziona tutto Primo Casuale Selezione utente Carica altro Trasparente Comprimi Espandi Comprimi Tutto Espandi Tutto Altro Temi Argomenti dell'emulatore Parametri integrati Parametri personalizzati Ulteriori argomenti dell'emulatore Sovvrascrivi gli argomenti dell'emulatore Azione di avvio Seleziona metadata da importare Seleziona un gioco da importare. Ricerca del metadata Aggiornamento disponibile Modifiche dall'ultimo aggiornamento Installa l'aggiornamento Controlla aggiornamenti Errore aggiornamento Controllo della nuova versione fallito. Nessuna nuova versione trovata, hai già la versione più recente. Impossibile scaricare e installare l'aggiornamento. Un'attività in background è attualmente in corso. Vuoi annullarla e procedere con l'aggiornamento? Un'attività in background è attualmente in corso. Vuoi annullarla e uscire da Playnite? Un'attività in background è attualmente in corso. Cambiare modalità annullerà il processo, vuoi cambiare comunque? Un aggiornamento per Playnite è disponibile Ricarica la lista dei temi Applica il tema selezionato Osserva la modifica del file Applicare automaticamente il tema quando il file di sorgente viene cambiato Durata dello script Script da eseguire prima dell'avvio di un gioco Script da eseguire dopo l'uscita da un gioco Script da eseguire dopo l'avvio di un gioco Esegui all'avvio dell'applicazione Esegui alla chiusura dell'applicazione Script di avvio gioco Script di gioco avviato Script di arresto gioco Esegui lo script globale Globale Filtrato Corrente Nuovo Testa lo script Mostra solo gli elementi selezionati. Salva come predefiniti Aggiungi ai preferiti Rimuovi dai preferiti Nascondi questo gioco Rimuovi dalla lista nascosta Abilita Supporto HDR Disabilita Supporto HDR Modifica… Calcola dimensione installazione Calcola la dimensione dell'installazione (tutti i giochi) Calcola la dimensione dell'installazione (solo i dati mancanti) Dimensione installazione Imposta la categoria… Stato di completamento Rimuovi Gioca Installa Opzioni di Gioco Dettagli Disinstalla Apri percorso gioco Crea collegamento nel desktop Apri il manuale Altro Gestito dal plugin della libreria Il processo di avvio del gioco sarà gestrito dal plugin della libreria responsabile per questo gioco. Nessuna informazione rilevante riguardo il gioco '{0}' è stata trovata nella pagina specificata. Consiglio: puoi utilizzare un processo di scaricamento del metadata più avanzato durante la modifica del gioco attraverso l'opzione del menu "Modifica". Non disponibile mentre alcune azioni sono in corso. La descrizione del testo è sensibile alla sintassi HTML Il tempo di gioco è registrato in secondi. La dimensione dell'installazione è indicata in byte. La data di uscita deve essere impostata in "anno-mese-giorno". I valori del mese e del giorno possono essere ommessi. I valori da 0 a 100 o nulli per nessun punteggio. Lo sviluppo di Playnite è supportato da questi patrons e membri Ko-Fi: Codice, traduzione e altri contributori in nessun ordine particolare: Annullare il monitoraggio del gioco? Il monitoraggio dell'installazione è tutt'ora in corso. Desidera cancellare il processo e far tornare il gioco a uno stadio precedente? Il monitoraggio del gioco in esecuzione è tutt'ora in corso, desideri cancellare il processo e far tornare il gioco a uno stadio precedente? Tempo di gioco Giocato l'ultima volta {0}g {1}h {2}m {0}o {1}m {0} minuti {0} secondi Non giocato Apertura della modalità desktop… Apertura della modalità schermo intero… Caricamento della libreria di gioco… Calcolo dimensione installazione… Calcolo della dimensione di installazione di {0}… Installazione del file script fallita. Script installato con successo. Installazione script Errore script Impossibile eseguire la funzione di estensione. Aprire la cartella dei metadati Calcola Calcola automaticamente la dimensione di installazione usando i Roms se il gioco ha una o la directory di installazione se è stata impostata {0} client non installato. Il client di {0} si aprirà ora. Accedi e poi chiudi questo messaggio. In attesa dell'accesso dell'utente, chiudi questo quando hai finito… Cartella di installazione del gioco non trovata. Configurazione dell'azione del gioco non valida. Risoluzione dei problemi di sincronizzazione dell'account Risoluzione dei problemi Rinomina oggetto Aggiungi nuovo elemento Inserisci nome Inserisci nuovo nome Meno di un'ora da 1 a 10 ore da 10 a 100 ore da 100 a 500 ore da 500 a 1000 ore Più di 1000 Playnite deve essere riavviato per compeltare l'installazione. Desidera raivviare ora? L'estensione non è confezionata correttamente Il tema non è stato impacchettato a dovere. L'estensione "{0}" non è stata caricata correttamente. Impossibile caricare l'estensione "{0}", l'attuale versione di Playnite non è supportata. Tema "{0}" non è stato caricato correttamente. Impossibile caricare il tema "{0}", l'attuale versione di Playnite non è supportata. L'estensione non è stata caricata correttamente. Tema non è stato caricato correttamente. Tema/Estensione sta usando una versione API non supportata. Installazione avvenuta con successo. Installare il componente aggiuntivo? Generico Impossibile installare il componente aggiuntivo "{0}". Installazione estensione fallita. {0} Vuoi installare una nuova estensione? {0} Autore {1} Versione {2} Vuoi aggiornare l'estensione "{0}"? Versione attuale: {1} Nuova versione: {2} Impossibile installare il tema. {0} Vuoi installare un nuovo tema? {0} Di {1} Versione {2} Vuoi aggiornare il tema "{0}"? Versione attuale: {1} Nuova versione: {2} Stai per lasciare Playnite e navigare nella pagina web seguente utilizzando il browser web predefinito. Vuoi continuare? {0} L'immagine/le immagini selezionato/te potrebbero essere troppo grandi per la prestazione ottimale. Usare immagini molto grandi potrebbe risultare in una responsività della UI pesantemente peggiorata e un'aumento dell'utilizzo della memoria. Dimensioni massime raccomandate: Icone: {0} px altezza Copertine: {1} px altezza Sfondi: {2} px altezza Avviso di prestazione Non mostrare più File con estensione {0} non è compatibile. Estensione del file incompatibile Il file dell'immagine selezionata potrebbe essere troppo grande per delle performance ottimali. Sicuro di voler installare il tema selezionato? La disintallazione sarà messa in coda al prossimo avvio dell'applicazione. Le estensioni native non possono essere disinstallate. Questa estensione non è supportata da questa versione di Playnite. Sei sicuro di voler disinstallare l'estensione selezionata? La disinstallazione sarà accodata al prossimo avvio dell'applicazione. Le estensioni native non possono essere disinstallate Questa estensione non supporta questa versione di Playnite. Cartella d'installazione Dati della directory Generazione pacchetto di diagnostica... Invio pacchetto di diagnostica... importazione file... Cos'è questo? Vuoi davvero eseguire questa operazione? Tempo totale di gioco Tempo di gioco medio Tempo di gioco massimo Dimensione totale installazione Panoramica Barra laterale Mostra barra laterale Ripristina impostazioni Tutte le impostazioni dell'applicazione saranno riportate ai valori predefiniti, esclusi: - Posizione del database - Elenco di esclusione delle importazioni - Impostazioni delle estensioni, incluse le integrazioni delle librerie Il riavvio dell'applicazione è richiesto per finire il processo. Vuoi resettare le impostazioni? Per gli sviluppatori Estensioni esterne Inserisci il percorso completo della cartella Obiettivi Forum Notizie Pagina del negozio La prima configurazione non è completa. Playnite ora si riavvierà alla modalità desktop per terminare la procedura. Giocati di recente Preferiti Più giocati Tutti Ci sono filtri applicati. Ci sono filtri aggiuntivi applicati. Risultati della ricerca per: Impossibile rinominare il file o la cartella è già esistente. Limita la selezione al filtro attuale Selezionare altro Componenti aggiuntivi... Installato/i Opzioni dei componenti Sfoglia Aggiornamenti Aggiornamenti {0} La gestione delle estensioni e dei temi installati, comprese le loro impostazioni, è stata spostata in un nuovo menu "Componenti aggiuntivi". Tutte le estensioni di integrazione della libreria attualmente installate possono essere configurate qui. Se vuoi installare o disinstallare ulteriori integrazioni, usa l'opzione "Estensioni" dal menu principale. Temi desktop Temi schermo intero Ricerca in corso... Questo componente aggiuntivo non è compatibile con questa versione di Playnite. Impossibile scaricare il pacchetto di installazione del componente aggiuntivo. Impossibile scaricare il manifesto di installazione del componente aggiuntivo. Riavvio dell'applicazione è richiesto per applicare le modifiche. Questo componente aggiuntivo è previsto per l'installazione. Installa Reinstalla Disinstalla Già installato Nessun aggiornamento disponibile per i componenti aggiuntivi. Aggiorna estensioni Changelog non disponibile Programmato per l'installazione Download non riuscito Licenza rifiutata Scaricando {0}... Controllo degli aggiornamenti per i componenti aggiuntivi... Ricerca aggiornamenti del programma... Sono disponibili uno o più aggiornamenti per componenti aggiuntivi. Seleziona gli elementi da aggiornare Istanza di sviluppo dell'estensione Accordo di licenza {0} Accetta Declina Include le azioni di esecuzione dell'integrazione della libreria. Selezione azione Modalità di monitoraggio Percorso Di Tracciamento Ritardo iniziale di monitoraggio Frequenza di monitoraggio Collegamento File Emulatore Script Predefinito Processo Cartella Processo originale Nome del processo Registra i messaggi fissati Le seguenti modifiche sovrascriveranno i dati di tutti i giochi selezionati! Nessuno Uniforme Solo Oggetti Inizio e fine solamente Sensibilità di scorrimento Scorrimento fluido Velocità di animazione Rimuovere l'oggetto? Sei sicuro di voler rimuovere questo elemento? Mostra i pulsanti nel pannello superiore: Impostazioni di visualizzazione generale Impostazioni di ragruppamento Impostazioni di ordinamento Parametri di filtraggio Posizione di oggetti del plugin Larghezza di separatore della sezione Sposta il pulsante del menu principale sulla barra laterale Pannello di esplorazione Selettore di gioco casuale Visualizza selettore casuale del gioco Seleziona un gioco casuale dalla vista Salva le impostazioni di raggruppamento e di ordinamento Mostra come filtro rapido in modalità schermo intero Negli ultimi 7 giorni Negli ultimi 31 giorni Negli ultimi 365 giorni Più di 365 giorni Configura Salva preimpostazione Riduci dopo aver avviato il gioco Riduci Playnite dopo l'avvio del gioco. Disattivare questa opzione può portare a problemi con i giochi che non ottengono la messa a fuoco dei comandi all'avvio! Dimensione carattere Dimensione caratteri piccola Abilita il supporto API del controller di gioco Supporto controller di gioco Se disabilitato, Playnite non accetta nessun input del controller di gioco. Disabilita se usi strumenti che traducono gli input del controller di gioco in ingressi del mouse/tastiere e stai ricevendo doppi ingressi in Playnite. Mostra oggetti nel menu principale: Pulsanti X/A nella pagina principale invertiti Inverti l'associazione di pulsanti per avviare un gioco e mostrando la pagina dei dettagli del gioco nella pagina principale. Scambio associazione pulsante conferma/annullamento Inverte le associazioni dei pulsanti A/B per conferma e cancellazione. Solo controller primario Accetta input dal controller primario solo se abilitato. Il pulsante guida focalizza Playnite Volume dell'interfaccia Volume di sfondo Muta quando è nello sfondo Impossibile inizializzare l'interfaccia audio. API di uscita API usato per l'uscita audio. Cambia se stai avendo problemi con il suono. Generale Effetti visivi Audio Disposizione Menu Input {0} in avvio... {0} in esecuzione… Maiuscole Spazio Scalatore di rendering dell'immagine Alternativo Bilanciato Qualità Qualità: Migliore qualità dell'immagine, lento, alto utilizzo della memoria. Bilanciato: Buona qualità, veloce, basso utilizzo della memoria. Alternativa: Migliore qualità, velocità media, basso utilizzo della memoria. Seleziona un file... Seleziona cartella... Script di avvio Tieni presente che entrambe le estensioni e i temi posso influenzare la prestazione, la stabilità e la sicurezza di Playnite. Se cominci ad avere qualche problema dopo l'installazione di un tema o di una estensione, prova a disattivare/disinstallare l'estensione/ni per determinare se è la causa del problema. Scegli all'avvio Scegli all'avvio Profili integrati Profilo integrato Profili personalizzati Profilo personalizzato Gestito da uno script integrato Specifiche dell'emulatore Specificazioni della piattaforma Specificazione della regione Avvia prima di avviare l'emulatore Eseguire dopo l'avvio dell'emulatore Avvia dopo essere uscito dall'emulatore Eseguibile dell'emulatore non trovato. Specifica dell'emulatore non trovata. Script avvio dell'emulatore non trovato. Separa in più giochi Raggruppa in un unico gioco indica la piattaforma Imposta regione Scansiona cartella Scansione delle configurazioni Escludi i modelli dalla scansione di integrità dati I file che corrispondono al modello specificato non saranno analizzati per il controllo integrità dati e saranno abbinati al nome del file. Vedi la pagina di aiuto dell'emulatore per maggiori informazioni. Scansiona con emulatore Il nome deve essere impostato quando si salva una nuova configurazione. L'emulatore o il profilo dell'emulatore non è impostato. La directory da scansionare non è specificata o non esiste. La configurazione della scansione non è impostata correttamente. Includi nella scansione automatica globale Impossibile scansionare le cartelle per gli emulatori. Impossibile scansionare le cartelle per i giochi emulati. Nascondi gli importati Profili da importare: Configurazioni della scansione automatica Salva come configurazione della scansione automatica Salva la configurazione per un suo successivo durante l'aggiornamento della libreria. Configurazioni salvate possono essere mangiate attraverso il menu "Configura emulatori". Importa utilizzando percorsi relativi Se possibile importa i file di gioco utilizzando i percorsi relativi alla cartella di installazione di Playnite o alla cartella di installazione dell'emulatore. Scansiona le sottocartelle Scansione all'interno degli archivi Unisci file correlati Unisci i file di gioco correlati, come i singoli dischi di gioco, sotto una voce di gioco. Aggiungi scanner Aggiungi scanner salvato Avvia scansione Aggiungi scansione della/delle configurazione/i con emulatori per scansionare cartelle specifiche. Accertati che gli emulatori siano configurati prima di importare i giochi (via il menu Libreria -> menu di Configurazione Emulatori). Stato di default assegnato ai giochi recentemente aggiunti Status assegnato ai giochi giocati per la prima volta Impossibile inizializzare lo script PowerShell. Se sei un utente Windows 7, prova a (re)installare PowerShell 5.1 per riparare il problema. Il preset del filtro con il nome specificato esiste già. Aggiornare il preset con le nuove impostazioni? Riempi automaticamente i nomi di ordinamento mancanti per i giochi aggiunti o modificati in batch Quando modifichi un gioco, aggiungi giochi tramite un aggiornamento della libreria, una scansione della cartella dell'emulatore o una normale scansione della cartella, compila automaticamente il campo “Nome di ordinamento” con una rappresentazione del nome del gioco più facilmente ordinabile. Ad esempio, “The Witcher 3” avrà come nome di ordinamento “Witcher 03”. In questo modo non verrà mai impostato un nome di ordinamento che non differisca dal nome del gioco e verranno aggiornati automaticamente solo i nomi di ordinamento vuoti. Queste parole verranno rimosse dall'inizio del valore del nome di ordinamento compilato automaticamente: Usalo per ignorare le parole all'inizio di una stringa per scopi di ordinamento. Il valore predefinito è "The", "An" e "A". Riempi il nome di ordinamento per i giochi senza uno Ordinamento Riempimento dei valori del nome… È stato rilevato che il servizio Nahimic è in esecuzione sul tuo sistema. Questo servizio è noto per causare problemi di rendering a Playnite (e altre applicazioni). Se incontri corruzione grafica o altri problemi di rendering in Playnite, ti consigliamo di disabilitare o disinstallare completamente il servizio Nahimic. Maggiori informazioni su https://playnite.link/nahimicsucks Playnite è in esecuzione con privilegi elevati (come amministratore). Questo non è raccomandato poiché dà privilegi elevati a tutte le estensioni installate e a tutti i giochi/app avviati da Playnite! Maggiori informazioni su https://playnite.link/adminfaq Mostra avviso se Playnite è in esecuzione con privilegi elevati Ottenere la dimensione reale sul disco quando si calcola la dimensione dei giochi Se abilitata, le scansioni saranno più lente e otterranno la dimensione reale che i file usano nell'unità. Se disabilitata, le scansioni saranno più rapide e useranno le dimensioni dei file stessi. I seguenti add-on sono stati segnalati come potenzialmente problematici, sia a causa di un alto impatto sulla stabilità/prestazione che per problemi di sicurezza. Ti consigliamo vivamente di disinstallarli: {0} Escludi i file online dalla scansione I file memorizzati sul cloud storage non saranno scansionati e importati se non sono disponibili localmente. Supportato solo per: Google Drive, DropBox, OneDrive Scansiona ma usa metodo semplificato senza il contenuto del file I file saranno importati ma usando un metodo meno accurato che non richiede il contenuto del file di essere scaricati e presenti localmente. Applica a tutti Sovrascrivi lo stato di installazione Quando impostato, Playnite ignorerà lo stato di installazione (inclusa la directory di installazione) impostato dal plugin di integrazione che importa questo gioco. Questa opzione potrebbe non funzionare completamente con plugin che utilizzano un metodo di importazione di gioco specifico a meno che non tengano conto anche di questa opzione di override. Solo manualmente Una volta al giorno Una volta a settimana Ad ogni avvio Controlla gli aggiornamenti del programma Controlla aggiornamenti aggiuntivi Aggiorna librerie Scansione cartelle di emulazione Includi giochi nascosti Modifica campi Seleziona / Deseleziona tutto Apri Attiva Assegna Inizia a digitare per cercare giochi… [F1] per aiuto A partire da # porta su un elenco di comandi disponibili. A partire da / porta su un elenco di provider di ricerca/plugin disponibili. Digitare la parola chiave di ricerca e terminare con SPACE passa immediatamente a quella ricerca. TAB: cambia azione INVIO: attiva l'azione selezionata SHIFT-ENTER: apri il menu item Includi giochi disinstallati Includi giochi nascosti Giochi non installati inclusi Giochi disinstallati esclusi Giochi nascosti inclusi Giochi nascosti esclusi Riproduci o installa Vai ai dettagli Menu di partita Modifica partita Apri ricerca Casella di ricerca Pulsante ricerca Azione di gioco primaria Azione secondaria di gioco CTRL-F apre la ricerca globale invece di focalizzare la casella di ricerca Salva le impostazioni del filtro di gioco tra le sessioni di ricerca Cerca per provider Parola chiave predefinita Parola chiave personalizzata Collegamenti di sistema Ricerca Playnite Impostazioni estensione Esclusioni File esclusi relativi alla cartella di scansione Cartelle escluse relative alla cartella di scansione Aggiungi file all'elenco di esclusione Aggiungi cartella all'elenco di esclusione Le esclusioni possono essere aggiunte solo alle configurazioni dello scanner salvate. Le esclusioni sono state aggiunte allo scanner "{0}". Sovrascrivi piattaforma Quando impostato lo scanner assegnerà questa piattaforma a tutti i giochi, sovrascrivendo le piattaforme automaticamente rilevate. Includi i comandi nella ricerca predefinita Quando disabilitato, i comandi non saranno inclusi nella ricerca predefinita finché non verrà utilizzato il prefisso # Usa corrispondenza fuzzy nel filtro nomi Se abilitato, il filtro dei nomi corrisponderà ai nomi dei giochi allo stesso modo della ricerca globale. La corrispondenza rigorosa può essere applicata su un singolo caso prefissando il filtro con il carattere ! Campi da visualizzare per i risultati del gioco: Stato nascosto Backup dati annullato. Backup dei dati fallito. Errore di backup dati Backup dei dati in corso… Ripristino dati dal backup… Impossibile ripristinare i dati dal backup. Impostazioni Libreria giochi Media libreria dei giochi Estensioni installate Dati estensioni Temi installati Seleziona i dati da ripristinare dal file di backup specificato. Playnite verrà automaticamente riavviato per avviare il processo di ripristino del backup. Seleziona gli elementi da includere con il backup dei dati. Le impostazioni dell'applicazione e i dati della libreria di gioco sono inclusi per impostazione predefinita. Playnite si riavvierà automaticamente per avviare il processo di backup. Backup automatico dei dati Frequenza di backup automatico Cartella di backup Backup rotanti Includi dati aggiuntivi: La cartella di backup deve essere impostata se il backup automatico è abilitato. Mostra solo le notifiche per le patch rilasciate Se abilitata, solo gli aggiornamenti disponibili per la versione principale attualmente installata comporteranno una notifica di aggiornamento. Le nuove versioni principali non comporteranno una notifica di aggiornamento. Usa date relative per la settimana passata Usa le date relative nel formato "Oggi", "Ieri" ecc. se la data è inferiore a una settimana. Il formato di data specificato verrà utilizzato per tutte le altre date. Ricerca immagine web Stringa di ricerca immagine icona Coprire la stringa di ricerca immagine Stringa di ricerca immagine sfondo Ricerca informazioni aggiuntive… Nessuna fonte dati disponibile Riproduci impostazioni di azione Usa le impostazioni dello scanner Seleziona il profilo all'avvio Seleziona emulatore all'avvio Automatico Sempre acceso Sempre spento Accessibilità (screen reader) supporto Menu applicazione Menu di partita Cartella programma Directory dati utente È stata rilevata la corruzione del file della libreria, Playnite ora verrà arrestato. Apri un nuovo problema sulla pagina GitHub di Playnite con una richiesta di correggere la corruzione nei tuoi file. Vuoi salvare le modifiche? Installazione portatile Nessun controllore rilevato ================================================ FILE: source/Playnite/Localization/ja_JP.xaml ================================================  日本語 Playnite の言語 終了 フィルター有効 フィルター無効 追加のフィルター フィルター フィルター 無効なデータ 変更した内容を保存しますか? ホームページ www.playnite.link ソースコードは Github で公開されています 診断パッケージを作成する システム情報を送信する Playnite について Made by Josef Němec カテゴリーを設定 カテゴリーを設定 カテゴリーを追加 チェックあり - カテゴリーの割り当て チェックなし - カテゴリーを削除 不確定 - 変更なし (複数のゲームを編集する場合) カテゴリーなし プラットフォームなし おっと! 何か間違いがあったようです。 回復不能なエラーが発生しました。 この問題の修正を支援する場合は、クラッシュの前に実行されたアクションを簡単に説明してから、診断パッケージを作成してください。オンラインの場合、そのパッケージは解析のためにPlayniteサーバーにアップロードされます。 「クラッシュのレポート」ボタンを使用して、GitHubで新しい問題を作成し、クラッシュを手動でレポートすることもできます。 ご協力ありがとうございます。 拡張機能 "{0}" で回復不能なエラーが発生しました。 ログファイルを保存し、問題を拡張機能の開発者に報告することをお勧めします。問題が再発する場合は、拡張機能を無効にしてください。 拡張機能 "{0}" で回復不能なエラーが発生しました。 この問題を拡張機能の開発者に報告することをお勧めします。問題が再発する場合は、拡張機能を無効にしてください。 不明な拡張機能またはテーマが原因で回復不能なエラーが発生しました。 サードパーティのアドオンを無効にして、問題のあるアドオンを特定し、アドオンの開発者に問題を報告することをお勧めします。 回復不能なエラーが発生しました。 この問題の修正を支援する場合は、診断情報を送信してください。よろしくお願いします。 拡張機能を無効にする ログファイルを保存 診断情報を送信 クラッシュレポート Playnite を再起動 セーフモードで再起動 すべてのサードパーティー製の拡張機能を無効にし、デフォルトテーマを使用します。 Playnite を終了 クラッシュ前の操作 (英語) ライブラリの管理 ゲームを削除しますか? 削除できません - ゲームまたはインストーラーが起動しています。 アンインストールできません - ゲームが実行中です。 {0} を削除してもよろしいですか? {0} ゲームを削除してもよろしいですか? {0} を削除してもよろしいですか? 「除外リストに追加」オプションを選択すると、次回ライブラリが更新されたときにゲームが再びインポートされるのを防ぐことができます。 {0} ゲームを削除してもよろしいですか? 「除外リストに追加」オプションを選択すると、次回ライブラリが更新されたときにゲームが再びインポートされるのを防ぐことができます。 現在使用されていない {0} の項目を削除しますか? 未使用のフィールドは見つかりませんでした。 はい (除外リストに追加) このページには保存されていない変更点があります ゲームライブラリのフォーマットを更新中… データベースの更新に失敗しました。 ゲームライブラリを更新できません。 {0} MBの空き容量が必要です。 ゲームエラー ゲームを開始できませんでした。データベースに '{0}' が見つかりません。 ゲームを開始できませんでした: {0} アクションを開始できませんでした: {0} ゲームのインストール先を開くことができません: {0} ゲームのインストールサイズを検出できませんでした: {0} インストールサイズのスキャン時にエラーが発生しました インストールサイズのスキャン中に {0} 個のエラーが発生しました ショートカットの作成に失敗しました: {0} マニュアルを開けませんでした: {0} ゲームをインストールできません: {0} ゲームをアンインストールできません: {0} 有効なゲーム起動アクションが見つかりませんでした。エミュレータアクションを使用する場合は、ゲームとエミュレーターの構成の間でプラットフォーム定義が一致していることを確認してください。 インストールの実装は利用できません。 このゲームを管理するライブラリプラグインがインストールされていないか、有効になっていません。 公式のメタデータダウンロードは利用できません。 ゲームが選択されていません。 ゲームスクリプトの実行に失敗しました。 アプリケーションスクリプトの実行に失敗しました。 グローバルスクリプトの実行に失敗しました。 エミュレータスクリプトの実行に失敗しました。 スクリプトアクションの実行に失敗しました。 PowerShell 3.0 以降がインストールされていません。 ゲームを起動する方法を特定できませんでした。 有効 無効 削除 未使用のものを除去する 名前の変更 コピー 追加 既定のアイコン 既定のカバー画像 既定の背景画像 終了 次へ 戻る 完了 戻る 消去 消去 消去 すべて消去 インポート タイトル 作者 モジュール シリーズ バージョン 最後のプレイ 最もプレイした プレイ回数 インストールサイズ フォルダ 備考 追加日 追加日 変更日 更新日 Web サイト ファイルの場所 OK 保存 閉じる キャンセル 確認 リセット はい いいえ ようこそ ローカルユーザー 一般 メディア 関連リンク インストール アクション ダウンロード中 メディアをダウンロード中… 読み込み中… タイプ プロファイル プロファイル 削除 ダウンロード 検索 解像度: すべて ズーム リストビュー カバー グリッドビュー 詳細ビュー カスタム URL 謝辞 ライセンス 貢献者 Playnite を終了しています… 今日 昨日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日 日曜日 過去 1 週間 過去 1 ヶ月間 過去 1 年間 1 年以上前 0 から 100MB 100MB から 1GB 1GB から 5GB 5GB から 10GB 10GB から 20GB 20GB から 40GB 40GB から 100GB 100GB 以上 インポートが正常に完了しました。 すべてのゲーム ゲーム ID データベース ID プリセット Play アクションからアイコンを取得できませんでした。 ファイルタイプのアクションはありません。 不足しているメタデータのみをダウンロード このオプションを有効にすると、すでに情報が含まれているデータフィールドのメタデータのダウンロードがスキップされます。 ゲームセレクト どのゲームを新しいメタデータで更新するか選択してください。 データベースに登録されているすべてのゲーム 現在フィルターが適用されているすべてのゲーム 選択したゲームのみ メタデータフィールドが選択されていません ダウンロード対象のメタデータフィールドが選択されていません。少なくとも1つを選択し、少なくとも1つのメタデータプロバイダを有効にしてください。 公式ストア IGDB Playnite によって、どのソースからデータを取得し、どの項目に自動的に入力されるのかを選択してください。 Playnite が使用するデータを改善するために、上のロゴをクリックして igdb.com データベースの更新に貢献することを検討してください。 メタデータを取得中… インストール済みのゲームをインポート中… {0} のゲームをインポート中… {0} からエミュレータのゲームをインポート中… ライブラリの更新をダウンロード中… ライブラリ内のゲームのサイズをスキャン中… インポートされたゲームのサイズをスキャン中… ライブラリの更新完了 リソースを解放中… 設定 設定… プラットフォームとエミュレータ エミュレータの設定… ライブラリの管理… ツール メタデータを取得… ソフトウェアツール… 連携設定… サードパーティークライアントを開く サードパーティークライアント ゲームライブラリを更新 ライブラリの更新をキャンセル エミュレータゲームのフォルダを更新 ゲームを追加 手動追加… 自動スキャン… エミュレータゲームを追加… マイクロソフトストアアプリケーション… Playnite について フィードバックを送信 フルスクリーンモードに切り替え 関連リンク ヘルプ Patreon でサポート Ko-Fi でサポート ユーザーマニュアル SDK ドキュメンテーション システムの再起動 システムのシャットダウン システムのスリープ システムの休止 システムのロック ユーザーのログアウト ゲームをランダムに選択 詳細パネルに表示されるゲームフィールド: アイテムの間隔 グリッドアイテムの背景を描画する グリッドアイテムの境界幅 ゲームアイコンのソースがない場合 ゲームカバーのソースがない場合 ゲーム背景のソースがない場合 ゲーム詳細の垂直方向の間隔 グリッドビューの詳細表示の位置 ゲームリストの詳細表示の位置 パネル間のセパレーターを描画する ゲームカバー画像の高さ ゲームリストアイコンの高さ フォント 等幅フォント フィルターパネルの位置 エクスプローラーパネルの位置 カバーアートのレンダリング ターゲットのアスペクト比 以下のオプションは、フルスクリーンモードでのレンダリングにも影響します! ストレッチモード DVD ボックス Epic Games Store GOG Galaxy 2.0 IGDB 四角 Steam バナー Steam 縦カバー Twitch * 変更を適用するためには再起動が必要です。 設定 一般設定 トップパネル 外観設定 ゲーム詳細 レイアウト 詳細設定 フルスクリーン 入力 パフォーマンス メタデータ 更新 検索 バックアップ ライブラリデータのバックアップ バックアップから復元 ライブラリの変更を自動的にインポート データベースファイルの場所が無効です。正しいファイルパスを設定する必要があります。 アカウント名は空にできません。 ゲームのインポート後にメタデータをダウンロード 最小化した状態で起動 PC 起動時に Playnite を起動 トレイに閉じた状態で起動 コンピュータの起動時に、Playnite を実行するよう登録することができませんでした。 フルスクリーンモードで起動 非同期画像読み込み 画像の読み込み時間を遅くする代わりに、ゲームリストのスクロールの滑らかさを向上させることができます。 カバー画像がない場合はゲーム名を表示 グリッドビューでゲーム名を表示 インストールされていないゲームの暗転表示 詳細ビューリストにゲームのアイコンを表示 グループの説明にアイテム数を表示 フィルターおよびエクスプローラーパネルで未使用の項目を隠す ハードウェアアクセラレーションを無効にする 表示崩れなどの UI の問題が発生した場合に使用します。 クイック起動リストに非表示ゲームを表示 ジャンプリストとトレイメニューリストに影響します。 クイック起動の項目数 ウィンドウの背景にゲームの背景画像を使用する 背景をぼかす 高品質 背景を暗くする グリッドビューで表示 テーマ テーマのプロファイル フルスクリーンテーマ フルスクリーンテーマのプロファイル データベースの場所 ログイン状態: Playnite 設定 Web キャッシュを削除 アカウントのリンク中に発生した問題を解決できる可能性があります。 システムトレイアイコンを表示 Playnite をシステムトレイに最小化 アプリウィンドウを閉じたときに Playnite をシステムトレイに最小化 ゲーム起動時: ゲーム終了後: プレイ日数を示すプレイ時間に書式を適用 日付の書式: これにより、リンクされているすべてのサービスからログアウトします。 アプリケーションの再起動が必要です。続行しますか? キャッシュをクリアしますか? 新しいテーマを適用するには Playnite の再起動が必要です。 さらにテーマを入手 新しいテーマを作る さらに拡張機能を入手 新しい拡張機能を作る 翻訳を手伝う 新しい設定を適用するためには Playnite を再起動する必要があります。今すぐ再起動しますか? 現在進行中のタスク (ダウンロード) は再起動でキャンセルされます。 Playnite を再起動しますか? Playnite はライブラリファイルを自動的に移動しません。場所を変更する前にそれらを移動/コピーする必要があります。ターゲットの場所にライブラリが存在しない場合、新しいライブラリが作成されます。 新しいデータベースの場所は、アプリケーションの再起動後にのみ使用されます。 「閉じる」アクションが設定されている場合は、ゲームプレイ時間が記録されません。 行数 列数 詳細ビューの行数 メイン画面に背景画像を表示 メタデータを再ダウンロードせずに既存のゲームに遡って適用することはありません。 ゲームのプレイ時間をライブラリにインポート: Playnite データベース内のゲームについて、ライブラリプラグインから報告されたプレイ時間をいつ Playnite にインポートするかを設定します。 この機能を利用するには、ゲームの処理を行うライブラリプラグインのサポートが必要です。 常に有効: Playnite データベース内の新しくインポートされたゲームと既存のゲームのプレイ時間をインポートします。 新しくインポートされたゲームのみ: 新しくインポートされたゲームのプレイ時間のみをインポートします。 常に無効: いかなる状況でもプレイ時間をインポートしません。 常に有効 新しくインポートされたゲームのみ 常に無効 デスクトップモードでゲームコントローラーのサポートを有効にします。 ガイドボタンでフルスクリーンモードを開く 新しく取り込んだゲームのメタデータの自動ダウンロード設定 ターゲットのディスプレイ 常にプライマリディスプレイを使用する ゲームのタイトルを表示 バッテリー状態を表示 バッテリー残量を表示 時計を表示 マウスカーソルを隠す クイックフィルター内のインストールゲームのみ表示 ボタン表記 レイアウト 横スクロール表示 以下のオプションから選択 利用可能な設定がありません 設定の読み込みに失敗しました これらのスクリプトは、ライブラリ内のすべてのゲームに対して実行されます。ゲームの詳細を編集する際に、個々のスクリプトを各ゲームに個別に割り当てることができます。 背景画像の遷移をアニメーション化 フォントサイズ 自動 エイリアス グレースケール ClearType Ideal (最適) Display (GDI 互換) テキストの書式設定 テキストのレンダリングモード テキストのレンダリングと書式のオプションは現在、ゲームの説明文には適用されません。 背景画像のプリロード 有効にすると、Playnite はメタデータのダウンロード中に背景のアートワークをダウンロードし、より多くのディスク容量を使用して、オフライン時にアートワークを利用できるようにします。 無効にすると、背景アートワークは最初に必要なときにのみダウンロードされ、スペースが少なくなりますが、アートワークが表示される前に遅延が発生し、オフライン時には一部の画像が利用できなくなる場合があります。 ゲーム終了時に他のクライアント (Steam 等) を自動的に終了する クライアントシャットダウンの猶予期間 (秒) ゲームのセッションが指定時間 (秒) より短いときに閉じない 以下のクライアントを自動的に閉じる: クライアント自動終了 インポートの除外リスト 大きすぎるゲームメディアを割り当てるときに警告を表示 ディレクトリを開くコマンド 年齢制限機関の設定 ライブラリの更新時にゲームのインストールサイズを更新 前回のスキャン以降にファイルの変更が検出された場合、ゲームのインストールサイズをスキャンして更新します。 無し 埋める 均等 均等に埋める インポートエラー 認証が必要です 認証に失敗しました ウェブビューの代替レンダリングモード 内蔵認証ダイアログなどのウェブビューで問題が発生する場合に使用します。 大きなゲーム説明文を分割して読み込む 大きな説明文はゲームの選択時に顕著なラグを引き起こす可能性があります。 有効にすると、説明文の一部のみが最初に読み込まれ、残りは必要に応じて読み込まれるようになります。 メタデータのインポート メタデータを取得 選択した構成を、今後のメタデータのダウンロードに使用するように設定します。 アプリケーション設定でも変更できます。 エミュレーションインポートウィザード このウィザードは、コンソールエミュレータのダウンロードとインポート、エミュレートされたゲームのインポート手順を案内します。 後から、いつでもメインメニューを介して、エミュレータやゲームを追加することができます。(エミュレータ設定の場合は[ツール]メニュー、エミュレートゲームの場合は[ゲームの追加]メニュー) 以下は Playnite が認識し自動的に設定できるエミュレータのリストです。あなたはこれらのウェブサイトを訪問し、ダウンロードしてインストールすることができます。エミュレータをインストールしたら、次の画面に進んでそれらを Playnite にインポートします。 また、後で設定メニューからカスタムエミュレータを設定してインポートすることもできます。 「フォルダから自動検出」ボタンを押すと、PC にインストールされているエミュレータをインポートできます。Playnite は選択されたフォルダから既知のエミュレータを探し、それらをインポートするオプションを提供します。ボタンを複数回使用して複数のフォルダからインポートすることができ、エミュレータは現在のリストの一番下に追加されます。 「エミュレータを使用してフォルダをスキャン」ボタンを押すことでゲームをインポートできます。適切なエミュレータを選択すると、Playniteにどのファイルタイプをスキャンしてインポートするかを指示します。ボタンを複数回使用して複数のフォルダからインポートすることができ、ゲームは現在のリストの一番下に追加されます。 インポート用のエミュレータが選択されていません。エミュレータを設定しない限り、エミュレートされたゲームを自動的にインポートすることはできません。 インポート処理を続行して終了してもよろしいですか? Playnite に設定されたエミュレータがありません。まずエミュレータを設定し、適切なファイルタイプを選択しなければ、ゲームをインポートすることはできません。エミュレータを追加しますか? エミュレータを使用してフォルダをスキャン ファイルを選択 フォルダから自動検出… エミュレータの設定… スキャン中 {0} をスキャン中… 初回設定 このウィザードは、自動ゲームインポートプロセスと外部ゲームライブラリの設定を案内します。PlayniteはSteamやGOGなどの複数のゲームサービスから自動的にゲームをインポートすることができます。また、アプリケーションの起動時にライブラリを自動的に更新することでライブラリを最新の状態に保ちます。 いつでも'Playnite'のメインメニューから、あらゆるプラットフォーム用のカスタムゲームを追加できます。 ライブラリ連携 以下は Playnite がサポートしているライブラリの一覧です。インストールしたいものを選択してください。 「アドオン」メニューから追加の連携機能をインストールすることができます。 設定完了 初期設定が完了しました。メインメニューから追加の連携を追加するだけでなく、後ですべての設定を変更できることを忘れないでください。 1 つ以上の拡張機能のダウンロードに失敗しました。 ウィザードの初回実行が終了したら、アドオンメニューから連携機能の再ダウンロードをお試しください。 {0} 連携機能をダウンロードしています… 推奨される連携リストをダウンロード中… 推奨される連携リストをダウンロードできませんでした。後でアドオンメニューから再ダウンロードできます。 プラットフォームとエミュレータを設定する エミュレータの設定 プラットフォーム プラットフォーム エミュレータ エミュレータ プラットフォームを追加 アイコンを選択 カバーを選択 画像を選択 項目を選択 背景を選択 ファイルを選択 URL を選択 エミュレータを追加 サポートされているプラットフォーム プラットフォームの変更を保存しますか? エミュレータの変更を保存しますか? 実行ファイル 引数 作業フォルダ サポートされているファイルの種類 エミュレータをインポート… エミュレータをダウンロード… 既知のエミュレータプロファイルからプリセット引数を読み込む エミュレータ {0} を削除してもよろしいですか? 現在 {1} 個のゲームで使用されています。 プラットフォーム {0} を削除してもよろしいですか? 現在 {1} 個のゲームおよび {2} 個のエミュレータで使用されています。 設定のヘルプ 並べ替え 並べ替え方向 グループ化 昇順 降順 グループ化しない ライブラリでグループ化 カテゴリーでグループ化 プラットフォームでグループ化 ビュータイプ ビュー エクスプローラーパネル フィルターパネル アイコン ライブラリアイコン カバー画像 背景画像 ソート用タイトル ライブラリ マニュアル タイトル インストール先 アカウント名 プラットフォーム カテゴリー ジャンル 発売日 発売年 開発元 タグ パブリッシャー インストール状態 すべてに一致 有効にすると、すべてのフィルターの項目を使用しているゲームだけがビューに含まれます。 無効の場合、いずれかのフィルターの項目を使用しているゲームがビューに含まれます。 インストール済み インストール済み 未インストール 非表示 お気に入り HDR を有効にする 有効にすると、ゲームを開始する前にプライマリディスプレイでHDRが有効になります。 ※HDR はあなたのプライマリディスプレイではサポートされていないことに注意してください。 最後のプレイ カテゴリー 説明 インストールフォルダ カバー画像 関連リンク ROM/ISO イメージのパス ジャンル ジャンル 企業 企業 開発元 開発元 パブリッシャー パブリッシャー カテゴリー カテゴリー タグ タグ 特徴 特徴 年齢別レーティング 年齢別レーティング 地域 地域 ソース ソース 最近のアクティビティ データベースエラー ライブラリデータベースを開けませんでした。 データベースが開かれていません。 ライブラリデータベースにアクセスできません。ファイル "{0}"は別のプロセスによって使用されているか、アクセスできない場所にあります。 診断パッケージを作成できませんでした。 診断パッケージを自動的にアップロードできませんでした。 診断情報が正常に送信されました。 診断パッケージが作成され、正常にアップロードされました。 クラッシュレポートに次のIDを添付してください: {0} からゲームをインポートできませんでした。 {0} からエミュレータゲームをインポートできませんでした。 選択したエミュレータプロファイルではゲームを検索できません。プロファイルにファイル拡張子やプラットフォームが含まれていません。 Playnite は起動に失敗しました。実行中のすべての Playnite のプログラムを閉じてからやり直してください。 テーマ "{0}"、カラープロファイル "{1}"の適用に失敗しました。 {2} リンクを開けません。URL が無効な形式です。 アプリケーションの起動に失敗しました。 Webview コンポーネントの初期化に失敗しました。Playnite は起動処理を続行できません。 詳細は https://playnite.link/cefstartup 定義ファイルが見つからないか壊れているため、エミュレータをインポートできません。 メニューアクションの実行に失敗しました。 ゲーム詳細を編集 画像 URL 関連リンクを追加 ROM の追加 保存 編集中のゲームにフィールドの変更を適用します。 アクションの追加 アクションの削除 プレイアクションの削除 ゲームを追加 フォルダをスキャン… インストール済みを検出 参照… Playnite を開く プロファイル設定 ゲーム名は空欄にできません。 ゲームアクショントラッキングディレクトリを空にすることはできません。 メタデータを検索する前にゲーム名を空欄にすることはできません。 無効なゲームデータ http:// または https:// で始まる有効な Web URL を入力してください。 URLの選択 メタデータをダウンロードできませんでした: {0} ダウンロードエラー フィルターを解除 非公開アカウント 公開アカウント API キー 起動エラー テーマエラー すべて消去 インストール中 アンインストール中 起動中 実行中 無効な URL 何もしない 最小化 ウィンドウを復元する UI から起動した場合のみウィンドウを復元する 終了 変更 高度な設定 未プレイ プレイ状況 プレイ状況 スコア 批評家スコア ユーザースコア ゲームスクリプト アプリケーションスクリプト スクリプト プラグイン メタデータのソース 拡張機能 拡張 ID スクリプトの再読み込み インタラクティブ SDK PowerShell すべてのスクリプトが正常に再読み込みされました。 指定された検索/フィルター条件に一致するゲームは見つかりませんでした アイテムが見つかりません デスクトップモードに切り替え Playnite を終了 ライブラリ すべて更新 開発元: バージョン: 更新日: モジュール: ライブラリ 統計 すべて 無し 通知 高さ サイズ より大きい 最大 デフォルト 選択 すべて選択 すべての選択を解除 最初 ランダム ユーザー選択 更に読み込む 透過 折り畳む 展開する すべて畳む すべてを展開 その他 テーマ エミュレータ引数 組み込み用引数 カスタム引数 追加のエミュレータ引数 エミュレータ引数を上書きする プレイアクション インポートするメタデータの選択 インポートするゲームを選択 メタデータ検索 更新が利用可能です 最終更新からの変更点 更新をダウンロードしてインストール 更新を確認 更新エラー 新しいバージョンを確認できませんでした。 新しいバージョンは見つかりませんでした。最新版です。 更新のダウンロードとインストールに失敗しました。 いくつかのバックグラウンドタスクが現在進行中です。キャンセルして更新を続けますか? いくつかのバックグラウンドタスクが現在進行中です。 キャンセルして Playnite を終了しますか? いくつかのバックグラウンドタスクが現在進行中です。モードを切り替えるとタスクがキャンセルされますが、それでも切り替えますか? Playnite の更新が利用可能です。 テーマリストを再読み込み 選択したテーマを適用 ファイルの変更を監視する ソースファイルが変更されたとき、自動的にテーマを適用する スクリプト実行時間 ゲームの開始前に実行 ゲームの終了後に実行 ゲームの開始後に実行 アプリケーションの起動時に実行 アプリケーションのシャットダウン時に実行 ゲーム起動時のスクリプト ゲーム実行時のスクリプト ゲーム終了時のスクリプト グローバルスクリプトの実行 全般 フィルター済み 現在 新規 スクリプトをテスト 選択した項目のみを表示 デフォルトとして保存 お気に入りに追加 お気に入りから削除 このゲームを非表示 非表示から削除 HDR を有効にする HDR を無効にする 編集… インストールサイズを計算 インストールサイズを計算する (すべてのゲーム) インストールサイズを計算する (不足しているデータのみ) インストールサイズ カテゴリーを設定 プレイ状況を設定 削除 プレイ インストール ゲームオプション 詳細 アンインストール インストール先を開く デスクトップショートカットを作成 説明書を開く さらに表示 ライブラリプラグインで管理 ゲームの開始プロセスは、このゲームを担当するライブラリプラグインによって管理されます。 指定されたページにゲーム「{0}」に関する関連情報は見つかりませんでした。 ヒント:「編集」メニューオプションで単一のゲームを編集中、より高度なメタデータのダウンロード処理を使用できます。 何らかのアクションが進行中のときは利用できません。 説明テキストは HTML 構文に依存します ゲーム時間は秒で記録されます。 インストールサイズはバイト単位で表示されます。 発売日は「年-月-日」形式で設定する必要があります。月と日の値は省略可能です。 0 から 100 までの値、またはスコアなしの場合は空です。 Playnite の開発はこれらの利用者によって支えられています: コード、ローカライゼーション、その他の貢献者の順番は特に決まっていません: ゲームモニタリングをキャンセルしますか? インストールモニタリングが現在実行中です。プロセスをキャンセルしてゲームを以前の状態に戻しますか? ゲーム実行モニタリングが現在実行中です。プロセスをキャンセルしてゲームを以前の状態に戻しますか? プレイ時間 最後のプレイ {0} 日 {1} 時間 {2} 分 {0} 時間 {1} 分 {0} 分 {0} 秒 未プレイ デスクトップモードへ切り替え中… フルスクリーンモードへ切り替え中… ゲームライブラリをロード中… インストールサイズを計算中… {0} のインストール容量を計算中… スクリプトファイルのインストールに失敗しました。 スクリプトは正常にインストールされました。 スクリプトのインストール スクリプトエラー 拡張機能の実行に失敗しました。 メタデータフォルダを開く 計算 ゲームに ROM がある場合はそれを、インストールディレクトリが設定されている場合はそれを使用して、インストールサイズを自動的に計算します。 {0} クライアントがインストールされていません。 クライアントが開きます。サインインしてからこのメッセージを閉じてください。 ユーザーがログインするのを待っています。完了したら閉じてください。 ゲームのインストールフォルダが見つかりません。 無効なゲームアクション設定です。 アカウント同期問題のトラブルシューティング 問題のトラブルシューティング アイテムの名前を変更 新しいアイテムの追加 名前を入力 新しい名前を入力 1 時間未満 1 から 10 時間 10 から 100 時間 100 から 500 時間 500 から 1000 時間 1000 時間以上 インストールを完了するためには再起動が必要です。今すぐ再起動しますか? 拡張機能は適切にパッケージされていません。 テーマは適切にパッケージされていません。 拡張機能 "{0}" を正常に読み込めませんでした。 拡張機能 "{0}" を読み込むことができません。現在の Playnite バージョンではサポートされていません。 テーマ "{0}" を正常に読み込めませんでした。 テーマ "{0}" を読み込むことができません。現在の Playnite のバージョンではサポートされていません。 拡張機能を正常に読み込めませんでした。 テーマを正常に読み込めませんでした。 テーマ/拡張機能にサポートされていない API が使用されています。 インストールに成功しました。 アドオンをインストールしますか? 全般 アドオン "{0}" のインストールに失敗しました。 拡張機能のインストールに失敗しました。 {0} 新しい拡張機能をインストールしますか? {0} By {1} Version {2} "{0}" 拡張機能をアップデートしますか? 現在のバージョン:{1} 新しいバージョン:{2} テーマのインストールに失敗しました。 {0} 新しいテーマをインストールしますか? {0} By {1} Version {2} "{0}" テーマをアップデートしますか? 現在のバージョン:{1} 新しいバージョン:{2} デフォルトのウェブブラウザを使って次のウェブページに行きます。続行しますか? {0} 選択した画像は最適なパフォーマンスには大きすぎる可能性があります。非常に大きな画像を使用すると、UIの反応が低下し、メモリ使用量が増加する場合があります。 推奨される最大解像度: アイコン: {0} メガピクセル 表紙: {1} メガピクセル 背景: {2} メガピクセル パフォーマンス警告 次回から表示しない ファイル拡張子 {0} は互換性がありません。 互換性のないファイル拡張子 選択された画像は最適なパフォーマンスには大きすぎる可能性があります。 選択したテーマをアンインストールしますか?アンインストールは次回アプリ起動時に行われます。 ビルトインテーマはアンインストールできません。 このテーマは現在の Playnite ではサポートされていません。 選択した拡張機能をアンインストールしますか?アンインストールは次回アプリ起動時に行われます。 ビルトインテーマはアンインストールできません。 この拡張機能は現在の Playnite ではサポートされていません。 インストールフォルダ データフォルダ 診断パッケージを生成中… 診断パッケージをアップロード中… ファイルのインポート... これは何ですか? 実行してもいいですか? 総プレイ時間 平均プレイ時間 トップのプレイ時間 合計インストールサイズ 概観 サイドバー サイドバーに表示 設定のリセット 以下を除くすべてのアプリケーションの設定が初期化されます。 - データベースの場所 - インポート除外リスト - ライブラリ連携を含む拡張機能の設定 処理を終了するには、アプリケーションの再起動が必要です。設定をリセットしますか? 開発者向け 外部拡張機能 フォルダのフルパスを入力してください。 実績 フォーラム ニュース ストアページ 初回設定はまだ完了していません。Playnite は手順を完了させるためにデスクトップモードで再起動します。 最近のプレイ お気に入り 最もプレイしたゲーム すべて フィルターが適用されています。 追加のフィルターが適用されています。 次の条件での検索結果: 同じ名前のアイテムが既に存在します。 選択を現在のフィルターに制限する 別のものを選ぶ アドオン… インストール済み 拡張機能の設定 参照 更新 {0} を更新 インストールされた拡張機能およびテーマの管理とその設定は、新しい「アドオン」メニューに移されました。 現在インストールされているライブラリ連携拡張機能はすべて、ここで設定できます。 追加の連携をインストールまたはアンインストールしたい場合は、メインメニューから「アドオン」オプションを使用してください。 デスクトップテーマ フルスクリーンテーマ 検索中… アドオンは、このバージョンの Playnite と互換性がありません。 アドオンインストールパッケージのダウンロードに失敗しました。 アドオンインストールマニフェストのダウンロードに失敗しました。 変更を適用するにはアプリケーションの再起動が必要です。 このアドオンはインストール予定です。 インストール 再インストール アンインストール インストール済み 新しいアドオンの更新は見つかりませんでした。 アドオンの更新 更新履歴は利用できません インストール予定 ダウンロードに失敗しました ライセンスが拒否されました {0} をダウンロード中… アドオンの更新を検索中... プログラムのアップデートを探しています… 1 つ以上のアドオンの更新が利用可能です。 更新する項目を選択 拡張機能開発インスタンス {0} ライセンス契約 同意 拒否 ライブラリ連携プレイアクションを含める アクションを選択 トラッキングモード トラッキングパス 初期トラッキング遅延 トラッキング間隔 リンク ファイル エミュレータ スクリプト デフォルト プロセス フォルダ 元のプロセス プロセス名 メッセージをログに記録する 以下の変更は、現在選択されているすべてのゲームのデータを上書きします。 無し 均一 項目のみ 最初と最後のみ スクロールの感度 スムーズスクロール アニメーション速度 アイテムを削除しますか? このアイテムを削除してもよろしいですか? トップパネルに表示するボタン: 全般的な表示設定 グループ設定 並べ替え設定 フィルタープリセット プラグインの項目の位置 セクションセパレータの幅 メインメニューボタンをサイドバーに移動 エクスプローラーパネル ランダムゲームピッカー ランダムゲームピッカー ビューからランダムにゲームを選択 グループ化と並べ替えの設定を保存する フルスクリーンモードでクイックフィルターとして表示 過去 7 日間 過去 31 日間 過去 365 日間 365 日以上前 設定 プリセットを保存 ゲーム開始後に最小化 ゲームを開始した後、Playnite を最小化します。 無効にすると、ゲーム起動時に入力フォーカスが当たらないという問題が発生することがあります。 文字サイズ フォントサイズ小 ゲームコントローラーAPIのサポートを有効にする ゲームコントローラーのサポート 無効にすると、Playnite はあらゆるゲームコントローラーの入力を受け付けなくなります。 ゲームコントローラーの入力をマウス/キーボードの入力に変換するツールを使用していて、Playnite で入力が二重になってしまう場合は無効にしてください。 メインメニューに項目を表示する: X/A メインビューボタンを入れ替える ゲームを開始したりメインビューにゲームの詳細を表示するためにボタンの割当を入れ替えます。 確認/キャンセルボタン設定を入れ替える 確認とキャンセルのための A/B ボタンの割り当てを反転します。 プライマリーコントローラーのみ 有効な場合にのみプライマリコントローラからの入力を受け付けます。 ガイドボタンで Playnite にフォーカス インターフェイスの音量 背景の音量 バックグラウンドでミュート オーディオインターフェースの初期化に失敗しました。 出力 API 音声出力に使用する API です。サウンドに問題がある場合は変更してください。 一般 外観 音声 レイアウト メニュー 入力 {0} を起動中… {0} を実行中… Caps スペース 画像レンダリングスケーラー 代替 バランス 画質 画質: 最高の画質、低速、高メモリ使用量。 バランス: 中画質、高速、低メモリ使用量。 代替: 高画質、中速、低メモリ使用量。 ファイルを選択 フォルダを選択… 起動スクリプト 拡張機能とテーマのどちらとも Playnite のパフォーマンス、安定性、セキュリティに大きな影響を与える可能性がありますので、ご注意ください。 テーマや拡張機能をインストールした後に何らかの問題が発生した場合は、まずそれらを無効化またはアンインストールして、問題の原因であるかどうかを確認してください。 起動時に選択 起動時に選択 ビルトインプロファイル ビルトインプロファイル カスタムプロファイル カスタムプロファイル 内蔵スクリプトで処理 エミュレータの指定 プラットフォームの指定 地域の指定 エミュレータの起動前に実行 エミュレータの起動後に実行 エミュレータの終了後に実行 エミュレータの実行ファイルが見つかりません。 エミュレータの指定が見つかりません。 エミュレータ起動スクリプトが見つかりません。 別々のゲームとして分割する 1つのゲームに統合する プラットフォームの設定 地域を設定 フォルダをスキャン スキャン設定 チェックサムスキャンからパターンを除外する 指定されたパターンに一致するファイルは、チェックサムのスキャンを行わず、ファイル名でマッチングされます。詳細はエミュレータのヘルプを参照してください。 エミュレータでのスキャン 新しい設定を保存するときに名前を設定する必要があります。 エミュレータまたはエミュレータプロファイルが設定されていません。 スキャンするディレクトリが指定されていないか、存在しません。 スキャン設定が正しく設定されていません。 一括スキャンのオートスキャンに含める エミュレータのフォルダをスキャンできませんでした。 エミュレータゲームのフォルダをスキャンできませんでした。 インポート済みを非表示 インポートするプロファイル: オートスキャン設定 オートスキャン設定として保存 ライブラリの更新時に使用するために設定を保存します。保存した設定は「エミュレータの設定」メニューで管理できます。 相対パスを使用してインポート 可能であれば、Playnite のインストールフォルダまたはエミュレータのインストールフォルダからの相対パスを使用してゲームをインポートしてください。 サブフォルダをスキャン アーカイブ内をスキャン 関連ファイルを統合する 個々のゲームディスクのように、関連するゲームファイルを 1 つのゲームエントリの下に統合します。 スキャナを追加 保存したスキャナを追加 スキャンを開始 特定のフォルダをスキャンするために、エミュレータのスキャン設定を追加します。ゲームをインポートする前に、エミュレータが適切に設定されていることを確認してください(「ライブラリ」→「エミュレータの設定」メニューから)。 新しく追加されたゲームに付与される既定のステータス 初めてプレイするゲームに付与されるステータス PowerShell スクリプトランタイムの初期化に失敗しました。Windows 7 を使用している場合は、PowerShell 5.1 を (再) インストールすると問題が解決します。 指定された名前のフィルタープリセットはすでに存在します。新しい設定でプリセットをアップデートしますか? 一括追加または編集されたゲームについて、欠落しているソート名を自動的に補完する ゲームを編集する際、ライブラリ更新、エミュレータフォルダスキャン、または通常のフォルダスキャンを通じてゲームを追加する際は、「ソート名」フィールドにゲームの名前をより適切にソートできる形式で自動的に入力します。例えば「ロックマン 3」の場合、ソート名は「ロックマン 03」となります。ゲーム名と同一のソート名は設定されず、空欄のソート名のみが自動的に更新されます。 これらの単語は自動的に入力されたソート用タイトルの先頭から削除されます: 並べ替えの目的で文字列の先頭にある単語を無視する場合に使用します。デフォルトは "The", "An", "A" です。 ソート用タイトルのないゲームにソート名を設定 並べ替え ソート用タイトルの値を設定中… Nahimic サービスがシステムで実行されていることが検出されました。このサービスは、Playnite (および他のアプリ) にレンダリングの問題を引き起こすことが知られています。 もし Playnite でグラフィックの破損やその他のレンダリングの問題が発生した場合、Nahimic サービスを無効にするか完全にアンインストールすることをお勧めします。 詳細については以下の URL を参照してください。 https://playnite.link/nahimicsucks Playnite は管理者権限で実行されています。インストールされているすべての拡張機能と、Playnite から起動されたすべてのゲーム/アプリに管理者権限を与えるため、これはお勧めしません。 詳細については以下のURLを参照してください。 https://playnite.link/adminfaq Playnite が管理者権限で実行されている場合に警告を表示 ゲームのサイズを計算するときにドライブ上の実際のサイズを取得する 有効にすると、スキャンは遅くなり、ファイルがドライブで使用する実際のサイズを取得します。 無効にすると、スキャンはより高速になり、ファイルのサイズ自体が使用されます。 次のアドオンは、高い安定性/パフォーマンスへの影響またはセキュリティの問題のいずれかが原因で、潜在的に問題があると報告されています。 アンインストールすることを強くお勧めします。 {0} オンラインファイルをスキャン対象から除外する クラウドストレージに保存されているファイルは、ローカルで利用できない場合はスキャンおよびインポートされません。 サポート対象:Googleドライブ、DropBox、OneDrive スキャンしますが、ファイルの内容を含まないシンプルな方法を使用します ファイルはインポートされますが、ファイルの内容をダウンロードしてローカルに表示する必要がない、精度の低い方法を使用します。 すべてに適用 インストール状態を上書き 設定すると、Playnite はこのゲームをインポートする連携プラグインによって設定されたインストール状態 (インストールディレクトリを含む) を無視します。 このオプションは、特定のゲームのインポート方法を使用するプラグインでは、このオーバーライドオプションも考慮に入れない限り、完全に動作しない場合があります。 手動のみ 1日に1回 週に1回 すべての起動時 プログラムの更新を確認 アドオンの更新を確認 ライブラリを更新 エミュレータゲームのフォルダをスキャン 非表示ゲームを含める フィールドを編集 すべて選択/選択解除 開く 有効にする 割り当て ゲームを検索するために入力を開始… [F1] でヘルプを表示 # で始めると、利用可能なコマンドのリストが表示されます。 / で始めると、利用可能な検索プロバイダ/プラグインのリストが表示されます。 検索キーワードを入力し、最後に Space を入力すると、すぐにその検索に切り替わります。 未インストールのゲームを含める 非表示ゲームを含める アンインストールされたゲームを含める アンインストールされたゲームを除外する 非表示ゲームを含める 非表示ゲームを除外する プレイまたはインストール 詳細に移動 ゲームメニュー ゲームを編集 検索を開く 検索ボックス 検索ボタン 第1ゲームアクション 第2ゲームアクション Ctrl+F で検索ボックスにフォーカスする代わりにグローバル検索を開く 検索セッション間でゲームフィルターの設定を保存 検索プロバイダー デフォルトのキーワード カスタムキーワード システム全体のショートカット Playnite の検索 拡張機能の設定 除外 スキャンフォルダに関連する除外ファイル スキャンフォルダに関連する除外フォルダ 除外リストにファイルを追加 除外リストにフォルダを追加 除外は、保存されたスキャナ設定にのみ追加できます。 "{0}" スキャナーに除外しました。 プラットフォームを上書き 設定すると、スキャナはこのプラットフォームをすべてのゲームに割り当て、自動的に検出されたプラットフォームを上書きします。 デフォルトの検索にコマンドを含める 無効にすると、# プレフィックス が使用されるまで、コマンドはデフォルトの検索に含まれません。 名前フィルターであいまい検索を使用 有効にすると、名前フィルターはグローバル検索と同じようにゲーム名をマッチさせます。 フィルターの前に ! 文字を付けることで、個々のケースに対して厳密なマッチングを行うことができます。 ゲーム結果に表示されるフィールド: 非表示状態 データのバックアップはキャンセルされました。 データのバックアップに失敗しました。 データのバックアップに失敗しました! データのバックアップ中… バックアップからデータを復元中… バックアップからデータを復元できませんでした。 設定 ゲームライブラリ ゲームライブラリメディア インストールされている拡張機能 拡張機能データ インストールされているテーマ 指定したバックアップファイルから復元するデータを選択してください。 Playnite は自動的に再起動し、バックアップの復元処理を開始します。 データのバックアップに含める項目を選択します。アプリケーションの設定とゲームライブラリのデータは、デフォルトで含まれます。 Playnite は自動的に再起動し、バックアップ処理を開始します。 データの自動バックアップ 自動バックアップ間隔 バックアップフォルダ バックアップの循環 追加データを含める: 自動バックアップが有効な場合は、バックアップフォルダを設定する必要があります。 パッチリリースの通知のみを表示 有効にすると、現在インストールされているメジャーリリースで利用可能な更新のみが更新通知されます。 新しいメジャーリリースの更新は通知されません。 過去 1 週間に相対日付を使用 日付が 1 週間以内の場合は、「今日」、「昨日」などの相対日付を使用します。 指定された日付書式は、その他すべての日付に使用されます。 ウェブ画像検索 アイコン画像の検索文字列 カバー画像の検索文字列 背景画像の検索文字列 アドオン情報を取得中… メタデータソースがありません プレイアクション設定 スキャナ設定を使用する 起動時にプロファイルを選択 起動時にエミュレータを選択 自動 常にオン 常にオフ アクセシビリティ(スクリーンリーダー)のサポート アプリケーションメニュー ゲームメニュー プログラムフォルダ ユーザーデータディレクトリ ライブラリファイルの破損が検出されました。Playnite はシャットダウンします。 Playnite の Github ページで新しい issue を開き、ファイルの破損の修正をリクエストしてください。 変更した内容を保存しますか? ポータブルインストール コントローラーが検出されませんでした ================================================ FILE: source/Playnite/Localization/ko_KR.xaml ================================================  한국어 Playnite 언어 종료 필터 적용 중 필터 비활성화됨 추가 필터 필터 필터 잘못된 데이터 변경 사항을 저장하시겠습니까? 홈페이지: www.playnite.link GitHub에서 소스 코드 보기 진단 패키지 생성 시스템 정보 전송 Playnite에 관하여 Josef Němec이 개발함 카테고리 지정 카테고리 설정 카테고리 추가 체크됨 - 카테고리 지정 체크되지 않음 - 카테고리 삭제 다중 값 - 변경 사항 없음 (여러 게임 편집 시) 카테고리 없음 플랫폼 없음 이런! 문제가 발생했어요… 복구할 수 없는 오류가 발생했습니다. 이 문제를 해결할 수 있도록 도움을 주시고 싶으시다면 오류가 발생하기 이전에 헀던 동작에 대해서 설명해주시고 분석 정보를 보내주세요. 온라인이면 패키지가 분석을 위해서 Playnite 서버로 보내집니다. 또는 '충돌 보고' 버튼을 눌러서 GitHub에 직접 새로운 이슈를 만들어서 보고할 수도 있습니다. 도움을 주셔서 감사합니다. 확장 프로그램 "{0}"에 의해 복구할 수 없는 오류가 발생했습니다. 로그 파일을 저장한 다음 확장 개발자에게 이 문제를 보고하는 것을 권장합니다. 오류가 계속 발생한다면 확장을 비활성화해주세요. 확장 프로그램 "{0}"에 의해 복구할 수 없는 오류가 발생했습니다. 확장 개발자에게 이 문제를 보고하는 것을 권장합니다. 오류가 계속 발생한다면 확장을 비활성화해주세요. 알 수 없는 확장 프로그램 또는 테마로 인해 복구할 수 없는 오류가 발생했습니다. 서드파티 애드온을 비활성화하고 문제가 있는 애드온을 제거한 뒤 애드온 개발자에게 이 문제를 보고하는 것을 권장합니다. 복구할 수 없는 오류가 발생했습니다. 이 이슈를 고칠 수 있도록 돕고 싶다면 분석 정보를 보내주세요. 감사합니다. 확장 프로그램 비활성화 로그 파일 저장 분석 정보 보내기 오류 보고 Playnite 재시작 안전 모드로 재시작하기 모든 서드파티 확장 프로그램을 비활성화하고 기본 테마를 사용합니다. Playnite 나가기 충돌이 발생하기 전에 수행한 행동 (영어로 작성): 라이브러리 관리자 게임을 제거 하시겠습니까? 제거 불가 - 게임 또는 설치 관리자가 실행 중입니다. 제거 불가 - 게임이 실행되고 있습니다. 정말로 {0}을(를) 제거하시겠습니까? 정말로 게임 {0}개를 제거하시겠습니까? 정말 {0}을(를) 제거하시겠습니까? "제외 목록에 추가" 옵션을 선택하면 다음에 라이브러리가 업데이트될 때 게임을 다시 가져오지 않습니다. 정말로 {0} 게임을 제거하시겠습니까? "예외 목록에 추가" 옵션을 선택하면 다음에 라이브러리가 업데이트되도 불러오지 않도록 할 수 있습니다. 정말로 현재 사용하지 않는 {0}개의 항목을 제거하시겠습니까? 사용하지 않는 필드를 찾을 수 없습니다. 녜 (예외 목록에 추가) 저장하지 않은 변경사항이 있습니다 게임 라이브러리 형식 업데이트 중… 데이터베이스 업데이트를 실패했습니다. 게임 라이브러리를 업데이트할 수 없습니다. {0} MB의 여유 공간이 필요합니다. 게임 에러 게임을 시작할 수 없습니다. '{0}'을 데이터베이스에서 찾을 수 없습니다. 게임을 시작할 수 없습니다: {0} 액션을 시작할 수 없습니다: {0} 게임 위치를 열 수 없습니다: {0} 설치 용량을 인식 실패: {0} 설치 용량 계산 오류 설치용량을 계산하는데 {0}개의 오류가 발생했습니다. 바로가기를 생성하지 못했습니다: {0} 수동 열기 실패: {0} 게임을 설치할 수 없습니다: {0} 게임을 삭제할 수 없습니다: {0} 올바른 게임 시작 액션을 찾을 수 없습니다. 에뮬레이터 액션을 사용할 때는 게임과 에뮬레이터 구성 간의 플랫폼 정의가 일치하는지 확인하십시오. 설치 구현을 사용할 수 없습니다. 이 게임을 담당하는 라이브러리 플러그인이 비활성화되었거나 설치되지 않았습니다. 공식 메타데이터 다운로드를 사용할 수 없습니다. 선택한 게임이 없습니다. 게임 스크립트 실행에 실패했습니다. 프로그램 스크립트 실행에 실패했습니다. 전역 스크립트 실행에 실패했습니다. 에뮬레이터 스크립트 실행에 실패했습니다. 플레이 스크립트 액션 실행에 실패했습니다. PowerShell 3.0 또는 그 이후 버전이 설치되어 있지 않습니다. 게임을 시작하는 방법을 결정하지 못했습니다. 활성화 비활성화 제거 사용되지 않는 항목 제거 이름 바꾸기 복사 추가 기본 아이콘 기본 커버 이미지 기본 배경 이미지 완료 다음 이전 완료 이전 지우기 초기화 무시 모두 무시 가져오기 게임 이름 제작자 모듈 시리즈 버전 최근 플레이 가장 많이 플레이 플레이 횟수 설치 용량 폴더 메모 추가일 추가된 날짜 수정일 변경된 날짜 웹사이트 경로 확인 저장 닫기 취소 확인 재설정 아니오 환영합니다 로컬 사용자 일반 미디어 링크 설치 액션 다운로드 중… 미디어 다운로드 중... 로딩 중… 유형 프로필 프로필 제거 다운로드 검색 해상도: 모두 확대 및 축소 목록 보기 커버 바둑판식 보기 자세히 보기 사용자 지정 URL 도움 주신 분들 라이센스 기여자 Playnite 나가는 중… 오늘 어제 월요일 화요일 수요일 목요일 금요일 토요일 일요일 지난 주 지난 달 지난 해 1년 이상 0 ~ 100MB 100MB ~ 1GB 1GB ~ 5GB 5GB ~ 10GB 10GB ~ 20GB 20GB ~ 40GB 40GB ~ 100GB 100GB 이상 가져오기를 성공적으로 완료했습니다. 모든 게임 게임 Id 데이터베이스 Id 프리셋 플레이 액션에서 아이콘을 가져올 수 없습니다. 액션에서 File type을 정의하지 않았습니다. 누락된 메타데이터만 다운로드 이 옵션을 활성화할 경우 이미 다운로드 받은 정보는 다운로드하지 않습니다. 게임 선택 새 메타데이터로 업데이트할 게임을 선택하십시오: 데이터베이스의 모든 게임 현재 필터된 모든 게임 선택한 게임만 공식 스토어 IGDB 어떤 항목을 자동으로 채울지, 데이터를 얻기 위해 사용할 소스가 어디인지 선택하십시오. 위의 로고를 클릭하여 Playnite가 사용하는 igdb.com의 데이터 베이스 강화에 기여해주십시오. 메타데이터 다운로드 중... 설치된 게임 가져오는 중… {0} 개의 게임을 가져오는 중… {0}에서 에뮬레이터 게임을 가져오는 중... 라이브러리 업데이트 중… 라이브러리에 있는 게임의 용량을 계산하는 중... 가져온 게임의 용량을 계산하는 중... 라이브러리 업데이트 완료 리소스 해제 중... 설정 설정… 플랫폼 및 에뮬레이터 에뮬레이터 설정… 라이브러리 관리… 도구 메타데이터 다운로드... 소프트웨어 도구… 통합 구성… 서드파티 클라이언트 열기 서드파티 클라이언트 게임 라이브러리 업데이트 라이브러리 업데이트 취소 에뮬레이터 폴더 업데이트 게임 추가 직접추가... 자동으로 검색... 에뮬레이터 게임... Microsoft Store 응용프로그램 Playnite에 관하여 피드백 보내기 전체 화면 모드로 전환 링크 도움말 Patreon으로 후원하기 Ko-fi로 후원하기 사용자 설명서 SDK 개발 문서 시스템 재시작 시스템 종료 시스템 정지 시스템 최대 절전 시스템 잠금 사용자 로그아웃 무작위로 게임 고르기 상세보기에 표시할 게임 정보 간격 배경에 표시 테두리 두께 게임 아이콘 없음 게임 이미지 없음 게임 배경 출처 없음 상세보기 세로 간격 바둑판식 방식에서 상세정보 위치 상세보기에서 게임 목록 위치 구분선 그리기 커버 이미지 높이 게임 목록 아이콘 높이 글꼴 고정폭 글꼴 필터 패널 방식에서 상세정보 위치 탐색창 방식에서 상세정보 위치 커버 아트 렌더링 고정 비율 설정 다음 옵션은 전체 화면 모드의 타일 렌더링에도 영향을 미칩니다! 늘이기 모드 DVD 박스 에픽게임즈 스토어 GOG Galaxy 2.0 IGDB 정사각형 Steam 베너 Steam 수직 커버 Twitch * 재시작이 필요한 설정 항목 설정 일반 상단 패널 보기 방식 게임 상세 정보 레이아웃 고급 설정 전체 화면 입력 성능 메타데이터 업데이트 검색 백업 라이브러리 데이터 백업 백업된 데이터 복원 라이브러리 변경 사항을 자동으로 가져오기 올바르지 않은 데이터베이스 파일 위치입니다. 올바른 파일 위치로 설정되어야 합니다. 계정 이름은 비워둘 수 없습니다. 게임을 가져온 후 메타데이터 다운로드 Playnite를 최소화된 상태로 시작 컴퓨터를 부팅했을 때 Playnite 실행 트레이로 숨김 Playnite를 시작프로그램으로 등록하지 못했습니다. 전체 화면 모드로 시작 비동기 이미지 로딩 이미지 로드 시간을 늦추는 대신 게임 목록의 스크롤이 더 부드러워집니다. 커버 아트가 없을 경우 게임 이름 표시 바둑판식 보기에서 게임 이름 표시 설치하지 않은 게임 어둡게 표시 자세히 보기 방식의 목록에서 게임 아이콘 표시 그룹 설명에 항목 수 표시 필터 및 탐색기 패널에 할당된 필드만 표시 하드웨어 가속 비활성화 끊기는 현상이 발생하거나 비슷한 UI 문제가 발생할 때 사용하십시오. 빠른 실행 목록에서 숨겨진 게임 표시 점프 목록과 알림 영역 목록이 영향을 받습니다. 빠른 실행 항목 갯수 게임 이미지를 배경으로 사용 배경 흐림 고품질 배경 어둡기 바둑판식 보기에서 표시 테마 테마 프로파일 전체 화면 테마 전체 화면 테마 프로파일 데이터베이스 위치 로그인 상태: Playnite 설정 웹 캐시 삭제 계정을 연동하는데 발생하는 문제를 해결할 수도 있습니다. 알림 영역에 아이콘 표시 알림 영역으로 Playnite 최소화 프로그램 창을 닫았을 때 알림 영역으로 Playnite 최소화 게임 시작 시: 게임 종료 시: 플레이한 일수를 표시하기 위해 플레이 시간 형식 지정 날짜 표시 방법: 연동된 모든 서비스를 로그아웃시킵니다. 응용 프로그램을 재시작해야합니다. 계속하시겠습니까? 캐시를 삭제하시겠습니까? 새로운 테마를 적용하기 위해선 Playnite를 재시작해야합니다 더 많은 테마 새로운 테마 생성 더 많은 확장 프로그램 받기 새 확장 만들기 Playnite 번역을 도와주세요 새 설정을 적용하려면 Playnite를 재시작해야 합니다. 지금 재시작하시겠습니까? 재시작하면 현재 진행 중인 작업 (다운로드)이 중지됩니다. Playnite를 재시작하시겠습니까? Playnite는 자동으로 라이브러리 파일을 이동할 수 없습니다. 위치를 변경하기 전에 반드시 직접 파일을 이동하거나 복사해야 합니다. 해당 경로에 라이브러리가 없다면 새로 생성됩니다. 새 데이터베이스 위치는 Playnite가 재시작되기 전까지 사용되지 않습니다. "닫기" 액션이 설정되어있으면 플레이 시간이 기록되지 않습니다. 행 수 열 수 자세히 보기 방식의 열 수 메인 화면에 배경 이미지 표시 메타데이터를 다시 다운로드하지 않으면 기존 게임에는 소급 적용되지 않습니다. 플레이 시간 가져오기: Playnite 데이터베이스의 게임에 대해 라이브러리 플러그인이 보고한 플레이 시간을 Playnite가 가져올 시점을 구성합니다. 이 기능을 사용하려면 게임 처리를 담당하는 라이브러리 플러그인의 지원이 필요합니다. 항상: Playnite 새로 가져온 게임과 기존 게임의 플레이 시간을 가져옵니다. 새로 가져온 게임에만: 새로 가져온 게임의 플레이 시간만 가져옵니다. 안 함: 플레이 시간을 가져오지 않습니다. 항상 가져오기 새로운 게임을 가져올 때만 가져오지 않음 데스크탑 모드에서 컨트롤러 지원 활성화 가이드 버튼으로 전체 화면 모드 전환 새로 가져온 게임에 대한 자동 메타데이터 다운로드 설정입니다. 대상 디스플레이 항상 주 디스플레이 사용 게임 이름 표시 배터리 상태 보기 배터리 잔량 % 표시 시계 표시 마우스 커서 숨기기 빠른 필터 내 설치된 것만 표시 버튼 표시 레이아웃 수평 스크롤 하위 섹션 중 하나 선택 사용 가능한 설정이 없습니다 설정 불러오기 실패 이 스크립트는 라이브러리의 모든 게임에서 실행됩니다. 독립된 스크립트는 각각의 게임에 게임 자세한 정보 설정을 통해서 지정할 수 있습니다. 배경 이미지 애니메이션 전환 글꼴 크기 자동 사용 안함 회색조 클리어타입 일반 디스플레이 텍스트 표시 모드 텍스트 안티 앨리어싱 텍스트 안티 앨리어싱 및 표시 모드 설정은 게임 설명에 적용되지 않습니다. 배경화면 미리 불러오기 이 옵션을 설정하면 Playnite는 메타데이터를 다운로드할 때 배경 아트워크를 다운로드하며, 더 많은 디스크 공간을 사용하며 오프라인에서 아트워크를 사용할 수 있습니다. 해제할 경우 배경 아트워크는 처음에 필요할 때에만 다운로드되며, 더 적은 공간을 사용하지만 아트워크가 표시되기까지 시간이 걸리며 몇몇 이미지는 오프라인에서 사용할 수 없게 됩니다. 게임을 나가면 자동으로 서드파티 클라이언트 닫기 클라이언트 종료 지연 시간 (초) 다음보다 게임 세션이 짧으면 닫지 않음 (초) 다음 클라이언트 자동으로 닫기: 자동으로 클라이언트 닫기 예외 목록 불러오기 너무 큰 게임 미디어를 지정할 때 경고 표시하기 경로 열기 명령어 선호하는 등급 분류 기구 라이브러리 업데이트 시 설치 용량 업데이트 마지막 계산 이후에 게임 파일의 변경이 감지된 경우 게임의 설치 용량를 계산하고 업데이트합니다. 적용 안함 채우기 원본 비율로 크기에 맞추기 원본 비율로 가장 크게 맞추기 왼쪽 오른쪽 상단 하단 가져오기 오류 인증이 필요함 인증 실패 대체 웹 페이지 렌더링 사용 통합 인증창 등 웹페이지 표시에 문제가 있는 경우에 사용합니다. 큰 설명 데이터 부분 로딩 설명 데이터가 크면 게임을 선택할 때 눈에 띄게 지연이 발생할 수 있습니다. 이 옵션을 활성화하면 설명 내용의 일부만 처음에 불러오고 나머지는 필요할 때 불러올 수 있습니다. 메타데이터 가져오기 메타데이터 다운로드 선택된 설정을 모든 메타데이터 다운로드에 적용합니다. 설정에서도 수정할 수 있습니다. 에뮬레이터 게임 가져오기 도구 이 마법사는 콘솔 에뮬레이터를 다운로드 및 불러오기를 하는 방법과 에뮬레이팅 게임을 불러오는 방법에 대해서 알려줍니다. 추가적인 에뮬레이터나 게임을 언제든 메인 메뉴에서 추가할 수 있다는 것을 기억하십시오. (에뮬레이터 설정은 "라이브러리" 메뉴에서 가능하며 에뮬레이터 게임은 "게임 추가" 메뉴에서 가능합니다.) 다음은 Playnite가 자동으로 인식하고 구성할 수 있는 에뮬레이터 목록입니다. 웹사이트에서 설치 프로그램을 다운로드할 수 있습니다. 에뮬레이터를 수동으로 설치한 뒤 에뮬레이터 설정 창에서 에뮬레이터를 가져올 수 있습니다. '폴더에서 자동으로 감지…'버튼을 클릭하여 PC에 설치된 에뮬레이터를 가져올 수 있습니다. Playnite는 선택한 폴더에서 알려진 에뮬레이터를 검색하고 가져올 수 있는 옵션을 제공합니다. 이 버튼을 여러 번 사용하여 다른 폴더에서 에뮬레이터를 가져올 수 있습니다. 에뮬레이터는 현재 목록의 맨 아래에 추가됩니다. '에뮬레이터를 사용하여 폴더 스캔' 버튼을 클릭하여 게임을 가져올 수 있습니다. 적절한 에뮬레이터를 선택하면 어떤 파일 유형을 스캔하고 가져와야 하는지를 Playnite에 알려줍니다. 이 버튼을 여러 번 사용하여 다른 폴더에서 게임을 가져올 수 있습니다. 현재 목록의 맨 아래에 게임이 추가됩니다. 불러오기 위한 에뮬레이터가 선택되지 않았습니다. 에뮬레이터 설정 없이 자동으로 에뮬레이터 게임을 불러올 수 없습니다. 불러오기 과정을 계속 진행하시겠습니까? Playnite에 에뮬레이터 설정이 없습니다. 에뮬레이터 설정과 파일 형식 없이 게임을 불러올 수 없습니다. 에뮬레이터를 추가하시겠습니까? 에뮬레이터를 사용하여 폴더 검색 파일 선택 폴더에서 자동 감지… 에뮬레이터 설정… 검색 중... {0} 검색 중... 처음 설정 이 프로세스는 외부 게임 라이브러리를 자동으로 가져오고 구성하는 과정을 안내합니다. Playnite는 Steam이나 GOG와 같은 여러 게임 서비스에서 자동으로 게임을 가져올 수 있다. 나중에 메인 메뉴에서 플랫폼에 대한 사용자 지정 게임이나 에뮬레이션 게임을 수동으로 추가할 수도 있습니다. 라이브러리 통합 자동적으로 다음과 같은 서비스에서 게임을 불러옵니다. 게임에 변화 (설치 상태)가 Playnite의 초기 실행 또는 설정을 통하여 자동으로 업데이트됩니다. 선택된 설정은 초기 불러오기 및 불러오기 절차에 반영됩니다. 설정 완료 초기 설정이 완료되었습니다. 이후에도 '설정' 메뉴에서 모든 설정을 변경할 수 있습니다. 또한 Playnite 로고 메뉴를 눌러서 이후에도 다른 게임을 추가할 수 있습니다. 하나 이상의 확장 프로그램을 다운로드하지 못했습니다. 최초 실행 마법사가 완료된 후 애드온 메뉴에서 통합을 다시 다운로드할 수 있습니다. {0} 통합 다운로드 중... 권장 통합 목록 다운로드 중... 권장 통합 목록을 다운로드하지 못했습니다. 나중에 애드온 메뉴를 통해 통합을 다시 다운로드할 수 있습니다. 플랫폼 및 에뮬레이터 설정 에뮬레이터 설정 플랫폼 플랫폼 에뮬레이터 에뮬레이터 플랫폼 추가 아이콘 선택 커버 선택 이미지 선택 항목 선택 배경 선택 파일 선택 URL 선택 에뮬레이터 추가 지원되는 플랫폼 플랫폼 변화를 저장하시겠습니까? 에뮬레이터 변화를 저장하시겠습니까? 실행파일 실행 변수 작업 디렉터리 지원되는 파일 유형 에뮬레이터 불러오기... 에뮬레이터 다운로드... 에뮬레이터 프로파일에서 실행 변수 프리셋 불러오기 정말로 {0} 에뮬레이터를 제거하시겠습니까? 현재 {1} 게임에서 사용되고 있습니다. 정말로 {0} 플랫폼을 제거하시겠습니까? 현재 {1} 게임과 {2} 에뮬레이터에서 사용되고 있습니다. 설정 도움말 정렬 기준 정렬 순서 묶기 기준 오름차순 내림차순 묶지 않기 라이브러리 별로 묶기 카테고리로 묶기 플랫폼으로 묶기 유형 보기 보기 탐색창 필터 창 아이콘 라이브러리 아이콘 커버 이미지 배경 이미지 정렬 이름 라이브러리 매뉴얼 이름 설치 드라이브 계정이름 플랫폼 카테고리 장르 출시일 출시 년도 개발사 태그 배급사 설치 상태 모든 조건 일치 이 옵션을 설정하면 설정된 모든 필터 조건을 만족하는 게임만 표시합니다. 해제하면 필터에서 설정된 필터 조건에 해당하는 모든 게임을 표시합니다. 설치됨 설치됨 설치되지 않음 숨겨짐 즐겨찾기 HDR 지원 활성화 활성화하면 게임을 시작하기 전에 기본 디스플레이에서 HDR이 활성화됩니다. 기본 디스플레이에서는 HDR이 지원되지 않을 수 있습니다. 최근 플레이 카테고리 설명 설치 경로 커버 이미지 링크 이미지, ROM or ISO 경로 장르 장르 회사 회사 개발사 개발 배급사 배급 카테고리 카테고리 태그 태그 기능 기능 연령 등급 심의 등급 지역 지역 소스 소스 최근 활동 데이터베이스 오류 라이브러리 데이터 베이스 열기 실패 데이터베이스가 열리지 않았습니다. 라이브러리 데이터베이스에 접근 할 수 없습니다. "{0}" 파일이 다른 프로세서에 의하여 사용되고 있거나 접근할 수 없는 경로에 있습니다. 진단 패키지 생성 실패. 진단 패키지 자동 업로드 실패. 진단 정보가 성공적으로 보내졌습니다. 분석 패키지가 성공적으로 생성되었으며 성공적으로 제출되었습니다. 다음 아이디를 이슈 리포트에 첨부해주세요: {0}(으)로부터 게임 불러오기 실패. {0}에서 게임을 가져오지 못했습니다. 선택된 에뮬레이터 프로파일로 부터 게임을 찾을 수 없습니다. 프로파일이 파일 확장자나 플랫폼 정보를 포함 하고 있지 않습니다. Playnite 실행 실패. 실행 중인 프로그램을 모두 종료하고 다시 시도해 보십시오. "{0}", 색상 프로필 "{1}" 적용 실패 {2} 링크를 열 수 없습니다. URL이 올바른 포맷이 아닙니다. 프로그램 실행 실패. 웹 보기 구성 요소를 초기화하지 못했습니다. Playnite가 시작 프로세스를 진행할 수 없습니다. 자세한 내용은 https://playnite.link/cefstartup을 참고하십시오. 정의 파일이 없거나 손상되어 에뮬레이터를 가져올 수 없습니다. 메뉴 작업을 실행하는 데에 실패했습니다. 게임 상세정보 편집 이미지 URL 링크 추가 ROM 추가 변경 사항 저장 편집 중인 필드 변경사항을 적용합니다. 액션 추가 액션 제거 실행 액션 제거 게임 추가 폴더 검색... 설치된 프로그램 감지 찾아보기... Playnite 열기 프로필 설정 게임 이름은 공백일 수 없습니다. 게임 액션 추적 디렉터리는 비워 둘 수 없습니다. 메타 데이터를 찾기 전까지 게임 이름은 공백일 수 없습니다. 유효하지 않은 게임 데이터 http:// 또는 https://로 시작하는 웹 URL을 입력하십시오. URL 선택 메타 데이터 다운로드 실패 : {0} 다운로드 오류 필터 초기화 비공개 계정 공개 계정 API 키 시작 오류 테마 오류 모두 초기화 설치 중 설치 제거 중 시작 중 실행 중 유효하지 않은 URL 아무것도 하지않음 최소화 창 복구 UI에서 실행할 때에만 창 복구 닫기 변경 고급 설정 안함 완료 상태 완료 상태 유저 점수 크리틱 점수 커뮤니티 점수 게임 스크립트 프로그램 스크립트 스크립트 플러그인 메타데이터 출처 확장 프로그램 확장 프로그램 ID 스크립트를 다시 로드 Interactive SDK PowerShell 모든 스크립트 다시 로드 성공. 검색/필터 기준에 맞는 게임 없음 항목 없음 데스크톱 모드로 전환 Playnite 나가기 라이브러리 모두 업데이트 작성자: 버전: 업데이트: 모듈: 라이브러리 통계 전체 없음 알림 높이 크기 작게 보통 크게 더 크게 가장 크게 기본값 선택 모두 선택 모두 선택 해제 첫번째 무작위 유저 선택 더 보기 투명 접기 펼치기 모두 접기 모두 펼치기 기타 테마 에뮬레이터 실행 변수 내장 실행 변수 사용자 실행 변수 추가 에뮬레이터 실행 변수 에뮬레이터 실행 변수 덮어쓰기 플레이 액션 불러올 메타데이터 선택 불러올 게임 선택 메타데이터 검색 업데이트 사용 가능 마지막 업데이트 이후 변화 업데이트 다운로드 및 설치 업데이트 확인 업데이트 오류 새로운 버전 확인에 실패했습니다. 새로운 버전을 찾지 못했습니다. 최신 버전입니다. 업데이트 다운로드 및 설치 실패. 백그라운드 작업이 진행중입니다. 작업을 중단하고 업데이트를 진행하시겠습니까? 백그라운드 작업이 진행중입니다. 작업을 중단하고 Playnite를 종료하시겠습니까? 백그라운드 작업이 진행중입니다. 모드를 전환하면 작업이 중단됩니다. 전환하시겠습니까? Playnite 업데이트 사용 가능 테마 이미지 다시 불러오기 선택된 테마 적용 파일 변화 감시 감시 파일이 변경되었을 경우 자동으로 테마 적용 스크립트 런타임 게임 시작 전 실행할 스크립트 게임 종료 전 실행할 스크립트 게임 시작 후 실행할 스크립트 프로그램 시작 시 실행할 스크립트 프로그램 종료 시 실행할 스크립트 게임 시작 전 실행 스크립트 게임 시작 후 실행 스크립트 게임 중단 후 실행 스크립트 전역 스크립트 실행 모든 게임 필터링 된 게임 현재 신규 스크립트 테스트 선택된 항목만 표시합니다. 기본값으로 저장 즐겨찾기에 추가 즐겨찾기에서 제거 이 게임 숨기기 숨김에서 제거 HDR 지원 활성화 HDR 지원 비활성화 편집... 설치 용량 계산 설치 용량 계산 (모든 게임) 설치 용량 계산 (누락된 데이터만) 설치 용량 카테고리 설정... 완료 상태 설정 제거 플레이 설치 게임 옵션 자세히 설치 제거 설치 위치 열기 바탕화면 바로가기 생성 설명서 열기 더 보기 라이브러리 플러그인에 의해 관리됨 이 게임의 실행 과정이 라이브러리 플러그인에 의해 관리됩니다. 지정된 페이지에서 '{0}' 게임에 대한 관련 정보를 찾을 수 없습니다. 팁: "편집" 메뉴 옵션을 통해 단일 게임을 편집하면 고급 메타데이터 다운로드 기능을 사용할 수 있습니다. 다른 절차가 진행중일 경우 사용할 수 없습니다. 설명은 html 구문에 민감합니다. 게임 시간은 초 단위로 기록됩니다. 설치 용량은 바이트로 표시됩니다. 출시 날짜는 '년-월-일' 형식으로 작성되어야 합니다. 월, 일은 생략할 수 있습니다. 0에서 100사이의 값 또는 점수가 없으면 빈칸. Playnite 개발은 다음의 Patron과 Ko-fi 후원자들의 지원을 받습니다. 코드, 번역 및 기타 기여는 특별한 순서가 없습니다: 게임 모니터링을 취소하시겠습니까? 설치 모니터링이 실행 중입니다. 중단하고 게임을 이전 상태로 되돌리시겠습니까? 게임 실행 모니터링이 실행 중입니다. 중단하고 게임을 이전 상태로 되돌리시겠습니까? 플레이 시간 최근 플레이 {0}일 {1}시간 {2}분 {0}시간 {1}분 {0} 분 {0} 초 플레이 하지 않음 데스크탑 모드 실행... 전체화면 실행... 게임 라이브러리 불러오는중... 설치 용량 계산 중... {0}의 설치 용량 계산 중... 스크립트 파일 설치 실패. 스크립트 설치 성공. 스크립트 설치 스크립트 오류 확장기능 실행에 실패했습니다. 메타데이터 폴더 열기 계산 ROM 또는 설치 디렉토리에서 설치 용량을 자동으로 계산합니다. {0} 클라이언트가 설치되지 않았습니다. {0} 클라이언트가 실행됩니다. 로그인후 이 메세지를 닫아주세요. 로그인을 기다리고 있습니다. 로그인 후 이 창을 닫아주십시오... 게임 설치 폴더를 찾을 수 없음. 잘못된 액션 설정. 계정 연동 문제 가이드 문제 해결 항목 이름 재설정 새 항목 추가 이름 입력 새로운 이름 입력 1시간 미만 1시간에서 10시간 10시간에서 100시간 100시간에서 500시간 500시간에서 1000시간 1000시간 이상 설치를 완료하려면 Playnite를 재시작해야 합니다. 지금 재시작하시겠습니까? 올바른 확장 프로그램 패키지가 아닙니다. 올바른 테마 패키지가 아닙니다. "{0}" 확장 프로그램을 제대로 불러오지 못했습니다. {0} 확장 프로그램을 로드할 수 없습니다. 현재 Playnite 버전에서 지원되지 않습니다. "{0}" 테마를 제대로 불러오지 못했습니다. {0} 테마를 로드할 수 없습니다. 현재 Playnite 버전에서 지원되지 않습니다. 확장 프로그램을 제대로 불러오지 못했습니다. 테마를 제대로 불러오지 못했습니다. 테마 또는 확장 프로그램이 지원하지 않는 API 버전을 사용중입니다. 설치 성공. 애드온을 설치하시겠습니까? 일반 애드온 "{0}" 설치에 실패했습니다. 확장 설치에 실패했습니다. 새 확장 프로그램을 설치하시겠습니까? {0} 게시자: {1} 버전: {2} "{0}" 확장 프로그램을 업데이트하시겠습니까? 현재 버전: {1} 새 버전: {2} 테마 설치에 실패했습니다. 새 테마를 설치하시겠습니까? {0} 게시자: {1} 버전: {2} "{0}" 테마를 업데이트하시겠습니까? 현재 버전: {1} 새 버전: {2} Playnite에서 나가서 기본 브라우저를 통해 해당 웹 페이지로 가려고 합니다. 계속하시겠습니까? {0} 선택한 이미지는 너무 크며 성능이 저하될 수 있습니다. 큰 이미지를 사용하는 것은 UI 반응에 나쁜 결과를 가져올 수 있으며 더 많은 메모리를 사용합니다. 추천 최대 해상도: 아이콘: {0} 메가 픽셀 커버: {1} 메가 픽셀 배경: {2} 메가 픽셀 퍼포먼스 주의 다시 표시 안함 파일 확장자 "{0}"은 지원되지 않습니다. 지원되지 않는 확장자 선택한 이미지는 너무 크며 성능이 저하될 수 있습니다. 선택한 테마를 제거하시겠습니까? 다음에 프로그램을 실행할 때 제거됩니다. 기본 탑재된 테마는 제거할 수 없습니다. 이 테마는 현재 버전의 Playnite를 지원하지 않습니다. 선택한 확장 프로그램을 제거하시겠습니까? 프로그램이 다시 실행될 때 제거됩니다. 기본 탑재된 확장 프로그램은 제거할 수 없습니다. 이 확장 프로그램은 현재 버전의 Playnite를 지원하지 않습니다. 설치 경로 데이터 경로 분석 패키지 생성 중… 분석 패키지 업로드 중… 파일 가져오기… 이게 뭔가요? 정말로 이 작업을 수행하시겠습니까? 총 플레이 시간 평균 플레이 시간 최고 플레이 시간 전체 설치 용량 개요 사이드바 사이드바에 표시 설정 초기화 다음을 제외한 모든 애플리케이션 설정이 기본값으로 재설정됩니다. - 데이터베이스 위치 - 가져오기 제외 목록 - 라이브러리 통합을 포함한 확장 프로그램 설정 작업을 완료하려면 프로그램을 다시 시작해야 합니다. 설정을 재설정하시겠습니까? 개발자용 외부 확장 프로그램 전체 경로를 입력하십시오. 도전 과제 포럼 소식 상점 페이지 초기 설정이 완료되지 않았습니다. 최초 설치를 완료하기 위하여 Playnite가 데스크탑 모드로 재실행됩니다. 최근 플레이 즐겨찾기 가장 많이 플레이 모두 필터 적용됨. 적용된 추가 필터가 있습니다. 검색 결과: 같은 이름이 이미 사용중입니다. 현재 필터로 선택 제한 다른 게임 선택 애드온… 설치된 확장 프로그램 확장 프로그램 설정 탐색 업데이트 업데이트 ({0}) 설치된 확장 프로그램 및 테마 관리 및 설정 기능이 새 "애드온" 메뉴로 이동되었습니다. 현재 설치된 모든 라이브러리 통합 확장 프로그램을 여기서 구성할 수 있습니다. 추가 통합을 설치하거나 제거하려면 메인 메뉴의 "애드온" 옵션을 사용하십시오. 데스크톱 테마 전체화면 테마 검색 중... 애드온이 현재 버전의 Playnite와 호환되지 않습니다. 애드온 설치 패키지 다운로드에 실패했습니다. 애드온 설치 매니페스트 다운로드에 실패했습니다. 변경사항을 적용하려면 프로그램을 재시작해야 합니다. 이 애드온은 설치될 예정입니다. 설치 설치 제거 이미 설치 됨 새로운 업데이트가 없습니다. 애드온 업데이트 변경 이력 없음 설치 예약됨 다운로드 실패 라이선스 동의 거절 {0} 다운로드 중... 업데이트된 애드온을 확인하는 중... 하나 이상의 애드온 업데이트가 있습니다. 업데이트 할 항목 선택 개발용 확장 프로그램 {0} 라이선스 동의 수락 거부 라이브러리 통합 플레이 액션 포함 동작 선택 트래킹 모드 트래킹 경로 초기 추적 지연 빈도 추적 링크 파일 에뮬레이터 스크립트 기본값 프로세스 폴더 원본 프로세스 추적 메시지 로깅하기 다음 변경 사항은 현재 선택한 모든 게임의 데이터를 덮어씁니다! 없음 원본 비율로 크기에 맞추기 항목만 처음과 마지막 만 스크롤 감도 부드러운 스크롤 애니메이션 속도 항목을 제거하시겠습니까? 정말로 이 항목을 삭제하시겠습니까? 상단 패널에 표시할 버튼: 일반 보기 설정 그룹 설정 정렬 설정 필터 프리셋 플러그인 아이템 정렬 섹션 간격 메인 메뉴 버튼을 사이드바로 이동 탐색 패널 무작위로 게임 고르기 보기에서 무작위로 선택 보기에서 무작위로 게임 선택 그룹 및 정렬 설정 저장 전체화면 모드에서 빠른 필터 보기 지난 7일 지난 31일 지난 365일 지난 365일보다 더 오래됨 설정 프리셋 저장 게임이 시작되면 최소화 게임이 시작되면 Playnite를 최소화합니다. 이 설정을 끄면 게임이 시작될 때 입력이 되지 않는 문제가 발생할 수 있습니다! 글꼴 크기 작은 글꼴 크기 게임 컨트롤러 지원 이 설정을 해제하면 Playnite는 게임 컨트롤러 입력을 처리하지 않습니다. 게임 컨트롤러 입력을 마우스/키보드 입력으로 변환하는 도구로 인해 Playnite에서 이중 입력이 발생할 경우 해제하십시오. 메인 메뉴 표시 항목: 메인 보기에서 X/A버튼 바인딩 바꾸기 메인 보기에서 게임 시작 또는 세부 정보 표시 버튼의 바인딩을 바꿉니다. 확인/취소 버튼 바인딩 바꾸기 A/B 버튼의 확인 취소 바인딩을 바꿉니다. 주 컨트롤러만 사용 이 옵션을 설정하면 주 컨트롤러의 입력만 처리합니다. 가이드 버튼으로 Playnite 열기 인터페이스 볼륨 배경음악 볼륨 프로그램이 활성화 되어있을 때만 재생 오디오 인터페이스 초기화에 실패했습니다. 출력 API 오디오 출력에 사용할 API. 소리 재생에 문제가 있는 경우 변경하십시오. 일반 비주얼 오디오 레이아웃 메뉴 입력 {0} 시작 중... {0} 실행 중... 대/소문자 공백 이미지 스케일링 균형 성능 품질 품질: 최고 품질, 느림, 높은 메모리 사용. 성능: 보통 품질, 빠름, 적은 메모리 사용. 균형: 고품질, 보통, 적은 메모리 사용. 파일 선택... 폴더 선택... 시작 스크립트 애드온과 테마 모두 Playnite의 성능, 안정성 및 보안에 큰 영향을 미칠 수 있습니다. 테마 또는 확장 프로그램을 설치한 후 일부 문제가 발생하기 시작하는 경우 먼저 해당 항목을 비활성화 또는 제거하여 문제를 유발하는지 여부를 확인하십시오. 시작 시 선택 시작 시 선택 내장 프로필 내장 프로필 사용자 프로필 사용자 프로필 내장 스크립트로 처리 에뮬레이터 사양 플랫폼 사양 지역 사양 에퓰레이터 시작 전 실행 스크립트 에퓰레이터 시작 후 실행 스크립트 에퓰레이터 종료 후 실행 스크립트 에퓰레이터 실행파일을 찾을수 없습니다. 에뮬레이터 사양을 찾을수 없습니다. 에뮬레이터 시작 스크립트를 찾을수 없습니다. 별도 게임으로 분리 하나의 게임으로 병합 플랫폼 설정 지역 설정 검색할 폴더 검색 설정 체크섬 검색에서 제외 지정된 패턴과 일치하는 파일은 체크섬을 확인하지 않고 파일 이름으로 일치합니다. 자세한 내용은 에뮬레이터 도움말 페이지를 참조하십시오. 검색할 에뮬레이터 새 구성을 저장할 때 이름을 설정해야 합니다. 에뮬레이터나 에뮬레이터 프로필이 설정되지 않았습니다. 검색할 디렉터리가 지정되지 않았거나 존재하지 않습니다. 검색 구성이 올바르게 설정되지 않았습니다. 자동 검색에 일괄 검색 포함 폴더에서 에뮬레이터를 검색하지 못했습니다. 에뮬레이터 게임 폴더를 검색하지 못했습니다. 이미 가져온 항목 숨김 불러올 프로필: 자동 검색 설정 자동 검색 설정으로 저장 나중에 라이브러리 업데이트 중에 사용할 수 있도록 구성을 저장합니다. 저장된 구성은 "에뮬레이터 설정" 메뉴를 통해 관리할 수 있습니다. 상대경로로 가져오기 가능한 경우 Playnite의 설치 폴더 또는 에뮬레이터의 설치 폴더와 관련된 경로를 검색하여 게임 파일을 가져옵니다. 하위 폴더 검색 압축파일 내 검색 관련 파일 병합 개별 게임 디스크 등의 관련 게임 파일을 하나의 게임 항목 아래에 병합합니다. 검색기 추가 저장된 검색기 추가 검색 시작 검색 설정을 추가하여 특정 폴더를 검색합니다. 게임을 가져오기 전에 에뮬레이터가 올바르게 구성되었는지 확인하십시오.(라이브러리 -> 에뮬레이터 설정 메뉴) 게임을 처음 추가했을 때의 상태 게임을 최초로 플레이했을 때의 상태 PowerShell 스크립트 런타임을 초기화하지 못했습니다. 윈도우즈 7 사용자인 경우 PowerShell 5.1을 다시 설치하여 문제를 해결해 보십시오. 지정한 이름의 필터 프리셋이 이미 있습니다. 프리셋을 새 설정으로 업데이트하시겠습니까? 다음 단어를 자동으로 부여되는 정렬 이름의 앞부분에서 제외 정렬을 위해 문자열의 시작 부분에 있는 단어를 무시할 때 사용합니다. 기본값은 "The", "An" 및 "A"입니다. 정렬 이름이 없는 게임의 값 채우기 정렬 정렬 이름 설정 중... 시스템에 Nahimic 서비스가 실행 중인 것으로 탐지되었습니다. 이 서비스는 Playnite(및 기타 앱)에 렌더링 문제를 일으키는 것으로 알려져 있습니다. Playnite에서 그래픽 손상 또는 기타 렌더링 문제가 발생하는 경우 Nahimic 서비스를 사용하지 않도록 설정하거나 완전히 제거하는 것이 좋습니다. 자세한 내용은 https://playnite.link/nahimicsucks를 참고하십시오. Playnite가 관리자 권한으로 실행되고 있습니다. 설치된 확장 프로그램과 게임/앱에 높은 권한을 부여하기 때문에 추천하지 않습니다. 더 많은 정보를 얻고 싶다면 https://playnite.link/adminfaq 를 방문해주세요. Playnite가 상승된 권한으로 실행 중인 경우 경고 표시 게임의 용량을 계산할 때 실제 디스크 사용량을 가져옵니다. 이 옵션을 설정하면 된 경우 느리지만 실제 드라이브에서 파일이 차지하는 용량을 가져올 수 있습니다. 해제하면 파일의 크기만 빠르게 가져옵니다. 다음 애드온(들)은 안정성 및 성능에 큰 영향을 주거나 보안 문제로 인해 잠재적으로 문제가 있는 것으로 보고되었습니다. 다음을 제거하는 것을 권장합니다. {0} 검색 시 온라인 파일 제외 로컬에서 사용할 수 없는 경우 클라우드 저장소에 저장된 파일을 검색하고 가져오지 않습니다. Google Drive, DropBox, OneDrive에서만 지원됩니다. 파일 내용을 확인하지 않는 간단한 스캔 방식 사용 파일을 가져오지만 파일 내용을 로컬로 다운로드하여 표시할 필요가 없는 정확도가 낮은 방법을 사용합니다. 모두 적용 설치 상태 재정의 설정하면 Playnite는 이 게임을 가져오는 통합 플러그인이 설정한 설치 상태(설치 디렉터리 포함)를 무시합니다. 이 옵션은 특정 게임 가져오기 방법을 사용하는 플러그인에서도 이 오버라이드 옵션을 고려하지 않으면 제대로 작동하지 않을 수 있습니다. 수동으로만 하루에 한 번 일주일에 한 번 시작할 때마다 프로그램 업데이트 확인 애드온 업데이트 확인 라이브러리 업데이트 에뮬레이터 폴더 검색 숨긴 게임 포함 정보 편집 모두 선택/해제 열기 활성화 적용 검색어를 입력하십시오... [F1] 도움말 보기 검색어 앞에 #을 붙이면 이용 가능한 명령어 목록을 표시합니다. 검색어 앞에 /을 붙이면 이용 가능한 검색 공급자 및 플러그인 목록을 표시합니다. Typing search keyword and ending with SPACE switches immediately to that search. TAB: 동작 변경 ENTER: 선택한 동작 활성화 SHIFT-ENTER: 선택한 항목 메뉴 열기 제거된 게임 포함 숨긴 게임 포함 제거된 게임 포함 제거된 게임 제외 숨긴 게임 포함됨 숨긴 게임 제외됨 플레이 또는 설치 자세히 보기 게임 메뉴 게임 편집 검색 열기 검색 상자 검색 버튼 기본 동작 보조 동작 CTRL+F를 눌렀을 때 검색 상자로 이동하는 대신 검색 열기 검색 세션간 게임 필터 저장 검색 공급자 기본 키워드 사용자 키워드 시스템 단축키 Playnite 검색 확장 프로그램 설정 제외 항목 폴더 검색에서 제외할 파일 폴더 검색에서 제외할 폴더 제외 목록에 파일 추가 제외 목록에 폴더 추가 제외 항목은 저장된 검색기 설정에만 추가할 수 있습니다. 제외 항목이 "{0}" 검색기에 추가되었습니다. 플랫폼 재정의 자동으로 인식한 플랫폼 값을 선택한 플랫폼으로 덮어씁니다. 명령어를 기본 검색에 포함 이 옵션을 선택하지 않으면 # 접두어를 사용하지 않을 경우 명령어가 검색결과에 포함되지 않습니다. 이름 필터에서 퍼지 매칭 사용 활성화하면 이름 필터가 글로벌 검색과 동일한 방식으로 게임 이름을 검색합니다. 필터 앞에 ! 문자를 추가하여 대/소문자가 일치한 항목을 검색할 수 있습니다. 결과에 표시할 항목 숨김 상태 백업이 취소되었습니다. 데이터 백업에 실패했습니다. 데이터 백업 오류 데이터 백업 중... 데이터 복원 중... 백업 데이터 복원에 실패했습니다. 설정 게임 라이브러리 게임 라이브러리 미디어 설치된 확장 프로그램 확장 프로그램 데이터 설치된 테마 복원할 백업 파일을 선택하십시오. 백업 데이터 복원을 위해 Playnite가 자동으로 재시작됩니다. 데이터 백업에 포함할 항목을 선택하십시오. 프로그램 설정 및 게임 라이브러리 데이터는 기본적으로 포함됩니다. 백업 작업을 시작하기 위해 Playnite가 자동으로 다시 시작됩니다. 데이터 자동 백업 자동 백업 주기 백업 폴더 백업 세트 수 포함할 추가 데이터: 자동 백업이 설정된 경우 백업 폴더를 지정해야 합니다. 패치 릴리즈에 대한 알림만 표시 이 옵션을 설정하면 현재 설치된 Major 버전에 대한 업데이트에 대해서만 알람을 표시합니다. 새로운 Major 버전이 릴리즈되면 알림이 발생하지 않습니다. 일주일 이내의 기간은 상대 시간 표시 일주일 이내의 기간에 대해서는 "오늘", "어제" 등의 상대 시간으로 표시합니다. The specified date format will be used for all other dates. 웹 이미지 검색 아이콘 이미지 검색어 커버 이미지 검색어 배경 이미지 검색어 애드온 정보를 가져오는 중... 사용 가능한 메타데이터 출처가 없습니다. 플레이 액션 설정 검색기 설정 사용 시작 시 프로필 선택 시작 시 에뮬레이터 선택 자동 항상 켜기 항상 끄기 접근성(스크린 리더) 지원 프로그램 메뉴 게임 메뉴 프로그램 폴더 사용자 데이터 디렉터리 라이브러리 파일 손상이 감지되어 Playnite를 종료합니다. Playnite GitHub 페이지에서 손상된 파일을 수정해 달라는 이슈를 생성해 주십시오. 변경 사항을 저장하시겠습니까? 포터블 설치 컨트롤러가 감지되지 않음 ================================================ FILE: source/Playnite/Localization/locstatus.json ================================================ { "ar": 98, "bg": 98, "ca": 98, "cs": 100, "cy": 0, "da": 26, "de": 100, "el": 98, "es-ES": 98, "et": 98, "fa": 100, "fi": 68, "fr": 100, "ga-IE": 0, "gl": 97, "he": 99, "hr": 47, "hu": 100, "id": 97, "it": 100, "ja": 100, "ko": 97, "lt": 53, "mr": 56, "nl": 99, "no": 98, "pl": 100, "pt-BR": 100, "pt-PT": 61, "ro": 48, "ru": 98, "sk": 61, "sr": 88, "sv-SE": 76, "tr": 100, "uk": 100, "vi": 100, "zh-CN": 100, "zh-TW": 98 } ================================================ FILE: source/Playnite/Localization/lt_LT.xaml ================================================  Lietuvių Playnite kalba Išeiti Filtras aktyvus Filtras išjungtas Papildomi filtrai Filtrai Filtras Neteisingi duomenys Išsaugoti pakeitimus? Tinklapis: www.playnite.link Atvirąjį kodą rasite GitHub Sukurti diagnostinį paketą Siųsti sistemos informaciją Apie Playnite Kūrėjas: Josef Němec Priskirti kategorijai Nustatyti kategorijas Pridėti kategoriją Pažymėta - Priskirti kategoriją Atžymėžta - Pašalinti kategoriją Nepažymėta - Jokių pakeitimų (Kai modifikuojami keli žaidimai) Nenurodyta kategorija Nenurodyta platforma Ooops!Taškas nutiko blogai... Įvyko nepataisoma klaida. Jei norite padėti išspręsti šią problemą, trumpai apibūdinkite, ką darėt prieš užlužima, tada atsiųskite diagnostikos informaciją. Jei esate prisijungę, paketas bus įkeltas į Playnite serverį analizei. Arba galite spustelėti mygtuką „Pranešti apie gedimą“, kad sukurtumėte naują „GitHub“ problemą ir praneštumėte apie gedimą rankiniu būdu. Ačiū už pagalbą. Plėtinys "{0}" sukėlė nesuprantama klaida. Mes rekomenduojame išsaugoti "log" failą ir pranešti problema plėtinio kurėjui.Jeigu klaida tesiasi, išjunkite plėtinį. Plėtinys "{0}" sukėlė nesuprantama klaida. Mes rekomenduojame pranešti problema plėtinio kurėjui.Jeigu klaida tesiasi, išjunkite plėtinį. Nežinomas priedo ar tema sukėlė nepataisoma klaida. Rekomenduojame išjungti trečiūjų šalių priedus, izoliuoti problematiška prieda ir pranešti gedimą priedo kūrėjui ne mum Nesuprantama atsitiko klaida. Jeigu norite padėti sutaisyti klaidą, atsiųskite diagnostine informaciną.Ačių. Išjunkite plėtinį Išsaugoti "log" failą Siųsti diag. informaciją Pranešti apie problemą Iš naujo paleisti Playnite Perkrauti saugumo režime Išjunkite visas trečiujų šalių plėtinius ir naudoti numatytają tema. Uždaryti Playnite Veiksmai padaryti prieš gedimą : Bibliotekos vedlys Pašalinti žaidimą(us)? Negalima ištrinti - Žaidimas arba instaliotojas yra paleistas. Negalima ištrinti - žaidimas yra paleistas Ar tikrai norite pašalinti {0}? Ar jūs esate tikras ,kad norite ištrinti "{0}" šita žaidimą? Ar tikrai norite pašalinti {0}? Pasirinkus "įtraukti į išimčių sąrašą" žaidimas nebebus importuotas kito bibliotekos atnaujinimo metu. Ar tikrai norite pašalinti {0} žaidimus? Pasirinkus opciją "pridėti į išimčių sąrašą" programa neleis žaidimų automatiškai įtraukti kitą kartą atnaujinus biblioteką. Ar tikrai norite pašalinti {0} įrašų, kurie šiuo metu nėra nenodojami ? Nerasta nenaudojamų laukų. Taip (įtraukti į išimčių sąrašą) Šioje sekcijoje yra neįrašytų pakeitimų Atnaujinamas žaidimų bibliotekos formatas... Duombazės atnaujinimas nepavyko, nepavyko atidaryti duombazės failo. Nepavyksta atnaujinti žaidimų bibliotekos. Reikia {0} MB papildomos vietos. Žaidimo Klaida Nepavyko paleisti žaidimo. '{0}' nerastas duombazėje. Nepavyko paleisti žaidimo: {0} Nepavyko atlikti veiksmo: {0} Nepavyko atidaryti failo vietos: {0} Nepavyko aptikti instaliuoto žaidimo dydį: {0} Instaliuoto dydžio skenavimo klaida Buvo {0} klaidos(-ą) instaliuoto dydžio skenavimo Nepavyko sukurti nuorodos: {0} Nepavyko atidaryti žaidimo vadovo: {0} Nepavyko įrašyti žaidimo: {0} Nepavyko ištrinti žaidimo: {0} Nerasta tinkamų žaidimo paleidimo veiksmų. Kai naudojate emuliatoriaus, įsitikinkite, kad platformos atitinka žaidimo ir emuliatoriaus konfigūraciją. Įdiegimo funkcija nėra prieinama. Bibliotekos įskiepis valdantis šį žaidimą nėra įgalintas ar įdiegtas Oficialių metaduomenų atsisiuntimas nėra galimas. Nepasirinktas joks žaidimas Žaidimo skripto vykdymas nepavyko. Programos script paleidimo klaida. Globalaus skripto vykdymas nepavyko. Emuliatoriaus skripto vykdymas nepavyko. Žaidimo veiksmo skripto vykdymas nepavyko. Neįdiegta PowerShell 3.0 ar naujesnė versija. Nepavyko nustatyti kaip paleisti žaidimą. Įgalinta Išjungta Pašalinti Pašalinti nenodojamus Pervadinti Kopijuoti Pridėti Numatytoji piktograma Numatytasis viršelio paveikslėlis Numatytasis fono paveikslėlis Baigti Toliau Atgal BAIGTA GRĮŽTI IŠVALYTI Išvalyti Atmesti Atmesti visus Įtraukti Pavadinimas Kūrėjas Modulis Serija Versija Paskutinį kartą žaista Daugiausiai žaista Žaista kartų Įdiegtas dydis Aplankas Pastabos Pridėta Pridėjimo data Modifikuota Redagavimo data Tiklalapis Kelias iki žaidimo Gerai Išsaugoti Uždaryti Atšaukti Patvirtinti Atstatyti Taip Ne Sveiki Vietinis vartotojas Bendra Medija Nuorodos Instaliavimas Veiksmai Siunčiama… Siunčiama medija… Kraunama... Typas Profilis Profiliai Pašalinti Atsisiųsti Paieška Raiška: Betkoks Keisti dydį Sąrašo rodinys Viršeliai Tinklelio rodinys Išsamusis rodinys Pasirinktinis Nuoroda Ypatinga padėka Licenzija Prisidėję Playnite išjungiamas... Šiandien Vakar Pirmadienį Antradienį Trečiadienį Ketvirtadienį Penktadienį Šeštadienį Sekmadienį Praėjusią savaitę Pastarąjį mėnesį Pastaraisiais metais Seniau nei prieš metus Nuo 0 iki 100MB Nuo 100MB iki 1GB Nuo 1GB iki 5GB Nuo 5GB iki 10GB Nuo 10GB iki 20GB Nuo 20GB iki 40GB Nuo 40GB iki 100GB 100GB ir daugiau Importavimas sėkmingai baigtas. Visi žaidimai Žaidimo Id Duombazės Id Ruošiniai Stulpelis Stulpeliai Eilutė Eilutės Nepavyko gauti žaidimo veiksmo ikonos. Nėra failo tipo priskirto veiksmui. Atsiųsti tik trūkstamus metaduomenis Įjungus šita parinkima bus praleisti metadatos atsiuntimai, kuriuose jau yra informacijos. Žaidimų pasirinkimas Pasirinkite, kuriuos žaidimus norite atnaujinnti su nauja metadata: Visi žaidimai iš duombazės Visi išfiltruoti žaidimai Tik pasirinkti žaidimai Oficiali parduotuvė IGDB Pasirinkite laukus, kuriuos Playnite užpildys automatiškai, ir šaltinį, iš kurio Playnite gaus informaciją. Apsvarstykite galimybę spustelėti aukščiau esantį logo ir atnaujinti igdb.com databazę, kad patobulintumėte Playnite duomenis. Siunčiami metaduomenys… Importuojami neįrašyti žaidimai Importuojami {0} žaidimai... Importuoti emuliuotus žaidimus iš {0}… Siunčiami bibliotekos atnaujinimai... Skenuojamas žaidimų dydis kurių yra bibliotekoje... Skenuojamas importuotų žaidimų dydį... Bibliotekos atnaujinimai baigti Atlaisvinami resursai... Konfigūravimas Nustatymai… Platformos ir emuliatoriai Konfigūruoti emuliatorius… Bibliotekos vedlys... Įrankiai Atsisiųsti metaduomenis... Programiniai įrankiai... Konfigūruoti integracijas... Atidaryti kitą klientą Kiti klientai Atnaujinti žaidimų biblioteką Atšaukti bibliotekos atnaujinimą Atnaujinti emuliuojamus aplankalus Pridėti žaidimą Rankiniu būdu… Skenuoti automatiškai... Emuliuojamas žaidimas... Microsoft Store aplikacija... Apie Playnite Siųsti atsiliepimą Perjungti į viso ekrano rėžimą Nuorodos Pagalba Paremti "Patreon" platformoje Paremkite Ko-fi Vartotojo instrukcijos SDK Documentacija Perkrauti sistemą Išjungti sistemą Užmigdyti sistemą Sistemą užmigdyti įrašius Ūžrakto sistema Atjungti vartotoja Pasirinkti atsitiktinį žaidimą Žaidimo laukai bus rodomi detalių panelėje: Atstumas tarp objektų. Rodyti tinklelio daiktus fone Tinlelio daiktų kampo plotis Nenurodytas žaidimo ikonos šaltinis Nenurodytas žaidimo viršelio šaltinis Nenurodytas žaidimo fono šaltinis Vertikalus atsstumas tarp žaidimų detalių Papildomos informacijos pozicija tinklelio rėžimu Detalus žaidimų pozicijų sarašas Rodyti skirtuka tarp panelių Žaidimo viršelio paveikslėlio aukštis Žaidimų sarašo ikonų aukštis Programos šriftas Lygiaplotis šriftas Filtrų skydelio pozicija Naršymo skydelio pozicija Viršelio paveikslėlio atvaizdavimas Pageidaujamas vaizdo santykis Šie nustatymai taip pat turi įtakos plytelių atvaizdavimui pilno ekrano režimu! Ištempto vaizdo rėžimas Žaidimo dėžutė Epic Games Store GOG Galaxy 2.0 IGDB Kvadratinis Steam baneris Steam vertikalus viršelis Twitch * Reikia paleisti iš nauja ,kad pradėtu veikt Nustatymai Bendri Viršutinė panelė Išvaizda Informacija apie žaidimą Išdėstymas Išplėstiniai Viso ekrano rėžimas Įvestis Efektyvumas Metaduomenys Atnaujinama Paieška Atsarginė kopija Išsaugoti bibliotekos duomenis Atkurti atsarginę kopiją Įtraukti bibliotekos pakeitimus automatiškai Neteisinga duombazės failo vieta, nurtodykite tikslų kelią iki failo. Vartotojo vardas negali būti tuščias. Atsisiųsti metaduomenis po žaidimų importavimo Paleisti Playnite sumažintą Leisti Playnite įjungus kompiuterį Pradėti uždaryta į užduočių juosta Nepavyko užregistruoti Playnite paleidimo paleidžiant kompiuterį. Paleisti viso ekrano rėžime Asinchroninis paveikslėlių krovimas Pagreitina navigavimą žaidimų sąraše, bet sulėtina paveikslėlių krovimą. Rodyti žaidimo pavadinimą, kai nėra viršelio paveikslėlio Rodyti žaidimo pavadinimą tinklelio rėžime Patamsinti neįdiegtus žaidimus Rodyti žaidimų piktogramas sąrašo rodinyje Rodyti objektų skaičių grupės aprašyme Rodyti tik pasirinktus laukus filtre ir explorer panelėse Išjungti aparatinės įrangos spartinimą Naudoti, kai pastebimas strigimas vartotojo sąsajoje. Rodyti paslėptus žaidimus greito paleidimo sarašus Įtakos dešinio pelės sąrašui ir užduočių juostos programėłės meniu sąrašams. Greito paleidimo daiktų skaičius Naudoti žaidimą kaip lango fona Sulieti foną Aukšta Kokybė Užtamsinti foną Rodyti tinklelio rodinyje Tema Temos profilis Viso ekrano rėžimo tema Viso ekrano rėžimo temos profilis Duomenų bazės vieta Prisijungimo būsena: Playnite nustatymai Išvalyti talpyklą Gali padėti išspręsti problemas, atsiradusias bandant susieti paskyras. Rodyti sistemos dėklo piktogramą Sutraukti Playnite į sistemos dėklą Sutraukti Playnite į sistemos dėklą kai programos langas yra uždarytas Kai žaidimas paleidžiamas: Kai žaidimas uždaromas: Rodyti žaidimo laiką dienomis Datos formatas Šis veiksmas atjungs Jus nuo visų paskyrų. Reikalingas programos paleidimas iš naujo, ar norite tęsti? Išvalyti Talpyklą? Pritaikius naują temą Playnite reikės paleisti iš naujo Gauti daugiau temų Sukurti naują temą Gauti daugiau plėtinių Sukurti naują plėtinį Padėk mums išversti Playnite Norint pritaikyti naujus nustatymus, Playnite reikia paleisti iš naujo. Paleisti iš naujo dabar? Paleidus iš naujo bus atšauktos visos šiuo metu vykdomos aktyvios užduotys (atsiuntimai). Paleisti Playnite iš naujo? Playnite negali automatiškai perkelti bibliotekos failų. Prieš keisdami vietą, turite rankiniu būdu perkelti / nukopijuoti failus. Jei tiksliname aplanke vietoje nėra bibliotekos, bus sukurta nauja. Nauja duomenų bazės vieta nebus naudojama, kol „Playnite“ nebus paleista iš naujo. Žaidimo laikas nebus išsaugotas jei pasirinksite komandą "Uždaryti". Eilučių skaičius Stulpelių skaičius Detalaus rodinio eilučių skaičius Rodyti fono paveikslėlį pagrindiniame lange Playnite kaip foną naudos turimas žaidimo nuotraukas, o ne paveikslėlius gautus iš duombazės. Netaikoma žaidimams, kurie jau yra bibliotekoje. Importuoti žaidimo laika iš bibliotekos: Konfigūruojamas, kada Playnite turėtų importuoti žaidimo laiką, kurį praneša žaidimų bibliotekos priedai Playnite duomenų bazėje. Norint naudotis šia funkcija, reikalingas bibliotekos papildinių, atsakingų už žaidimo (-ų) tvarkymą, palaikymas. Visada: importuoja naujų importuotų ir esamų žaidimų žaidimo laiką Playnite duomenų bazėje. Tik naujai importuotiems žaidimams: importuojamas tik naujų importuotų žaidimų žaidimo laikas. Niekada: neimportuoja žaidimo laiko jokiomis aplinkybėmis. Visada Tik neseniai importuotiem žaidimam Niekada Įgalinti žaidimų pultelio palaikymą darbalaukio rėžime Kažkoks mygukas atidaro pilno ekrano režima atroda kažkas pamiršo angliškai Automatinis Metadatos atsiuntimų nustatymai naujai importotiem žaidimam. Naudojamas ekranas Visada naudoti pagrindinį ekraną Rodyti Žaidimų Pavadinimus Rodyti akumuliatoriaus būseną Rodyti akumuliatoriaus įkrovimą procentais Rodyti laikrodį Slėpti pelės rodiklį Instaliuota tik greituose filtruose Pulto mygtuko stilius Išdėstymas Gulsčiasis slinkimas Parinkite vieną iš subkategorijų Nėra pasiekiamų nustatymų Nepavyko užkrauti nustatymus Šie script vykdomi kiekvienam bibliotekoje esančiam žaidimui. Redaguojant žaidimo informaciją, kiekvienam žaidimui galima priskirti atskirus script. Animuotas fono paveikslėlio perėjimas Šrifto dydžiai Automatinis teksto Aliasing Pilki tonai ClearType Idealus Ekranas Teksto formatavimo būdas Teksto atvaizdavimo būdas Šiuo metu žaidimų aprašymams teksto atvaizdavimo ir formatavimo metodai nenaudojami. Iš anksto rodyti fono paveikslėlį Jei įjungta, „Playnite“ atsisiųs fono meno kūrinius, kol atsisiųs metaduomenis, naudos daugiau vietos diske ir padarys meno kūrinius pasiekiamus neprisijungus. Jei išjungta, fono meno kūriniai atsisiunčiami tik tada, kai reikia pirmą kartą, naudojant mažiau vietos, tačiau gali užtrukti, kol meno kūriniai bus rodomi, o kai kurie vaizdai gali būti nepasiekiami neprisijungus. Automatiškai išjungti trečiūjų šalių paleidiklį kai žaidimas išsijungs Kliento uždarymo atidėjimas (sekundėmis) Neuždarykite lango po žaidimo sesijos per mažiau negu (sekundėmis) Automatiškai uždaryti šiuos klientus: Automatiškai uždaryti klientus Importuoti išimčių sąrašą Rodyti pranešimą kada žaidimų biblioteka per didelė Aplanko atidarymo komanda Numatyta amžiaus vertinimo organizacija Atnaujinimo dydis po bibliotekos atnaujinimo Skenuoja ir atnaujina instaliuotų žaidimų dydį jei bus aptikta ,kad failai buvo modifuokoti po praeito skenavimo Nieko Užpildyti Suvienodinti Suvienodinti ir užpildyti Kairėje Dešinėje Viršuje Apačioje Įtraukimo klaida Reikalinga autentifikacija Autentifikuoti nepavyko Alternativus webview rendering režimas Naudoti kada vyksta problemos su webviews, pavizdžiui authentikacijos dialogo integracija. Dalinis krovimas ilgiems žaidimų aprašymams Ilgi aprašymai gali pastebimai striginti programa kada pasirenkami žaidimai. Kada įjungta, užkraus tik dalį teksto ,o vėliau užkraus kitus kada reikalinga. Metadata įtraukimas Atsisiųsti metadatą Nustatyti kurią konfiguracija naudoti naujiem metadata atisiuntimams. Gali būti pakeista nustatymose Emuliacijos importavimo vedlys Šis langas parodys kaip atsiūsti ir importuoti konsolių emuliatorius ir importuoti emuliuotus žaidimus. Nepamirškite, kad papildomus emuliatorius ir / ar žadimus galite įtraukti pagrindiniame meniu (pasirinkus "Įrankiai" norint įtraukti emuliatorių ir "Pridėti žaidimą" norint pridėti emuliuojamą žaidimą). Nėra pasirinkta emuliatorių įtraukimui. Negalėsite automatiškai įtraukti emuliuojamų žaidimų kol nesukonfiguruotas emuliatorius. Ar tikrai nori tęsti ir nutraukti emuliatoriaus įtraukimo procedūrą? Ieškoti aplanke naudojant emulatorių Pasirinkti failus Automatiškai Surasti Aplanke... Konfigūruoti emuliatorius… Ieškoma… Pirminė konfigūracija Bibliotekos integracija Konfigūravimas baigtas Konfigūruoti platformas ir emulatorius Nustatyti emuliatorius Platformos Platforma Emuliatoriai Emuliatorius Pridėti platformą Pasirinkti piktogramą Pasirinkti viršelį Pasirinkite Paveikslėlį Pasirinkti elementą Pasirinkite foną Pasirinkti failą Pasirinkti URL Pridėti emuliatorių Palaikomos platformos Ar norite išsaugoti platformos pakeitimus? Ar norite išsaugoti emuliatoriaus pakeitimus? Programinis failas Argumentai Pasirinkta direktorija Palaikomi failų formatai Įtraukti emuliatorius... Atsisiųsti Emuliatorius... Paleisti argumentus, kurie pateikti emuliatoriaus profilyje Pagalba Rūšiuoti pagal: Grupuoti pagal Kylantys Besileidžiantys Negrupuoti Grupuoti pagal šaltinį Grupuoti pagal kategoriją Grupuoti pagal platformą Rodinio tipas Peržiūra Naršymo skydelis Filtrų skydelis Piktograma Viršelis Fono paveikslėlis Rūšiavimo vardas Šaltinis Žaidimo vadovas Pavadinimas Vartotojo vardas Platforma Kategorija(os) Žanras(ai) Išleidimo data Išleidimo metai Kūrėjas(ai) Žymė(s) Leidėjas(ai) Diegimo būsena Įrašytas Įdiegtas Neįdiegta Paslėptas Mėgstamas Paskutinį kartą žaista Kategorija Aprašymas Žaidimo direktorija Viršelio paveikslėlis Nuorodos Atvaizdo/ISO kelias Žanras Žanrai Įmonė Kompanijos Kūrėjas Kūrėjai Leidėjas Leidėjai Kategorija Kategorijos Žymė Žymės Ypatybė Ypatybės Amžiaus reitingas Amžiaus reitingai Regionas Regionai Šaltinis Šaltiniai Duombazės klaida Nepavyko atidaryti bibliotekos duombazės. Duombazė neatidaryta. Nepavyko pasiekti bibliotekos duombazės. Failas "{0}" yra nepasiekiamas arba jį naudoja kitas procesas. Nepavyko sukurti diagnostinio paketo. Nepavyko automatiškai patalpinti diagnostikos paketo. Diagnostinė informacija buvo sėkmingai išsiųsta. Diagnostinis paketas sukurtas ir perduotas sėkmingai. Prašome įtraukti nurodytą ID į klaidos pranešimą: Importuoti žaidimus iš {0}. Nepavyko surasti žaidimų pasirinktam emuliatoriaus profiliui.Nenurodytas failo tipas arba platforma. Nepavyko pritaikyti temos "{0}", spalvų profilio "{1}" {2} Nepavyko atidaryti nuorodos, netinkamas URL formatas. Nepavyko paleisti aplikacijos. Keisti žaidimo informaciją Paveikslėlio URL Pridėti nuorodą Išsaugoti pakeitimus Pridėti veiksmą Pašalinti veiksmą Pašalinti žaidimo veiksmą Pridėti žaidimus Ieškoti aplanke… Aptikti įrašytus Naršyti… Atidaryti Playnite Profilio nustatymai Žaidimo pavadinimas negali būti tuščias. Žaidimo pavadinimas negali būti tuščias kai ieškoma metadata. Neteisinga žaidimo informacija Įveskite teisingą URL, kuris prasideda http:// arba https:// Pasirinkti URL Nepavyko atsisiųsti metadata: {0} Atsiuntimo klaida Išvalyti filtrus Privatus vartotojas Viešas vartotojas API raktas Paleisties klaida Temos klaida Išvalyti viską Vyksta diegimas Išdiegti Paleidžiama Veikia fone Neteisingas URL Nieko nedaryti Sumažinti Atkurti programos langą Uždaryti Pakeisti Išplėsti Niekada Užbaigimo būsena Vartotojo įvertinimas Kritikų įvertinimas Žaidėjų įvertinimas Skriptai Įskiepiai Metaduomenų šaltiniai Plėtiniai Plėtinio ID Iš naujo paleisti skriptus Visi skriptai paleisti iš naujo sėkmingai. Nepavyko rasti žaidimų pagal paieškos/filtro kriterijus Nerasta elementų Perjungti į darbalaukio rėžimą Uždaryti Playnite Bibliotekos Naujinti viską Sukurta: Versija: Atnaujinta: Modulis: Biblioteka Statistika Visi Nieko Pranešimai Plotis Aukštis Dydis Mažas Normalus Didelis Didesnis Didžiausias Numatytas Pasirinkti Pasirinkti viską Panaikinti visus pasirinkimus Pirmas Atsitiktinis Įkelti daugiau Permatomas Sutraukti Išskleisti Sutraukti Visus Išskleisti Visus Kita Apipavidalinimas Emuliatoriaus argumentai Papildomi emuliatoriaus argumentai Teikti pirmenybę papildomiems emuliatoriaus argumentams Žaidimo veiksmas Pasirinkti žaidimus įtraukimui Metadata paieška Galimas atnaujinimas Pasikeitimai nuo paskutinio atnaujinimo Įrašyti atnaujinimą Tikrinti, ar yra atnaujinimų Atnaujinimo klaida Nepavyko patikrinti ar yra naujinimų. Nerasta atnaujinimų, naudojate naujausią versiją. Nepavyko atsiųsti ir įdiegti atnaujinimo. Yra pasiekiamas Playnite atnaujinimas Atnaujinti temų sąrašą Pritaikyti pasirinktą temą Ieškoti failų pakeitimų Automatiškai atnaujinti temą kai pasikeičia šaltinio failai Paleisti po žaidimo išjungimo Paleisti po žaidimo paleidimo Žaidimo paleidimo skriptas Žaidimo paleido skriptą Žaidimas sustabdė skriptą Paleisti globalų skriptą Globalus Filtruotas Dabartinis Naujas Išbandyti skriptą Rodyti tik pasirinktus elementus. Išsaugoti kaip numatytąjį Pridėti prie mėgstamų Pašalinti iš mėgstamų Slėpti šį žaidimą Pašalinti iš paslėptų Keisti… Apskaičiuoti įdiegties dydį Apskaičiuoti įdiegties dydį (visiems žaidimams) Apskaičiuoti įdiegties dydį (tik ten, kur nėra) Įdiegties dydis Nustatyti kategoriją… Nustatyti žaidimo būseną Pašalinti Žaisti Įrašyti Žaidimo Parinktys Išsamiau Ištrinti Atidaryti žaidimo direktoriją Sukurti darbalaukio nuorodą Atidaryti žaidimo vadovą Daugiau Valdomas bibliotekos įskiepių Žaidimo paleidimo procesą valdys bibliotekos įskiepis atsakingas už šį žaidimą. Patarimas: Detalesnis metadata atsisiuntimo valdymas pavieniui žaidimui galimas pasirinkus punktą "Keisti" . Negalimas, kai vyksta veiksmas fone. Aprašymo tekstas yra jautrus HTML sintaksei Įdiegties dydis nurodytas baitais. Vertė nuo 0 iki 100 arba tuščią kai nėra rezultato. Playnite kūrimą remia šie Patron ir Ko-fi nariai: Prie kodo, lokalizacijos ir kito tobulinimo prisidėjo: Stabdyti žaidimo stebėjimą? Šiuo metu stebimas žaidimo įrašymo procesas. Ar norite tai nutraukti ir gražinti žadimą į ankstesnę būseną? Šiuo metu stebimas žaidimo paleidimas. Ar norite tai nutraukti šį procesą ir gražinti žadimą į ankstesnę būseną? Iš viso žaista Paskutinį kartą žaista {0} d. {1} val. {2} min. {0}val. {1}min {0} minučių {0} sekundes Nežaista Atidaryti darbalaukio rėžime… Atidaromas viso ekrano rėžimas… Kraunama žaidimų biblioteka… Skaičiuojamas įdiegties dydis... Skaičiuojamas {0} įdiegties dydis... Nepavyko įdiegti skripto failo. Skriptas įdiegtas sėkmingai. Įdiegti skriptą Skripto klaida Nepavyko įvykdyti įskiepio funkcijos. Atidaryti metaduomenų aplanką Apskaičiuoti Automatiškai apskaičiuoja įdiegties dydį pasinaudodamas ROM'ais, jei žaidimas tokių turi, arba įdiegties aplanku, jei toks nustatytas {0} klientas yra neįdiegtas. Bus atidarytas {0} klientas. Prašome prisijungti ir uždaryti šį langą. Nerasta įdiegto žaidimo katalogas. Neteisingai nustatytas žaidimo veiksmas. Spręsti vartotojo sinchronizacijos problemas Pervadinti objektą Pridėti objektą Įveskite pavadinimą Įvesti naują pavadinimą Mažiau nei valandą Nuo 1 iki 10 valandų Nuo 10 iki 100 valandų Nuo 100 iki 500 valandų Nuo 500 iki 1000 valandų 1000+ Plėtinio "{0}" nepavyko tinkamai įkelti. Nepavyko įkelti plėtinio "{0}", dabartinė Playnite versija nepalaikoma. Temos "{0}" nepavyko tinkamai įkelti. Nepavyko įkelti temos "{0}", dabartinė Playnite versija nepalaikoma. Plėtinio nepavyko tinkamai įkelti. Temos nepavyko tinkamai įkelti. Tema/Plėtinys naudoja nepalaikomą API versiją. Įdiegta sėkmingai. Įdiegti plėtinį? Bendriniai Nepavyko įdiegti "{0}" plėtinio. Nepavyko įdiekti plėtinių. {0} Nepavyko įdiegti temos. {0} Daugiau neberodyti Failai su plėtiniu {0} yra nesuderinami. Nesuderinas failo plėtinys. Žaidimo aplankas Duomenų aplankas Generuojamas diagnostinis paketas Įkeliamas diagnostinis paketas... Importuoti failą... Kas tai? Ar tikrai norite atlikti šį veiksmą? Bendras žaidimo laikas Vidutinis žaidimo laikas Apžvalga Šoninė juosta Rodyti šoninėje juostoje Atstatyti nustatymus Kūrėjams Pasiekimai Forumas Naujienos Parduotuvės tinklalapis Neseniai žaista Mėgstamiausi Daugiausiai žaista Visi Yra taikomi filtrai. Yra taikomi papildomi filtrai. Paieškos rezultatai pagal: Pasirinkti kitą Plėtiniai... Įdiegta Plėtinių nustatymai Naršyti Atnaujinimai Atnaujinimai ({0}) Ieškoma... Plėtinys nesuderinamas su šia Playnite versija. Nepavyko atsiųsti plėtinio diegimo paketo. Nepavyko atsiųsti plėtinio diegimo manifesto failo. Įdiegti Išdiegti Jau įdiegta Nerasta jokių plėtinio atnaujinimų. Atnaujinti plėtinius Atsisiuntimas nepavyko Licencija atmesta Atsiunčiama {0}… Ieškoma plėtinių atnaujinimų… Rasta vieno arba daugiau plėtinių atnaujinimų. {0} licencijos sutartis Suvienodinti Per paskutines 7 dienas Per paskutinę 31 dieną Per paskutines 365 dienas Daugiau nei prieš 365 dienas Konfigūruoti Išsaugoti ruošinį Šrifto dydis Šrifto dydis Mažas Išvesties API Bendri Išvaizda Garsas Išdėstymas Meniu Įvestis {0} paleidžiamas... Didžiosios raidės Alternatyvus Subalansuotas Kokybiškas Pasirinkite failą... Pasirinkite aplanką... Praleidimo skriptas Pasirinkti paleidžiant Redaguoti žaidimą Atidaryti paiešką Paieškos laukas Paieškos mygtukas Išimtys Nustatymai ================================================ FILE: source/Playnite/Localization/mr_IN.xaml ================================================  मराठी प्लेनाइटची भाषा बाहेर पडा गाळणी सक्षम गाळणी अक्षम अधिक गाळण्या गाळण्या गाळणी अवैध डेटा बदल सेव्ह करावे? मुखपृष्ठ: www.playnite.link GitHub येथे सोर्स कोड पाहा निदानी पाकीट निर्माण करा निदानी माहिती पाठवा प्लेनाइटविषयी माहिती निर्माता: योसेफ नेमेक वर्ग नेमा वर्ग सेट करा वर्ग जोडा वर्ग नाही प्लॅटफॉर्म नाही अरेरे! काहीतरी गडबड झाली आहे... एक्स्टेंशन अक्षम करा लॉग फाइल सेव्ह करा निदान माहिती पाठवा क्रॅशचा अहवाल द्या प्लेनाइट रीस्टार्ट करा सेफ मोडमध्ये रीस्टार्ट करा सर्व बाह्य एक्स्टेंशन अक्षम करून डीफॉल्ट थीम वापरत आहे. प्लेनाइटहून बाहेर पडा क्रॅश व्हायच्या आधी आपण केलेल्या क्रिया (इंग्रजीत): लायब्ररी व्यवस्थापक गेम काढावे? काढता आले नाही - गेम किंवा इन्स्टॉलर चालू आहे. अनइन्स्टॉल करता आले नाही - गेम चालू आहे. {0} नक्की काढून टाकायचे आहे का? तुम्हाला नक्की {0} गेम काढून टाकायचे आहेत का? {0} नक्की काढून टाकायचे आहे का? "वगळायच्या यादीत जोडा" हा पर्याय निवडल्याने पुढच्या अपडेटच्या वेळी दिलेला गेम पुन्हा आयात केला जाणार नाही. {0} गेम नक्की काढून टाकायचे आहेत का? "वगळायच्या यादीत जोडा" हा पर्याय निवडल्याने पुढच्या अपडेटच्या वेळी दिलेले गेम पुन्हा आयात केला जाणार नाहीत. न वापरलेले असे कोणतेही रकाने सापडले नाहीत. होय (वगळायच्या यादीत जोडा) या भागात सेव्ह न केलेले बदल आहेत गेम लायब्ररी स्वरूप अपडेट करत आहे… डेटाबेस अपडेट अपयशी झाले. गेम लायब्ररी अपडेट करता आली नाही. {0} एम.बी.पूर्ती मोकळी जागा आवश्यक आहे. गेममध्ये त्रुटी गेम सुरू करता आला नाही. '{0}' डेटाबेसमध्ये सापडला नाही. गेम सुरू करता आला नाही: {0} क्रिया सुरू करता आला नाही: {0} गेम स्थान उघडता आले नाही: {0} गेमच्या स्थापनेचा आकार शोधून काढता आला नाही: {0} स्थापना आकार स्कॅन त्रुटी स्थापना आकार स्कॅन करताना {0} त्रुट्या आढळल्या शॉर्टकट बनवण्यात अपयशी: {0} मॅन्युअल उघडण्यात अयशस्वी: {0} गेम स्थापित करता आला नाही: {0} गेम विस्थापित करता आला नाही: {0} या गेमसाठी जबाबदार असणारा लायब्ररी प्लगइन एक तर निष्क्रिय आहे नाही तर स्थापित केला नाही. कोणताही गेम निवडलेला नाही. गेमची स्क्रिप्ट कार्यवाही अयशस्वी झाली. अ‍ॅप्लिकेशनची स्क्रिप्ट कार्यवाही अयशस्वी झाली. जागतिक स्क्रिप्ट कार्यवाही अयशस्वी झाली. एम्युलेटरची स्क्रिप्ट कार्यवाही अयशस्वी झाली. खेळायची स्क्रिप्ट क्रिया कार्यवाही अयशस्वी झाली. PowerShell 3.0 किंवा त्याची नवीन आवृत्ती स्थापित नाही. गेम सुरू कसा करायचा हे निश्चित करता आले नाही. सक्षम अक्षम काढून टाका न वापरलेले काढून टाका नाव बदला कॉपी करा जोडा डीफॉल्ट आयकन डीफॉल्ट कव्हर प्रतिमा डीफॉल्ट पार्श्वभूमी प्रतिमा समाप्त करा पुढचे मागचे झाले मागे साफ करा साफ करा डिसमिस करा सर्व डिसमिस करा आयात करा नाव निर्माता मॉड्यूल श्रेणी आवृत्ती याआधी खेळलेले सर्वात जास्त खेळलेले खेळल्याची मोजणी स्थापनेचा आकार फोल्डर टिप जोडले जोडण्याची तारीख बदलले बदलण्याची तारीख संकेतस्थळ स्थान ओके सेव्ह करा बंद करा रद्द करा निश्चित करा रिसेट होय नाही सुस्वागतम स्थानिक प्रयोक्ता साधारण माध्यमे दुवा स्थापना क्रिया डाउनलोड करत आहे… माध्यमे डाउनलोड करत आहे… लोड करत आहे… प्रकार प्रोफाइल प्रोफाइल काढून टाका डाउनलोड करा शोध घ्या रेझल्युशन: कोणतेही झूम यादी दृश्य कव्हर ग्रिड दृश्य तपशील दृश्य सानुकूल यू.आर.एल. विशेष आभार परवाना योगदानकर्ते प्लेनाइटहून बाहेर पडत आहे… आज काल सोमवार मंगळवार बुधवार गुरुवार शुक्रवार शनिवार रविवार मागील आठवडा मागील महिना मागील वर्ष एक वर्षापूर्वी ० ते १०० एम.बी. १०० एम.बी. ते १ जी.बी. १ ते ५ जी.बी. ५ ते १० जी.बी. १० ते २० जी.बी. २० ते ४० जी.बी. ४० ते १०० जी.बी. १०० जी.बी. किंवा जास्त यशस्वीरित्या आयात प्रक्रिया पूर्ण झाली. सर्व गेम गेम आय.डी. डेटाबेस आय.डी. प्रीसेट स्तंभ स्तंभ ओळ ओळी "खेळा" क्रियेने आयकन मिळवता आला नाही. "फाइल" प्रकारची कोणतीही क्रिया उपस्थित नाही. केवळ नसलेला मेटाडेटा डाउनलोड करा हा पर्याय सक्षम केल्याने ज्या डेटा रकान्यांमध्ये आधीच माहिती आङे, त्यांसाठी मेटाडेटा डाउनलोड केला जाणार नाही. गेम निवड नवीन मेटाडेटाने कोणते गेम अपडेट करायचे आहेत हे निवडा: डेटाबेसमधील सर्व गेम या वेळी गाळलेले सर्व गेम केवळ निवडलेले गेम अधिकृत दुकान IGDB प्लेनाइटने कोणते रकाने आपोआप भरले पाहिजेत व कोणत्या स्रोतांपासून डेटा मिळवली पाहिजे हे निवडा. वरील लोगोवर क्लिक करून igdb.com च्या डेटाबेसला अपडेट करायचा विचार करा. प्लेनाइट जो डेटा वापरतो तो या योगदानाने सुधारेल. मेटाडेटा डाउनलोड करत आहे… इनस्टॉल केलेले गेम आयात करत आहे… {0} गेम आयात करत आहे… एम्युलेट केलेले गेम {0} पासून आयात करत आहे… लायब्ररी अपडेट डाउनलोड करत आहे… लायब्ररीतील गेम्सचा आकार स्कॅन करत आहे… आयात केलेल्या गेम्सचा आकार स्कॅन करत आहे… लायब्ररी अपडेट पूर्ण झाले संसाधन सोडून देत आहे… कॉन्फिगरेशन सेटिंग्झ… प्लॅटफॉर्म आणि एम्युलेटर एम्युलेटर कॉन्फिगर करा… लायब्ररी व्यवस्थापक… साधने मेटाडेटा डाउनलोड करा… सॉफ्टवेअर साधने… एकत्रिकरण कॉन्फिगर करा… थर्ड पार्टी क्लायंट उघडा थर्ड पार्टी क्लायंट गेम लायब्ररी अपडेट करा लायब्ररी अपडेट रद्द करा एम्युलेट केलेले फोल्डर अपडेट करा गेम जोडा हाताने… आपोआप स्कॅन करा… एम्युलेट केलेला गेम… मायक्रोसॉफ्ट स्टोअर अ‍ॅप्लिकेशन… प्लेनाइटविषयी माहिती फीडबॅक पाठवा फुलस्क्रीन मोड सुरू करा दुवा मदत पेट्रीऑनवर समर्थन करा Ko-fi वर समर्थन करा प्रयोक्ता माहितीपुस्तिका एसडीके दस्तऐवज सिस्टम रीस्टार्ट करा सिस्टम बंद करा सिस्टम विलंबित करा सिस्टम हायबरनेट करा सिस्टम लॉक करा प्रयोक्त्याला लॉगआउट करा यादृच्छिकरित्या गेम निवडा तपशील पॅनलवर दाखवले जाणारे गेमचे रकाने: वस्तू अंतरण ग्रिड वस्तू पार्श्वभूमी दर्शवा ग्रिड वस्तू सीमेची रुंदी गेम आयकन स्रोत अनुपस्थित गेम कव्हर स्रोत अनुपस्थित गेम पार्श्वभूमी स्रोत अनुपस्थित ग्रिड दृश्यात तपशील स्थान तपशील दृश्यात गेम यादी स्थान पॅनलांच्या मध्ये विभाजक लावा गेम कव्हर चित्राची उंची गेम यादी आयकनची उंची अ‍ॅप्लिकेशन फाँट मोनोस्पेस्ड फाँट गाळणी पॅनलचे स्थान एक्सप्लोरर पॅनलचे स्थान कव्हर प्रतिमा रेंडरिंग प्रसर गुणोत्तर निशाण खालील पर्यायांचा फुलस्क्रीन मोडमधील टाइल रेंडरिंगला देखील परिणाम पडेल! खेचा डी.व्ही.डी. बॉक्स एपिक गेम्स स्टोअर GOG Galaxy 2.0 IGDB चौकोन स्टीम बॅनर स्टीम उभे कव्हर ट्विच * लागू करण्यास रीस्टार्ट करणे आवश्यक सेटिंग्झ साधारण सर्वात वरचा पॅनल देखावा गेमचे तपशील लेआऊट आधुनिक फुलस्क्रीन इनपुट परफॉर्मन्स मेटाडेटा अपडेट करत आहे शोध घ्या बॅकअप लायब्ररी डेटा बॅकअप करा डेटा बॅकअप पुनःस्थापित करा लायब्ररीत बदल आपोआप आयात करा अवैध डेटाबेस फाईल स्थान. योग्य फाईल मार्ग सेट केला पाहिजे. खात्याचे नाव रिकामे नसू शकत. गेम आयात केल्यावर मेटाडेटा डाउनलोड करा प्लेनाईट मिनिमाइझ करून सुरू करा प्लेनाईट कम्प्यूटर सुरू केल्याबरोबर सुरू करा सुरू करताना बंद करून ट्रेमध्ये ठेवा कम्प्यूटर सुरू केल्याबरोबर प्लेनाईट सुरू करणे याची नोंद करण्यात अपयशी फुलस्क्रीन मोडमध्ये सुरू करा असंकालिकपणे प्रतिमा लोड करा गेम याद्यांमध्ये स्क्रोलिंग सुधारून सुरळीत होऊ शकते, पण चित्र लोड व्हायचा वेळ कमी होऊ शकेल कव्हर प्रतिमा गहाळ असल्यास गेमचे नाव दाखवा ग्रि़ड दृश्यात गेमचे नाव दाखवा स्थापित न केलेले गेम अंधारा तपशील दृश्य यादीत गेम आयकन दाखवा गटाच्या वर्णनांवर वस्तूंची मोजणी दाखवा गाळणी आणि एक्सप्लोरर पॅनलांवर केवळ नेमलेले रकाने दाखवा हार्डवेअर अ‍ॅक्सलरेशन अक्षम करा स्टटरिंग किंवा त्यासारख्या यूआय समस्या अनुभवल्यास याचा वापर करावा जलद सुरूवात यादींत लपवलेले गेम दाखवा जम्प यादी आणि ट्रे मेन्यू यादी यांवर परिणाम होईल. जलद सुरूवात वस्तूंची संख्या गेम पार्श्वभूमी प्रतिमा ही विंडो पार्श्वभूमी म्हणून वापरा पार्श्वभूमी अस्पष्ट करा उच्च गुणवत्तेने पार्श्वभूमी अंधारा ग्रिड दृश्यात दाखवा थीम थीम प्रोफाइल फुलस्क्रीन थीम फुलस्क्रीन थीम प्रोफाइल डेटाबेस स्थान लॉगइन स्थिती: प्लेनाईट सेटिंग्झ वेब कॅश साफ करा खाते लिंक करताना आढळलेल्या समस्या सुटल्या जाऊ शकतात. सिस्टम ट्रे आयकन दाखवा प्लेनाईटला मिनिमाइझ करून सिस्टम ट्रेमध्ये ठेवा अ‍ॅप्लिकेशन विंडो बंद केल्यावर प्लेनाईटला मिनिमाइझ करून सिस्टम ट्रेमध्ये ठेवा गेम सुरू झाल्यावर: गेम बंद झाल्यावर: दिनांक स्वरूप: याने आपण सर्व जोडलेल्या सुविधांपासून लॉग-आउट व्हाल. अ‍ॅप्लिकेशन रीस्टारट करावं लागेल. पुढे जायचे का? कॅश साफ करायचे का? नवीन थीम लागू करायला प्लेनाईट रीस्टार्ट करावे लागणार आहे अधिक थीम मिळवा नवीन थीम बनवा अधिक एक्स्टेंशन मिळवा नवीन एक्स्टेंशन बनवा प्लेनाईटचा अनुवाद करण्यात आमची मदत करा नवीन सेटिंग्झ लागू करण्यासाठी प्लेनाईट रीस्टार्ट करावे लागेल. आता रीस्टार्ट करावे का? रीस्टार्ट केल्याने या वेळी चालू असलेल्या क्रिया (डाउनलोड) रद्द केल्या जातील. प्लेनाइट रीस्टार्ट करायचे का? प्लेनाईट आपल्या लायब्ररीच्या फायली आपोआप हलवू शकत नाही. स्थान बदलण्या अगोदर आपण फायली स्वहस्ते मूव्ह/कॉपी केल्या पाहिजेत. निशाण स्थानावर लायब्ररी नसल्यास नवीन लायब्ररी निर्माण केली जाईल. प्लेनाईट रीस्टार्ट करेपर्यंत हे नवीन डेटाबेस स्थान वापरले जाणार नाही. "बंद करा" हा पर्याय सेट असला तर खेळायची कालावधी रेकॉर्ड केली जाणार नाही. ओळींची संख्या स्थंभांची संख्या तपशील दृश्यात दिसणार्‍या ओळींची संख्या मुख्य स्क्रीनवर पार्श्वभूमी प्रतिमा दाखवा लायब्ररीतील गेम खेळलेल्याची कालावधी आयात करा: नेहमीच फक्त अलीकडे आयात केलेल्या गेम्ससाठी कधीच नाही डेस्कटॉम मोडमध्ये कंट्रोलर समर्थन सक्षम करा गाइड बटण दाबून फुलस्क्रीन मोड उघडतो निशाण डिस्प्ले नेहमीच प्राथमिक डिस्प्ले वापरा गेम शिर्षक दाखवा बॅटरी स्थिती दाखवा बॅटरी टक्‍केवारी दाखवा घड्याळ दाखवा माउस करसर लपवा जलद गाळण्यांमध्ये केवळ स्थापित गेम दाखवा बटण चिन्ह लेआऊट आडवे स्क्रोलिंग एक उपभाग निवडा कोणतेही सेटिंग्झ उपलब्ध नाही सेटिंग्झ लोड करण्यात अपयशी फाँट आकार आपोआप एलिअस केलेले ग्रेस्केल क्लियरटाइप नमुनेदार डिस्प्ले मजकूर फॉरमॅट मोड मजकूर रेंडर मोड मजकूर रेंडर आणि फॉरमॅट पद्धती यावेळी गेम वर्णनांसाठी वापरल्या जात नाहीत. पार्श्वभूमी प्रतिमा प्रीलोड करा गेम बंद झाल्यानंतर बाह्य क्लाएंट आपोआप बंद करा खालील क्लाएंट आपोआप बंद करा: क्लायंट आपोआप बंद करा वगळायची यादी आयात करा फोल्डर उघडण्याचा आदेश लायब्ररी अपडेट झाल्यावर गेम्सच्या स्थापनांचे आकार अपडेट करा कोणतेही नाही भरा एकसमान भरण्यास एकसमान डाविकडे उजवीकडे वर खाली आयात करण्यात त्रुटी प्रमाणीकरणाची गरज आहे प्रमाणीकरण अयशस्वी झाली वैकल्पिक वेब दृश्य रेन्डर करण्याचा मोड मेटाडेटा आयात करणे मेटाडेटा डाउनलोड करणे एम्युलेशन आयात विझर्ड एम्युलेटर वापरून फोल्डर स्कॅन करा फायली निवडा फोल्डरमधून आपोआप शोधून काढा… एम्युलेटर कॉन्फिगर करा… स्कॅन करत आहे… {0} स्कॅन करत आहे… पहिल्या वेळीचे कॉन्फिगरेशन लायब्ररी एकत्रीकरण कॉन्फिगरेशन पूर्ण झाले {0} एकत्रीकरण डाउनलोड करत आहे… सुचवलेले एकत्रीकरणाची यादी डाउनलोड करत आहे… सुचवलेल्या एकत्रीकरणाची यादी डाउनलोड करताना अयशस्वी झाले. काही वेळा नंतर अ‍ॅडऑन मेन्यूमधून एकत्रीकरण पुन्हा डाउनलोड करायचा आपण प्रयत्न करू शकता. प्लॅटफॉर्म आणि एम्युलेटर कॉन्फिगर करा एम्युलेटर कॉन्फिगर करा प्लॅटफॉर्म प्लॅटफॉर्म एम्युलेटर एम्युलेटर प्लॅटफॉर्म जोडा आयकन निवडा कव्हर निवडा प्रतिमा निवडा वस्तू निवडा पार्श्वभूमी निवडा फाईल निवडा यू.आर.एल. निवडा एम्युलेटर जोडा समर्थित प्लॅटफॉर्म प्लॅटफॉर्म बदल सेव्ह करायचे का? एम्युलेटर बदल सेव्ह करायचे का? एग्झेक्युटेबल आर्ग्युमेंट वापरातील डिरेक्टरी समर्थित फाईल प्रकार एम्युलेटर आयात करा… एम्युलेटर डाउनलोड करा… तुम्हाला {0} हा एम्युलेटर नक्की काढून टाकायचा आहे का? सध्या {1} गेम किंवा गेम्स त्याच्या वापर करत आहेत. तुम्हाला {0} हा प्लॅटफॉर्म नक्की काढून टाकायचा आहे का? सध्या {1} गेम्स व {2} एम्युलेटर त्याच्या वापर करत आहेत. सेटिंग मदत क्रमवारी क्रमवारी दिशा याने गट बनवा चढत्या क्रमात उतरत्या क्रमात गट बनवू नका लायब्ररीप्रमाणे गट बनवा वर्गाप्रमाणे गट बनवा प्लॅटफॉर्मप्रमाणे गट बनवा दृश्याचा प्रकार दृश्य एक्सप्लोरर पॅनल गाळणी पट्टी आयकन लायब्ररी आयकन कव्हर प्रतिमा पार्श्वभूमी प्रतिमा क्रमातील नाव लायब्ररी माहितीपुस्तिका नाव स्थापनेची ड्राइव्ह अकाउंटचे नाव प्लॅटफॉर्म वर्ग शैली रिलीझ तारीख रिलीझ वर्ष विकासक खूणचिट्ठी प्रकाशक स्थापना स्थिती सर्व गाळण्या जुळल्या पाहिजेत स्थापित स्थापित विस्थापित लपवलेला आवडीचा एचडीआर समर्थन सक्षम करा याआधी खेळलेले वर्ग वर्णन स्थापना फोल्डर कव्हर प्रतिमा दुवा इमेज, रॉम किंवा आयएसओचा मार्ग शैली शैल्या कंपनी कंपन्या विकासक विकासक प्रकाशक प्रकाशक वर्ग वर्ग खूणचिट्ठी खूणचिट्ठ्या फीचर फीचर वयाचे रेटिंग वयाचे रेटिंग प्रदेश प्रदेश स्रोत स्रोत अलीकडील उपक्रम डेटाबेस त्रुटी लायब्ररी डेटाबेस उघडण्यात अपयशी. डेटाबेस उघडले नाही. लायब्ररी डेटाबेस उघडता आला नाही. "{0}" ही फाईल एक तर दुसर्‍या प्रॉसेसमध्ये वापरात आहे, नाही तर ती एका न उघडता येणार्‍या स्थानी आहे. {0} पासून गेम आयात करण्यात अपयशी. {0} पासून एम्युलेट केलेले गेम आयात करण्यात अपयशी. "{0}" ही थाम व "{1}" ही रंग प्रोफाइल लागू करण्यात अपयशी {2} लिंक उघडता आली नाही, यू.आर.एल. वैध स्वरूपात नाही. अ‍ॅप्लिकेशन सुरू करण्यात अपयशी. गेमचे तपशील संपादित करा प्रतिमा यू.आर.एल. दुवा जोडा रॉम जोडा बदल सेव्ह करा कृती जोडा काढून टाका गेम जोडा फोल्डर स्कॅन करा… स्थापित असलेले शोधून काढा चाळा… प्लेनाइट उघडा प्रोफाइल सेटिंग गेमचे नाव रिकामे ठेवले जाऊ शकत नाही. मेटेाडेटाचा शोध घेण्याअगोदर गेमचे नाव रिकामे असू शकत नाही. अवैध गेम डेटा http:// किंवा https:// यांनी सुरू होणारे वैध वेब यूआरएल प्रविष्ट करा यू.आर.एल. निवडा मेटाडेटा डाउनलोड करण्यात अयशस्वी: {0} डाउनलोड त्रुटी गाळण्या पुसून टाका खाजगी खाते सार्वजनिक खाते API की सुरू करताना त्रुटी थीम त्रुटी सर्व पुसून टाका स्थापित करत आहे विस्थापित करत आहे सुरू करत आहे चालू आहे अवैध यू.आर.एल. काहीही करू नका मिनिमाइझ करा विंडो रिस्टोर करा बंद करा बदला प्रगत कधीच नाही पूर्ण केल्याची स्थिती पूर्ण केल्याच्या स्थित्या प्रयोक्ता स्कोर समीक्षकांचा स्कोर सामूहिक स्कोर गेम स्क्रिप्ट अ‍ॅप्लिकेशन स्क्रिप्ट स्क्रिप्ट प्लगइन मेटाडेटा स्रोत एक्स्टेंशन एक्स्टेंशन आयडी स्क्रिप्ट रीलोड करा दिलेल्या शोध/गाळणी निकषासाठी कोणतेही गेम सापडले नाहीत कोणत्याही वस्तू सापडल्या नाहीत डेस्कटॉप मोड चालू करा प्लेनाइटहून बाहेर पडा लायब्ररी सर्व अपडेट करा निर्माता: आवृत्ती: अपडेट केले: मॉड्यूल: लायब्ररी आकडेवारी सर्व कोणतेही नाही सूचना रुंदी उंची आकार छोटा साधारण मोठा अजून मोठा सर्वात मोठा डीफॉल्ट निवडा सर्व निवडा सर्व निवडी काढा पहिले यादृच्छिक प्रयोक्त्याची निवड अधिक लोड करा पारदर्शक संक्षिप्त करा विस्तारा सर्व संक्षिप्त करा सर्व विस्तारा इतर थीम एम्युलेटर आर्ग्युमेंट खेळायची क्रिया आयात करायचा मेटाडेटा निवडा आयात करायचे गेम निवडा मेटाडेटा शोध अपडेट उपलब्ध अपडेट डाउनलोड करून स्थापित करा अपडेटकरीता तपासा अपडेट करण्यात त्रुटी नवीन आवृत्तीचा तपास घेण्यात अयशस्वी. कोणतीही नवीन आवृत्ती सापडली नाही. आपल्याकडे आधीच नवीनतम आवृत्ती आहे. अपडेट डाउनलोड करून स्थापित करण्यात अयशस्वी. सध्या पार्श्वभूमीत कोणतेतरी कार्य चालू आहे. ते रद्द करून अपडेट चालू ठेवायचे आहे का? सध्या पार्श्वभूमीत कोणतेतरी कार्य चालू आहे. ते रद्द करून प्लेनाइट बंद करायचे आहे का? सध्या पार्श्वभूमीत कोणतेतरी कार्य चालू आहे. मोड बदलल्याने ते कार्य रद्द केले जाईल. तरीही मोड बदलायचा आहे का? प्लेनाईटसाठी अपडेट उपलब्ध आहे थीम्सची यादी पुनः लोड करा निवडलेली थीम लागू करा स्क्रिप्ट रनटाइम गेम सुरू करण्याआधी बजावा गेमहून बाहेर पडताना बजावा गेम सुरू झाल्यानंतर बजावा गेम स्क्रिप्ट सुरू करत आहे गेमने स्क्रिप्ट सुरू केली गेमने स्क्रिप्ट थांबवली जागतिक स्क्रिप्ट बजावा जागतिक गाळलेले वर्तमान नवीन चाचणी स्क्रिप्ट केवळ निवडलेल्या वस्तु दाखवा. डीफॉल्ट म्हणून सेव्ह करा आवडीच्या गेम्समध्ये जोडा आवडीच्या गेम्समधून काढून टाका हा गेम लपवा लपवलेल्यांमधून काढून टाका एचडीआर समर्थन सक्षम करा एचडीआर समर्थन अक्षम करा संपादित करा… स्थापनेचा आकार मोजा स्थापनेचा आकार मोजा (सर्व गेम्ससाठी) स्थापनेचा आकार मोजा (केवळ नसलेल्या डेटासाठी) स्थापनेचा आकार वर्ग सेट करा… पूर्ण केल्याची स्थिती सेट करा काढून टाका खेळा स्थापित करा गेम पर्याय तपशील विस्थापित करा स्थापना स्थान उघडा डेस्कटॉप शॉर्टकट बनवा माहितीपुस्तिका उघडा अधिक लायब्ररी प्लगइनद्वारे व्यवस्था गेम सुरू करायच्या प्रॉसेसचे व्यवस्थापन हे या गेमसाठी जबाबदार असलेला लायब्ररी प्लगइन करेल. दिलेल्या पानावर '{0}' गेमविषयी कोणतीही निगडित माहिती सापडली नाही. टीप: एक गेम संपादित करत असताना अधिक आधुनिक मेटाडेटा डाउनलो प्रक्रियेचा वापर करू शकता. "संपादन" मेन्यू पर्याय पाहा. एखादे कार्य चालू असताना हे उपलब्ध नाही. वर्णन मजकुरात एचटीएमएल सिंटॅक्स वापरले जाईल खेळलेल्याची कालावधी सेकंदांमध्ये नोंदवली जाते. स्थापनेचा आकार बाइटमध्ये दर्शवला जाईल. रिलीझ दिनांक "वर्ष-महिना-दिवस" या स्वरूपात सेट केले पाहिजे. महिना आणि दिवस यांचे मूल्य वगळले तरी चालतील. 0 पासून 100 पर्यंतचे मूल्य, किंवा स्कोअर द्यायचा नसेल तर रिकामे ठेवा. कोड, भाषांतर व इतर योगदानकर्ता, अनुक्रमात न लावता: गेमचे अनुबोधन रद्द करावे का? स्थापना अनुबोधन सध्या चालू आहे. ही प्रक्रिया रद्द करून गेमला आधीच्या स्थितीत परत करायचे आहे का? खेळल्याची कालावधी याआधी खेळले {0}दि {1}ता {2}मि {0} ता. {1} मि. {0} मिनिटे {0} सेकंद खेळले नाही डेस्कटॉप मोडमध्ये उघडत आहे… फुलस्क्रीन मोडमध्ये उघडत आहे… गेम लायब्ररी लोड कर आहे… स्थापनेचा आकार मोजत आहे… {0} च्या स्थापनेचा आकार मोजत आहे… स्क्रिप्ट फाइल स्थापित करण्यात अयशस्वी. स्क्रिप्ट यशस्वीरित्या स्थापित केली. स्क्रिप्ट स्थापित करा स्क्रिप्टमध्ये त्रुटी मेटाडेटा फोल्डर उघडा मोजा {0} हा क्लाएंट स्थापित नाही. {0} क्लाएंट आता उघडला जाईल. कृपया त्यात साइन-इन करा व मगच हा संदेश बंद करा. प्रयोक्ता साइन-इन होण्यास थांबले आहे. तसे झाल्यावर हा संदेश बंद करा… गेमच्या स्थापनेचा फोल्डर सापडला नाही. वस्तुचे नाव बदला नवीन वस्तु जोडा नाव प्रविष्ट करा नवीन नाव प्रविष्ट करा एक तासापेक्षा कमी १ ते १० तास १० ते १०० तास १०० ते ५०० तास ५०० ते १००० तास १००० पेक्षा अधिक तास स्थापना पूर्ण करण्यास पेलनाईट रीस्टार्ट करावे लागणार आहे. आता रीस्टार्ट करायचे का? एक्स्टेंशन नीट पॅकेज केले नाही. थीम नीट पॅकेज केली नाही. "{0}" हे एक्स्टेंशन नीट लोड होण्यात अयशस्वी. "{0}" हे एक्स्टेंशन लोड करता आले नाही. प्लेनाइटची सध्याची आवृत्ती समर्थित नाही. "{0}" ही थीम नीट लोड करण्यात अपयशी. ही थीम किंवा हा एक्सटेंशन असमर्थित API आवृत्ती वापरत आहे. स्थापना यशस्वीरित्या पार पडली. अ‍ॅडऑन स्थापित करायचे का? जेनेरिक "{0}" हा अ‍ॅडऑन स्थापित करण्यात अपयशी. एक्सटेंशन स्थापित करण्यात अपयशी. {0} तुम्हाला नवीन एक्स्टेंशन स्थापित करायचा आहे का? {0} निर्माता: {1} आवृत्ती {2} थीम स्थापित करण्यात अपयशी. {0} तुम्हाला नवीन थीम स्थापित करायची आहे का? {0} निर्माता: {1} आवृत्ती {2} पुन्हा दाखवू नका निवडलेली थीम नक्की विस्थापित करायचे आहे का? अ‍ॅप्लिकेशन यापुढे सुरू करताना विस्थापन रांगेत लावले जाईल. अंतर्गत थीम विस्थापित करता येत नाहीत. ही थीम प्लेनाइटच्या या आवृत्तीचे समर्थन करत नाही. निवडलेले एक्स्टेंशन नक्की विस्थापित करायचे आहे का? अ‍ॅप्लिकेशन यापुढे सुरू करताना विस्थापन रांगेत लावले जाईल. एकत्रित एक्स्टेंशन विस्थापित करता येत नाहीत. हे एक्स्टेंशन प्लेनाइटच्या या आवृत्तीचे समर्थन करत नाही. स्थापना फोल्डर डेटा फोल्डर फाइल आयात करा… हे काय आहे? तुम्हाला नक्की असे करायचे आहे का? खेळलेल्याची एकूण कालावधी खेळलेल्याची सरासरी कालावधी खेळलेल्याची कमाल कालावधी एकूण स्थापनेचा आकार विहंगावलोकन साइडबार साइडबारमध्ये दाखवा सेटिंग रिसेट करा विकासकांकरिता बाह्य एक्स्टेंशन संपूर्ण फोल्डर पाथ प्रविष्ट करा. अचीव्हमेंट मंच बातम्या दुकानाचे पृष्ठ प्रारंभिक सेटअप पूर्ण झाले नाही. प्लेनाईट आता डेस्कटॉप मोडमध्ये रीस्टार्ट होईल, ज्याने ही प्रक्रिया पूर्ण करता येईल. अलीकडे खेळलेले आवडीचे सर्वात जास्त खेळलेले सर्व गाळण्या वापरल्या आहेत. अधिक गाळण्या वापरल्या आहेत. या शोधाचे परिणाम दाखवत आहे: याच नावाची वस्तू आधीपासून अस्तित्वात आहे. वर्तमान गाळण्यांपुरती निवड मर्यादित करा दूसरा निवडा अ‍ॅडऑन… स्थापित एक्स्टेंशन सेटिंग चाळा अपडेट अपडेट ({0}) स्थापित केलेले एक्स्टेंशन व थीम यांचे व्यवस्थापन व त्यांचे सेटिंग एका नवीन "अ‍ॅडऑन" मेन्यू येथे हलवण्यात आले आहे. थीम - डेस्कटॉप थीम - फुलस्क्रीन शोध घेत आहे… अ‍ॅडऑन स्थापना पॅकेज डाउनलोड करण्यात अपयशी. अ‍ॅडऑन स्थापना मॅनिफेस्ट डाउनलोड करण्यात अपयशी. प्रलंबित बदल लागू करण्यास अ‍ॅप्लिकेशन रीस्टार्ट आवश्यक आहे. या अ‍ॅडऑनची स्थापना शेड्यूल केली गेली आहे. स्थापित करा विस्थापित करा आधिच स्थापित केले आहे कोणतेही नवीन अ‍ॅडऑन अपडेट सापडले नाहीत. अ‍ॅडऑन अपडेट करा चेन्जलॉग उपलब्ध नाही स्थापना करण्यास शेड्यूल केले डाउनलोड अयशस्वी परवाना नाकारला गेला {0} डाउनलोड करत आहे… अपडेट करण्यास वस्तू निवडा स्वीकारा नाकारा क्रिया निवडा दुवा फाईल एम्युलेटर स्क्रिप्ट डीफॉल्ट प्रक्रिया फोल्डर कोणतेही नाही एकसमान केवळ वस्तू केवळ सुरुवातीला व शेवटला स्क्रोल संवेदनशीलता सुरळीत स्क्रोल अ‍ॅनिमेशन वेग वस्तु काढून टाकायची का? ही वस्तू नक्की काढून टाकायची आहे का? ही बटणे वरच्या पॅनलवर दाखवा: साधारण दृश्य सेटिंग गटपाडणी सेटिंग क्रमवारी सेटिंग गाळणी प्रीसेट प्लगइन वस्तू स्थान विभाग विभाजक रुंदी मुख्य मेन्यू बटण बाजूच्या पट्टीत ठेवा एक्सप्लोरर पॅनल यादृच्छिक गेम निवडक दृश्यातून एखादे गेम यादृच्छिकरित्या निवडा गट बनवायचे व क्रमवारी लावायचे सेटिंग सेव्ह करा फुलस्क्रीन मोडमध्ये जलद गाळणी म्हणून दाखवा गेल्या 7 दिवसांत गेल्या ३१ दिवसांत गेल्या ३६५ दिवसांत ३६५ पेक्षा जास्त दिवसांपूर्वी कॉन्फिगर प्रीसेट सेव्ह करा गेम सुरू केल्यानंतर मिनिमाइझ करा एखादा गेम सुरू केल्यानंतर प्लेनाइट मिनिमाइझ करा. हे अक्षम केले तर गेम सुरू केल्यावर गेममध्ये इनपुट न मिळण्याच्या समस्या आढळू शकतील! फाँट आकार छोटा फाँट आकार गेम कंट्रोलर समर्थन मेन मेन्यूवर वस्तू दाखवा: केवळ प्राथमिक कंट्रोलर सक्षम केल्यावर केवळ प्राथमिक कंट्रोलरपासून इनपुट स्वीकारले जाईल. इंटरफेस व्हॉल्यूम पार्श्वभूमी व्हॉल्यूम पार्श्वभूमीत असताना म्यूट करा ऑडियो इंटरफेस आरंभ करण्यात अयशस्वी. आउटपुट API ऑडियो आउटपुटसाठी वापरला जाणारा API. आवाजात समस्या अनुभवत असलात तर हे बदला. साधारण दृश्य ऑडिओ मांडणी मेन्यू इनपुट {0} सुरू होत आहे… {0} चालू आहे… कॅपिटल स्पेस चित्र रेंडर करणारे स्केलर वैकल्पिक संतुलित गुणवत्ता फाईल निवडा… फोल्डर निवडा… स्टार्टअप स्क्रिप्ट स्टार्टअपच्या वेळी निवडा स्टार्टअपच्या वेळी निवडा अंतर्गत प्रोफायली अंतर्गत प्रोफाईल सानुकूल प्रोफायली सानुकूल प्रोफाईल एम्युलेटर सुरू करण्याअगोदर बजावा एम्युलेटर सुरू केल्यानंतर बजावा एम्युलेटरहून बाहेर पडताना बजावा एम्युलेटर एग्झेक्युटेबल सापडले नाही. एक गेम म्हणून विलीन करा प्लॅटफॉर्म सेट करा प्रदेश सेट करा फोल्डर स्कॅन करा कॉन्फिगरेशन स्कॅन करा चेकसम स्कॅनमधून पॅटर्न वगळा विनिर्दिष्ट केलेल्या पॅटर्नांशी जुळणार्‍या फायली चेकसमसाठी स्कॅन केल्या जाणार नाहीत. त्याऐवजी त्यांची जुळणी फाइलच्या नावाशी होईल. अधिक माहितीसाठी एम्युलेटर मदत पान पाहा. एम्युलेटरने स्कॅन करा नवीन कॉन्फिगरेशन सेव्ह करताना नाव सेट केले पाहिजेच. एम्युलेटर किंवा एम्युलेटर प्रोफाइल सेट केले नाही. आयात केलेले लपवा आयात करायचे प्रोफाइल: सबफोल्डर स्कॅन करा स्कॅनर जोडा वाचवून ठेवलेला स्कॅनर जोडा स्कॅन सुरू करा ही स्थिती पहिल्यांदाच खेळल्यावर गेम्सना नेमली जाते या नावाचा गाळणी प्रीसेट आधीच अस्तित्वात आहे. तो प्रीसेट नवीन सेटिंग्झने अपडेट करायचा आहे का? क्रमवारी गेमांचा आकार मोजताना ड्राइव्हवरील प्रत्यक्ष आकार मिळवा हे सक्रिय केल्याने स्कॅनचा वेग कमी असेल व ड्राइव्हवर असलेल्या फायलींचा प्रत्यक्ष आकार मिळवला जाईल. हे निष्क्रिय केल्याने स्कॅनचा वेग जास्त असेल व फायलींचा स्वतःचा आकार वापरला जाईल. स्कॅनमधून ऑनलाइन फाइल वगळा स्थानिक पातळीवर उपलब्ध नसल्यास क्लाउड स्टोरेजवर साठवलेल्या फायली स्कॅन करून आयात केल्या जाणार नाहीत. केवळ यांसाठी समर्थित आहे: गूगल ड्राइव्ह, ड्रॉपबॉक्स, वनड्राइव्ह सर्वांवर लागू करा दिवसातून एकदा आठवड्यातून एकदा प्रत्येक स्टार्टअपला लायब्ररी अपडेट करा एम्युलेशन फोल्डर स्कॅन करा लपवलेले गेम सामील करा रकाने संपादित करा सर्व निवडा / निवड काढा उघडा सक्रिय करा नेमणूक करा गेम्सचा शोध घेण्यास टाइप करायला सुरुवात करा… मदतीसाठी [F1] दाबा विस्थापित केलेले गेम सामील करा लपवलेले गेम सामील करा विस्थापित केलेले गेम सामील करून विस्थापित गेम वगळून लपवलेले गेम सामील करून लपवलेले गेम सामील नसून खेळा किंवा स्थापित करा तपशील पाहा गेम मेन्यू गेम संपादित करा शोध घ्या शोध पट्टी शोध बटण प्राथमिक गेम क्रिया द्वितीयक गेम क्रिया CTRL-F दाबल्याने शोध बॉक्सवर लक्ष केंद्रित करण्याऐवजी जागतिक शोध उघडावे शोध पुरवणारे प्लेनाइट शोध एक्स्टेंशन सेटिंग वगळलेले फाइल वगळायच्या यादीत जोडा फोल्डर वगळायच्या यादीत जोडा वगळायच्या वस्तू केवळ सेव्ह केलेल्या स्कॅनर कॉन्फिगरेशनशी जोडता येतात. "{0}" स्कॅनर यात वगळायच्या वस्तू जोडण्यात आल्या आहेत. लपवल्याची स्थिती डेटा बॅकअप रद्द करण्यात आले. डेटा बॅकअप अयशस्वी. डेटा बॅकअप त्रुटी डेटा बॅकअप चालू आहे… बॅकअपमधून डेटा पुनःस्थापित करत आहे… सेटिंग गेम लायब्ररी गेम लायब्ररी मीडिया स्थापित केलेले एक्स्टेंशन एक्स्टेंशन डेटा स्थापित केलेल्या थीम बाकअप फोल्डर अधिक डेटा सामील करा: वेब चित्र शोध अ‍ॅडऑनविषयी माहिती मिळवत आहे… कोणतेही मेटाडेटा स्रोत उपलब्ध नाही प्ले क्रिया सेटिंग सुरू झाल्यावर प्रोफाईल निवडावी सुरू झाल्यावर एम्युलेटर निवडावे आपोआप नेहमीच चालू नेहमीच बंद अ‍ॅप्लिकेशन मेन्यू गेम मेन्यू प्रोग्राम फोल्डर प्रयोक्ता डेटा फोल्डर तुम्ही केलेले बदल तुम्हाला सेव्ह करायचे आहेत का? सुवाह्य स्थापना कोणतेही कंट्रोलर सापडले नाहीत ================================================ FILE: source/Playnite/Localization/nl_NL.xaml ================================================  Dutch Playnite Taal Afsluiten Filter Actief Filter Uitgeschakeld Aanvullende filters Filters Filter Ongeldige Data Wijzigingen Opslaan? Homepagina op www.playnite.link Source Code op GitHub Maak diag. package Diagnostische informatie verzenden Over Playnite Gemaakt door Josef Němec Wijs Categorie Toe Stel Categorieën In Categorie Toevoegen Aangevinkt - Wijs categorie toe Uitgevinkt - Verwijder categorie Er tussenin - Geen wijzigingen (bij bewerken van meerdere spellen) Geen Categorie Geen Platform Oops! Er ging iets fout... Er onherstelbare fout heeft opgetreden. Als je ons wilt helpen dit probleem op te lossen, omschrijf dan alsjeblieft kort welke acties je hebt ondernomen vóór de crash, en maak dan een diagnose pakket. Als je online bent wordt dit pakket geüpload naar de Playnite server voor analyse. Als alternatief kan je de 'Rapporteer Crash' knop gebruiken om een nieuw probleem topic aan te maken op GitHub en de crash handmatig te rapporteren. Bedankt voor je hulp. Extensie {0} veroorzaakte een onherstelbare fout. We raden aan om het log bestand op te slaan en het probleem te rapporteren aan extensie ontwikkelaar. Als het probleem zich blijft voordoen. Schakel de extensie dan uit. Extensie {0} heeft een onherstelbare fout veroorzaakt. We raden aan om het probleem te rapporteren aan de extensie ontwikkelaar. Als het probleem zich blijft voordoen, schakel de extensie dan uit. Onbekende extensie of thema veroorzaakte een onherstelbare fout. We bevelen aan extensies van derde partijen uit te schakelen, de problematische extensie te isoleren en het probleem te rapporteren aan de ontwikkelaar van die extensie. Onherstelbare fout opgetreden. Als je ons wilt helpen dit probleem op te lossen, stuur ons dan alsjeblieft diagnostische informatie. Bedankt. Schakel de extensie uit. Sla het log bestand op. Verstuur diag. info Rapporteer Probleem Herstart Playnite Herstart in Veilige modus Schakel alle 3de partij extensies uit en gebruik het standaard thema. Sluit Playnite af Acties ondernomen vóór de crash (in Engels): Bibliotheek Beheerder Verwijder Spel(len)? Kan niet verwijderen - Game of de installer is bezig. Kan niet de-installeren - Game is bezig. Weet u zeker dat u {0} wilt verwijderen? Weet u zeker dat u {0} spellen wilt verwijderen? Weet u zeker dat u {0} wilt verwijderen? Het selecteren van de "Ja (voeg toe aan uitsluitingslijst)" optie zorgt ervoor dat bij het updaten van de bibliotheek, deze game niet opnieuw wordt geïmporteerd. Weet u zeker dat u {0} spellen wilt verwijderen? Als u de optie "toevoegen aan uitsluitingslijst" selecteert, wordt voorkomen dat de game de volgende keer dat de bibliotheek wordt bijgewerkt, opnieuw wordt geïmporteerd. Weet je zeker dat je {0} entries wilt verwijderen die momenteel niet worden gebruikt? Geen ongebruikte velden gevonden. Ja (voeg toe aan uitsluitingslijst) Er zijn niet-opgeslagen wijzigingen in deze sectie Database versie aan het updaten… Database update mislukt. Kan de spelbibliotheek niet bijwerken. {0} MB vrije ruimte is vereist. Spel Error Kan het spel niet starten. '{0}' kon niet worden gevonden in de database. Kan het spel niet starten: {0} Kan de actie niet starten: {0} Kan de spellocatie niet openen: {0} Kon de installatiegrootte van het spel niet detecteren: {0} Fout bij installatiegrootte scan Er waren {0} fouten tijdens het scannen van de installatiegrootte Snelkoppeling maken mislukt: {0} Kan handleiding niet openen: {0} Kan het spel niet installeren: {0} Kan het spel niet deïnstalleren: {0} Geen geldige spelstartup acties gevonden. Zorg ervoor dat platformdefinities overeenkomen tussen het spel en de emulator configuratie. Installatie implementatie is niet beschikbaar De bibliotheek plug-in die verantwoordelijk is voor deze game is uitgeschakeld of niet geïnstalleerd. Officiële metadata download is niet beschikbaar. Er is geen spel geselecteerd. Game's script actie executie is gefaald. Uitvoering van applicatiescript is mislukt. Globale script actie executie is gefaald. Emulator Script uitvoering is mislukt. Speel script actie is mislukt. PowerShell 3.0 of nieuwer is niet geïnstalleerd. Kon niet bepalen hoe de game te starten. Ingeschakeld Uitgeschakeld Verwijderen Verwijder ongebruikt Hernoemen Kopiëren Toevoegen Standaard Icoon Standaard Cover Afbeelding Standaard achtergrondafbeelding Klaar Volgende Vorige GEREED TERUG WISSEN Wissen Negeren Negeer alles Importeren Naam Auteur Module Series Versie Laatst Gespeeld Meest gespeeld Aantal Keer Gespeeld Installatiegrootte Map Notities Toegevoegd Datum Toegevoegd Bewerkt Datum Gewijzigd Website Pad Oké Opslaan Sluiten Annuleren Bevestigen Reset Ja Nee Welkom Lokale Gebruiker Algemeen Media Links Installatie Acties Downloaden… Downloaden media… Laden… Type Profiel Profielen Verwijderen Download Zoeken Resolutie: Alle Zoom Lijstweergave Covers Rasterweergave Details-weergave Aangepast URL Speciale dank aan Licentie Bijdragers Playnite wordt afgesloten… Vandaag Gisteren Maandag Dinsdag Woensdag Donderdag Vrijdag Zaterdag Zondag Afgelopen Week Afgelopen Maand Afgelopen Jaar Meer dan 1 jaar geleden 0 tot 100MB 100MB tot 1GB 1GB tot 5GB 5GB tot 10GB 10GB tot 20GB 20GB tot 40GB 40GB tot 100GB 100GB of meer Importeren succesvol voltooid. Alle Spellen Spel Id Database-ID Voorinstellingen Kolom Kolommen Rij Rijen Kon geen icoon uit Speel actie halen. Er is geen actie met het type bestand aanwezig. Download alleen missende metadata Het inschakelen van deze optie zal het downloaden van metadata overslaan voor gegevensvelden die al informatie bevatten. Spellen selectie Selecteer alsjeblieft de spellen die moeten worden geüpdatet met nieuwe metadata: Alle spellen uit de database Alle huidig gefilterde spellen Alleen geselecteerde spellen Geen metadata velden geselecteerd Er zijn geen metadata velden geselecteerd om te downloaden. Selecteer er ten minste één en schakel ten minste één metadata aanbieder in. Officiële Winkel IGDB Selecteer alsjeblieft welke velden Playnite automatisch moet invullen en welke bronnen gebruikt moeten worden om deze data te verkrijgen. Overweeg om op het bovenstaande logo te klikken en updates bij te dragen aan de igdb.com database om de gegevens waar Playnite gebruik van maakt te verbeteren. Metadata aan het downloaden… Geïnstalleerde spellen aan het importeren… {0} spellen aan het importeren… Importeer geemuleerde games van {0}… Bibliotheek updates aan het downloaden… Scannen van spelgrootte in bibliotheek… Grootte van geïmporteerde spellen scannen… Biblotheekupdate afgerond Bronnen vrijgeven… Configuratie Instellingen… Platforms en Emulators Emulators configureren Bibliotheekbeheer… Gereedschappen Download Metadata... Softwaretools Configureer Integraties Open Cient van derde Partij 3de Partij Clients Herlaad Spellen Lijst Annuleer bibliotheek update Update Geemuleerde mappen Voeg Spel Toe Handmatig… Scan Automatisch... Geëmuleerd Spel... Microsoft Store applicatie… Over Playnite Stuur Feedback Overschakelen naar volledige scherm modus Koppelingen Hulp Steun op Patreon Ondersteun {0} op Ko-fi Gebruikershandleiding SDK Documentatie Opnieuw opstarten Systeem uitschakelen Systeem Onderbreken Systeem in slaapstand zetten Vergrendel systeem Gebruiker afmelden Kies een Willekeurig Spel Spelvelden weer te geven op detailspaneel: Itemafstand Teken grid item achtergrond Grensbreedte rasteritem Bron ontbrekende spelicoon Bron ontbrekende spelomslag Mist game achtergrond bron Verticale afstand tot speldetails Positie details in rasterweergave Positie spellijst in detailsweergave Teken scheidingslijn tussen panelen Afbeeldingshoogte spelomslag Icoonhoogte spellijst Toepassingslettertype Lettertype vaste breedte Positie filter paneel Positie verkenner paneel Omslagweergave Gerichte hoogte-breedteverhouding De volgende opties heb ook invloed op tegel rendering in Fullscreen Modus Uitrekmodus DVD-box Epic Games Store GOG Galaxy 2.0 IGDB Vierkant Steam-banner Steam verticale cover Twitch * Vereist herstart om toe te passen Instellingen Algemeen Boven paneel Uiterlijk Speldetails Opmaak Geavanceerd Volledig scherm Invoer Prestatie Metadata Bijwerken Zoeken Back-up Backup Bibliotheekgegevens Back-up Herstellen Importeer veranderingen in de bibliotheek automatisch Ongeldige database bestandslocatie, er moet een correct bestandspad ingesteld zijn. Accountnaam mag niet leeg zijn. Download metadata na het importeren van spellen Start Playnite geminimaliseerd Start Playnite wanneer u computer start Start gesloten in het systeemvak Het registreren van Playnite om te starten bij het opstarten van de computer is mislukt. Start in Volledig Scherm Modus Asynchroon laden van afbeeldingen Zorgt voor soepeler scrollen in de spellijst in ruil voor het langzamere laden van afbeeldingen. Toon spelnaam als omslag ontbreekt Toon spelnaam op cover weergave Verduister niet geïnstalleerde games Toon game iconen in Lijstweergave Toon Item hoeveelheid op groep beschrijving Toon alleen toegewezen velden op filter en verkenning panelen Gebruik hardware acceleratie niet Gebruik als je stotteren ervaart in de UI of soortgelijke problemen Toon verborgen games in Snel start lijst Beïnvloed spring lijsten en lade menu lijsten. Aantal snel start items Gebruik spelachtergrond als vensterachtergrond Vervaag achtergrond Hoge kwaliteit Verdonker achtergrond Toon in rasterweergave Thema Themaprofiel Volledig Scherm Thema Volledig Scherm Thema Kleur Database Locatie Loginstatus: Playnite Instellingen Leeg web cache Kan problemen oplossen bij het linken van accounts. Systeemvakicoon weergeven Minimaliseer Playnite naar systeemvak Minimaliseer Playnite naar systeemvak wanneer de applicatie is gesloten Als een spel start: Na het sluiten van het spel: Maak speeltijd op om het aantal gespeelde dagen aan te geven Datumnotatie: Dit logt je uit bij alle gelinkte services. Herstart van applicatie is nodig. Wil je verdergaan? Leeg Cache? Om het thema toe te passen moet Playnite opnieuw opgestart worden Vind meer thema's Maak nieuw thema Vind meer extensies Creëer nieuwe extensie Help ons met vertalen van Playnite Playnite moet herstart worden alvorens de nieuwe instelling worden toegepast. Wil je nu herstarten? Herstarten zal huidige taken (downloads) die nu bezig zijn stopzetten. Herstart Playnite? Playnite verplaatst bibliotheek bestanden niet automatisch, verplaats/kopieer ze voor dat de locatie is veranderd. Als er geen bibliotheek aanwezig is op de aangewezen locatie zal er een nieuwe gemaakt worden. Speeltijd zal niet worden bijgehouden als de "Sluit" actie is ingesteld. Aantal rijen Aantal kolommen Aantal detailweergave rijen Toon Achtergrond afbeelding op het Hoofd Scherm Geldt niet retrospectief voor bestaande spellen zonder metadata opnieuw te downloaden. Importeer de speeltijd van spellen in de bibliotheek: Configureert wanneer Playnite de speeltijd moet importeren die door bibliotheek plugins voor spellen in de Playnite database wordt gerapporteerd. Ondersteuning door de bibliotheek plugins die verantwoordelijk zijn voor het verwerken van de spel(s) is nodig om deze functie te kunnen gebruiken. Altijd: Importeer de speeltijd voor nieuw geïmporteerde en bestaande spellen in de Playnite database. Alleen voor nieuw geïmporteerde spellen: Importeer speeltijd alleen voor nieuwe geïmporteerde spellen. Nooit: Importeer onder geen enkele omstandigheid speeltijd. Altijd Alleen voor nieuw geïmporteerde spellen Nooit Activeer controller ondersteuning in Desktop modus Gids knop opent volledig scherm modus Automatisch Metadata download instelling voor nieuwe geïmporteerde games. Doel scherm Gebruik altijd het primaire beeldscherm Toon Speltitels Toon Batterijstatus Toon Batterijpercentage Toon Klok Verberg muis cursor Alleen Geïnstalleerd in Snelle Filters Knop Prompts Layout Horizontaal Scrollen Selecteer een van de subsecties Geen instellingen beschikbaar Mislukt om instellingen te laden Deze script worden uitgevoerd voor elke game in de bibliotheek. Individuele scripts kunnen toegewezen worden aan elke game afzonderlijk tijdens het bewerken van game details Animeer achtergrond afbeelding transities Lettertype grootte Automatisch Gealiast Grijswaarden ClearType Ideaal Scherm Tekstopmaak Tekst weergavemodus Tekstweergave- en vomgevingmethodes worden voorlopig nog niet gebruikt voor gamebeschrijvingen. Laad achtergrond afbeelding vooraf Wanneer ingeschakeld, zal Playnite achtergrondafbeeldingen samen met de metadata downloaden, wat meer schijfruimte gebruikt maar de afbeeldingen offline beschikbaar maakt. Wanneer uitgeschakeld, worden achtergrondafbeeldingen enkel wanneer nodig gedownload, wat minder schrijfruimte gebruikt. Dit kan ervoor zorgen dat er een vertraging is voor de afbeeldingen worden weergegeven en dat sommige afbeeldingen offline niet beschikbaar zijn. Sluit andere clients automatisch af na het sluiten van een game. Afsluitvertraging van andere clients (in seconden) Sluit niet af na gamesessies die minder lang duurden dan (in seconden) Sluit automatisch de volgende clients af: Sluit clients automatisch Importeer uitsluitings lijst Geef een waarschuwing weer wanneer te grote gamemedia worden toegekend. "Open map"-commando Gewenste leeftijdsclassificatie-organisatie Update installatiegrootte van spellen bij update bibliotheek Scant en werkt de installatiegrootte van spellen bij als blijkt dat de bestanden zijn gewijzigd sinds de laatste scan Geen Vullen Uniform Uniform te vullen Links Rechts Boven Onder Importeer Error Authenticatie nodig Authenticatie mislukt Alternatieve web weergave rendering modus Gebruik wanneer er problemen zijn met webviews, bijvoorbeeld die van authenticatie van integraties. Gedeeltelijk laden van grote spelomschrijvingen Grote beschrijvingen kunnen merkbare vertraging veroorzaken bij het selecteren van spellen. Wanneer ingeschakeld, wordt slechts een deel van de beschrijvingstekst geladen met een optie om de rest op verzoek te laden. Metadata Importeren Download Metadata Stel de geselecteerde configuratie in om gebruikt te worden voor alle toekomstige metadata downloads. Kan ook gewijzigd worden in de applicatie instellingen. Emulatie Import Wizard Deze wizard zal je door het download- en importeerproces van emulators en geëmuleerde games leiden. Je kunt later altijd extra emulators en/of geëmuleerde games toevoegen via het configuratiescherm voor emulators (Bibliotheek > Emulators configureren ). De volgende emulators kan Playnite automatisch herkennen en configureren. Je kan de installatieprogramma's van hun website downloaden. Wanneer je de emulators (handmatig) geïnstalleerd hebt, kan je ze importeren in het configuratiemenu. Je kan geïnstalleerde emulators importeren door op de knop 'Automatisch herkennen in map' te klikken. Playnite zal de geselecteerde map doorzoeken en de optie geven gekende emulators te importeren. Je kan dit verschillende keren doen om emulators in verschillende mappen te importeren. De emulators zullen onderaan de lijst worden toegevoegd. Je kan games importeren door op de knop 'Map scannen met emulator' te klikken. Door de juiste emulator te selecteren, weet Playnite welke bestandstypen moeten worden gescand en geïmporteerd. Je kunt deze knop meerdere keren gebruiken om games uit verschillende mappen te importeren. Games worden onderaan de huidige lijst toegevoegd. Er zijn geen emulators geselecteerd om te importeren. Je kan niet automatisch geëmuleerde spellen importeren zonder eerst emulators te configureren. Weet je zeker dat je verder wilt gaan en het importeerproces wilt afsluiten? Er zijn geen emulator geconfigureerd in Playnite. Je kan geen geëmuleerde games importeren zonder eerst een emulator te configureren en de juiste bestandstypen te selecteren. Wil je nu emulators toevoegen? Scan map met Emulator Selecteer bestanden Automatisch herkennen in map Emulators configureren Scannen… {0} scannen... Eerste Configuratie Deze wizard leidt je door het automatische import- en configuratieproces van externe spelbibliotheken. Playnite kan automatisch games importeren van meerdere gameservices, zoals Steam of GOG, en ook je bibliotheek up-to-date houden door deze automatisch bij te werken wanneer de applicatie wordt gestart. Houd er rekening mee dat je later altijd nog handmatig een aangepast spel voor elk platform kunt toevoegen door op de knop 'Playnite' in het hoofdmenu te klikken. Bibliotheek Integratie Importeer automatisch games uit de onderstaande services. Alle latere spelwijzigingen (installatiestatus) worden automatisch bijgewerkt bij het opstarten van Playnite of wanneer ze handmatig worden geactiveerd. De geselecteerde instellingen zijn van invloed op de eerste en alle volgende importen. Configuratie Klaar De eerste installatie is voltooid. Onthoud dat je alle instellingen later kunt wijzigen in het menu 'Instellingen'. Je kunt later ook een ander spel toevoegen door op het Playnite-icoon te klikken. Downloaden van één of meerdere extensions is mislukt. Je kan proberen deze opnieuw te downloaden via het add-ons menu na de eerste keer opstarten. Integratie {0} downloaden... Lijst van aanbevolen integraties aan het downloaden… Downloaden van lijst van aanbevolen integraties mislukt. U kunt later proberen om de integraties opnieuw te downloaden via het Uitbreidingen menu. Platforms en emulators configureren Emulators configureren Platforms Platform Emulators Emulator Voeg Platform Toe Selecteer Icoon Selecteer Cover Afbeelding selecteren Item selecteren Selecteer achtergrond Selecteer Bestand Selecteer URL Voeg Emulator Toe Ondersteunde Platform(s) Wil je de platform veranderingen opslaan? Wil je de emulator veranderingen opslaan? Uitvoerbaar bestand Argumenten Werkmap Ondersteunde Bestandstypes Importeer Emulators... Download Emulators... Laad argumenten presets van bekend emulator profiel Weet je zeker dat je {0} emulator wilt verwijderen? Het wordt op dit moment door {1} spel(len) gebruikt. Weet u zeker dat je {0} platform wilt verwijderen? Het wordt momenteel gebruikt door {1} game(s) en {2} emulator(s).Begin the Emulation Import Wizard Instellingen hulp Sorteren op Sorteerrichting Groeperen Op Oplopend Aflopend Niet groeperen Groepeer op Leverancier Groepeer op Categorie Groepeer op Platform Weergave Type Weergave Verkenner Paneel Filter Paneel Icoon Bibliotheek Icoon Cover Afbeelding Achtergrond Afbeelding Sorteer Naam Leverancier Handleiding Naam Installatieschijf Account Naam Platform Categorie(ën) Genre(s) Datum van uitgave Release Jaar Ontwikkelaar(s) Tag(s) Uitgever(s) Installatiestatus Alle filterwaarden vereist Indien ingeschakeld: alleen spellen die aan alle items in elk filter voldoen zullen worden weergegeven. Indien uitgescshakeld: spellen die voldoen aan tenminste één item in elk filter zullen worden weergegeven. Geïnstalleerd Geïnstalleerd Niet geïnstalleerd Verborgen Favoriet HDR ondersteuning inschakelen Indien ingeschakeld, zal HDR ingeschakeld worden op de primaire display voordat u het spel start. Merk op dat HDR niet ondersteund wordt op uw primair beeldscherm. Laatst Gespeeld Categorie Beschrijving Installatie Map Cover Afbeelding Links Image/ISO Pad Genre Genres Bedrijf Bedrijven Ontwikkelaar Ontwikkelaars Uitgever Uitgevers Categorie Categorieën Tag Tags Functie Functies Leeftijdsclassificatie Leeftijdsclassificatie Regio Regio's Bron Bronnen Recente Activiteit Databasefout Openen van de bibliotheek database is mislukt. Database is niet geopend. Kan geen toegang krijgen tot de bibliotheek database. Bestand "{0}" wordt gebruikt door een ander proces of bevind zich op een ontoegankelijke locatie. Diagnostics package maken mislukt. Automatisch uploaden van diagnostische pakket mislukt. Diagnostische informatie is succesvol verzonden. Het diagnostische pakket is gemaakt en verzonden. Voeg de volgende ID toe aan het probleemrapport: Kan geen spellen importeren van {0}. Kan geen geëmuleerde spellen importeren van {0}. Kan niet zoeken naar spellen met het geselecteerde emulator profiel. Het profiel bevat geen extensies of platforms. Playnite kan niet worden gestart. Sluit alle andere instanties van Playnite en probeer het opnieuw. Kan thema "{0}", kleurprofiel "{1}" niet toepassen {2} Kan link niet openen, URL is ongeldig. Het starten van de applicatie is mislukt. Kan de webweergave component niet initialiseren. Playnite kan niet doorgaan met het opstart-proces. Meer informatie op https://playnite.link/cefstartup Kan emulators niet importeren vanwege ontbrekend of beschadigd definitiebestand. Kan menuactie niet uitvoeren. Bewerk Spel Details Afbeelding URL Voeg Link Toe ROM toevoegen Sla Wijzigingen Op Wijzigingen in dit veld toepassen op spel(len) die bewerkt worden. Voeg Actie Toe Verwijder Actie Verwijder Speel Actie Voeg Spellen Toe Scan Map... Detecteer Geïnstalleerd Bladeren... Open Playnite Profiel Instellingen Spelnaam kan niet leeg zijn. Speeltijd volgmap kan niet leeg zijn. Spelnaam kan niet leeg zijn bij het zoeken naar metadata. Ongeldige spel data Voer een geldige web URL beginnend met http:// of https:// in Selecteer URL Downloaden van metadata mislukt: {0} Download Fout Wis Filters Privé Account Openbaar Account API Sleutel Opstart Error Thema Error Wis Alles Aan het installeren Aan het deinstalleren Aan het starten Actief Ongeldige URL Doe niets Minimaliseer Herstel Venster Herstel venster alleen wanneer gelanceerd vanuit UI Sluiten Wijzig Geavanceerd Nooit Voltooiingsstatus Voltooiingsstatussen Gebruikersscore Criticusscore Communityscore Spel scripts Applicatie scripts Scripts Plugins Metadata Bronnen Extensies Extensie ID Herlaad Scripts Interactieve SDK PowerShell Alle scripts succesvol herladen. Geen games gevonden voor opgegeven zoek of filtercriteria Geen items gevonden Omschakelen naar Desktop Modus Sluit Playnite Bibliotheken Alles bijwerken Aangemaakt door: Versie: Bijgewerkt: Module: Bibliotheek Statistieken Alles Geen Meldingen Breedte Hoogte Formaat Klein Normaal Groot Groter Grootst Standaard Selecteer Selecteer alles Selectie opheffen Eerste Willekeurig Gebruikersselectie Laad meer Doorzichtig Samenvouwen Uitvouwen Alles samenvouwen Alles uitklappen Andere Thema's Emulator Argumenten Ingebouwde argumenten Aangepaste Argumenten Extra Emulator Argumenten Overschrijf Emulator Argumenten Speel actie Selecteer metadata om te importeren Selecteer Spellen om te Importeren Metadata zoeken Update Beschikbaar Veranderingen sinds laatste update Installeer Update Controleer op updates Update Fout Controleren op nieuwe versie mislukt. Geen nieuwe versie gevonden, u bent up-to-date. Downloaden en installeren van update mislukt. Er is momenteel een achtergrondtaak actief. Wilt u deze annuleren en verdergaan met de update? Er is momenteel een achtergrondtaak actief. Wilt u deze annuleren en Playnite sluiten? Er is momenteel een achtergrondtaak actief. Van modus wisselen zal deze taak annuleren. Wil je nog steeds van modus wisselen? Er is een update voor Playnite beschikbaar. Herlaad thema lijst Geselecteerd thema toepassen Kijk naar bestandsveranderingen Pas het thema automatisch toe als de bronbestanden veranderen Script runtime Script dat moet worden uitgevoerd voordat een spel wordt gestart Script om uit te voeren na het afsluiten van een game Script om uit te voeren nadat een spel is gestart Uitvoeren bij opstarten programma Uitvoeren bij afsluiten van programma Spel starten script Spel gestart script Spel gestopt script Globaal script uitvoeren Globaal Gefilterd Huidig Nieuw Test script Alleen geselecteerde items tonen. Opslaan als standaard Voeg toe aan Favorieten Verwijder uit Favorieten Verberg deze game Verwijder uit verborgen HDR ondersteuning inschakelen HDR ondersteuning uitschakelen Bewerk… Bereken installatiegrootte Installatiegrootte berekenen (alle spellen) Installatiegrootte berekenen (alleen ontbrekende gegevens) Installatiegrootte Categorie Instellen... Stel Voltooiingstatus in Verwijderen Speel Installeer Spelinstellingen Details Deïnstalleer Open spel locatie Maak bureaublad snelkoppeling Open Handleiding Meer Beheerd door bibliotheek-plugin Het startproces van het spel wordt beheerd door de bibliotheekplug-in die verantwoordelijk is voor dit spel. Er is geen relevante informatie over het spel '{0}' gevonden op de opgegeven pagina. Tip: Je kan een geavanceerder metadata download proces gebruiken wanneer je een individueel spel bewerkt via de "Bewerken" menu optie. Niet beschikbaar als sommige acties nog bezig zijn. Beschrijvingstekst is HTML syntax sensitief Speltijd wordt opgenomen in seconden. Installatiegrootte is aangegeven in bytes. Datum van uitgave moet in 'jaar-maand-dag' datumnotatie. Maand en Dag kunnen weggelaten worden. Waarden van 0 tot 100 of leeg voor geen score. Playnite ontwikkeling wordt ondersteund door deze patrons en Ko-fi leden: Code, lokalisatie en andere and other bijdragers in willekeurige volgorde: Spelmonitoring annuleren? Er wordt momenteel installatiemonitoring uitgevoerd. Wil je het proces annuleren en het spel terugbrengen naar de vorige staat? Spel executie monitoring wordt momenteel uitgevoerd. Wil je het proces annuleren en het spel terugbrengen naar de vorige staat? Tijd Gespeeld Laatst Gespeeld {0}d {1}u {2}m {0}u {1}m {0} minuten {0} seconden Niet Gespeeld Openen Desktop modus… Openen volledig scherm modus… Laden spel bibliotheek… Installatiegrootte berekenen… Installatiegrootte van {0} berekenen… Kan script bestand niet installeren. Script succesvol geïnstalleerd. Installatiescript Scriptfout Uitvoeren van extensie functie mislukt. Open metadata map Berekenen Berekent automatisch de installatiegrootte met behulp van de Roms als die er zijn, of als het spel een installatiemap heeft {0} client is niet geïnstalleerd. {0} client zal nu openen. Log in a.u.b. en sluit dan dit bericht. Wachten op aanmelding van gebruiker, sluit dit alstublieft af wanneer je klaar bent… Installatiemap van spel niet gevonden. Ongeldige spel-actie configuratie. Probleemoplossingen account sync Probleemoplossing Item hernoemen Nieuw item toevoegen Naam invoeren Nieuwe naam invoeren Minder dan een uur 1 tot 10 uur 10 tot 100 uur 100 tot 500 uur 500 tot 1000 uur 1000+ Playnite moet opnieuw worden opgestart om de installatie te voltooien. Wil je nu opnieuw opstarten? Extensie is niet correct verpakt. Thema is niet correct verpakt. Extensie "{0}" kon niet correct geladen worden. Kan "{0}" extensie niet laden; huidige versie van Playnite wordt niet ondersteund. Thema "{0}" kon niet correct geladen worden. Kan "{0}" thema niet laden; huidige versie van Playnite wordt niet ondersteund. Extensie kon niet correct geladen worden. Thema kon niet correct geladen worden. Thema/Extensie gebruikt niet-ondersteunde API versie. Installatie is gelukt. Extensie installeren? Algemeen Installeren van extensie "{0}" mislukt. Extensie installeren mislukt. {0} Wilt u een nieuwe extensie installeren? {0} Door {1} Versie {2} Wilt u de extensie "{0}" updaten? Huidige versie: {1} Nieuwe versie: {2} Thema installeren mislukt. {0} Wilt u een nieuw thema installeren? {0} Door {1} Versie {2} Wilt u thema "{0}" updaten? Huidige versie: {1} Nieuwe versie: {2} Je staat op het punt Playnite te verlaten en naar de volgende webpagina te gaan met je standaard webbrowser. Wil je doorgaan? {0} De geselecteerde afbeelding(en) zijn mogelijk te groot voor optimale prestaties. Het gebruik van zeer grote afbeeldingen kan leiden tot een slechtere UI-respons en meer geheugengebruik. Maximaal aanbevolen resoluties: Pictogrammen: {0} megapixels Omslagen: {1} megapixels Achtergronden: {2} megapixels Performantie Waarschuwing Niet Opnieuw Weergeven Bestand met extensie {0} is niet compatibel. Ongeschikte bestandsextensie Het geselecteerde afbeeldingsbestand is mogelijk te groot voor optimale prestaties. Weet je zeker dat je het geselecteerde thema wilt verwijderen? Verwijdering wordt in de wachtrij geplaatst tot de volgende start van de toepassing. Ingebouwde thema's kunnen niet worden verwijderd. Dit thema ondersteunt deze versie van Playnite niet. Weet u zeker dat u de geselecteerde extensie wilt verwijderen? Verwijdering wordt in de wachtrij geplaatst tot de volgende start van de toepassing. Ingebouwde extensies kunnen niet worden verwijderd. Deze extensie ondersteunt deze versie van Playnite niet. Installatie map Gegevensmap Diagnostiek pakket genereren... Opladen diagnostiek pakket... Bestand importeren Wat is dit? Weet je zeker dat je dit wilt doen? Totale speeltijd Gemiddelde speeltijd Hoogste Speeltijd Totale installatiegrootte Overzicht Zijbalk Toon in zijbalk Instellingen terugzetten Alle applicatie-instellingen zullen terug worden gezet naar hun standaardwaarde, met uitzondering van: - Database locatie - Import uitsluitingslijst - Extensie instellingen, inclusief bibliotheek-integraties De applicatie moet opnieuw opgestart worden om het proces af te ronden. Wil je de instellingen terugzetten naar de standaardwaardes? Voor ontwikkelaars Externe extensies Vul volledig pad naar map in. Prestaties Forum Nieuws Winkel pagina De initiële installatie is niet voltooid. Playnite zal nu herstarten naar Desktop Mode om de procedure te voltooien. Onlangs Gespeeld Favorieten Meest gespeeld Alle Er zijn filters toegepast. Er zijn aanvullende filters toegepast. Zoekresultaten voor: Een item met dezelfde naam bestaat al. Beperk de selectie tot de huidige filter Kies iets anders Uitbreidingen… Geïnstalleerd Extensie instellingen Zoeken Updates Updates {0} Beheer van geïnstalleerde extensies en thema's, inclusief hun instellingen, is verhuisd naar een nieuw "Add-ons" menu. Alle nu geïnstalleerde bibliotheek-integratie extensies kunnen hier geconfigureerd worden. Als je extra integraties wil installeren of verwijderen, gebruik dan de "Add-ons" optie vanuit het hoofdmenu. Thema's Desktop Thema's Volledig Scherm Zoeken… Add-on is niet te gebruiken met deze versie van Playnite. Downloaden van add-on installatie pakket mislukt. Downloaden van het installatiemanifest van de add-on is mislukt. De toepassing moet opnieuw starten om de nieuwe instellingen toe te passen. Installatie van deze add-on is ingepland. Installeer Opnieuw installeren Verwijder Reeds geïnstalleerd Geen nieuwe extensie updates gevonden. Werk add-ons bij Changelog is niet beschikbaar Ingepland voor installatie Download mislukt Licentie afgewezen Downloaden {0} Aan het zoeken naar add-on updates... Op zoek naar programma-updates… Eén of meerdere add-on updates zijn beschikbaar Selecteer items om te updaten Extensie ontwikkelings-instantie {0} licentieovereenkomst Accepteer Weigeren Speel acties van de bibliotheek overnemen Actie selecteren Speeltijd volgmodus Trackingpad Initiële trackingvertraging Trackingfrequentie Link Bestand Emulator Script Standaard Proces Map Oorspronkelijk proces Procesnaam Log trace berichten De volgende wijzigingen overschrijven data voor alle geselecteerde spellen! Geen Uniform Alleen items Alleen start en eind Scrollgevoeligheid Vloeiend scrollen Animatiesnelheid Item verwijderen? Weet je zeker dat je dit item wil verwijderen? Toon knoppen op het top-paneel: Algemene weergaveinstellingen Groeperingsinstellingen Sorteerinstellingen Filter presets Plugin items positie Sectie scheidingslijn breedte Verhuis hoofdmenuknop naar de zijbalk Verkenningspaneel Kies een willekeurig spel Willekeurige spelkiezer bekijken Kies een willekeurig spel uit de zichtbare lijst Bewaar sorteer- en groeperingsinstellingen Toon als snelfilter in Fullscreen modus In de afgelopen 7 dagen In de afgelopen 31 dagen In de afgelopen 365 dagen Meer dan 365 dagen geleden Configureer Voorkeursinstellingen opslaan Minimaliseer na spel opstarten Minimaliseer Playnite nadat een spel opgestart wordt Dit uitschakelen kan leiden tot problemen waarbij spellen geen invoer-focus krijgen bij het opstarten! Lettertype grootte Klein lettertype grootte Ondersteuning voor spelcontroller Als dit uitgeschakeld is accepteert Playnite geen spelcontrollerinvoer. Schakel dit uit als je tools gebruikt die spelcontrollerinvoer vertalen naar muis- of toetsenbordinvoer en je dubbele invoer krijgt in Playnite. Toon items in het hoofdmenu: Omgekeerde X/A hoofdweergave knop binding Verwisselt de knoppen voor het starten van een spel en het tonen van de speldetailpagina in de hoofdweergave. Verwissel bevestigings- en annulerings-knop Knoppen A/B omkeren voor bevestiging en annulering. Alleen primaire controller Accepteer alleen inputs van primaire controller wanneer ingeschakeld. Guide knop focust Playnite Interface volume Achtergrondvolume Dempen in de achtergrond Audio-interface initialiseren mislukt Uitvoer API API voor het uitvoeren van audio. Wijzigen als je problemen ondervindt met geluid. Algemeen Visueel Audio Layout Menu's Invoer {0} start... {0} draait... Hoofdletters Spatiebalk Afbeelding rendering scaler Alternatief Gebalanceerd Kwaliteit Kwaliteit: Beste beeldkwaliteit, traag, hoog geheugengebruik. Gebalanceerd: Goede kwaliteit, snel, laag geheugengebruik. Alternatief: Betere kwaliteit, gemiddelde snelheid, laag geheugengebruik. Selecteer bestand... Selecteer map... Opstartscript Hou er rekening mee dat zowel extensies als thema's in grote mate Playnite's prestaties, stabiliteit en veiligheid kunnen beïnvloeden. Als je problemen ervaart na het installeren van een thema of extensie, probeer die dan eerst uit te schakelen of te de-installeren om te kijken of dat de oorzaak is. Kiezen bij opstarten Kiezen bij opstarten Ingebouwde profielen Ingebouwd profiel Aangepaste profielen Aangepast profiel Afgehandeld door een ingebouwd script Emulatorspecificatie Platformspecificatie Regiospecificatie Uitvoeren vóór het starten van de emulator Uitvoeren nadat de emulator gestart is Uitvoeren na het afsluiten van de emulator Uitvoerbaar bestand van de emulator niet gevonden. Emulatorspecificatie niet gevonden. Emulator opstartscript niet gevonden. Splitsen als aparte spellen Samenvoegen tot één spel Platform instellen Regio instellen Map scannen Scan instellingen Patronen uitsluiten van checksum-scan Bestanden die overeenkomen met de patronen zullen niet gescand worden op hun checksum en zullen gematcht worden op hun bestandsnaam. Zie emulator-hulppagina voor meer informatie. Scan met emulator Naam is verplicht bij het opslaan van een nieuwe configuratie. Emulator of emulatorprofiel is niet ingesteld. Map om te scannen is niet opgegeven of bestaat niet. Scan-configuratie is niet goed ingesteld. Betrek bij bulk scan auto-scan Map scannen op emulators mislukt. Map(pen) scannen op geëmuleerde spellen mislukt. Geïmporteerde verbergen Profielen om te importeren: Auto-scan instellingen Opslaan als auto-scan configuratie Slaat de configuratie op voor later gebruik bij een bibliotheekupdate. Opgeslagen configuraties kunnen beheerd worden via het "Configureer Emulators" menu. Importeren via relatieve paden Indien mogelijk spelbestanden importeren met behulp van relatieve paden ten opzichte van Playnite's installatiemap of de emulator's installatiemap. Submappen scannen Scan in archieven Samenvoegen van gerelateerde bestanden Voeg gerelateerde spelbestanden samen, net als individuele spelschijven, onder één spel-invoer. Scanner toevoegen Opgeslagen scanner toevoegen Scan starten Voeg scanconfiguratie(s) met emulators toe om specifieke mappen te scannen. Zorg ervoor dat emulators goed geconfigureerd zijn voordat je spellen importeert (via het Bibliotheek -> Configureer Emulators menu). Standaardstatus die toegewezen wordt aan nieuwe spellen Status die toegewezen wordt aan spellen die voor het eerst gespeeld zijn Kon de Powershell script runtime niet initializeren. Als u Windows 7 gebruikt, probeer dan PowerShell 5.1 (opnieuw) te installeren om dit op te lossen. Filter preset met de opgegeven naam bestaat al. Preset updaten met nieuwe instellingen? Automatisch ontbrekende sorteernamen invullen voor spellen die in batch toegevoegd of bewerkt zijn Als je een spel bewerkt, voeg dan spellen toe via een bibliotheek update, een scan van een emulator map of een normale map, vul automatisch het "Sorteernaam" veld in met een betere weergave van de naam van het spel. Bijvoorbeeld "The Witcher 3" krijgt een Sorteernaam van "Witcher 03". Dit zal nooit een sorteernaam instellen die niet verschilt van de spelnaam, en het zal alleen automatisch sorteernamen bijwerken die leeg zijn. Deze woorden zullen verwijderd worden van het begin van de automatisch ingevulde sorteernaam: Gebruik dit voor het negeren van woorden aan het begin van een naam voor sorteerdoeleinden. De standaardwaarde is "The", "An" en "A". Vul Sorteernaam voor spellen zonder Sortering Vullen Sorteernaam waardes… Nahimic service is gedetecteerd op dit systeem. Van deze service is bekend dat hij rendering problemen bij Playnite (en andere apps) kan veroorzaken. Als je grafische corruptie of andere rendering problemen in Playnite ervaart, bevelen we aan deze service uit te zetten of Nahimic service compleet te de-installeren. Meer informatie is te vinden op https://playnite.link/nahimicsucks Playnite draait nu met administrator rechten. Dit is niet aanbevolen sinds dit ook verhoogde rechten geeft aan alle geïnstalleerde extensies en alle spellen/applicaties die vanuit Playnite gestart worden! Meer informatie op https://playnite.link/adminfaq Toon waarschuwing als Playnite met administrator rechten draait Krijg de werkelijke grootte op de schijf bij het berekenen van de grootte van spellen Indien ingeschakeld, zijn scans langzamer en krijgen ze de werkelijke grootte die bestanden innemen op de schijf. Indien uitgeschakeld, zijn scans sneller en zal de grootte van de bestanden zelf worden gebruikt. De volgende extensie(s) zijn gerapporteerd als mogelijk problematisch, door grote stabiliteits- of prestatie-impact of beveiligingsproblemen. We raden sterk aan deze te verwijderen: {0} Online bestanden uitsluiten van scan Bestanden in cloud opslag worden niet gescand en geïmporteerd als deze niet lokaal beschikbaar zijn. Alleen ondersteund voor: Google Drive, DropBox, OneDrive Scan met versimpelde methode zonder bestandsinhoud te bekijken Bestanden worden geïmporteerd met een minder accurate methode die geen bestandsinhoud op de lokale schijf vereist. Op alle toepassen Installatiestatus overschrijven Wanneer ingesteld, negeert Playnite de installatiestatus (inclusief installatie directory) die is ingesteld door de integratieplugin die dit spel importeert. Deze optie werkt mogelijk niet volledig met plugins die een specifieke game import methode gebruiken, tenzij ze ook rekening houden met deze overschrijving. Alleen handmatig Eens per dag Eens per week Bij elke start Controleren op programma-updates Controleren op add-on updates Bibliotheken bijwerken Scan emulatiemappen Inclusief verborgen spellen Velden bewerken Selecteer / Deselecteer alle Open Activeren Toewijzen Begin met typen om een spel te zoeken... [F1] voor hulp Starten met # toont een lijst met beschikbare commando's. Starten met / toont een lijst met beschikbare zoekaanbieders/plugins. Typt zoekwoord en eindigen met SPATIE schakelt direct naar die zoekopdracht. TAB: switch actie ENTER: activeer geselecteerde actie SHIFT-ENTER: open item menu Inclusief gedeïnstalleerde spellen Inclusief verborgen spellen Niet geïnstalleerde spellen inbegrepen Niet-geïnstalleerde spellen uitgesloten Inclusief verborgen spellen Exclusief verborgen spellen Spelen of installeren Ga naar details Spelmenu Bewerk spel Open zoeken Zoekbalk Zoekknop Primaire spelactie Primaire spelactie CTRL-F opent globale zoekopdracht in plaats van de focus op het zoekvak Spelfilter instellingen tussen zoeksessies opslaan Zoekmachines Standaard trefwoorden Aangepast trefwoord Systeemwijde snelkoppeling Zoeken in Playnite Extensie instelling Uitsluitingen Uitgesloten bestanden ten opzichte van de scan map Uitgesloten mappen ten opzichte van de scan map Bestand toevoegen aan uitsluitingslijst Map toevoegen aan uitsluitingslijst Uitsluitingen kunnen alleen worden toegevoegd aan de configuraties van de opgeslagen scanner. Uitsluitingen zijn toegevoegd aan de "{0}" scanner. Platform overschrijven Wanneer ingesteld, zal scanner dit platform toewijzen aan alle spellen, en de automatisch gedetecteerde platformen overschrijven. Opdrachten opnemen in standaard zoeken Indien uitgeschakeld, zullen commando's niet worden opgenomen in de standaard zoekopdracht totdat # prefix is gebruikt. Fuzzy matching in naam filter gebruiken Wanneer ingeschakeld, zal de naam filter gelijk zijn aan de spelnamen zoals globale zoekopdrachten. Strikte matching kan op individuele gevallen worden afgedwongen door een filter met een ! Velden die worden weergegeven voor spellen in zoekresultaten: Verborgen Status Gegevens back-up geannuleerd Gegevens back-up mislukt. Fout in data back-up Gegevens back-up bezig... Data herstellen van back-up... Gegevens herstellen van back-up mislukt. Instellingen Bibliotheek Spelbibliotheekmedia Geïnstalleerde Extensies Extensiedata Geïnstalleerde Thema's Selecteer de data te herstellen vanuit het opgegeven back-upbestand. Playnite zal automatisch herstarten om de backup te herstellen. Selecteer items om op te nemen in data backup. Applicatieinstellingen en gamebibliotheek zijn standaard inbegrepen. Playnite zal automatisch herstarten om het back-upproces te starten. Automatische data-back-up Automatische back-up frequentie Back-up map Roterende back-ups Aanvullende gegevens toevoegen: Reservekopiemap moet worden ingesteld als automatische back-up is ingeschakeld. Toon alleen meldingen voor patch releases Wanneer ingeschakeld, zullen alleen updates die beschikbaar zijn voor momenteel geïnstalleerde hoofdrelease resulteren in een update notificatie. Nieuwe grote releases resulteren niet in een update notificatie. Gebruik relatieve datums voor de afgelopen week Gebruik relatieve datums in "Vandaag", "gisteren" etc. formaat als de datum minder dan een week oud is. De opgegeven datumnotatie zal worden gebruikt voor alle andere datums. Webafbeelding zoeken Pictogramafbeelding zoekstring Zoekstring omslagfoto Zoekstring voor achtergrondafbeelding Add-on informatie verkrijgen... Geen metadatabron beschikbaar Speel Actie instellingen Instellingen voor scanner gebruiken Selecteer profiel bij opstarten Selecteer emulator bij het opstarten Automatisch Altijd aan Altijd uit Toegankelijkheid (schermlezer) ondersteuning Applicatie menu Spel menu Programma map Gebruikers data locatie Corruptie bibliotheekbestand is gedetecteerd, Playnite wordt nu afgesloten. Open het nieuwe probleem op Playnite's GitHub-pagina met een verzoek om corruptie in je bestanden op te lossen. Wil je de wijzigingen opslaan? Draagbare installatie Geen controllers gedetecteerd ================================================ FILE: source/Playnite/Localization/no_NO.xaml ================================================  Norsk Bytt språk Avslutt Filter i bruk Filter deaktivert Flere filtre Filtre Filter Ugyldig data Lagre endringer? Hjemmeside på www.playnite.link Kildekode på GitHub Opprett diagnostikkpakke Send diagnoseinformasjon Om Playnite Laget av Josef Němec Tildel kategori Velg kategorier Legg til kategori På - Tildel kategori Av - Fjern kategori Ubestemt - Ingen endringer (ved redigering av flere spill) Ingen kategori Ingen plattform Huff da! Noe gikk galt ... En uopprettelig feil har oppstått. Hvis du vil hjelpe oss med å løse dette problemet, kan du kort beskrive handlingene som ble utført før krasjet, og deretter sende diagnoseinformasjon. Hvis du er tilkoblet internett, blir pakken lastet opp til Playnite-serveren for analyse. Alternativt kan du trykke på "Rapporter krasj"-knappen for å opprette en ny issue på GitHub og rapportere krasjet manuelt. Takk for hjelpen. Utvidelsen "{0}" forårsaket en uopprettelig feil. Vi anbefaler at du lagrer loggfilen og rapporterer problemet til utvikleren av utvidelsen. Hvis problemet fortsetter bør du deaktivere utvidelsen. Utvidelsen"{0}" forårsaket en uopprettelig feil. Vi anbefaler at du rapporterer problemet til utvikleren av utvidelsen. Hvis problemet fortsetter, deaktivere utvidelsen. En ukjent utvidelse eller et ukjent tema forårsaket en uopprettelig feil. Vi anbefaler at du deaktiverer tredjepartstillegg etter tur for å isolere den som skaper problemer, og rapporterer feilen til utvikleren av tillegget. Uopprettelig feil oppstod. Hvis du vil hjelpe oss med å løse dette problemet, gjerne send oss diagnoseinformasjon. Takk. Deaktiver utvidelse Lagre loggfil Send diagnoseinformasjon Rapporter krasj Restart Playnite Restart i trygg modus Deaktivering av alle tredjepartsutvidelser og bruker av standardtema. Lukk Playnite Utførte handlinger før krasjet (på Engelsk): Biblioteksbehandler Fjern spill? Kan ikke fjerne — Spillet eller installatøren kjører. Kan ikke avinstallere - Spill kjører. Er du sikker på at du vil fjerne {0}? Er du sikker på at du vil fjerne {0} spill? Er du sikker på at du vil fjerne {0}? Ved å velge "legg til ekskluderingsliste", forhindrer du spillet i å bli importert igjen neste gang biblioteket oppdateres. Er du sikker på at du vil fjerne {0} spill? Velger du alternativet "legg til ekskluderingsliste", blir ikke spillene importert på nytt neste gang biblioteket oppdateres. Er du sikker på at du vil fjerne {0} oppføringer som for øyeblikket ikke er i bruk? Ingen ubrukte felt ble funnet. Ja (legg til ekskluderingslisten) Det er ulagrede endringer i denne seksjonen Oppdaterer formatet til spillbiblioteket… Databaseoppdatering feilet. Kan ikke oppdatere spillbibliotek. {0} MB av tilgjengelig plass er nødvendig. Spillfeil Kan ikke starte spill. '{0}' ble ikke funnet i databasen. Kan ikke starte spill: {0} Kan ikke starte handling: {0} Kan ikke åpne spillokasjon: {0} Kunne ikke finne spillinstallasjonsstørrelse: {0} Feil ved skanning av installasjonsstørrelse Det oppstod {0} feil under installasjonsstørrelse skanning Opprettelse av snarvei feilet: {0} Kunne ikke åpne manualen: {0} Kan ikke installere spill: {0} Kan ikke avinstallere spill: {0} Ingen gyldige spilloppstartshandlinger ble funnet. Når du bruker emulatorhandlinger, må du sørge for at plattformdefinisjonene samsvarer mellom spillet og emulatorkonfigurasjonen. Installasjonsimplementering er ikke tilgjengelig. Bibliotekutvidelsen som er ansvarlig for dette spillet er deaktivert eller ikke installert. Offisiell metadatanedlastning er ikke tilgjengelig. Inget spill er valgt. Utføringen av spillets skripthandling mislyktes. Kjøring av applikasjonsskript mislyktes. Utføringen av global skripthandling mislyktes. Kjøring av emulatorskript mislyktes. Utføringen av spillets skripthandling mislyktes. PowerShell 3.0 eller nyere er ikke installert. Kunne ikke avgjøre hvordan spillet skal startes. Aktivert Deaktivert Fjern Fjern ubrukte oppføringer Endre navn Kopier Legg til Standard ikon Standard coverbilde Standard bakgrunnsbilde Fullfør Neste Tilbake FERDIG TILBAKE FJERN Tøm Avvis Avvis alle Importer Navn Forfatter Modul Serier Versjon Sist spilt Mest spilte Antall spilløkter Installasjonsstørrelse Mappe Notater Lagt til Dato lagt til Redigert Endringsdato Nettside Bane OK Lagre Lukk Avbryt Bekreft Nullstill Ja Nei Velkommen Lokal bruker Generelt Media Lenker Installasjon Handlinger Laster ned … Laster ned media … Laster … Type Profil Profiler Fjern Last ned Søk Oppløsning: Alle Forstørr Listevisning Omslag Rutenettvisning Detaljert visning Egendefinert URL Spesiell takk til Lisens Bidragsytere Lukker Playnite … I dag I går Mandag Tirsdag Onsdag Torsdag Fredag Lørdag Søndag Siste uke Siste måned Siste år Mer enn ett år siden 0 til 100 MB 100 MB til 1 GB 1 GB til 5 GB 5 GB til 10 GB 10 GB til 20 GB 20 GB til 40 GB 40 GB til 100 GB 100 GB eller mer Importeringen var vellykket. Alle spill Spill-id Database-id Forhåndsinnstillinger Kolonne Kolonner Rad Rader Kunne ikke hente ikon fra spill-handling. Ingen handling er tilgjengelig for filtypen. Last bare ned manglende metadata Aktivering av dette alternativet vil hoppe over nedlasting av metadata for datafelt som allerede har informasjon. Spillvalg Velg hvilke spill som skal oppdateres med nye metadata: Alle spill Alle filtrerte spill Kun valgte spill Ingen metadatafelt valgt Ingen metadatafelt er valgt for nedlasting. Velg minst ett, og aktiver minst en metadata-leverandør for feltet. Offisiell butikk IGDB Velg hvilke felt som skal fylles automatisk av Playnite, og hvilke kilder data skal hentes fra. Klikk gjerne på logoen over og bidra med oppdateringer til igdb.com-databasen, slik at dataene Playnite bruker forbedres. Laster ned metadata … Importerer installerte spill … Importerer {0} spill … Importerer emulerte spill fra {0}… Laster ned biblioteksoppdateringer … Analyserer størrelse på spill i biblioteket… Finner størrelsen til importerte spill … Biblioteksoppdatering vellykket Frigir ressurser … Konfigurasjon Innstillinger … Plattformer og emulatorer Konfigurer emulatorer … Biblioteksbehandler … Verktøy Last ned metadata … Programvareverktøy … Konfigurer integrasjoner … Åpne tredjepartsklient Tredjepartsklienter Oppdater spillbibliotek Avbryt biblioteksoppdatering Oppdater emuleringsmapper Legg til spill Manuelt … Gjennomsøk automatisk … Emulert spill … Microsoft Store-applikasjon… Om Playnite Gi tilbakemelding Bytt til fullskjermmodus Lenker Hjelp Støtt på Patreon Gi din støtte via Ko-fi Brukermanualer SDK-dokumentasjon Systemomstart Slå av systemet Suspender systemet Dvalemodus Lås systemet Logg ut bruker Velg et tilfeldig spill Spillfelt som vises i detaljpanelet: Gjenstandsmellomrom Tegn rutenettbakgrunn Grensebredde til rutenettgjenstander Kilde for manglende spill-ikon Kilde for manglende spillomslag Manglende bakgrunnskilde for spillet Vertikal avstand til spilldetaljer Detaljkolonnens posisjon i rutenettvisning Spillistens posisjon i detaljvisning Tegn separator mellom panelene Bildehøyde for spillomslag Spilllistens ikonhøyde Applikasjonsfont Font med fast bredde Filtrer panelets plassering Utforskerpanelets posisjon Gjengivelse av omslagskunst Målformat Følgende alternativer påvirker også gjengivelse i fullskjermmodus! Strekkmodus DVD-boks Epic Games Store GOG Galaxy 2.0 IGDB Kvadrat Steam-banner Vertikalt omslag fra Steam Twitch *Krever omstart Innstillinger Generelt Toppanel Utseende Spilldetaljer Utforming Avansert Fullskjerm Inndata Ytelse Metadata Oppdaterer Søk Sikkerhetskopier Sikkerhetskopier bibliotekdata Gjenopprett sikkerhetskopi Importer endringer i biblioteket automatisk Ugyldig plass for databasefil, du må oppgi en gyldig filbane. Kontonavnet kan ikke være tomt. Last ned metadata etter importering av spill Start Playnite minimert Kjør Playnite når du starter datamaskinen Forbli lukket i systemstatusfeltet ved oppstart Kunne ikke sette Playnite til å starte når datamaskinen starter. Start i fullskjermmodus Asynkron innlasting av bilder Gir jevnere rulling i spillister i bytte mot tregere innlasting av bilder. Vis spillnavn hvis omslagskunst mangler Vis spillnavn i rutenettvisning Mørklegg avinstallerte spill Vis spillikoner på detaljvisningslisten Vis antall gjenstander i gruppebeskrivelser Vis bare tildelte felt i filter- og utforskerpaneler Deaktiver maskinvareakselerasjon Brukes når du opplever hakking eller lignende problemer med brukergrensesnittet Vis skjulte spill i hurtiglister Påvirker "jumplisten" og skuffmenyer Antall hurtigstart-elementer Bruk bakgrunnsbilde fra spillet som vindusbakgrunn Uklar bakgrunn Høy kvalitet Mørk bakgrunn Vis i rutenettvisning Tema Temaprofil Fullskjermstema Fullskjermtemaprofil Databaseplassering Innloggingsstatus: Playnite-innstillinger Tøm nettbuffer Kan løse problemer som oppstår når du kobler kontoer. Vis ikon i systemstatusfeltet Minimer Playnite til systemstatusfeltet Minimer Playnite til systemstatusfeltet når programvinduet lukkes Når spillet starter: Etter at spillet lukkes: Formater spilletid for å angi antall dager spilt Datoformater: Dette vil logge deg ut av alle tilknyttede tjenester. Restart av applikasjon er nødvendig. Vil du fortsette? Tøm hurtigminne? Det kreves omstart av Playnite for å bruke nytt tema Hent flere temaer Lag nytt tema Hent flere utvidelser Lag ny utvidelse Hjelp oss med å oversette Playnite Playnite må startes på nytt for å kunne bruke nye innstillinger. Start på nytt nå? Omstart vil avbryte aktive oppgaver (nedlastinger) som pågår. Start Playnite på nytt? Playnite flytter ikke bibliotekfiler automatisk, du må flytte / kopiere dem før stedet endres. Hvis det ikke er noe bibliotek på målstedet, opprettes et nytt. Ny databaseplassering vil bare bli brukt etter at Playnite startes på nytt. Spilletid blir ikke registrert hvis "Lukk" -handling er angitt. Antall rader Antall kolonner Antall detaljvisningsrader Vis bakgrunnsbilde på hovedskjermen Gjelder ikke retrospektivt på eksisterende spill uten å laste ned metadata på nytt. Importer spilletid for spill i biblioteket: Angir når Playnite skal importere spilletiden som rapporteres av biblioteksutvidelser for spill i Playnite-databasen. Støtte i biblioteksutvidelsen som er ansvarlig for å håndtere spillet er nødvendig for å kunne bruke denne funksjonen. Alltid: Importerer spilletid for nylig importerte og eksisterende spill i Playnite-databasen. Bare for nylig importerte spill: Importer kun spilletid for nylig importerte spill. Aldri: Importer aldri spilletid under noen omstendigheter. Alltid Bare for nylig importerte spill Aldri Aktiver kontrollerstøtte i skrivebordsmodus Guide-knappen åpner fullskjermmodus Automatiske nedlastingsinnstillinger for metadata til nylig importerte spill. Skjermvalg Bruk alltid primærskjerm Vis spilltitler Vis batteristatus Vis batteriprosent Vis klokke Skjul musepekeren Kun installert i hurtigfilter Kontrollertype Visning Vannrett rullefelt Velg en av underseksjonene Ingen innstillinger tilgjengelig Kan ikke laste inn innstillinger Disse skriptene blir utført for hvert spill på biblioteket. Individuelle skript kan tilordnes hvert spill separat når du redigerer spilldetaljer. Animer bakgrunnsoverganger Skriftstørrelser Auto Med anti-aliasing Gråtone ClearType Ideell Skjerm Tekstformateringsmodus Tekstopptegningsmodus Metoder for tekstgjengivelse og -formatering gjelder for øyeblikket ikke for spillenes beskrivelsestekst. Last inn bakgrunnsbilder på forhånd Om denne aktiveres, vil Playnite laste ned bakgrunnbilder når metadata lastes ned, noe som vil kreve mer lagringsplass, men sikre at bakgrunnsbilder er tilgjengelig uten internettilkobling. Om den deaktiveres, blir bakgrunnsbilder først lastet ned når de trengs, noe som sparer plass, men som samtidig kan resultere i en forsinkelse før bakgrunnsbilder vises og at noen bilder ikke vil være tilgjengelig uten internettilkobling. Lukk tredjepartsklient automatisk etter at spillet avsluttes Avslutningsforsinkelse for klient (i sekunder) Ikke lukk etter spilleøkter kortere enn (i sekunder) Lukk følgende klienter automatisk: Lukk klienter automatisk Importer ekskluderingsliste Vis advarsel når for store spillmedier tildeles Åpne katalog-kommando Foretrukket aldersgrenseorganisasjon Oppdater størrelsen til spill ved biblioteksoppdatering Analyser og oppdater installasjonsstørrelsen for spill hvis det oppdages at filer er endret siden forrige skanning Ingen Fyll Bevar forhold Fyll men bevar forhold Venstre Høyre Topp Bunn Importeringsfeil Godkjennelse er påkrevd Autentisering mislyktes Alternativ rendering-modus for web view Bruk ved problemer med web view, som for eksempel i autentiseringsvinduer for integrasjoner. Delvis lasting av store spillbeskrivelser Store beskrivelser kan gi merkbare forsinkelser ved valg av spill. Når dette valget er aktivert, vil bare en del av beskrivelsen begynne å lastes inn med mulighet for å laste resten på etterspørsel. Importer metadata Last ned metadata Angi at valgt konfigurasjon skal brukes til fremtidige nedlastinger av metadata. Kan også endres i applikasjonsinnstillingene. Veiviser for emulering Denne veiviseren vil lede deg gjennom prosessen med å laste ned og importere konsollemulatorer og importere emulerte spill. Husk at du alltid kan legge til flere emulatorer og/eller spill senere via hovedmenyen (under "Bibliotek"-menyen for emulatorinnstillinger og "Legg til spill"-menyen for emulerte spill). Nedenfor ser du en liste over emulatorene Playnite kan gjenkjenne og konfigurere automatisk. Du kan laste ned emulatorinstallasjonsprogrammer fra deres respektive nettsider. Når de er installert (manuelt), kan du importere dem ved hjelp av emulatorkonfigurasjonsvinduet. Du kan importere alle emulatorer som er installert på din PC ved å trykke på "Automatisk gjenkjenning fra mappe …" -knappen. Playnite søker i valgt mappe etter alle kjente emulatorer og gir mulighet til å importere dem. Du kan importere fra flere mapper ved å bruke nevnte knapp flere ganger. Emulatorer vil bli lagt til nederst i gjeldende liste. Du kan importere spill ved å trykke på "Skann mappe ved hjelp av Emulator" -knappen. Valg av rett emulator vil fortelle Playnite hvilke filtyper som skal skannes og importeres. Du kan importere fra flere mapper ved å bruke nevnte knapp flere ganger. Spill vil bli lagt til nederst i gjeldende liste. Ingen emulatorer er valgt for import. Du vil ikke kunne importere emulerte spill automatisk uten å konfigurere emulatorer først. Er du sikker på at du vil fortsette, og avslutte importprosessen? Det er ingen emulatorer konfigurert i Playnite. Du kan ikke importere spill uten å konfigurere emulator først og velge passende filtyper. Vil du legge til noen emulatorer nå? Skann mappe ved hjelp av Emulator Velg filer Automatisk gjenkjenning fra mappe … Konfigurer emulatorer … Gjennomsøker … Søker {0}… Førstegangs konfigurering Denne veiviseren vil hjelpe deg gjennom den automatiske prosessen for importering og konfigurasjon av eksterne spillbiblioteker. Playnite kan automatisk importere spill fra ulike spilltjenester som Steam eller GOG, og holde biblioteket ditt oppdatert ved å oppdatere det automatisk ved oppstart av applikasjonen. Husk at du alltid kan legge til et hvilket som helst spill fra en hvilken som helst plattform manuelt fra hovedmenyen, ved å klikke på 'Playnite' -knappen. Biblioteksintegrering Importer spill automatisk fra tjenestene nedenfor. Eventuelle senere spillendringer (installasjonsstatus) blir automatisk oppdatert ved oppstart av Playnite eller når oppdatering utløses manuelt. Valgte innstillinger påvirker innledende og all påfølgende import. Konfigurasjonen er fullført Det første oppsettet er fullført. Husk at du kan endre alle innstillinger senere i 'Innstillinger' -menyen. Du kan også legge til et hvilket som helst annet spill senere ved å klikke på Playnite-logo-menyen. Kunne ikke laste ned en eller flere utvidelser. Du kan prøve å laste ned integrasjoner på nytt fra utvidelsesmenyen etter at førstekjøringsveiviseren er ferdig. Laster ned integrasjonen {0} … Laster ned liste over anbefalte integrasjoner … Kunne ikke laste ned liste over anbefalte integrasjoner. Du kan prøve å laste ned integrasjoner senere fra tilleggsmenyen. Konfigurer plattformer og emulatorer Konfigurer emulatorer Plattformer Plattform Emulatorer Emulator Legg til plattform Velg ikon Velg omslag Velg bilde Velg gjenstand Velg bakgrunn Velg fil Velg URL Legg til emulator Støttede plattform(er) Vil du lagre plattformendringer? Vil du lagre emulatorendringer? Kjørbar fil Argumenter Arbeidsmappe Filtyper som støttes Importer emulatorer … Last ned emulatorer … Last inn argumenter forhåndsinnstilt fra kjent emulatorprofil Er du sikker på at du vil fjerne emulatoren {0}? Den brukes for øyeblikket av {1} spill. Er du sikker på at du vil fjerne plattformen {0}? Den brukes for øyeblikket av {1} spill og {2} emulator(er). Hjelp til innstillinger Sorter etter Sorteringsretning Grupper etter Stigende Synkende Ikke grupper Sorter etter Bibliotek Grupper etter kategori Grupper etter plattform Visningstype Vis Utforskerkolonne Filterkolonne Ikon Biblioteksikon Omslagsbilde Bakgrunnsbilde Sorteringsnavn Bibliotek Manual Navn Lagringsstasjon Kontonavn Plattform Kategori Sjanger Utgivelsesdato Lanseringsår Utvikler Stikkord Utgiver Installasjonsstatus Bruk alle filtre Hvis denne aktiveres, vil kun spill som passer med alle filtre bli inkludert i visningen. Ved deaktivering vil alle spill som passer med et hvilket som helst aktivt filter bli inkludert i visningen. Installert Installert Ikke installert Skjult Favoritt Aktiver HDR-støtte Når dette valget er aktivert, blir HDR aktivert på hovedskjermen før spill starter. Merk at HDR ikke støttes på din hovedskjerm. Sist spilt Kategori Beskrivelse Installasjonsmappe Omslagsbilde Lenker Bilde-, ROM- eller ISO-filbane Sjanger Sjangere Selskap Selskaper Utvikler Utviklere Utgiver Utgivere Kategori Kategorier Stikkord Stikkord Funksjon Funksjoner Aldersgrense Aldersgrense Region Regioner Kilde Kilder Siste aktivitet Databasefeil Åpning av biblioteksdatabasen mislyktes. Databasen er ikke åpnet. Kan ikke få tilgang til bibliotekdatabasen. Filen "{0}" brukes av en annen prosess, eller den er utilgjengelig. Kunne ikke lage diagnosepakke. Kunne ikke laste opp diagnosepakke automatisk. Diagnostisk informasjon ble sendt. Diagnostikkpakke ble opprettet og lastet opp. Vennligst legg ved følgende ID til krasjrapporten din: Kunne ikke importere spill fra {0}. Kunne ikke importere emulerte spill fra {0}. Kan ikke søke etter spill basert på valgt emulatorprofil. Profilen inneholder ingen filutvidelser eller plattformer. Playnite klarte ikke å starte. Lukk Playnite-prosesser som allerede kjører, og prøv igjen. Kunne ikke bruke temaet "{0}", fargeprofil "{1}" {2} Kan ikke åpne linken. URL har ikke et gyldig format. Kunne ikke starte applikasjonen. Kan ikke initialisere web-visningskomponenten. Playnite kan ikke fortsette oppstartsprosessen. Mer informasjon er tilgjengelig på https://playnite.link/cefstartup Kan ikke importere emulatorer på grunn av manglende eller ødelagt definisjonsfil. Kunne ikke utføre menyhandling. Rediger spilldetaljer Bilde-URL Legg til lenke Legg til ROM Lagre endringer Gjennomfør endringene i aktiverte felt for spill som redigeres. Legg til handling Slett handling Fjern spillhandling Legg til spill Søk gjennom mappe … Oppdag installert Bla … Åpne Playnite Profilinnstillinger Spillnavn kan ikke være tomt. Sporingsmappe for spillhandling kan ikke være tom Spillnavn kan ikke være tomt før du søker i metadata. Ugyldig spilldata Angi gyldig nettadresse som starter med http:// eller https:// Velg URL Kunne ikke laste ned metadata: {0} Feil ved nedlasting Fjern filtre Privat konto Offentlig konto API-nøkkel Oppstartsfeil Temafeil Fjern alt Installerer Avinstallerer Starter Kjører Ugyldig URL Gjør ingenting Minimer Gjenopprett vindu Gjenopprett vinduet kun ved oppstart fra brukergrensesnittet Lukk Endre Avansert Aldri Fullføringsstatus Fullføringsstatuser Brukerpoeng Kritikerpoeng Samfunnspoeng Spillskript Applikasjonsskript Skript Utvidelser Metadatakilder Tilleggsfunksjoner Tilleggsfunksjons-ID Oppdater skript Interaktivt SDK-PowerShell Alle skriptene ble lastet på nytt. Ingen spill funnet for spesifiserte søke- og filterkriterier Ingen elementer funnet Bytt til skrivebordmodus Lukk Playnite Biblioteker Oppdater alle Laget av: Versjon: Oppdatert: Modul: Bibliotek Statistikk Alle Ingen Varslinger Bredde Høyde Størrelse Liten Normal Stor Større Størst Standard Velg Velg alle Opphev alle valg Først Tilfeldig Brukervalg Last inn flere Gjennomsiktig Fold sammen Utvid Fold sammen alle Utvid alle Andre Temaer Emulator-argumenter Innebygde argumenter Tilpassede argumenter Ytterligere emulator-argumenter Overstyr emulator-argumenter Spillhandling Velg metadata som skal importeres Velg spill som skal importeres Metadatasøk Oppdatering er tilgjengelig Endringer siden siste oppdatering Last ned og installer oppdatering Se etter oppdateringer Oppdateringsfeil Oppdateringssjekk mislyktes. Ingen ny versjon funnet. Du er oppdatert. Kunne ikke laste ned og installere oppdatering. En bakgrunnsprosess pågår. Ønsker du å stoppe den og gjennomføre oppdatering? En bakgrunnsprosess pågår. Ønsker du å stoppe den og lukke Playnite? En bakgrunnsprosess pågår. Bytte av modus vil avbryte prosessen. Ønsker du likevel å bytte? En oppdatering for Playnite er tilgjengelig Last inn temaliste på nytt Bruk valgte tema Se på filendringer Bruk tema automatisk når kildefilen endres Skriptkjøringsmiljø Skript som skal utføres før du starter et spill Skript som skal utføres etter du avslutter et spill Skript som skal utføres etter at et spill er startet Kjør ved programstart Kjør ved programavslutning Skript som kjøres før spillstart Skript som kjøres etter oppstart Skript som kjøres etter avslutning Utfør globalt skript Globalt Filtrert Gjeldende Ny Test skript Vis kun valgte elementer. Lagre som standard Legg til i favoritter Fjern fra favoritter Skjul dette spillet Fjern fra skjult Aktiver HDR-støtte Deaktiver HDR-støtte Rediger … Beregn installasjonsstørrelse Beregn installasjonsstørrelse (alle spill) Beregn installasjonensstørrelse (kun manglende data) Installasjonsstørrelse Velg kategori … Velg fullføringsstatus Fjern Spill Installer Spillalternativer Detaljer Avinstaller Åpne installasjonssted Lag skrivebordssnarvei Åpne manual Mer Administreres av biblioteksutvidelsen Spillets startprosess vil bli administrert av biblioteksutvidelsen som er ansvarlig for dette spillet. Ingen relevant informasjon om «{0}» - spillet er funnet på den angitte siden. Tips: Du kan bruke en mer avansert nedlastingsprosess for metadata når du redigerer hvert enkelt spill via menyen "Rediger". Ikke tilgjengelig mens noen handlinger pågår. Beskrivelsestekst er HTML-syntaksfølsom Spilletiden registreres i sekunder. Installeringsstørrelse blir angitt i bytes Lanseringsdato må velges med "år-måned-dag"-format. Måned og dag kan utelates. Verdier fra 0 til 100, eller tom for ingen poeng. Utviklingen av Playnite støttes av disse Patreon-brukerne: Bidragsytere til koding, lokalisering og annet i tilfeldig rekkefølge: Vil du avbryte spillovervåking? Installasjonsovervåking kjører for øyeblikket. Vil du avbryte prosessen og returnere spillet til sin tidligere tilstand? Spillutførelsesovervåking kjører for øyeblikket. Vil du avbryte prosessen og returnere spillet til sin tidligere tilstand? Spilletid Sist spilt {0}d {1}t {2}m {0}t {1}m {0} minutter {0} sekunder Ikke spilt Åpner skrivebordsmodus … Åpner fullskjermmodus … Laster inn spillbibliotek … Beregner installasjonsstørrelse… Beregner installasjonsstørrelse for {0}… Kunne ikke installere skriptfilen. Skriptet ble installert. Installer skript Skriptfeil Kunne ikke utføre utvidelsesfunksjonen. Åpne metadatamappe Beregn Beregn installasjonsstørrelsen automatisk ved hjelp av rom-fil dersom spillet har en, eller installasjonsmappe dersom en slik er angitt. {0}-klient er ikke installert. {0}-klienten vil nå åpnes. Vennligst logg inn, og lukk deretter denne meldingen. Venter på at brukeren skal logge inn. Vennligst lukk dette vinduet når du er ferdig … Spillets installasjonskatalog ikke funnet. Ugyldig spillhandlingskonfigurasjon. Feilsøking for kontosynkroniseringsproblemer Feilsøking av problemer Gi nytt navn til gjenstand Legg til ny gjenstand Angi navn Angi nytt navn Mindre enn 1 time 1 til 10 timer 10 til 100 timer 100 til 500 timer 500 til 1000 timer 1000+ Playnite må startes på nytt for å fullføre installasjonen. Vil du starte på nytt nå? Utvidelse er ikke pakket ordentlig. Tema er ikke pakket ordentlig. Utvidelsen "{0}" lastet ikke inn riktig. Kan ikke laste utvidelsen "{0}". Denne versjonen av Playnite er ikke støttet. Temaet "{0}" lastet ikke inn riktig. Kan ikke laste inn temaet "{0}". Denne versjonen av Playnite er ikke støttet. Utvidelsen lastet ikke inn riktig. Temaet lastet ikke inn riktig. Tema eller utvidelse bruker ikke-støttet API-versjon. Installasjonen var vellykket. Installér utvidelsen? Generelt Installasjon av utvidelsen "{0}" mislyktes. Kunne ikke installere utvidelsen. {0} Vil du installere en ny utvidelse? {0} Av {1} Versjon {2} Vil du oppdatere utvidelsen "{0}"? Nåværende versjon: {1} Ny versjon: {2} Kunne ikke installere tema. {0} Vil du installere et nytt tema? {0} Av {1} Versjon {2} Vil du oppdatere temaet "{0}"? Nåværende versjon: {1} Ny versjon: {2} Du er i ferd med å forlate Playnite, og navigere til følgende webside ved hjelp av din standardnettleser. Vil du fortsette? {0} Valgte bilde(r) er for store til å gi optimal ytelse. Bruk av veldig store bilder kan føre til betydelig dårligere brukergrensesnittresponsivitet og økt minnebruk. Maksimale anbefalte størrelser: Ikoner: {0} megapiksler Omslag: {1} megapiksler Bakgrunner: {2} megapiksler Ytelsesadvarsel Ikke vis igjen Fil med filendelsen {0} er ikke kompatibel. Inkompatibel filendelse Den valgte bildefilen kan være for stor til å gi optimal ytelse. Er du sikker på at du vil avinstallere valgt tema? Avinstallering vil stå i kø til neste applikasjonsstart. Innebygde temaer kan ikke avinstalleres. Dette temaet støtter ikke denne versjonen av Playnite. Er du sikker på at du vil avinstallere valgte utvidelse? Avinstallering vil stå i kø til neste applikasjonsstart. Innebygde utvidelser kan ikke avinstalleres. Denne utvidelse støtter ikke denne versjonen av Playnite. Installasjonskatalog Dataområde Genererer diagnostikkpakke … Laster opp diagnostikkpakke … Importer fil … Hva er dette? Er du sikker på at du vil gjøre dette? Total spilletid Gjennomsnittlig spilletid Mest spilt Total installasjonsstørrelse Oversikt Sidepanel Vis i sidestolpe Tilbakestill innstillinger Alle programinnstillinger vil bli tilbakestilt til standardverdier, med unntak av : - Databaselokasjon - Importekskluderingsliste - Utvidelsesinnstillinger, inkludert bibliotekintegrasjoner Omstart av programmet er nødvendig for å fullføre prosessen. Ønsker du å tilbakestille innstillingene? For utviklere Eksterne utvidelser Fyll inn komplett mappesti. Prestasjoner Forum Nyheter Butikkside Første gangs oppsett er ikke fullført. Playnite vil nå restarte til skrivebordsmodus for å fullføre prosessen. Nylig spilt Favoritter Mest spilt Alle Filtre er i bruk. Ytterligere filtre er i bruk. Søkeresultater for: En gjenstand med samme navn finnes fra før. Begrens valget til gjeldende filter Velg en annen Tillegg … Installert Utvidelsesinnstillinger Bla gjennom Oppdateringer Oppdateringer ({0}) Administrering av installerte utvidelser og temaer, inkludert deres innstillinger, er flyttet til en ny "Tillegg-"meny. Alle installerte biblioteksintegrasjonsutvidelser kan konfigureres der. Hvis du vil installere eller avinstallere ytterligere integrasjoner, kan du bruke "Tillegg"-valget i hovedmenyen. Temaer skrivebord Temaer fullskjerm Søker … Tillegg er ikke kompatibelt med denne versjonen av Playnite. Kunne inn laste ned installasjonspakke for tillegg. Kunne ikke last ned installasjonsmanifest for tillegg. Omstart av programmet er påkrevd for at nye innstillinger skal tre i kraft. Dette tillegget er satt i kø for installasjon. Installer Avinstaller Allerede installert Ingen nye oppdateringer for utvidelser er funnet. Oppdater tillegg. Endringslogg er ikke tilgjengelig. Satt i kø for installasjon. Nedlasting mislyktes Lisens ble avvist Last ned {0}… Ser etter oppdateringer for tillegg … En eller flere oppdateringer for tillegg er tilgjengelig. Velg elementer som skal oppdateres Utvidelse utvikles eksternt Lisensavtale for {0} Godta Avslå Inkluder spillhandlinger fra biblioteksutvidelser Velg handling Sporingsmodus Sporingssti Første sporingsforsinkelse Sporingsfrekvens Lenke Fil Emulator Skript Standard Prosess Mappe Opprinnelig prosess Logg sporingsmeldinger Følgende endringer overskriver data for alle valgte spill! Ingen Uniformt Kun elementer Kun første og siste Rullefølsomhet Jevn rulling Animasjonshastighet Fjern element? Er du sikker på at du vil fjerne dette elementet? Vis knapper i toppanelet: Generelle visningsinnstillinger Grupperingsinnstillinger Sorteringsinnstillinger Filteroppsett Posisjon for plugin-knapper Seksjonsskillenes bredde Flytt hovedmenyknappen til sidestolpen Utforskerkolonne Tilfeldig spill-velger Velger for tilfeldig spill fra visning Velg et tilfeldig spill fra visningen Lagre grupperings- og sorteringsinnstillinger Vis som hurtigfilter i fullskjermsmodus Siste 7 dager Siste 31 dager Siste 365 dager Mer enn 365 dager Konfigurer Lagre oppsett Minimer etter at spill er startet Minimer Playnite etter at spill er startet. Deaktivering av denne funksjonen kan gjøre at spill ikke får fokus ved oppstart! Skriftstørrelse Liten skriftstørrelse Støtte for spillkontroller Deaktivering vil gjøre at Playnite ikke tar imot kommandoer fra spillkontrollere. Deaktiver hvis du bruker verktøy som oversetter spillkontrollerkommandoer til mus- og tastaturkommandoer, og du får doble trykk i Playnite. Vis oppføringer på hovedmenyen: Inverter X- og A-knappene på hovedskjermen Bytt om knappene for oppstart av spill og visning av spilldetaljsiden i hovedskjermen. Bytt om knapper for bekreftelse og kansellering Bytt om A/B-knappenes bindinger for bekreftelse og kansellering. Kun hovedkontroller Når denne er aktivert godtas kun kommandoer fra hovedkontrolleren. Guide-knappen fokuserer Playnite Grensesnittvolum Bakgrunnsvolum Demp når programmet er i bakgrunnen Kunne ikke ta i bruk lydgrensesnitt. Utgangs-API Valg av API som brukes til lydavspilling. Bytt hvis du opplever problemer med lyden. Generelt Visuelt Lyd Oppsett Menyer Inndata {0} starter … {0} kjører … Store bokstaver Mellomrom Skalerer for bilde-rendering Alternativ Balansert Kvalitet Kvalitet: Beste bildekvalitet, treg, høy minnebruk. Balansert: God bildekvalitet, rask, lav minnebruk. Alternativ: Bedre bildekvalitet, medium hastighet, lav minnebruk. Velg fil … Velg mappe … Oppstartsskript Husk at både utvidelser og temaer kan ha stor innvirkning på Playnites ytelse, stabilitet og sikkerhet. Velg ved oppstart Velg ved oppstart Innebygde profiler Innebygget profil Egendefinerte profiler Egendefinert profile Håndtert av et innebygget skript Emulator-valg Plattformvalg Regionsvalg Utfør før emulator startes Kjør etter at emulator er startet Kjør etter at emulator er avsluttet Emulator-program ble ikke funnet. Emulator-spesifisering ble ikke funnet. Oppstartsskript for emulator ble ikke funnet. Del opp som separate spill Slå sammen til ett spill Velg plattform Velg region Søk gjennom mappe Søk gjennom konfigurasjoner Ekskluder mønster fra kontrollsum-skann Filer som passer med spesifiserte mønstre vil ikke ble sjekket mot kontrollsum, og vil bli koblet med filmnavn. Se hjelpesiden for emulatorer for mer informasjon. Søk med emulator Navn må velges når nye konfigurasjon skal lagres. Emulator eller emulator-profil er ikke valgt. Mappe som skal søkes i er ikke spesifisert eller eksisterer ikke. Søkekonfigurasjon er ikke riktig angitt. Inkluder i globalt automatisk søk Kunne ikke søke gjennom mapper etter emulatorer. Kunne ikke søke gjennom mapper etter emulerte spill. Skjul importerte Profiler som skal importeres: Autosøk-konfigurasjoner Lagre som autosøk-konfigurasjon Lagrer konfigurasjon til senere bruk under biblioteksoppdatering. Lagrede konfigurasjoner kan administreres via "Konfigurer emulatorer"-menyen. Importer med relative baner Importer spillfiler ved hjelp av baner som er relative til Playnites installasjonsmappe eller emulatorers installasjonsmappe hvis mulig. Søk gjennom undermapper Søk gjennom arkiv Slå sammen relaterte filer Slå sammen relaterte spillfiler, som for eksempel individuelle spilldisker, under én spilloppføring. Legg til skanner Legg til lagret skanner Start søk Bruk søkekonfigurasjoner med emulatorer for å søke gjennom bestemte mapper. Pass på at emulatorer er riktig konfigurert før du importerer spill (via Bibliotek -> Konfigurer Emulatorer). Standardstatus tilordnet spill som nylig er lagt til Status tilordnet spill som spilles for første gang Kunne ikke initialisere PowerShell-skriptkjøringsmiljø. Hvis du bruker Windows 7, kan du prøve å (re-)installere PowerShell 5.1 for å rett opp problemet. Et filteroppsett med dette navnet eksisterer allerede. Vil du oppdatere oppsettet med nye innstillinger? Disse ordene vil bli fjernet fra begynnelsen av den automatisk innfylte sorteringsnavneverdien: Bruk dette feltet for å ignorere ord i starten av en streng med tanke på sortering. Standardverdiene er "The", "An", og "A". Fyll inn sorteringsnavn for spill som ikke har Sortering Fyller inn sorteringsnavnverdier … Tjenesten Nahimic er oppdaget på maskinen din. Det er kjent at denne tjenesten kan forårsake grafiske feil i Playnite (og andre programmer). Hvis du opplever grafiske feil eller andre problemer i Playnite, så anbefaler vi at du deaktiverer eller avinstallerer Nahimic. Mer informasjon er tilgjengelig her: https://playnite.link/nahimicsucks Playnite kjører med forhøyede rettigheter (som administrator). Dette gir forhøyede rettigheter til alle spill/apper som startes fra Playnite, og er derfor ikke anbefalt! Mer informasjon er tilgjengelig på https://playnite.link/adminfaq Vis advarsel hvis Playnite kjører med forhøyede rettigheter Bruk den faktiske størrelsen på disk når størrelsen til spill beregnes Ved aktivering går skanning saktere og du får den faktiske størrelsen filer opptar på disken. Ved deaktivering går skanning fortere og størrelsen til selve filene blir brukt. Følgende tillegg har blitt rapportert som potensielt problematisk , enten fordi stabilitet eller ytelse påvirkes, eller på grunn av sikkerhetsproblemer. Vi anbefaler avinstallering på det sterkeste: {0} Ekskluder filer i skyen fra søk Filer som er lagret på skytjenester blir ikke skannet og importert om de ikke er tilgjengelig lokalt. Kun støttet for Google Drive, DropBox, OneDrive Søk, men med forenklet metode uten filinnhold Filer vil bli imponert, men ved bruk av en mindre nøyaktig metode som ikke krever at filinnhold er lastet ned og tilgjengelig lokalt. Bruk for alle Overstyr installasjonsstatus Når dette er valgt, vil Playnite ignorere statusen til installasjonen (inkludert installasjonsmappe) satt av integrasjonsutvidelsen som importerer dette spillet. Dette alternativet vil kanskje ikke fungere riktig med utvidelser som bruker en spesifikk spillimportmetode med mindre de også tar hensyn til dette overstyringsalternativet. Bare manuelt En gang om dagen En gang i uken Ved hver oppstart Se etter programoppdateringer Ser etter oppdateringer for tillegg Oppdater biblioteker Søk gjennom emuleringsmapper Inkluder skjulte spill Rediger felt Velg / velg bort alt Åpne Aktiver Tildel Begynn å skrive for å søke etter spill … [F1] for hjelp # gir deg en liste over tilgjengelige kommandoer. / gir deg en liste over tilgjengelige søkeleverandører/programtillegg. Inntasting av søkeord etterfulgt av Mellomrom, gjør at du bytter umiddelbart til dette søket. TAB: bytt handling ENTER: aktiver valgt handling SHIFT-ENTER: åpne elementmeny Inkluder avinstallerte spill Inkluder skjulte spill Avinstallerte spill inkludert Avinstallerte spill ekskludert Skjulte spill inkludert Skjulte spill ekskludert Spill eller installer Gå til detaljer Spillmeny Rediger spill Åpne søk Søkefelt Søkeknapp Primær spillhandling Sekundær spillhandling CTRL-F åpner globalt søk i stedet for å fokusere søkefeltet Lagre spillets filterinnstillinger mellom søkeøkter Søkeleverandører Standardnøkkelord Tilpassede nøkkelord Systemsnarvei Playnite-søk Utvidelsesinnstillinger Utelatelser Ekskluderte filer relativt til søkemappe Ekskluderte mapper relativt til søkemappe Legg til fil i ekskluderingsliste Legg til mappe i ekskluderingsliste Ekskluderinger kan bare legges til i lagrede skannekonfigurasjoner. Ekskluderinger er lagt til i "{0}" søk. Overstyr plattform Når dette er valgt vil skanningen tilordne denne plattformen til alle spill, og samtidig overskrive automatisk angitte plattformer. Inkluder kommandoer i standardsøk Når deaktivert, vil ikke kommandoer inkluderes i standard søk med mindre #-prefiks er brukt. Bruk tilnærmede treff i navnefilter Når dette valget er aktivert, vil navnefilter samsvare med spillnavn på samme måte som globalt søk. Strengt samsvar kan håndheves i hvert enkelt tilfelle ved å bruke utropstegn (!) før filternavnet! Felter som skal vises for spillresultater: Skjult status Sikkerhetskopiering av data ble avbrutt. Sikkerhetskopiering mislyktes. Feil ved sikkerhetskopiering Sikkerhetskopiering av data pågår … Gjenoppretter data fra sikkerhetskopi … Gjenoppretting av sikkerhetskopi mislyktes. Innstillinger Spillbibliotek Media i spillbiblioteket Installerte utvidelser Utvidelsesdata Installerte temaer Velg data som skal gjenopprettes fra angitt sikkerhetskopifil. Playnite vil automatisk starte på nytt for å gjennomføre gjenopprettingsprosessen. Velg elementer som skal inkluderes i sikkerhetskopien. Programinnstillinger og spillbibliotekdata er inkludert som standard. Playnite vil automatisk starte på nytt for å gjennomføre sikkerhetskopieringsprosessen. Automatisk sikkerhetskopiering Frekvens for automatisk sikkerhetskopiering Sikkerhetskopimappe Roterende sikkerhetskopier Inkluder ekstra data: Sikkerhetskopieringsmappen må angis dersom automatisk sikkerhetskopiering er aktivert. Vis kun varsler for deloppdateringer Når denne funksjonen er aktivert vil bare oppdateringer som er tilgjengelig for nåværende installerte hovedversjon av programmet føre til varsel om oppdatering. Nye hovedversjoner vil ikke resultere i oppdateringsvarsel. Bruk relative datoer for den siste uken Bruke relative datoer i formatet «i dag», «i går» osv. hvis datoen er mindre enn en uke gammel. Det angitte datoformatet vil bli brukt til alle andre datoer. Bildesøk på web Søkestreng for ikonbilde Søkestreng for omslagsbilde Søkestreng for bakgrunnsbilde Henter tilleggsinformasjon … Ingen metadatakilde er tilgjengelig Innstillinger for spillhandlinger Bruk skanneinnstillinger Velg profil ved oppstart Velg emulator ved oppstart Automatisk Alltid på Alltid av Tilgjengelighetsstøtte (skjermleser) Applikasjonsmeny Spillmeny Programmappe Mappe for brukerdata Feil i bibliotekfil er oppdaget. Playnite avsluttes. Åpne ny sak på Playnite's GitHub-side med en forespørsel om å fikse feilen i filene dine. Vil du lagre endringene du har gjort? Flyttbar installasjon Ingen kontrollere funnet ================================================ FILE: source/Playnite/Localization/pl_PL.xaml ================================================  Polski Język Zamknij aplikację Filtr aktywny Filtr wyłączony Dodatkowe filtry Filtry Filtr Nieprawidłowe dane Zapisać zmiany? Strona główna - www.playnite.link Kod źródłowy na GitHub Utwórz pakiet diagnostyczny Wysyłaj informacje diagnostyczne O aplikacji Autor: Josef Němec Dodaj do kolekcji Dodaj Utwórz nową kolekcję • Zaznacz, aby dodać do kolekcji • Odznacz, aby usunąć z kolekcji • Nieokreślone pozostaną bez zmian (podczas edycji wielu gier) Bez kategorii Brak platformy Ups! Coś poszło nie tak. Wystąpił nieodwracalny błąd. Jeśli chcesz nam pomóc w naprawieniu tego błędu, prosimy o krótki opis działań podjętych przed awarią, a następnie stworzenie pakietu diagnostycznego. Jeśli jesteś online, pakiet zostanie przesłany na serwer Playnite do analizy. Alternatywnie, możesz użyć przycisku 'Zgłoś awarię', aby utworzyć nowy problem na GitHubie i zgłosić awarię ręcznie. Dziękujemy za pomoc. Rozszerzenie „{0}” spowodowało nieodwracalny błąd. Zalecamy zapisanie pliku dziennika i zgłoszenie problemu twórcy rozszerzenia. Jeśli problem będzie się powtarzał, wyłącz to rozszerzenie. Rozszerzenie „{0}” spowodowało nieodwracalny błąd. Zalecamy zapisanie pliku dziennika i zgłoszenie problemu twórcy rozszerzenia. Jeśli problem będzie się powtarzał, wyłącz to rozszerzenie. Nieznane rozszerzenie lub motyw spowodował nieodwracalny błąd. Zalecamy wyłączenie rozszerzeń innych firm, aby wyizolować problematyczny dodatek i zgłosić problem do jego twórców. Wystąpił nieodwracalny błąd. Jeśli chcesz nam pomóc rozwiązać ten problem, wyślij informacje diagnostyczne. Dziękujemy. Wyłącz rozszerzenie Zapisz plik dziennika Wyślij dane diagnostyczne Zgłoś błąd Uruchom ponownie Uruchom ponownie w trybie awaryjnym Zablokuj wszystkie nieoficjalne rozszerzenia i użyj domyślnego motywu. Zamknij aplikację Opisz jak doszło do błędu (w języku angielskim): Menedżer biblioteki Usunąć? Nie można usunąć - gra lub instalator działa w tle. Nie można odinstalować uruchomionej gry. Czy na pewno chcesz usunąć {0}? Czy jesteś pewien, że chcesz usunąć {0} gier? Czy na pewno chcesz usunąć {0}? Wybranie opcji "dodaj do listy wykluczeń" uniemożliwi ponowne zaimportowanie gry przy aktualizacji biblioteki następnym razem. Na pewno chcesz usunąć {0} gier? Wybranie opcji "dodaj do listy wykluczeń", pozwoli pominąć zaimportowanie tej gry podczas kolejnej aktualizacji biblioteki. Czy jesteś pewien, że chcesz usunąć {0} wpisów, które nie są aktualnie w użyciu? Nie znaleziono żadnych niewykorzystanych pól. Tak (dodaj do listy wyjątków) W tej sekcji są niezapisane zmiany Aktualizowanie formatu biblioteki gier… Aktualizacja bazy danych nie powiodła się. Nie można zaktualizować biblioteki gier. Potrzeba {0} MB wolnego miejsca. Błąd gry Nie można uruchomić gry. '{0}' nie znaleziono w bazie danych. Nie można uruchomić gry: {0} Nie można uruchomić akcji: {0} Nie można otworzyć lokalizacji gry: {0} Nie można wykryć rozmiaru instalacji gry: {0} Błąd skanowania rozmiaru instalacji Liczba błędów podczas sprawdzania rozmiaru instalacji: {0} Stworzenie skrótu się nie powiodło: {0} Błąd otwarcia instrukcji: {0} Nie można zainstalować gry: {0} Nie można odinstalować gry: {0} Nie znaleziono prawidłowych akcji uruchamiania gry. Podczas używania akcji emulatora, upewnij się, że definicje platformy pasują do konfiguracji gry i emulatora. Instalacja implementacji nie jest dostępna. Wtyczka biblioteki odpowiedzialna za tę grę nie jest zainstalowana lub włączona. Oficjalne metadane nie są dostępne. Nie wybrano gry. Uruchomienie skryptu gry nie powiodło się. Nieudane uruchomienie aplikacji skryptu. Uruchomienie globalnego skryptu nie powiodło się. Uruchomienie skryptu emulatora nie powiodło się, Uruchomienie skryptu akcji nie powiodło się. PowerShell 3.0 lub nowszy nie jest zainstalowany. Nie można określić jak uruchomić grę. Włączone Wyłączone Usuń Usuń nieużywane Zmień nazwę Kopiuj Dodaj Domyślna ikona Domyślny obraz okładki Domyślny obraz tła Zakończ Następne Wstecz GOTOWE COFNIJ WYCZYŚĆ Wyczyść Odrzuć Odrzuć wszystkie Importuj Nazwa Autor Moduł Seria Wersja Ostatnio uruchomiono Najczęściej grane Liczba uruchomień Rozmiar instalacji Folder Notatki Dodano Dodano Zmodyfikowano Data modyfikacji Strona WWW Ścieżka OK Zapisz Zamknij Anuluj Zatwierdź Reset Tak Nie Witaj Użytkownik lokalny Ogólne Media Odnośniki Instalacja Polecenia Pobieranie… Pobieranie metadanych… Wczytywanie… Typ Profil Profile Usuń Pobierz Szukaj Rozdzielczość: Dowolny Powiększenie Widok listy Okładki Widok siatki Widok szczegółowy Personalizacja Adres Szczególne podziękowania Licencja Współtwórcy Zamykanie aplikacji Dzisiaj Wczoraj Poniedziałek Wtorek Środa Czwartek Piątek Sobota Niedziela Zeszły tydzień Zeszły miesiąc Zeszły rok Więcej niż rok temu Od 0 do 100 MB Od 100MB do 1GB Od 1GB do 5GB Od 5GB do 10GB Od 10GB do 20GB Od 20GB do 40GB Od 40GB do 100GB 100 GB lub więcej Import zakończony pomyślnie. Wszystkie gry ID gry ID bazy danych Szablony Kolumna Kolumny Wiersz Wiersze Nie udało się pobrać ikony z polecenia "Graj". Dla tego typu pliku nie jest dostępne żadne polecenie. Pobieraj tylko brakujące metadane Ta opcja pominie pobieranie metadanych tam, gdzie już znajdują się informacje. Wybór gier Wybierz, które gry należy zaktualizować o nowe metadane: Wszystkie gry z bazy danych Wszystkie aktualnie filtrowane gry Tylko wybrane gry Nie wybrano żadnego pola metadanych Nie wybrano żadnego pola metadanych do pobrania. Wybierz co najmniej jedno i włącz dla niego co najmniej jednego dostawcę metadanych. Oficjalny sklep IGDB Wybierz pola, które mają być automatycznie wypełniane przez Playnite oraz źródła, z których należy pobrać dane. Zachęcamy do kliknięcia na powyższe logo oraz wprowadzania poprawek w bazie danych IGDB.com w celu udoskonalenia danych widocznych w Playnite. Pobieranie metadanych… Importowanie zainstalowanych gier… Importowanie gier {0}… Importowanie emulowanych gier z {0}… Aktualizowanie biblioteki… Skanowanie rozmiaru gier w bibliotece... Skanowanie rozmiaru importowanych gier... Ukończono aktualizację biblioteki Zwalnianie zasobów… Konfiguracja Ustawienia Platformy i emulatory Konfiguracja emulatorów Zarządzanie biblioteką Narzędzia Pobierz metadane Narzędzia Konfiguracja integracji Otwórz klienta Platformy Aktualizacja biblioteki Anuluj aktualizację biblioteki Aktualizuj foldery emulowane Dodaj grę Manualnie Automatyczne skanowanie Gra emulowana Gra z Microsoft Store O aplikacji Wyślij opinię Tryb pełnoekranowy Odnośniki Pomoc Wesprzyj na Patreon Wesprzyj na Ko-fi Instrukcja użytkownika Dokumentacja SDK Uruchom ponownie system Zamknij system Uśpij system Hibernacja systemu Blokada systemu Wyloguj użytkownika Wylosuj grę Wyświetlane pola gry w panelu szczegółowym: Odstęp Pokazuj tło pod tytułami Szerokość ramki okładki Brak źródła ikony gry Brak źródła okładki gry Brak źródła tła gry Odstęp pionowy szczegółów gry Pozycja szczegółów w widoku siatki Pozycja listy gier w widoku szczegółowym Separator pomiędzy panelami Wysokość obrazu okładki gry Wysokość ikony na liście gier Czcionka aplikacji Czcionka o stałej szerokości Pozycja panelu filtrowania Pozycja panelu eksploratora Renderowanie okładek Współczynnik proporcji Powyższa opcja wpływa również na renderowanie okładek w trybie pełnoekranowym! Sposób skalowania Pudełko DVD Epic Games Store GOG Galaxy 2.0 IGDB Pole Baner Steam Pionowe okładki Steam Twitch * Wymaga ponownego uruchomienia Ustawienia Ogólne Panel górny Wygląd Szczegóły gry Układ Zaawansowane Pełny ekran Wprowadź Wydajność Metadane Aktualizacja Szukaj Kopia zapasowa Wykonaj kopię zapasową Przywróć kopię zapasową Importuj automatycznie zmiany w bibliotece Nieprawidłowe położenie pliku bazy danych, należy ustawić właściwą ścieżkę pliku. Nazwa konta nie może być pusta. Pobierz metadane po imporcie gier Uruchamiaj Playnite w trybie zminimalizowanym Uruchom Playnite wraz z systemem Uruchom zamknięty do zasobnika Nie zarejestrowano Playnite do uruchomienia podczas startu systemu. Uruchom w trybie pełnoekranowym Asynchroniczne ładowanie obrazu Poprawia płynność przewijania listy gier kosztem wolniejszego ładowania się grafiki Pokaż nazwę gry, jeśli brakuje okładki Pokaż nazwy gier w widoku siatki Przyciemnij niezainstalowane gry Pokaż ikony gier na liście widoku szczegółowego Pokazuj liczbę gier obok nazwy grupy Pokazuj tylko zaznaczone pola w filtrze i panelu eksploratora Wyłącz przyspieszanie sprzętowe Użyj, gdy występują problemy z szarpaniem (suttering) lub podobne problemy z interfejsem użytkownika Pokazuj ukryte gry w pasku szybkiego dostępu Wpływa na menu kontekstowe oraz menu zasobnika. Liczba pozycji w pasku szybkiego dostępu Dynamiczne tło - używaj tła gier jako tła aplikacji Rozmycie tła Wysoka jakość Przyciemnienie tła Pokaż w widoku siatki Motyw Motyw profilu Motyw pełnoekranowy Pełnoekranowy motyw profilu Lokalizacja bazy danych Status logowania: Ustawienia aplikacji Wyczyść pamięć podręczną Może pomóc rozwiązać problemy napotkane podczas próby powiązania kont. Wyświetlaj ikonę w zasobniku systemowym Minimalizuj Playnite do zasobnika systemu Kiedy okno aplikacji jest zamykane, zminimalizuj Playnite do zasobnika systemowego Po uruchomieniu gry: Po zamknięciu gry: Formatuj czas gry, aby wskazać liczbę dni rozgrywki Formaty dat: Spowoduje to wylogowanie Cię z wszystkich powiązanych usług. Wymagany jest restart aplikacji, czy chcesz kontynuować? Wyczyścić pamięć podręczną? Uruchom ponownie, aby zastosować nowy motyw Pobierz więcej motywów Stwórz nowy motyw Pobierz więcej rozszerzeń Stwórz nowe rozszerzenie Pomóż w tłumaczeniu aplikacji Należy uruchomić ponownie Playnite, aby zastosować zmiany. Uruchomić ponownie teraz? Ponowne uruchomienie anuluje wszystkie aktualnie wykonywane zadania (pobierania). Uruchomić ponownie aplikację? Playnite nie przenosi plików biblioteki automatycznie, musisz je przenieść / skopiować przed zmianą lokalizacji. Jeśli w docelowej lokalizacji nie ma plików biblioteki, zostanie utworzony nowy plik. Nowa lokalizacja bazy danych zostanie użyta dopiero po ponownym uruchomieniu aplikacji. Czas gry nie będzie rejestrowany, jeśli wybrano opcję "Zamknij". Liczba wierszy Liczba kolumn Liczba wierszy z szczegółami Pokaż obraz tła na ekranie głównym Nie dotyczy obrazów tła w istniejących grach bez ponownego pobierania metadanych. Importuj czas gry z gier w bibliotece: Konfiguruje kiedy Playnite powinien importować czas gry zgłoszony przez wtyczki biblioteki dla gier w bazie Playnite. Obsługa wtyczek biblioteki odpowiedzialnych za obsługę gier jest potrzebna, aby móc korzystać z tej funkcji. Zawsze: Importuje czas gry dla nowo zaimportowanych i istniejących gier w bazie danych Playnite. Tylko dla nowo zaimportowanych gier: Importuj czas gry tylko dla nowo zaimportowanych gier. Nigdy: Nigdy nie importuj czasu gry w żadnych okolicznościach. Zawsze Tylko dla nowo zaimportowanych gier Nigdy Włącz obsługę kontrolera w trybie pełnoekranowym Przycisk Guide otwiera tryb pełnoekranowy Automatyczne pobieranie metadanych dla nowo zaimportowanych gier. Docelowy wyświetlacz Zawsze używaj wyświetlacza głównego Pokaż tytuły gier Pokaż status baterii Pokaż procent baterii Pokaż zegar Ukryj kursor myszy Tylko zainstalowane (w podręcznych filtrach) Przyciski kontrolera Układ Przewijanie w poziomie Wybierz jedną z podsekcji Brak dostępnych ustawień Nie udało się wczytać ustawień Skrypty poniżej są wykonywane dla każdej gry w bibliotece Playnite. Indywidualne skrypty mogą być przypisane do każdej gry oddzielnie podczas edycji informacji o grze. Animacja przejść obrazów tła Wielkość czcionki Automatycznie Bez wygładzania Skala szarości ClearType Ideal Display Sposób formatowania tekstu Sposób renderowania tekstu Ustawienia renderowania i formatowania tekstu nie są obecnie stosowane do tekstu w opisach gier. Ładowanie wstępne obrazów tła Jeżeli włączone, Playnite będzie pobierał tła gier kiedy będzie pobierał metadane. Ta opcja używa więcej miejsca na dysku i pozwala oglądać tła gier kiedy jesteś w trybie offline. Jeżeli wyłączone, Playnite będzie pobierał tła gier tylko w tedy kiedy będą wymagane po raz pierwszy. Ta opcja używa mniej miejsca na dysku, ale może spowodować opóźnienie przed wyświetleniem tła, a niektóre tła mogą być niedostępne offline. Automatycznie zamykaj aplikację klienta po wyjściu z gry Czas oczekiwania na zamknięcie klienta (w sekundach) Nie zamykaj po sesji gry krótszej niż (w sekundach) Automatycznie zamykaj następujące klienty: Automatyczne zamykaj klienta Importuj listę wyjątków Wyświetlaj ostrzeżenie przy dodawaniu zbyt dużych plików z obrazami Otwórz folder Preferowany system klasyfikacji wiekowej Aktualizuj rozmiar gier przy aktualizacji biblioteki Skanuje i aktualizuje rozmiar instalacji gier, jeśli zostanie wykryte, że ich pliki zostały zmodyfikowane od ostatniego skanowania Brak Wypełnij Dopasuj Dopasuj i wypełnij Po lewej Po prawej Na górze Na dole Importuj błąd Wymagane uwierzytelnienie Błąd uwierzytelnienia Tryb alternatywnego renderowanie podglądu sieci Użyj, jeśli masz problem z podglądem strony, np. z oknami autoryzacji integracji. Częściowe ładowanie dużych opisów gier Duże opisy mogą spowodować zauważalne opóźnienie podczas wybierania gier. Gdy włączone, tylko część opisu zostanie załadowana z opcją załadowania reszty na żądanie. Importowanie metadanych Pobierz metadane Wybierz dane konfigurację pobierania metadanych do pobierania w przyszłości. Możesz to zmienić w ustawieniach aplikacji. Kreator importu emulatorów Kreator poprowadzi Cię przez proces pobierania i importowanie emulatorów oraz pobieranie emulowanych gier. Pamiętaj, że możesz zawsze dodać dodatkowe emulatory (menu---> biblioteka--> konfiguracja emulatorów) i/lub gry (menu-->dodaj grę-->emulowana gra) później używając menu głównego. Poniżej znajduje się lista emulatorów, które Playnite może rozpoznawać i automatycznie konfigurować. Możesz je pobrać i zainstalować, odwiedzając ich stronę internetową. Po zainstalowaniu emulatorów (recznie) przejdź do następnego ekranu, aby zaimportować je do Playnite. Możesz także skonfigurować i importować dowolny niestandardowy emulator później za pośrednictwem menu konfiguracji. Można importować dowolne emulatory zainstalowane na komputerze, naciskając przycisk "Skanowanie folderów...". Playnite przeszuka wybrany folder dla wszystkich znanych emulatorów i udostępni opcję ich importowania. Możesz importować dane z wielu folderów, używając tego przycisku kilka razy, emulatory zostaną dodane na dole bieżącej listy. Możesz importować gry, naciskając przycisk "Skanuj folder dla ROM/ISO". Wybór odpowiedniego emulatora powie Playnite, które typy plików powinny być skanowane i importowane. Możesz importować z wielu folderów, używając tego przycisku kilka razy, gry zostaną dodane na dole bieżącej listy. Nie wybrano żadnych emulatorów do zaimportowania. Automatyczne importowanie emulowanych gier nie będzie możliwe bez wcześniejszego skonfigurowania emulatora. Czy na pewno chcesz kontynuować i opuścić proces importowania? W Playnite nie ma skonfigurowanych emulatorów. Nie można importować gier bez uprzedniego skonfigurowania emulatora i wybrania odpowiednich typów plików. Czy chcesz teraz dodać kilka emulatorów? Skanuj folder dla ROM/ISO Wybierz pliki Skanowanie folderów… Konfiguracja emulatorów… Skanowanie… Skanowanie {0}... Pierwsza konfiguracja Ten kreator poprowadzi cię przez proces automatycznego importu gier i konfiguracji zewnętrznych bibliotek. Playnite może automatycznie importować gry z wielu platform, takich jak Steam czy GOG, a także na bieżąco aktualizować twoją kolekcję poprzez automatyczną synchronizację bibliotek w trakcie uruchamiania aplikacji. Zauważ, że zawsze możesz dodać dowolną grę na którąkolwiek z platform ręcznie, klikając przycisk 'Playnite' w menu głównym. Integracja biblioteki Automatycznie importuj gry z następujących usług. Wszelkie późniejsze zmiany (status instalacji) zostaną automatycznie zaktualizowane przy uruchomieniu Playnite. Wybrane ustawienia dotyczą początkowego i wszystkich kolejnych importów. Konfiguracja zakończona Wstępne ustawienia zakończone. Pamietaj, że możesz zmienić wszystkie ustawienia w "Ustawienia". Aby dodać inne gry poźniej, kliknij na logo Playnite. Nie udało się pobrać jednego lub więcej rozszerzeń. Możesz spróbować pobrać ponownie integracje z menu dodatków po zakończeniu kreatora pierwszego uruchomienia. Pobieranie {0} integracji... Pobieranie listy rekomendowanych integracji… Nie udało się pobrać listy zalecanych integracji. Możesz spróbować ponownie pobrać integrację później w menu Dodatki. Konfiguruj platformy i emulatory Konfiguracja emulatorów Platformy Platforma Emulatory Emulator Dodaj platformę Wybierz ikonę Wybierz okładkę Wybierz obraz Wybierz pozycję Wybierz tło Wybierz plik Wybierz URL Dodaj emulator Wspierana platforma Czy chcesz zapisać zmiany platformy? Czy chcesz zapisać zmiany emulatora? Plik wykonywalny Parametry Katalog roboczy Wspierane typy plików Importowanie emulatorów… Pobieranie emulatorów… Wczytaj ustawienia parametrów z profilu emulatora Czy na pewno chcesz usunąć emulator {0}? Emulator jest wykorzystywany przez następującą liczbę gier {1}. Czy na pewno chcesz usunąć platformę {0}? Platforma jest wykorzystywana przez następującą liczbę gier {1} i emulatorów {2}. Pomoc ustawień Sortuj według Kierunek sortowania Grupuj według Rosnąco Malejąco Nie grupuj Grupuj według bibliotek Grupuj według kolekcji Grupuj według platform Typ widoku Widok Panel eksploratora Panel filtrowania Ikona Ikona biblioteki Okładka Obraz tła Nazwa sortowania Biblioteka Instrukcja Nazwa Zainstaluj dysk Nazwa konta Platforma Kolekcja Gatunek Data wydania Rok wydania Producent Tagi Wydawca Stan instalacji Zastosuj wszystkie filtry Jeżeli aktywne, zostaną wyświetlone tylko te gry, które spełniają warunki dla wszystkich wybranych filtrów. Jeżeli nieaktywne, zostaną wyświetlone te gry, które spełniają minimum jeden z wybranych filtrów. Zainstalowane Zainstalowane Nie zainstalowane Ukryte Ulubione Włącz obsługę HDR Jeśli włączone, HDR będzie włączony na ekranie głównym przed rozpoczęciem gry. Zauważ, że HDR nie jest obsługiwany na Twoim głównym wyświetlaczu. Ostatnio uruchomiono Kolekcja Opis Ścieżka instalacji Okładka Odnośniki Ścieżka ROM lub ISO Gatunek Gatunek Firma Firmy Producent Producent Wydawca Wydawca Kolekcja Kolekcje Tag Tagi Funkcje Funkcje Klasyfikacja wiekowa Klasyfikacja wiekowa Region Regiony Źródło Źródła Ostatnia aktywność Błąd bazy danych Nie można otworzyć bazy danych biblioteki. Baza danych nie jest otwarta. Nie można uzyskać dostępu do bazy danych biblioteki. Plik "{0}" jest używany przez inny proces lub jest w niedostępnej lokalizacji. Nie można utworzyć diag. packege. Błąd przy udostępnianiu pakietu diagnostycznego. Informacje diagnostyczne zostały wysłane pomyślnie. Pakiet diagnostyczny utworzono i udostępniono. Dołącz następujący identyfikator do raportu błędu: Nie udało się zaimportować gier {0}. Nie udało się zaimportować emulowanych gier {0}. Nie można wyszukać gier według wybranego profilu emulatora. Profil nie zawiera żadnych rozszerzeń plików ani platform. Nie udało się uruchomić Playnite. Zamknij wszystkie działające procesy programu i spróbuj ponownie. Nie udało się zastosować motywu "{0}", profil kolorów "{1}" {2} Nie można otworzyć linku, URL ma nieprawidłowy format. Nie udało się uruchomić aplikacji. Nie udało się zainicjować komponentu widoku internetowego. Playnite nie może kontynuować procesu uruchamiania. Więcej informacji na https://playnite.link/cefstartup Nie można zaimportować emulatorów z powodu braku lub uszkodzenia pliku z konfiguracją. Nie udało się uruchomić akcji menu. Edytuj szczegóły gry Obraz URL Dodaj link Dodaj ROM Zapisz zmiany Zastosuj zmiany pola edytowanych gier. Dodaj polecenie Usuń polecenie Usuń polecenie "Graj" Dodaj Skanuj folder Wykryj zainstalowane Przeglądaj Otwórz Ustawienia profilu Nazwa gry nie może być pusta. Katalog śledzenia akcji gry nie może być pusty. Nazwa gry nie może być pusta przed wyszukiwaniem metadanych. Nieprawidłowe dane gry Wprowadź poprawny adres zaczynający się od https:// Wybierz URL Nie udało się pobrać metedanych: {0} Błąd pobierania Wyczyść filtry Konto prywatne Publiczne konto Klucz API Błąd uruchamiania Błąd motywu Wyczyść wszystko Instalowanie Odinstalowywanie Włączanie Uruchomiona Błędny URL Nic nie rób Minimalizuj Przywróć okno Przywróć okno tylko w przypadku uruchomienia z interfejsu użytkownika Zamknij Zmień Zaawansowane Stopień ukończenia Stopień ukończenia Ocena Krytycy Gracze Skrypty gry Skrypty aplikacji Skrypty Wtyczki Źródła metadanych Rozszerzenia ID rozszerzenia Przeładuj skrypty Interaktywne SDK PowerShell Wszystkie skrypty przeładowano pomyślnie. Nie znaleziono gier według określonych kryteriów wyszukiwania lub filtra Nie znaleziono gier Wyłącz tryb pełnoekranowy Zamknij aplikację Biblioteki Aktualizuj wszystko Autor: Wersja: Zaktualizowano: Moduł: Biblioteka Statystyki Wszystko Nic Powiadomienia Szerokość Wysokość Rozmiar Mała Normalna Duża Większa Największa Domyślne Wybierz Zaznacz wszystkie Odznacz wszystko Pierwszy Losowo Wybór użytkownika Wczytaj więcej Przezroczyste Zwiń Rozwiń Zwiń wszystkie Rozwiń wszystkie Inne Motywy Parametry emulatora Wbudowane parametry Własne parametry Dodatkowe parametry emulatora Nadpisz parametry emulatora Polecenie "Graj" Wybierz metadane do importowania Wybierz gry do importu Wyszukaj metadane Dostępna aktualizacja Zmiany od ostatniej aktualizacji Pobierz i zainstaluj aktualizację Szukaj aktualizacji Błąd aktualizacji Błąd sprawdzania nowej wersji. Aplikacja jest aktualna. Nie udało się pobrać i zainstalować aktualizacji. W tle jest wciąż aktywne zadanie. Czy chcesz je anulować i kontynuować aktualizację? W tle jest wciąż aktywne zadanie. Czy chcesz je anulować i zamknąć Playnite? W tle jest wciąż aktywne zadanie. Przełączenie trybu spowoduje anulowanie zadania, czy na pewno chcesz przełączyć? Dostępna jest aktualizacja aplikacji Przeładuj listę motywów Zatwierdź wybrany motyw Oglądaj zmiany w plikach Automatycznie zastosuj motyw, gdy plik źródłowy się zmieni Środowisko wykonawcze skryptu Skrypt do wykonania przed uruchomieniem gry Skrypt do wykonania po wyjściu z gry Skrypt do wykonania po uruchomieniu gry Wykonaj przy starcie aplikacji Wykonaj przy zamknięciu aplikacji Skrypt uruchamiania gry Skrypt uruchomionej gry Skrypt zatrzymanej gry Wykonanie skryptu globalnego Ogólne Filtrowane Aktualny Nowe Testuj skrypt Pokaż tylko wybrane elementy. Zapisz jako domyślne Dodaj do ulubionych Usuń z ulubionych Ukryj Usuń z ukrytych Włącz obsługę HDR Wyłącz obsługę HDR Edytuj Oblicz rozmiar instalacji Oblicz rozmiar instalacji (wszystkie gry) Oblicz rozmiar instalacji (tylko brakujące dane) Rozmiar instalacji Dodaj do kolekcji Ustaw stan ukończenia Usuń Graj Instaluj Opcje gry Szczegóły Odinstaluj Otwórz folder instalacji Utwórz skrót na pulpicie Otwórz instrukcję Więcej Zarządzane przez wtyczkę biblioteki Proces uruchamiania będzie zarządzany przez wtyczkę biblioteki obsługującą tę grę. Brak żadnych informacji o grze "{0}" na danej stronie. Wskazówka: Możesz użyć zaawansowanego sposobu pobierania metadanych dla pojedynczej gry za pomocą opcji z menu "Edytuj". Niedostępne, gdy niektóre procesy są w toku. W tekście opisu można umieścić kod HTML Czas gry jest liczony w sekundach Rozmiar instalacji jest wskazany w bajtach. Data premiery musi być w formacie 'rok-miesiąc-dzień'. Wartości Miesiąca i Dnia mogą zostać pominięte. Wartości od 0 do 100 lub puste bez oceny. Playnite jest rozwijany dzięki wsparciu patronów i członków Ko-fi: Programowanie, tłumaczenie oraz inni współtwórcy (kolejność przypadkowa): Anulować monitorowanie gry? Monitorowanie instalacji jest obecnie uruchomione, czy chcesz anulować proces i przywrócić grę do poprzedniego stanu? Monitorowanie wykonania gry jest aktualnie uruchomione, czy chcesz anulować proces i przywrócić grę do poprzedniego stanu? Czas gry Ostatnio uruchomiono {0}d {1}g {2}m {0}h {1}m {0}m {0} sekund(y) Włączanie trybu pełnoekranowego Otwieranie trybu pełnoekranowego… Ładowanie biblioteki gier… Obliczanie rozmiaru instalacji… Obliczanie rozmiaru instalacji {0}… Nie udało się zainstalować pliku skryptu. Skrypt pomyślnie zainstalowano. Instaluj skrypt Błąd skryptu Nie udało się uruchomić funkcji rozszerzenia. Otwórz folder metadanych Oblicz Automatycznie oblicza rozmiar instalacji przy użyciu Romów, jeśli gra taki posiada lub przy użyciu katalogu instalacyjnego, jeśli został ustawiony Klient {0} nie jest zainstalowany. Klient {0} zostanie uruchomiony. Zaloguj się, a następnie zamknij ten komunikat. Oczekiwanie na zalogowanie się użytkownika. Zamknij to, gdy się skończy… Nie odnaleziono katalogu instalacyjnego gry. Nieprawidłowa konfiguracja polecenia. Rozwiązywanie problemów z synchronizacją konta Rozwiązywanie problemów Zmień nazwę elementu Dodaj nowy element Wprowadź nazwę Podaj nową nazwę Mniej niż godzinę 1 do 10 godzin 10 do 100 godzin 100 do 500 godzin 500 do 1000 godzin 1000+ Aby ukończyć instalację Playnite, należy go ponownie uruchomić. Czy chcesz teraz uruchomić ponownie Playnite? Rozszerzenie zostało niepoprawnie spakowane. Motyw został niepoprawnie spakowany. Rozszerzenie "{0}" nie zostało prawidłowo wczytane. Nie można załadować "{0}" rozszerzeń, obecna wersja Playnite nie jest wspierana. Motyw "{0}" nie został prawidłowo wczytany. Nie można załadować "{0}" motywów, obecna wersja Playnite nie jest wspierana. Rozszerzenie nie zostało prawidłowo wczytane. Motyw nie został prawidłowo wczytany. Motyw/Rozszerzenie używa niewspieranego API. Zainstalowano poprawnie. Czy zainstalować rozszerzenie? Ogólny Nie udało się zainstalować rozszerzenia "{0}". Nie udało się zainstalować rozszerzenia. {0} Czy chcesz zainstalować nowe rozszerzenie? {0} Stworzone przez {1} Wersja {2} Czy chcesz zaktualizować rozszerzenie "{0}? Obecna wersja: {1} Nowa wersja: {2} Nie udało się zainstalować motywu. {0} Czy chcesz zainstalować nowy motyw? {0} Stworzone przez {1} Wersja {2} Czy chcesz zaktualizować motyw "{0}? Obecna wersja: {1} Nowa wersja: {2} Za chwilę opuścisz Playnite i przejdziesz do następującej strony internetowej przy użyciu domyślnej przeglądarki internetowej. Czy chcesz kontynuować? {0} Wybrany obraz jest zbyt duży, by zapewnić optymalną wydajność. Użycie bardzo dużych obrazów może skutkować gorszą responsywnością UI i większym zużyciem pamięci. Zalecana rozdzielczość maksymalna: Ikony: {0} megapiksele Okładki: {1} megapiksele Tła: {2} megapiksele Ostrzeżenie dotyczące wydajności Nie pokazuj ponownie Plik z rozszerzeniem {0} nie jest kompatybilny. Niekompatybilne rozszerzenie pliku Wybrany plik obrazu może być zbyt duży, aby uzyskać optymalną wydajność. Czy na pewno chcesz odinstalować wybrany motyw? Dezinstalacja będzie zakolejkowana do następnego uruchomienia aplikacji. Wbudowane motywy nie mogą być odinstalowane. Ten motyw nie wspiera obecnej wersji Playnite. Czy na pewno chcesz odinstalować wybraną wtyczkę? Dezinstalacja będzie zakolejkowana do następnego uruchomienia aplikacji. Wbudowane wtyczki nie mogą być odinstalowane. Ta wtyczka nie wspiera obecnej wersji Playnite. Ścieżka instalacji Katalog danych Tworzenie pakietu diagnostycznego... Wysyłanie pakietu diagnostycznego... Importuj plik... Co to jest? Czy na pewno chcesz to zrobić? Całkowity czas gry Średni czas gry Najdłuższy czas gry Całkowity rozmiar instalacji Przegląd Pasek boczny Wyświetl w panelu bocznym Zresetuj ustawienia Wszystkie ustawienia aplikacji zostaną zresetowanie do wartości domyślnych, oprócz: -Lokalizacji bazy danych -Listy wyjątków -Ustawień rozszerzeń, łącznie z biblioteką integracji Aplikacja musi zostać uruchomiona ponownie aby zakończyć operację. Na pewno chcesz zresetować ustawienia? Dla programistów Zewnętrzne rozszerzenia Wprowadź pełną ścieżkę folderu. Osiągnięcia Forum Aktualności Strona w sklepie Wstępna konfiguracja nie została ukończona. Playnite zostanie uruchomiony ponownie w trybie pełnoekranowym, aby zakończyć konfigurowanie. Ostatnio uruchomione Ulubione Najczęściej grane Wszystko Zastosowano filtry. Zastosowano dodatkowe filtry. Wyniki wyszukiwania dla: Element o tej nazwie już istnieje. Zawęź wybór do bieżącego filtra Wybierz inną Dodatki Zainstalowano Ustawienia rozszerzeń Przeglądaj Aktualizacje Aktualizacje ({0}) Zarządzanie zainstalowanymi rozszerzeniami, wraz z ich ustawieniami, zostało przeniesione do nowego menu "Dodatki". Wszystkie zainstalowane rozszerzenia bibliotek integracji mogą być tutaj konfigurowane. Jeśli chcesz zainstalować lub odinstalować dodatkowe integracje, użyj opcji "Dodatki" z głównego menu. Motywy pulpitu Motywy pełnoekranowe Wyszukiwanie... Dodatek nie jest kompatybilny z tą wersją Playnite. Nie udało się pobrać pakietu instalacyjnego dodatków. Nie udało się pobrać manifestu instalacyjnego dodatków. Wymagane jest ponowne uruchomienie aplikacji, aby zastosować oczekujące zmiany. Ten dodatek został zakolejkowany do instalacji. Instaluj Ponowna instalacja Odinstaluj Zainstalowano Nie znaleziono nowych aktualizacji dodatku. Aktualizacja dodatków Lista zmian jest niedostępna Zaplanowana instalacja Pobieranie nieudane Licencja odrzucona Pobieranie {0}... Wyszukiwanie aktualizacji dodatków... Wyszukiwanie aktualizacji programu… aktualizacja jednego lub większej ilości dodatków jest dostępna. Wybierz elementy do aktualizacji Instancja rozwoju rozszerzenia {0} umowa licencyjna Akceptuj Odrzuć Uwzględnij działania związane z integracją biblioteki Wybierz akcję Tryb śledzenia Ścieżka śledzenia Wstępne opóźnienie śledzenia Śledzenie częstotliwości Link Plik Emulator Skrypt Domyślnie Proces Folder Oryginalny proces Nazwa procesu Rejestruj powiadomienia śledzenia Powyższe zmiany nadpiszą dane dla wszystkich wybranych gier! Brak Jednolita Tylko obiekty Tylko początek i koniec Czułość przewijania Płynne przewijanie Szybkość animacji Usunąć element? Jesteś pewien, że chcesz usunąć ten element? Pokaż przyciski na panelu górnym: Ustawienia widoku głównego Ustawienia grupowania Ustawienia sortowania Szablony filtrowania Położenie elementów wtyczki Szerokość separatora sekcji Przenieś przycisk głównego menu do panelu bocznego Panel eksploratora Losowa gra Wyświetla losowy selektor gier Wybierz losową grę z widoku Zapisz ustawienia grupowania i sortowania Pokaż jako szybki filtr w trybie pełnego ekranu W minionych 7 dniach W minionych 31 dniach W minionych 356 dniach Więcej niż 365 dni temu Konfiguruj Zapisz szablon Minimalizuj po uruchomieniu gry Minimalizuj Playnite po uruchomieniu gry. Zablokowanie tej opcji może prowadzić do problemów z grami wymagającymi pełnej uwagi podczas uruchamiania! Rozmiar czcionki Mały rozmiar czcionki Włącz obsługę API kontrolera gier Wsparcie dla kontrolera gry Jeśli jest wyłączona, Playnite nie będzie akceptować żadnych wejść kontrolera gry. Wyłącz, jeśli używasz narzędzi, które tłumaczą wejścia kontrolera gry na wejścia myszy/klawiatury i otrzymujesz podwójne wejścia w Playnite Wyświetlaj w menu głównym: Odwrócone przypisanie przycisków X/A w widoku głównym Zamienia przypisania przycisków dla uruchamianej gry i pokazuje szczegóły gry w widoku głównym. Zamień powiązanie przycisku potwierdzenia/anulowania Odwraca przypisania przycisków A/B do potwierdzenia i anulowania. Tylko główny kontroler Akceptuj dane wejściowe z podstawowego kontrolera tylko wtedy, gdy włączone. Przycisk przewodnika wyzwala Playnite Głośność interfejsu Głośność tła Wycisz, kiedy jest w tle Nie można zainicjować interfejsu audio. Wyjście API API użyty jako wyjście audio. Zmień, jeśli masz problemy z dźwiękiem. Ogólny Wygląd Audio Układ Menu Wejście {0} jest uruchamiane.. {0} jest uruchomione... Caps Spacja Skaler renderowania obrazu Opcjonalnie Zbalansowane Jakość Jakość: Najlepsza jakość, niska wydajność, wysokie zużycie pamięci. Zbalansowane: Dobra jakość, wysoka wydajność, niskie zużycie pamięci. Opcjonalnie: Lepsza jakość, średnia wydajność, niskie zużycie pamięci. Wybierz plik... Wybierz folder... Skrypt startowy Proszę wziąć pod uwagę, że zarówno rozszerzenia jak i motywy mogą w dużym stopniu wpływać na wydajność, stabilność i bezpieczeństwo Playnite. Jeżeli doświadczasz problemów po instalacji motywu lub rozszerzenia, spróbuj je zablokować/odinstalować aby sprawdzić czy są źródłem problemu. Wybierz podczas uruchomienia Wybierz przy uruchamianiu Wbudowane profile Wbudowany profil Profile niestandardowe Profil niestandardowy Obsługiwane przez wbudowany skrypt Specyfikacja emulatora Specyfikacja platformy Specyfikacja regionu Wykonaj przed uruchomieniem emulatora Wykonaj po uruchomieniu emulatora Wykonaj po zakończeniu emulacji Nie znaleziono pliku wykonawczego emulatora. Nie znaleziono specyfikacji emulatora. Nie znaleziono skryptu uruchamiającego emulatora. Podziel jako oddzielne gry Połącz w jedną grę Ustaw platformę Ustaw region Skanuj folder Skanuj konfigurację Wyklucz wzorce ze skanu sumy kontrolnej Pliki spełniające dany wzorzec nie będą skanowane pod kątem sumy kontrolnej oraz zostaną dopasowane po nazwie pliku. Więcej informacji dostępne na stronie pomocy emulatora. Skanuj przy pomocy emulatora Należy podać nazwę przy zapisywaniu nowej konfiguracji. Emulator lub profil emulatora nie jest ustawiony. Skanowany katalog nie został określony lub nie istnieje. Konfiguracja skanowania nie została ustawiona poprawnie. Zawrzyj w automatycznym skanowaniu zbiorczym Skanowanie folderu w poszukiwaniu emulatorów nie powiodło się. Skanowanie folderów w poszukiwaniu emulowanych gier nie powiodło się. Ukryj zaimportowane Profile do importu: Automatyczne skanowanie konfiguracji Zapisz jako automatyczne skanowanie konfiguracji Zapisz konfiguracje do dalszego użytku w trakcie aktualizacji biblioteki. Zapisane konfiguracje mogą być zarządzane przez menu "Konfiguracja Emulatorów". Importuj używając ścieżek względnych Jeśli to możliwe, zaimportuj pliki gry używając ścieżek względem folderu instalacyjnego Playnite lub folderu instalacyjnego emulatora. Skanowanie podfolderów Zeskanuj wewnątrz archiwów Scal powiązane pliki Scal powiązane pliki gry, takie jak indywidualne dyski gry, pod jednym wpisem do gry. Dodaj skaner Dodaj zapisany skaner Rozpocznij skanowanie Dodaj konfiguracje skanowania emulatorami do skanowania konkretnych folderów. Upewnij się, że emulatory zostały poprawnie skonfigurowane przed importowaniem gier (za pomocą menu Biblioteka -> Konfiguruj Emulatory). Status domyślny przypisany do nowo dodanych gier. Status przypisany do gier uruchomianych po raz pierwszy Nie udało się znacjonalizować skryptu uruchomienia PowerShell. Jeżeli używasz Windows 7, spróbuj przeinstalować PowerShell 5.1 aby naprawić problem. Szablon filtrowania o podanej nazwie już istnieje. Zaktualizować przy pomocy nowych ustawień? Automatyczne uzupełnianie brakujących nazw sortowania dla gier dodanych lub edytowanych zbiorczo Podczas edycji gry, dodawania gier poprzez aktualizację biblioteki, skanowania folderu emulatora lub normalnego skanowania folderu program automatycznie wypełni pole „Nazwa sortowania” lepszym, łatwiejszym do sortowania oznaczeniem nazwy gry. Na przykład „The Witcher 3” otrzyma nazwę sortowania „Witcher 03”. Nie zostanie ustawiona nazwa sortowania, która różni się od nazwy gry, przy czym automatycznie aktualizowane będą tylko puste nazwy sortowania. Te słowa zostaną usunięte z początku automatycznie wypełnionej wartości Nazwa Sortowania: Użyj tego, aby zignorować słowa na początku ciągu do sortowania. Wartością domyślną jest "The", "An" i "A". Wypełnij Nazwę Sortowania dla gier, które go nie mają Sortowanie Wypełnianie wartości Nazwy Sortowania Wykryto uruchomioną usługę Nahimic na Twoim systemie. Usługa znana jest z tego, że powoduje poważne problemy z renderowaniem w Playnite (i innych aplikacjach). Jeżeli napotkasz jakiekolwiek problemy z grafiką lub inne problemy z renderowaniem w Playnite, zalecamy wyłączenie lub całkowite odinstalowanie usługi Nahimic. Więcej informacji na stronie https://playnite.link/nahimicsucks Playnite jest uruchomiony z uprawnieniami administratora. Nie jest to zalecane, ponieważ przyznaje to uprawnienia dla wszystkich zainstalowanych rozszerzeń i wszystkich gier/aplikacji uruchamianych z Playnite! Więcej informacji na https://playnite.link/adminfaq Pokaż ostrzeżenia jeżeli Playnite jest uruchomiony z uprawnieniami administratora Uzyskaj rzeczywisty rozmiar na dysku przy obliczaniu rozmiaru gier Jeśli włączone, skanowanie będzie wolniejsze, ale uzyska rzeczywisty rozmiar plików używanych na dysku. Jeśli wyłączone, skanowanie będzie szybsze , ale użyje rozmiaru samych plików. Poniższe rozszerzenia zostały zgłoszone jako potencjalnie problematyczne mający duży wpływ na stabilność/wydajność lub problemy z bezpieczeństwem. Zalecamy, ich odinstalowanie: {0} Wykluczenie online plików ze skanowania Pliki przechowywane w chmurze nie będą skanowane i zaimportowane, dopóki nie będą dostępne lokalnie. Wsparcie tylko dla: Google Drive, DropBox, OneDrive Skanuj używając uproszczonej metody bez zawartości plików Pliki zostaną zaimportowane, wykorzystując mniej dokładną metodę, która nie wymaga pobierania zawartości pliku i zapisania go lokalnie Zastosuj dla wszystkich Zastąp stan instalacji Po ustawieniu Playnite zignoruje stan instalacji (w tym katalog instalacyjny) ustawiony przez wtyczkę integracyjną, która importuje tę grę. Ta opcja może nie działać w pełni z wtyczkami, które używają konkretnej metody importu gry, chyba że wezmą również pod uwagę tę opcję nadpisania. Tylko ręcznie Raz dziennie Raz w tygodniu Przy każdym uruchomieniu Sprawdź aktualizacje programu Sprawdź aktualizację dodatków Aktualizuj bibliotekę Skanuj foldery emulacji Uwzględnij ukryte gry Edytuj pola Zaznacz / Odznacz wszystko Otwórz Aktywuj Przypisz Wpisz, aby wyszukać | Pomoc: [F1] Przydatne funkcje: • # – wyświetla listę dostępnych poleceń • / – wyświetla listę dostępnych dostawców wyszukiwarek/wtyczek • Wprowadzenie słowa kluczowego i spacji przełącza do wyników • TAB – przełącza akcję • ENTER – aktywuje wybraną akcję • SHIFT+ENTER – otwiera menu pozycji Uwzględnij niezainstalowane gry Uwzględnij ukryte gry Niezainstalowane gry są dołączone Niezainstalowane gry są wykluczone Ukryte gry uwzględnione Wykluczone ukryte gry Graj lub zainstaluj Szczegóły Menu gry Edytuj grę Otwórz wyszukiwarkę Pole wyszukiwania Przycisk szukaj Podstawowa akcja gry Drugorzędna akcja gry CTRL-F otwiera wyszukiwanie globalne zamiast podświetlenia pola wyszukiwania Zapisz ustawienia filtra gry pomiędzy sesjami wyszukiwania Dostawcy wyszukiwania Domyślne słowo kluczowe Własne słowo kluczowe Skrót systemowy Wyszukiwanie Playnite Ustawienia rozszerzenia Wykluczenia Wykluczone pliki względem folderu skanowania Wykluczone foldery względem folderu skanowania Dodaj plik do listy wykluczonych Dodaj folder do listy wykluczonych Wykluczenia mogą być dodawane tylko do zapisanych konfiguracji skanera. Wykluczenia zostały dodane do skanera "{0}. Nadpisz platformę Kiedy ustawiony, skaner przypiszę tę platformę do wszystkich gier, nadpisując automatycznie wykryte platformy. Dołącz polecenia w domyślnym wyszukiwaniu Po wyłączeniu, polecenia nie będą uwzględniane w domyślnym wyszukiwaniu, dopóki nie zostanie użyty prefiks #. Użyj analizy podobieństw tekstu w filtrze nazw Gdy włączone, filtr nazw będzie odpowiadał nazwom gry w taki sam sposób, jak wyszukiwanie globalne. Ścisłe dopasowanie może być wymuszone na pojedynczym przypadku używając prefiksu "!". Pola wyświetlane dla wyników gry: Ukryty Status Tworzenie kopii zapasowej zostało anulowane Tworzenie kopii zapasowej nie powiodło się Wystąpił błąd podczas tworzenia kopii zapasowej Tworzenie kopii zapasowej... Przywracanie danych z kopii zapasowej... Przywracanie danych z kopii zapasowej zakończone niepowodzeniem. Ustawienia Biblioteka gier Media biblioteki gier Zainstalowane rozszerzenia Dane rozszerzeń Zainstalowane motywy Wybierz dane do przywrócenia z określonego pliku kopii zapasowej. Playnite automatycznie uruchomi się ponownie, aby rozpocząć proces przywracania kopii zapasowej. Wybierz elementy, które mają być dołączone do kopii zapasowej danych. Ustawienia aplikacji i dane biblioteki gier są domyślnie włączone. Playnite automatycznie uruchomi się ponownie, aby rozpocząć proces tworzenia kopii zapasowej. Automatyczna kopia zapasowa danych Częstotliwość automatycznej kopii zapasowej Folder kopii zapasowych Rotacyjne kopie zapasowe Dołącz dodatkowe dane: Folder kopii zapasowej musi być określony, jeśli funkcja automatycznej kopii zapasowej jest włączona. Pokaż powiadomienia tylko o wydaniach łatki Gdy włączone, tylko aktualizacje dostępne dla aktualnie zainstalowanego głównego wydania spowodują powiadomienie o aktualizacji. Nowe główne wydania nie będą skutkować powiadomieniem o aktualizacji. Użyj dat względnych dla ostatniego tygodnia Użyj względnych dat w formacie "Dziś", "Wczoraj" itp. jeśli data nie jest starsza niż tydzień. Określony format daty będzie używany dla wszystkich innych dat. Wyszukiwanie obrazów w Internecie Ciąg wyszukiwania obrazów ikon Ciąg wyszukiwania obrazu okładki Ciąg wyszukiwania obrazów w tle Pobieranie informacji o dodatku… Brak źródła metadanych Otwórz ustawienia akcji Użyj ustawień skanera Wybierz profil przy uruchamianiu Wybierz emulator przy starcie Automatyczny Zawsze włączone Zawsze wyłączone Dostępność (czytnik ekranu) Menu aplikacji Menu gry Folder programu Katalog danych użytkownika Wykryto uszkodzenie pliku biblioteki, Playnite zostanie wyłączony. Otwórz nowy problem na stronie GitHub Playnite z prośbą o naprawienie uszkodzeń w plikach. Czy chcesz zapisać zmiany? Instalacja przenośna Nie wykryto kontrolerów ================================================ FILE: source/Playnite/Localization/pt_BR.xaml ================================================  Português Brasileiro Idioma do Playnite Sair Filtro ativado Filtro desativado Filtros adicionais Filtros Filtro Dados Inválidos Salvar alterações? Site oficial em www.playnite.link Código fonte no GitHub Criar pacote de diagnóstico Enviar informações de diagnóstico Sobre o Playnite Criado por Josef Němec Atribuir categorias Definir categorias Adicionar categoria Selecionada - Atribui a categoria Não selecionada - Remove a categoria Indeterminada - Nenhuma alteração (quando editando vários jogos) Sem categoria Sem plataforma Ops! Algo deu errado… Ocorreu um erro irrecuperável. Se você deseja nos ajudar à arrumar este problema, por favor, explique brevemente as ações realizadas antes do erro, e envie os dados de diagnóstico. Se você estiver online, o pacote será enviado para os servidores da Playnite para análise. Ou também, você pode clicar em "Relatar Falha" e criar um tópico no GitHub e relatar o travamento manualmente. Obrigado pela sua ajuda. A extensão "{0}" causou um erro irrecuperável. Recomendamos que você salve o arquivo de log e relate o problema para o desenvolvedor da extensão. Se os problemas persistirem, desabilite a extensão. A extensão "{0}" causou um erro irrecuperável. Recomendamos que você relate o problema para o desenvolvedor da extensão. Se os problemas persistirem, desabilite a extensão. Uma extensão ou tema desconhecido causou um erro irrecuperável. Ocorreu um erro irrecuperável. Se você deseja nos ajudar a corrigir este problema, por favor envie informações de diagnóstico. Obrigado. Desabilitar extensão Salvar arquivo de log Enviar diagnóstico Relatar falha Reiniciar o Playnite Reiniciar no modo de segurança Desabilitando todas as extensões de terceiros e usando o tema padrão. Sair do Playnite Ações executadas antes do travamento (em inglês): Gerenciador da biblioteca Remover jogo(s)? Não foi possível remover - Jogo ou instalador em execução. Não foi possível desinstalar - Jogo em execução. Tem certeza que deseja remover {0}? Tem certeza de que deseja remover estes {0} jogos? Tem certeza que deseja remover {0}? Selecionar a opção "adicionar a lista de exclusão" impedirá que o jogo seja importado novamente na próxima vez que a biblioteca for atualizada. Tem certeza de que deseja remover estes {0} jogos? Selecionar a opção "adicionar a lista de exclusão" impedirá que o jogo seja importado novamente na próxima vez que a biblioteca for atualizada. Você tem certeza de que deseja remover {0} entradas que atualmente não estão em uso? Nenhum campo não utilizado encontrado. Sim (adicionar à lista de exclusão) Existem alterações não salvas nesta seção Atualizando o formato da biblioteca de jogos… Falha ao atualizar o banco de dados, não foi possível abrir o arquivo. Não foi possível atualizar a biblioteca. Pelo menos {0} MBs de espaço livre são necessários para continuar. Erro do jogo Não foi possível iniciar o jogo. "{0}" não foi encontrado no banco de dados. Não foi possível iniciar o jogo: {0} Não foi possível iniciar a ação: {0} Não foi possível abrir o local do jogo: {0} Não foi possível detectar o tamanho da instalação do jogo: {0} Erro ao procurar o tamanho da Instalação Ocorreram {0} erros durante a varredura do tamanho da instalação Falha ao criar o atalho: {0} Falha ao abrir manual: {0} Não foi possível instalar o jogo: {0} Não foi possível desinstalar o jogo: {0} Nenhuma ação válida de inicialização do jogo foi encontrada. Ao usar ações do emulador, certifique-se de que as definições da plataforma correspondem entre a configuração do jogo e o emulador. Implementação da instalação indisponível. A extensão da biblioteca responsável por este jogo está desabilitada ou não foi instalada. O download de metadados oficial não está disponível. Nenhum jogo foi selecionado. A execução do script da ação do jogo falhou. A execução do script da aplicação falhou. A execução do script da ação global falhou. Execução do script do emulador falhou. A execução do script de ação de jogar falhou. PowerShell 3.0 ou mais recente não está instalado. Não foi possível determinar como iniciar o jogo. Habilitado Desabilitado Remover Remover não utilizadas Renomear Copiar Adicionar Ícone padrão Imagem de capa padrão Imagem de fundo padrão Concluir Próximo Anterior CONCLUÍDO VOLTAR LIMPAR Limpar Recusar Recusar tudo Importar Nome Autor Módulo Série Versão Última sessão Mais jogado Vezes jogado Tamanho da Instalação Pasta Notas Adicionado Data de adição Modificado Data de modificação Site Caminho OK Salvar Fechar Cancelar Confirmar Reiniciar Sim Não Boas-vindas Usuário local Geral Mídia Links Instalação Ações Baixando… Baixando mídia… Carregando… Tipo Perfil Perfis Remover Baixar Buscar Resolução: Qualquer Ampliar/Reduzir Visualização de lista Capas Visualização de grade Visualização de detalhes Personalizado URL Agradecimentos especiais Licença Colaboradores Saindo do Playnite… Hoje Ontem Segunda-feira Terça-feira Quarta-feira Quinta-feira Sexta-feira Sábado Domingo Na última semana No último mês No último ano Há mais de um ano 0 a 100MB 100MB a 1GB 1GB a 5GB 5GB a 10GB 10GB a 20GB 20GB a 40GB 40GB a 100GB 100GB ou mais Importação concluída com sucesso. Todos os jogos Id do jogo Id no banco de dados Predefinições Coluna Colunas Linha Linhas Não foi possível extrair ícone da ação Jogar. Não há ação do tipo de Arquivo presente. Baixar somente metadados ausentes Habilitar esta opção fará com que novos metadados não sejam baixados para os campos já preenchidos. Seleção de jogos Selecione quais jogos devem ser atualizados com novos metadados: Todos os jogos do banco de dados Todos os jogos filtrados atualmente Todos os jogos selecionados Nenhum campo de metadados selecionado Não há campos de metadados selecionados para download. Por favor, selecione pelo menos um e ative pelo menos um provedor de metadados para ele. Loja oficial IGDB Selecione quais campos devem ser preenchidos automaticamente pelo Playnite e quais fontes devem ser usadas para obter os dados. Por favor, considere clicar no logo acima e contribuir com informações para o banco de dados do igdb.com para melhorar os dados utilizados pelo Playnite. Baixando metadados… Importando jogos instalados… Importando jogos do(a) {0}… Importando jogos emulados de {0}… Baixando atualizações da biblioteca… Verificando o tamanho dos jogos na biblioteca… Verificando tamanho dos jogos importados… Atualização da biblioteca concluída Liberando recursos… Configuração Configurações… Plataformas e emuladores Configurar emuladores… Gerenciador da biblioteca… Ferramentas Baixar metadados… Ferramentas… Configurar Integrações… Abrir cliente de terceiros Clientes de terceiros Recarregar lista de jogos Cancelar atualização de biblioteca Atualizar pastas emuladas Adicionar jogos Manualmente… Escanear automaticamente… Jogo emulado… Aplicativo da Microsoft Store… Sobre o Playnite Enviar comentários Mudar para o modo tela cheia Links Ajuda Apoie-nos no Patreon Apoie-me no Ko-fi Manual do usuário Documentação do SDK Reiniciar o sistema Desligar o sistema Suspender o sistema Hibernar o sistema Bloquear Sistema Logout de Usuário Selecionar um jogo aleatório Campos de jogo exibidos no painel de detalhes: Espaçamento entre itens Desenhar fundo nos itens da grade Largura das bordas no esquema de grade Fonte do ícone do jogo ausente Fonte da capa do jogo ausente Fonte do fundo do jogo ausente Espaçamento vertical nos detalhes do jogo Posição dos detalhes na visualização de grade Posição da lista de jogos na visualização de detalhes Desenhar separador entre painéis Altura da capa do jogo Altura dos ícones na lista de jogos Fonte do aplicativo Fonte monoespaçada Posição do painel de filtros Posição do painel de navegação Renderização da arte da capa Proporção correta As seguintes opções também afetam a renderização lado-a-lado no modo tela inteira! Modo de ampliação Caixa do DVD Epic Games Store GOG Galaxy 2.0 IGDB Quadrado Banner da Steam Capa vertical da Steam Twitch * Requer reinicialização para aplicar Configurações Geral Painel superior Aparência Detalhes do jogo Esquema Avançado Tela cheia Entrada Desempenho Metadados Atualizando Busca Backup Dados da biblioteca de backup Restaurar Backup de Dados Importar alterações na biblioteca automaticamente Local do arquivo do banco de dados inválido, o caminho correto do arquivo deve ser configurado. O nome da conta não pode estar vazio. Baixar metadados depois de importar jogos Abrir o Playnite minimizado Abrir o Playnite quando o seu computador for iniciado Iniciar minimizado para a bandeja Falha ao registrar o Playnite para ser executado ao iniciar o sistema. Abrir no modo tela inteira Carregamento de imagens assíncrono Melhora a suavidade da rolagem nas listas de jogos, mas as imagens podem carregar mais lentamente. Mostrar o nome do jogo quando não houver capa Mostrar o nome dos jogos na visualização de grade Escurecer jogos não instalados Mostrar o ícone dos jogos na visualização de detalhes Mostrar um contador de itens nas descrições de grupos Mostrar somente campos selecionados nos painéis de filtro e explorar Desabilitar a aceleração de hardware Use se experienciar travamentos ou problemas com a interface. Mostrar jogos escondidos na lista de inicio rápido Afeta a lista de opções e lista de menu de bandeja. Número de itens na lista de inicialização rápida Usar a imagem de fundo do jogo como fundo da janela Desfocar fundo Alta qualidade Escurecer fundo Mostrar na visualização de grade Tema Perfil do tema Tema em tela cheia Perfil do tema em tela cheia Local do banco de dados Estado da sessão: Configurações do Playnite Limpar o cache web Pode resolver problemas encontrados ao vincular contas. Mostrar ícone da bandeja de sistema Minimizar Playnite para a bandeja de sistema Minimizar Playnite para a bandeja de sistema quando a janela for fechada Ao iniciar um jogo: Ao fechar um jogo: Formatar tempo jogado para indicar o número de dias jogados Formatos de datas: Todas as contas de serviços configuradas serão desvinculadas. Será necessário reiniciar o aplicativo, você deseja continuar? Limpar o cache? É necessário reiniciar o Playnite para aplicar um novo tema Baixar mais temas Como criar um novo tema Obter mais extensões Criar uma nova extensão Ajude-nos a traduzir Playnite Playnite precisar ser reiniciado para aplicar as novas configurações. Reiniciar agora? Reiniciar cancelará qualquer tarefa ativa (downloads) atualmente em andamento. Reiniciar o Playnite? Playnite não pode mover os arquivos da biblioteca automaticamente. Você deve mover/copiar os arquivos manualmente antes de alterar o local. Se não houver nenhuma biblioteca no local selecionado, uma nova será criada. O novo local do banco de dados não será utilizado até Playnite ser reiniciado. O tempo de jogo não será gravado se a ação de fechar for definida. Número de linhas Número de colunas Número de linhas na visualização de detalhes Mostrar imagem de fundo na tela principal Não se aplica retroativamente a jogos existentes sem baixar novamente os metadados. Importar tempo jogado de jogos na biblioteca: Configura quando o Playnite deve importar o tempo de jogo relatado pelos plugins da biblioteca para jogos no banco de dados do Playnite. O suporte pelos plugins da biblioteca responsáveis por lidar com o(s) jogo(s) é necessário para poder usar este recurso. Sempre: Importe o tempo de jogo para jogos recém-importados e para jogos existentes no banco de dados do Playnite. Somente para jogos recém-importados: Importe o tempo de jogo somente para novos jogos importados. Nunca: Não importe o tempo de jogo sob nenhuma circunstância. Sempre Somente para jogos recém-importados Nunca Habilitar suporte para controles no modo janela Botão painel abre o modo tela cheia Download automático de metadados para jogos importados recentemente. Monitor de destino Sempre usar monitor principal Mostrar títulos dos jogos Mostrar estado da bateria Mostrar porcentagem da bateria Mostrar relógio Esconder cursor do mouse Instalado apenas nos filtros rápidos Aparência dos botões Esquema Rolagem horizontal Selecione uma das subseções Não há configurações disponíveis Falha ao carregar configurações. Estes scripts serão executados para todos os jogo na sua biblioteca. Scripts individuais podem ser atribuídos a cada jogo separadamente ao editar os detalhes de um jogo. Transições para imagens de fundo animadas Tamanhos das fontes Automático Sem suavização Tons de cinza ClearType Ideal Tela Modo de formatação de texto Modo de renderização de texto Atualmente, as opções de renderização e formatação de texto não se aplicam ao texto na descrição dos jogos. Pré-carregar imagens de fundo Se habilitado, o Playnite baixará as imagens de fundo enquanto baixa os metadados. Mais espaço em disco será usado e disponibilizando as imagens mesmo quando estiver offline. Se desabilitado, as imagens de fundo só serão baixadas quando necessário. Menos espaço será usado, mas pode resultar em atrasos na exibição das imagens e algumas imagens podem não estar disponíveis quando estiver offline. Fechar automaticamente lançadores de terceiros ao sair do jogo Período de atraso ao fechar cliente (em segundos) Não fechar após sessões mais curtas do que (em segundos) Fechar automaticamente os seguintes clientes: Fechar clientes automaticamente Importar lista de exclusão Exibir aviso quando atribuir arquivos de mídia muito grandes a jogos Comando para abrir diretório Organização de classificação etária preferida Atualizar o tamanho da instalação de jogos ao atualizar a biblioteca Verifica e atualiza o tamanho de instalação dos jogos se for detectado que seus arquivos foram modificados desde a última verificação Nenhum Preenchimento Uniforme Preenchimento uniforme Esquerda Direita Topo Fundo Erro de importação Autenticação requerida Falha na autenticação Modo de renderização de visualização da web alternativo Usar quando experienciar problemas com web views, por exemplo os diálogos de autenticação de integrações. Carregamento parcial de descrições de jogo longas Grandes descrições podem causar um atraso visível ao selecionar os jogos. Quando ativado, somente parte do texto da descrição será inicialmente carregado, com uma opção para carregar o resto sob demanda. Importar metadados Baixar metadados Defina a configuração selecionada para ser usada sempre que metadados forem baixados. Esta opção também pode ser alterada nas configurações do aplicativo. Assistente de importação de emuladores Este assistente irá guiá-lo no processo de baixar e importar emuladores de consoles e jogos de console emulados. Tenha em mente que você sempre pode adicionar outros emuladores e/ou jogos mais tarde através do menu principal (no menu "Biblioteca" para configurações de emuladores e no menu "Adicionar jogos" para jogos emulados). Abaixo está alista de emuladores que Playnite pode reconhecer e configurar automaticamente. Você pode baixar os instaladores de seus respectivos websites. Depois de ter os emuladores instalados (manualmente), você pode importa-los no diálogo de configuração de emulador. Você pode importar qualquer emulador que esteja instalado em seu computador clicando no botão 'Detectar automaticamente da pasta…'. Playnite irá pesquisar a pasta selecionada por qualquer emulador conhecido e fornecerá a opção de importá-los. Você pode usar este botão várias vezes para importar emuladores de pastas diferentes. Emuladores serão adicionados na parte inferior da lista atual. Você pode importar jogos clicando no botão 'Escanear Pasta Usando Emulador'. Selecionando o emulador apropriado dirá para Playnite quais tipos de arquivo devem ser escaneados e importados. Você pode usar este botão várias vezes para importar jogos de pastas diferentes. Jogos serão adicionados na parte inferior da lista atual. Não há emuladores selecionados para importação. Você não poderá importar automaticamente nenhum jogo emulado sem configurar os emuladores primeiro. Tem certeza de que deseja continuar e sair do processo de importação? Não há emuladores configurados no Playnite. Você não pode importar jogos sem antes configurar o emulador e selecionar os tipos de arquivo apropriados. Deseja adicionar alguns emuladores agora? Escanear pasta usando um emulador Selecionar arquivos Detectar automaticamente da pasta… Configurar emuladores… Escaneando… Escaneando {0}… Configuração inicial Este assistente irá guiá-lo no processo de importação e configuração automático de bibliotecas de jogos externas. O Playnite pode importar jogos automaticamente de vários serviços, como Steam ou GOG, além de manter a sua biblioteca atualizada, atualizando automaticamente quando a aplicação é iniciada. Tenha em mente que você pode adicionar qualquer jogo personalizado de qualquer plataforma manualmente através do botão 'Playnite' no menu principal. Integração com a biblioteca Abaixo temos uma lista curada de algumas integrações de biblioteca que o Playnite suporta. Por favor selecione as que você deseja instalar. Mais integrações poderão ser instaladas posteriormente a partir do menu de "Extensões". Configuração concluída A configuração inicial foi concluída. Lembre-se que você pode alterar várias opções a qualquer momento no menu "Configurações". Você também sempre pode adicionar mais jogos clicando no menu no logo do Playnite. Falha ao baixar uma ou mais extensões. Você pode tentar baixar novamente as integrações pelo menu de extensões após o assistente de primeira execução terminar. Baixando {0} integrações... Baixando lista de integrações recomendadas… Falha ao baixar a lista de integrações recomendadas. Você pode tentar e baixar novamente as integrações mais tarde através do menu de extensões. Configurar plataformas e emuladores Configurar emuladores Plataformas Plataforma Emuladores Emulador Adicionar plataforma Selecionar ícone Selecionar capa Selecionar imagem Selecionar item Selecionar fundo Selecionar arquivo Selecionar URL Adicionar emulador Plataformas compatíveis Você deseja salvar as alterações nas plataformas? Você deseja salvar as alterações nos emuladores? Executável Argumentos Pasta de trabalho Tipos de arquivo compatíveis Importar emuladores… Baixar emuladores… Carregar argumentos predefinidos do perfil de um emulador conhecido Tem certeza de que deseja remover o emulador {0}? Ele está sendo usado por {1} jogos(s). Tem certeza de que deseja remover a plataforma {0}? Ela está sendo usada por {1} jogos(s) e {2} emulador(es). Ajuda para configuração Classificar por Direção de classificação Agrupar por Ascendente Descendente Não agrupar Agrupar por biblioteca Agrupar por categoria Agrupar por plataforma Tipo de visualização Visualização Painel de navegação Painel de filtros Ícone Ícone de Biblioteca Imagem de capa Imagem de fundo Nome de classificação Biblioteca Manual Nome Unidade de Instalação Nome da conta Plataforma Categorias Gêneros Data de lançamento Ano de lançamento Desenvolvedor Marcadores Distribuidora Estado da instalação Combinar todos os filtros Se habilitado, só jogos que usem todos os itens em todos os filtros serão incluídos na visualização. Se desabilitado, jogos que usem qualquer item em qualquer filtro serão incluídos na visualização. Instalado Instalado Não instalado Oculto Favorito Ativar o suporte HDR Se ativado, o HDR ficará ativo no monitor principal antes de iniciar o jogo. Note que seu monitor principal não suporta HDR. Última sessão Categorias Descrição Local de instalação Imagem de capa Links Caminho da imagem, ROM ou ISO Gênero Gêneros Empresa Empresas Desenvolvedor Desenvolvedores Distribuidora Distribuidoras Categoria Categorias Marcador Marcadores Característica Características Classificação indicativa Classificação indicativa Região Regiões Fonte Fontes Atividade Recente Erro no banco de dados Falha ao abrir o banco de dados da biblioteca. O banco de dados não está aberto. Não foi possível acessar o banco de dados da biblioteca. O arquivo "{0}" está sendo usado por outro processo ou está em um local inacessível. Falha ao criar o pacote de diagnóstico. Falha ao enviar automaticamente o pacote de diagnósticos. As informações de diagnóstico foram enviadas com sucesso. O pacote de diagnósticos foi criado e enviado com sucesso. Anexe a seguinte ID ao seu relatório de falha: Falha ao importar os jogos de {0}. Falha ao importar os jogos emulados de {0}. Não foi possível procurar por jogos usando o perfil do emulador selecionado. O perfil não contém extensões de arquivos ou plataformas. Não foi possível iniciar o Playnite. Feche todas as instâncias em execução e tente novamente. Falha ao aplicar o tema "{0}" usando o perfil de cores "{1}" {2} Não foi possível abrir o link, o URL não está em um formato válido. Falha ao iniciar o aplicativo. Falha ao inicializar a visualização do componente da web. Playnite não pode continuar com o processo de inicialização. Mais informações em https://playnite.link/cefstartup Não foi possível importar os emuladores devido ao arquivo de definição ausente ou corrompido. Falha ao executar ação do menu. Editar detalhes do jogo URL da imagem Adicionar link Adicionar ROM Salvar alterações Aplique as alterações ao (s) jogo (s) sendo editado (s). Adicionar ação Remover ação Remover ação de jogar Adicionar jogos Escanear pasta… Detectar instalação Procurar… Abrir o Playnite Configurações do perfil O nome do jogo não pode estar vazio.. Diretório de monitoramento de ação de jogo não pode estar vazia. O nome do jogo não pode estar vazio antes de buscar metadados. Dados do jogo inválidos Insira um URL válido iniciado com http:// ou https:// Selecionar URL Falha ao baixar metadados: {0} Erro ao baixar Limpar filtros Conta privada Conta pública Chave da API Erro de inicialização Erro no tema Limpar tudo Instalando Desinstalando Iniciando Executando URL inválido Não fazer nada Minimizar Restaurar janela Restaurar janela apenas quando lançado pela interface de usuário Fechar Alterar Avançado Nunca Estado de conclusão Estados de conclusão Avaliação do usuário Avaliação da crítica Avaliação da comunidade Scripts de jogo Scripts do aplicativo Scripts Extensões Fontes de metadados Extensões Id de extensão Recarregar scripts SDK Interativo PowerShell Todos os scripts foram recarregados com sucesso. Não foram encontrados jogos para os critérios de pesquisa/filtragem especificados Nenhum item encontrado Mudar para o modo janela Sair do Playnite Bibliotecas Atualizar tudo Criada por: Versão: Atualizado: Módulo: Biblioteca Estatísticas Tudo Nenhum Notificações Largura Altura Tamanho Pequeno Normal Grande Muito grande Enorme Padrão Selecionar Selecionar todos Desmarcar todos Primeiro Aleatório Selecionar usuário Carregar mais Transparente Recolher Expandir Recolher todos Expandir todos Outros Temas Argumentos do emulador Argumentos integrados Argumentos Customizados Argumentos adicionais do emulador Substituir argumentos do emulador Ação de jogar Selecionar metadados para importar Selecione jogos para importar Buscar por metadados Atualização disponível Alterações desde a última versão Baixar e instalar atualização Verificar se há atualizações Erro na atualização Falha ao verificar se há uma nova versão. Nenhuma nova versão encontrada, você está na mais recente. Falha ao baixar e instalar a atualização. Alguma tarefa de plano de fundo está atualmente em andamento. Deseja cancelar e continuar com a atualização? Alguma tarefa de plano de fundo está atualmente em andamento. Deseja cancelar e e sair do Playnite? Alguma tarefa de plano de fundo está atualmente em andamento. Alternar modos cancelará a tarefa, quer alterar de qualquer forma? Uma atualização para o Playnite está disponível Recarregar lista de temas Aplicar o tema selecionado Verificar alterações em arquivos Aplicar o tema automaticamente quando algum arquivo for alterado Execução do script Executar antes de iniciar um jogo Executar depois de sair de um jogo Executar após um jogo ser iniciado Executar ao iniciar o programa Executar ao fechar o programa Jogo iniciando o script Jogo iniciou o script Jogo encerrou o script Executar o script global Global Filtrado Atual Novo Testar script Mostrar apenas itens selecionados. Salvar como padrão Adicionar aos favoritos Remover dos favoritos Ocultar este jogo Remover de ocultos Ativar o suporte HDR Desativar o suporte HDR Editar… Calcular tamanho da instalação Calcular tamanho da instalação (Todos os jogos) Calcular tamanho da instalação (Somente dados faltantes) Tamanho da Instalação Definir categorias… Definir estado de conclusão Remover Jogar Instalar Opções do jogo Detalhes Desinstalar Abrir o local do jogo Criar atalho na área de trabalho Abrir manual Mais Gerenciado pela extensão da biblioteca O processo de inicialização do jogo será gerenciado pela extensão da biblioteca responsável por este jogo. Nenhuma informação relevante sobre o jogo '{0}' foi encontrado na página especificada. Dica: você pode usar um processo mais avançado para baixar metadados ao editar um único jogo através da opção "Editar..." no menu de contexto. Indisponível enquanto houver alguma ação em andamento. O texto de descrição é sensível à sintaxe HTML Tempo de jogo é gravado em segundos. O tamanho da instalação é indicado em bytes. A data de lançamento precisa estar no formato "ano-mês-dia". Os campos de mês e dia podem ser omitidos. Valores de 0 a 100. Deixe vazio para não definir uma avaliação. O desenvolvimento do Playnite é apoiado por estes patronos: Programadores, tradutores e demais colaboradores sem uma ordem específica: Cancelar o monitoramento do jogo? O monitoramento de instalação está atualmente em execução. Você deseja cancelar o processo e retornar o jogo ao estado anterior? O monitoramento de execução do jogo está atualmente em execução. Você deseja cancelar o processo e retornar o jogo ao estado anterior? Tempo de jogo Última sessão {0} d {1} h {2} m {0}h {1}m {0} minutos {0} segundos Não jogado Abrindo modo janela… Abrindo modo tela cheia… Carregando a biblioteca de jogos… Calculando tamanho da instalação… Calculando tamanho da instalação de {0}… Falha ao instalar o arquivo de script. Script instalado com sucesso. Instalar script Erro no script Falha ao executar a função da extensão. Abrir a pasta de metadados Calcular Calcula automaticamente o tamanho de instalação usando as Roms se o jogo possuir alguma ou o diretório de instalação caso haja sido definido Cliente do {0} não instalado. O cliente do(a) {0} será executado agora. Inicie a sessão e então feche esta janela. Aguardando que o usuário inicie a sessão, feche esta janela quando terminar… Diretório de instalação do jogo não encontrado. Configuração de ação do jogo inválida. Solução de problemas de sincronização de contas Soluções de problemas Renomear item Adicionar novo item Insira um nome Insira um novo nome Menos de uma hora 1 a 10 horas 10 a 100 horas 100 a 500 horas 500 a 1000 horas 1000+ Playnite precisa ser reiniciado para completar a instalação. Deseja reiniciar agora? Extensão não foi empacotada corretamente. O tema não foi empacotado corretamente. Falha ao carregar extensão "{0}". Impossível carregar extensão "{0}" , versão atual do Playnite não suportada. Falha ao carregar tema "{0}" Impossível carregar tema "{0}" , versão atual do Playnite não suportada. Falha ao carregar extensão. Falha ao carregar tema. O tema ou extensão está usando uma versão não compatível da API. Instalação bem-sucedida. Instalar extensão? Genérico Falha ao instalar a extensão "{0}". Falha ao instalar extensão. {0} Deseja instalar a nova extensão? {0} Por {1} Versão {2} Deseja atualizar a extensão {0}? Versão atual: {1} Nova versão: {2} Falha ao instalar tema. {0} Deseja instalar o novo tema? {0} Por {1} Versão {2} Deseja atualizar o tema {0}? Versão atual: {1} Nova versão: {2} Você está prestes a deixar o Playnite e abrir a seguinte página da web usando o seu navegador padrão. Você deseja continuar? {0} As imagens selecionadas podem ser grandes demais para desempenho ideal. O uso de imagens muito grandes pode resultar em uma perda significativa na responsividade da interface e um aumento no uso de memória. Resoluções máximas recomendadas: Ícones: {0} mega pixels Capas: {1} mega pixels Imagens de fundo: {2} mega pixels Aviso de desempenho Não mostrar novamente O arquivo com a extensão {0} não é compatível. Extensão de arquivo incompatível A imagem selecionada pode ser grande demais para um desempenho ideal. Tem certeza que deseja desinstalar o tema selecionado? A desinstalação será realizada na próxima vez que a aplicação for iniciada. Temas integrados não podem ser desinstalados. Este tema não é suportado por esta versão do Playnite. Tem certeza que deseja desinstalar a extensão selecionada? A desinstalação será realizada na próxima vez que a aplicação for iniciada. Extensões integradas não podem ser desinstaladas. Esta extensão não é suportada por esta versão do Playnite. Diretório de instalação Diretório de dados Gerando pacote de diagnóstico… Enviando pacote de diagnóstico… Importar arquivo… O que é isto? Tem certeza de que deseja fazer isso? Tempo de jogo Tempo médio de jogo Tempo máximo de jogo Tamanho total da instalação Visão geral Barra lateral Exibir na barra lateral Redefinir configurações Todas as configurações da aplicação serão redefinidas a seus valores padrão, excluindo: - O local do banco de dados - A lista de exclusão de importações - As configurações de extensões, incluindo integrações de biblioteca O reinicio da aplicação será necessário para terminar o processo. Você deseja redefinir as configurações? Para desenvolvedores Extensões externas Inserir o caminho completo da pasta. Conquistas Fórum Notícias Página na loja A configuração inicial não foi concluída. O Playnite irá reiniciar no modo janela para que o processo seja concluído. Jogados recentemente Favoritos Mais jogados Tudo Alguns filtros foram aplicados. Filtros adicionais foram aplicados. Buscar resultados para: Um item com o mesmo nome já existe. Limitar seleção ao filtro atual Selecionar outro Extensões... Instalado Configurações de extensões Navegar Atualizações Atualizações ({0}) O gerenciamento de extensões e temas instalados, incluindo suas configurações, foi movida para o novo menu de "extensões". Todas as integrações de biblioteca atualmente instaladas podem ser configuradas aqui. Se você deseja instalar ou desinstalar integrações adicionais, use a opção "extensões" do menu principal. Temas para desktop Temas de tela cheia Procurando... A extensão não é compatível com essa versão do Playnite. Falha ao baixar o pacote de instalação da extensão. Falha ao baixar o manifesto de instalação da extensão. O reinício da aplicação é necessário para aplicar as alterações pendentes. Esta extensão está agendada para instalação. Instalar Reinstalar Desinstalar Já instalado Nenhuma atualização de extensão foi encontrada. Atualizar extensões Registro de mudanças não disponível Agendado para instalação O download falhou Licença rejeitada Baixando {0}... Procurando por atualizações para as extensões... Procurando por atualizações do programa... Uma ou mais atualizações de extensões estão disponíveis. Selecione itens para atualizar Instância de extensão em desenvolvimento {0} acordos de licença Aceitar Recusar Incluir ações de jogar da integração da biblioteca Selecionar ação Modo de monitoramento Localizando caminho Atraso do rastreamento inicial Frequência de rastreio Link Arquivo Emulador Script Padrão Processo Pasta Processo original Nome do processo Registrar mensagens de vestígios As mudanças seguintes sobrescrevem dados em todos os jogos selecionados no momento! Nenhum Uniforme Itens apenas Início e fim apenas Sensibilidade de rolagem Rolagem suave Velocidade da animação Remover item? Você tem certeza que deseja remover este item? Mostrar botões no painel superior: Configurações gerais de visualização Configurações de agrupamento Configurações de classificação Predefinições de filtros Posição de itens de plugin Largura do separador de seções Mover o menu principal para a barra lateral Painel de navegação Escolher jogo aleatório Visualiza seletor de jogos aleatório Selecionar um jogo aleatório da lista Salvar configurações de agrupamento e classificação Mostrar como um filtro rápido no modo de tela cheia Nos últimos 7 dias Nos últimos 31 dias Nos últimos 365 dias Mais de 365 dias atrás Configurar Salvar predefinição Minimizar após iniciar o jogo Minimize o Playnite após um jogo ser iniciado. Desabilitar essa opção pode levar a problemas com os jogos não recebendo comandos ao iniciar! Tamanho da fonte Tamanho da fonte pequena Ativar suporte a API para controles de jogo Suporte para controles de jogo Se desativado, o Playnite não aceitará nenhuma entrada do controle de jogo. Desative se você usar ferramentas que traduzem entradas de controle de jogo para inserir mouse/teclados, e você está recebendo entradas duplas no Playnite. Mostrar itens no menu principal: Inverter o vínculo dos botões X/A na visualização padrão Trocar o vínculo entre os botões de iniciar um jogo e de mostrar os detalhes do jogo na visualização padrão. Inverter botões de confirmação/cancelamento Inverte o botão A/B para confirmação e cancelamento. Apenas o controlador primário Apenas aceitar as entradas do controlador primário quando habilitado. Botão guia foca o Playnite Volume da interface Volume em segundo plano Silenciar quando em segundo plano Falha ao inicializar a interface de áudio. API de saída API utilizada para a saída de audio. Troque-a se você experienciar problemas com o som. Geral Visuais Áudio Esquema Menus Entrada {0} está iniciando... {0} está em execução... Caps Espaço Forma de escala para renderizar imagens Alternativo Balanceado Qualidade Qualidade: Melhor qualidade de imagem, lento, alto uso de memória. Balanceado: Boa qualidade, rápido, baixo uso de memória. Alternativo: Melhor qualidade do que o balanceado, velocidade média, baixo uso de memória. Selecionar arquivo... Selecionar pasta... Script de inicialização Por favor, note que tanto extensões quanto temas podem afetar severamente a performance, estabilidade e segurança do Playnite. Se você começar a experienciar problemas após a instalação de um tema ou extensão, primeiro tente desabilitar-lo/desinstala-lo para checar se eram a raiz do problema. Escolher ao iniciar Escolher ao iniciar Perfis integrados Perfil integrado Perfis personalizados Perfil personalizado Manipulado por um script integrado Especificação de emulador Especificação de plataforma Especificação de região Executar antes de iniciar o emulador Executar depois do emulador ser iniciado Executar depois de fechar o emulador Executável do emulador não encontrado Especificação do emulador não encontrada Script de inicialização do emulador não encontrado Divida como jogos separados Unir em um jogo Definir plataforma Definir região Escanear pasta Verificando configurações Excluir padrão da verificação de Hash Arquivos que correspondem ao (s) filtros não serão verificados quanto Hash e serão comparados pelo nome do arquivo. Consulte a página de ajuda do emulador para obter mais informações. Escanear com emulador Nenhum nome informado ao salvar nova configuração. Emulador ou perfil de emulador não está definido. Para para pesquisa não especificada ou ela não existe. A configuração de pesquisa não está definida corretamente. Incluir escaneamentos automáticos no escaneamento em massa Falha em escanear pasta por emuladores. Falha em escanear pasta(s) por jogos emulados. Esconder importados Perfis para importar: Configurações de escaneamento automático Salvar como uma configuração de escaneamento automático Salve a configuração para um uso posterior durante a atualização da biblioteca. Configurações salvas podem ser gerenciadas pelo menu de "configurar emuladores". Importar usando caminhos relativos Se possível, importar arquivos de jogos usando caminhos relativos à pasta de instalação do Playnite ou à pasta de instalação do emulador. Analisar subpastas Analisar dentro dos arquivos Combine arquivos relacionados Combine arquivos relacionados, como jogos com vários discos, em apenas uma entrada de jogo. Adicionar escâner Adicionar escâner salvo Iniciar escaneamento Adicione configurações para emuladores escanearem pastas especificas. Tenha certeza que esses emuladores estão corretamente configurados antes de importar jogos (pelo menu "biblioteca" -> "configurar emuladores") Estado padrão designado a jogos recentemente adicionados Estado designado a jogos executados pela primeira vez Falha na inicialização da execução do script PowerShell. Se você for usuário Windows 7, tente (re)instalar PowerShell 5.1 para corrigir o problema. Já existe uma filtro com o nome especificado. Atualizar com novas configurações? Preenche automaticamente nomes de classificação ausentes para jogos adicionados em lote ou editados Quando você editar um jogo, adicionar jogos através de uma atualização de biblioteca, um escaneamento de pastas de emuladores ou uma verificação de pasta normal, preencha automaticamente o campo "Nome da Classificação" com uma melhor representação classificável do nome do jogo. Por exemplo, "The Witcher 3" receberá "Witcher 03". Isso nunca definirá um nome de classificação que não difere do nome do jogo, e só atualizará automaticamente os nomes de classificação vazios. Estas palavras serão removidas do início do valor de Nome de Classificação preenchido automaticamente: Usar isto para ignorar as palavras no início do nome do jogo para fins de classificação. O padrão é "The", "An" e "A". Preencher Nome de Classificação para jogos que não possuam nenhum Classificação Preenchendo valores de Nome de Classificação… Serviço Nahimic foi detectado rodando em seu sistema. Sabemos que serviço causa problemas de renderização no Playnite (e outros apps). Se você tiver quaisquer problemas de renderização ou gráficos corrompidos no Playnite, recomendamos desabilitá-lo ou desinstalá-lo completamente o serviço Nahimic. Mais informações em https://playnite.link/nahimicsucks Playnite está rodando com privilégios elevados (como administrador). Não é a recomendação já que dá privilégios elevados para todas as extensões instaladas e todos os jogos/apps iniciados pelo Playnite! Mais informações em https://playnite.link/adminfaq Mostrar aviso se Playnite estiver rodando com privilégios elevados Obter o tamanho real na unidade de armazenamento ao calcular o tamanho dos jogos Se ativado, as varreduras serão mais lentas e obterão o tamanho real que os arquivos ocupam na unidade de armazenamento. Se desativado, as varreduras serão mais rápidas e utilizarão o tamanho dos próprios arquivos. As seguintes extensões foram reportados como potencialmente problemáticos, seja pelo alto impacto na estabilidade/performance ou problemas de segurança. Recomendamos fortemente que os desinstale: {0} Excluir arquivos online do escaneamento Arquivos armazenados na nuvem não serão escaneados e importados se não estiverem disponíveis localmente. Suporte somente para: Google Drive, DropBox, OneDrive Escanear mas usando método simplificado sem conteúdo do arquivo Arquivos serão importados, mas usando método menos preciso que não requer que o conteúdo do arquivo seja baixado. Aplicar a todos Sobrescrever estado da instalação Quando definido, o Playnite ignorará o estado de instalação (incluindo o diretório de instalação) definido pelo plugin de integração que importa esse jogo. Esta opção pode não funcionar totalmente com plugins que usam método específico de importação de jogos, a menos que levem esta opção em conta. Apenas manualmente Uma vez por dia Uma vez por semana Em cada inicialização Verificar se há atualizações do programa Verificar atualizações de complementos Atualizar bibliotecas Escanear pastas de emulação Incluir jogos ocultos Editar campos Selecionar / Desmarcar todos Abrir Ativar Atribuir Comece a digitar para procurar por jogos… [F1] para obter ajuda Começar com # traz uma lista de comandos disponíveis. Começãr com / traz uma lista de provedores/plugins de pesquisa disponíveis. Digite a palavra-chave de busca e termine com ESPAÇO alterna imediatamente para essa pesquisa. TAB: ação de mudar ENTER: ativar a ação selecionada SHIFT-ENTER: abrir menu de itens Incluir jogos desinstalados Incluir jogos ocultos Jogos desinstalados incluídos Jogos desinstalados excluídos Jogos ocultos incluídos Jogos ocultos excluídos Jogar ou Instalar Ir para detalhes Menu do jogo Editar jogo Abrir a busca Caixa de busca Botão de pesquisa Ação primária do jogo Ação secundária do jogo CTRL-F abre a pesquisa global em vez de foco na caixa de pesquisa Salvar configurações de filtro de jogos entre sessões de pesquisa Provedores de pesquisa Palavras-chave padrão Palavra-chave personalizada Atalho de sistema Pesquisa Playnite Configurações de extensão Exclusões Arquivos excluídos relativos à pasta de escaneamento Pastas excluídas relativas à pasta de escaneamento Adicionar arquivo à lista de exclusão Adicionar pasta à lista de exclusão Exclusões só podem ser adicionadas às configurações do scanner salvo. Exclusões adicionadas ao scanner "{0}". Substituir plataforma Quando definido, o scanner atribuirá esta plataforma a todos os jogos, substituindo quaisquer plataformas detectadas automaticamente. Incluir comandos na pesquisa padrão Quando desativado, os comandos não serão incluídos na pesquisa padrão até que o prefixo # seja usado. Usar correspondência ampla no filtro de nomes Quando ativado, o filtro de nomes vai corresponder aos nomes de jogos da mesma maneira que a busca global. Uma filtragem mais rigorosa pode ser imposta em um caso individual, prefixando o filtro com o símbolo ! Campos a serem exibidos para os resultados do jogo: Status Oculto O backup dos dados foi cancelado. Backup de dados falhou. Erro no backup dos dados Backup de dados em progresso… Restaurando dados do backup… Falha ao restaurar dados do backup. Configurações Biblioteca de jogos Mídia da biblioteca do jogo Extensões instaladas Dados de extensões Temas instalados Selecione os dados a serem restaurados do arquivo de backup especificado. O Playnite reiniciará automaticamente para iniciar o processo de restauração do backup. Selecione os itens a serem incluídos no backup de dados. As configurações de aplicativo e os dados da biblioteca do jogo são incluídos por padrão. O Playnite será automaticamente reiniciado para iniciar o processo de backup. Backup automático de dados Frequência de backup automático Pasta de backup Rotação de backups Incluir dados adicionais: A pasta de backup precisa ser definida se o backup automático estiver ativado. Mostrar notificações apenas para versões de patch Quando ativado, apenas atualizações disponíveis para a versão principal atualmente instalada resultarão em notificação de atualização. Novas versões principais não resultarão em notificação de atualização. Usar datas relativas para a última semana Usar datas relativas em formato "Hoje", "Ontem" etc. se a data for menor que uma semana. O formato de data especificado será usado em todas as outras datas. Pesquisa de Imagens na Internet String de pesquisa de imagens de ícone String de pesquisa de imagens de capa String de pesquisa de imagens de fundo Obtendo informações do complemento… Nenhuma fonte de metadados está disponível Reproduzir configurações de ação Usar configurações do scanner Selecionar perfil na inicialização Selecionar emulador na inicialização Automático Sempre ligado Sempre desligado Suporte de acessibilidade (leitor de tela) Menu do aplicativo Menu do jogo Pasta do programa Pasta do usuário Corrupção em arquivo da biblioteca foi detectada. O Playnite será fechado. Avise do problema na página do Playnite no GitHub com um pedido de correção. Você quer salvar as alterações feitas? Instalação portátil Nenhum controle detectado ================================================ FILE: source/Playnite/Localization/pt_PT.xaml ================================================  Português Europeu Idioma do Playnite Sair Filtro Ativado Filtro Desativado Filtros adicionais Filtros Filtro Dados Inválidos Guardar alterações? Página Web em www.playnite.link Código Fonte no GitHub Criar pacote de diagnóstico Enviar informação de diagnóstico Sobre o Playnite Criado por Josef Němec Atribuir Categoria Definir Categorias Adicionar Categoria Marcado - Atribuir categoria Desmarcado - Remover categoria Indeterminado - Sem alterações (ao editar vários jogos) Sem Categoria Sem Plataforma Ups! Algo correu mal… Um erro irrecuperável ocorreu. Se queres ajudar-nos a corrigir este problema, por favor descreve, de forma breve, que ações foram tomadas antes do erro ter ocorrido e depois cria um pacote de diagnóstico. Se estiveres online, o pacote será transferido para o servidor do Playnite para análise. Alternativamente, podes usar o botão "Reportar Crash" para criar um novo tópico nas Issues do GitHub e reportar o crash manualmente. Obrigado pela tua ajuda. Extensão "{0}" causou um erro irrecuperável. Nós recomendamos salvar o teu ficheiro de log e reportar o problema para o desenvolvedor da extensão. Se o problema continuar a acontecer, desabilita a extensão. Extensão "{0}" causou um erro irrecuperável. Nós recomendamos reportar o problema para o desenvolvedor da extensão. Se o problema continuar a ocorrer, desabilita a extensão. Extensão desconhecida ou tema causou um erro irrecuperável. Recomendamos desativar complementos de terceiros, isolando o problema e relatando o problema ao desenvolvedor do complemento. Ocorreu um erro irrecuperável. Se quiseres ajudar-nos a corrigir este problema, por favor envia informação de diagnóstico. Obrigado. Desabilitar extensão Guardar o arquivo log Enviar informação do diagnóstico Reportar Crash Reiniciar o Playnite Reiniciar no Modo Seguro Desabilitando todas as extensões de terceiros e usando o tema padrão. Sair do Playnite Ações tomadas antes do crash (em Inglês): Gestor de Biblioteca Remover Jogo(s)? Não foi possível remover - Jogo ou instalador em execução. Não foi possível desinstalar - Jogo em execução. Tem a certeza que quer remover {0}? Tens a certeza de que queres remover estes {0} jogos? Tem certeza de que deseja remover estes {0} jogos? Selecionando a opção "adicionar a lista de exclusão" impedirá que o jogo seja importado novamente na próxima vez que a biblioteca for atualizada. Tem certeza de que deseja remover estes {0} jogos? Selecionando a opção "adicionar a lista de exclusão" impedirá que o jogo seja importado novamente na próxima vez que a biblioteca for atualizada. Tens a certeza que pretendes remover {0} entradas que não estão atualmente em uso? Não foram encontrados campos não utilizados. Sim (adicionar à lista de exclusão) Existem alterações não salvas nesta secção A atualizar o formato da biblioteca de jogos… Falha na atualização da base de dados. Não é possível atualizar a biblioteca de jogos. É necessário {0} MBs de espaço livre. Erro do Jogo Não foi possível iniciar o jogo. '{0}' não foi encontrado na base de dados. Não foi possível iniciar o jogo: {0} Não foi possível iniciar a ação: {0} Não foi possível abrir a localização do jogo: {0} Não foi possível detectar o tamanho de instalação do jogo: {0} Erro ao pesquisar o tamanho da instalação Ocorreram {0} erros durante a procura do tamanho da instalação Falha ao criar atalho: {0} Falha ao abrir o manual: {0} Não foi possível instalar o jogo: {0} Não foi possível desinstalar o jogo: {0} Não foi encontrada nenhuma ação válida. Quando usa um emulador de ações, tenha a certeza que as definições da plataforma correspondem às configurações tanto do jogo como do emulador. Implementação de instalação não está disponível O plugin da biblioteca responsável por este jogo está desabilitado ou desinstalado. Transferência de metadados oficiais não disponível. Nenhum jogo selecionado. A execução da ação de script do jogo falhou. A execução do script da aplicação falhou. A execução da ação de script global falhou. Execução do script do emulador falhou. Falha ao executar a ação do script. PowerShell 3.0 ou mais recente não está instalada. Não foi possível determinar como iniciar o jogo. Ativo Desativado Remover Remover não utilizados Alterar nome Copiar Adicionar Ícone Predefinido Imagem de Capa Predefinida Imagem de Fundo Predefinida Concluir Próximo Voltar FEITO VOLTAR LIMPAR Limpar Ignorar Ignorar Todos Importar Nome Autor Módulo Série Versão Última sessão de jogo Mais Jogado Número de Sessões de jogo Tamanho de instalação Pasta Notas Adicionado Data de Adição Modificado Data de Modificação Página Web Caminho OK Guardar Fechar Cancelar Confirmar redefinir Sim Não Bem-vindo Utilizador Local Geral Mídia Ligações Instalação Ações A transferir… Fazendo download da media... A carregar… Tipo Perfil Perfis Remover Transferir Pesquisar Resolução: Qualquer Ampliar/Reduzir Vista de Lista Capas Vista de Grelha Vista de Detalhes Personalizado Endereço Reconhecimento adicional Licença Contribuidores A fechar o Playnite… Hoje Ontem Segunda-feira Terça-feira Quarta-feira Quinta-feira Sexta-feira Sábado Domingo Semana Passada Mês Passado Ano Passado Há mais de um ano 0 a 100MB 100MB a 1GB 1GB a 5GB 5GB a 10GB 10GB a 20GB 20GB a 40GB 40GB a 100GB 100GB ou mais Importação concluída com sucesso. Todos os Jogos ID do Jogo ID da Base de Dados Predefinições Coluna Colunas Linha Linhas Não foi possível obter o ícone da ação Jogar. Não existe ação do tipo Arquivo presente. Transferir apenas metadados em falta Ativar esta opção irá saltar a transferência de metadados para campos já preenchidos. Seleção de jogos Por favor, seleciona quais os jogos que devem ser atualizados com novos metadados: Todos os jogos da base de dados Todos os jogos atualmente filtrados Apenas jogos selecionados Nenhum campo de metadados selecionado Nenhum campo de metadados foi selecionado para transferência. Por favor, selecione pelo menos um, e ative pelo menos um provedor de metadados para o mesmo. Loja Oficial IGDB Por favor, seleciona quais os campos que devem ser preenchidos automaticamente pelo Playnite e quais as fontes que devem ser usadas para obter esses dados. Por favor, considera clicar no logótipo acima e contribuir com atualizações para a base de dados do igdb.com de forma a melhorar os dados utilizados pelo Playnite. A transferir metadados… A importar jogos instalados… A importar {0} jogos… Importando jogos emulados de {0}… A transferir atualizações da biblioteca… Verificando o tamanho dos jogos na biblioteca… Verificando tamanho dos jogos importados… Atualização da biblioteca concluída A libertar recursos... Configuração Definições… Plataformas e Emuladores Configurar Emuladores… Gestor de Biblioteca… Ferramentas Transferir Metadados… Ferramentas do Software... Configurar integrações… Abrir Cliente de terceiros Clientes de terceiros Atualizar biblioteca de jogos Cancelar atualização da biblioteca Atualizar Pastas emuladas Adicionar Jogo Manualmente… Examinar Automaticamente… Jogo Emulado… Applicação da Microsoft Store… Sobre o Playnite Enviar Feedback Alterar para Modo de Ecrã Inteiro Ligações Ajuda Apoia o projeto no Patreon Dê apoio no Ko-fi Manual do utilizador Documentação SDK Reiniciar Sistema Desligar Sistema Suspender Sistema Hibernar Sistema Sistema de Bloqueio Terminar Sessão da Conta Escolher um Jogo Aleatório Campos que serão mostrados no painel de detalhes: Espaçamento de Itens Mostrar fundo dos itens da grelha Largura das bordas dos itens da grelha Fonte de ícone de jogo em falta Fonte de capa de jogo em falta Fonte de fundo de jogo em falta Espaçamento vertical para detalhes dos jogos Posição dos detalhes na vista de grelha Posição da lista de jogos na vista de detalhes Mostrar separador entre painéis Altura da imagem da capa do jogo Altura dos ícones da lista de jogos Fonte da aplicação Fonte monoespaçada Posicionamento do painel de filtros Posicionamento do painel do explorador Renderização de arte da capa Rácio de aspeto a utilizar As seguintes opções também afectam a renderização de mosaicos no modo de ecrã inteiro! Modo de redimensionamento Caixa de DVD Loja Epic Games GOG Galaxy 2.0 IGDB Quadrado Banner do Steam Capa vertical do Steam Twitch * É necessário reiniciar para aplicar Definições Geral Painéis do Topo Aparência Detalhes do Jogo Disposição Avançado Ecrã Inteiro Entrada Desempenho Metadados A atualizar... Pesquisa Cópia de segurança Recuperação da Informação da Biblioteca Restaurar cópia de segurança Importar alterações da biblioteca automaticamente Localização do ficheiro da base de dados inválida, um caminho correto deve ser definido. Nome de conta não pode estar vazio. Transferir metadados após importar jogos Executar o Playnite minimizado Abrir Playnite quando o seu computador for ligado Iniciar minimizado na bandeja Falha ao registar o Playnite para iniciar quando o computador iniciar. Iniciar no Modo de Ecrã Inteiro Carregamento assíncrono de imagens Melhora a suavidade de scroll nas listas de jogos, em troca de carregamento de imagens mais lento. Mostrar nome do jogo se a capa estiver em falta Mostrar nomes dos jogos em vista de Grelha Escurecer jogos não instalados Mostra ícones do jogo na vista de Detalhes Mostra a contagem de itens nas descrições de grupo Mostrar somente campos selecionados nos painéis de filtro e explorador Desativar aceleração de hardware Usa em caso de bloqueios ou problemas similares com a interface Mostrar jogos ocultos nas listas de início rápido Afeta a Lista de Saltos e as listas do menu bandeja Número de itens de início rápido Utilizar a imagem de fundo do jogo como fundo da janela Desfocar fundo Alta Qualidade Escurecer fundo Mostrar na vista de Grelha Tema Perfil de Tema Tema de Ecrã Inteiro Perfil de Tema de Ecrã Inteiro Localização da Base de Dados Estado de autenticação: Definições do Playnite Limpar web cache Pode resolver problemas encontrados ao ligar contas. Mostrar ícone de bandeja Minimizar o Playnite para a bandeja do sistema Minimizar o Playnite para a bandeja do sistema quando a janela da aplicação for fechada Quando o jogo inicia: Após o jogo fechar: Formatar tempo jogado para indicar o número de dias jogados Formato da data: Isto eliminará o acesso de todos os serviços ligados. É necessário reiniciar o programa, pretendes continuar? Limpar Cache? É necessário reiniciar o Playnite para aplicar o novo tema Obter mais temas Criar novo tema Obter mais extensões Criar nova extensão Ajude-nos a traduzir o Playnite Playnite precisa de ser reiniciado para aplicar as novas configurações. Reiniciar agora? Reiniciar irá cancelar qualquer tarefa ativa (transferências) em progresso. Reiniciar Playnite? Playnite não consegue mover os ficheiros da sua biblioteca automaticamente. Deves movê-los/copá-los manualmente antes de alterar a localização. Se nenhuma biblioteca estiver presente no destino indicado, uma nova será criada. O tempo de jogo não será gravado se a ação "Fechar" for ativada. Número de linhas Número de colunas Número de linhas na vista de detalhes Mostrar Imagem de Fundo no Ecrã Principal Não se aplica retroativamente a jogos preexistentes sem transferir novamente os metadados. Importar tempo de jogo na biblioteca: Configura quando o Playnite deve importar o tempo de jogo relatado pelos plugins da biblioteca para os jogos no banco de dados do Playnite. É necessário, para puder usar esta funcionalidade, que os plugins da biblioteca que têm responsabilidade pelo(s) jogo(s) tenham suporte. Sempre: Importa o tempo de jogo a novos jogos importados e os já existentes no banco de dados do Playnite. Apenas a jogos recém-importados: Importa o tempo de jogo somente a novos jogos importados. Nunca: Nunca importa o tempo de jogo sob nenhuma circunstância. Sempre Apenas para jogos recém-importados. Nunca Ativar suporte de comando no modo Desktop Botão Guia do comando abre o modo de Ecrã Inteiro Opções de transferência automática de metadados para jogos recém-importados. Monitor desejado Usar sempre o monitor principal Mostrar Títulos dos Jogos Mostrar Estado de Bateria Mostrar Percentagem de Bateria Mostrar relógio Ocultar cursor do rato Instalado Apenas em Filtros Rápidos Estilo dos Botões Disposição Scroll Horizontal Seleciona uma das subsecções Sem definições disponíveis Falha ao carregar as configurações Estes scripts são executados para todos os jogos na biblioteca. Scripts individuais podem ser atribuídos a cada jogo separadamente ao editar os detalhes do jogo. Animar transições entre imagens de fundo Tamanhos de fonte Automático Sem suavização Escala de cinza ClearType Ideal Monitor Modo de formatação de texto Modo de renderização de texto A renderização de texto e a formatação de métodos atualmente não se aplicam às descrições dos jogos. Carrega imagens de fundo previamente Se ativado, o Playnite transferirá imagens de fundo enquanto transfere os metadados, utilizando mais espaço de disco e tornando as imagens disponíveis offline. Se desativado, as imagens de fundo serão transferidas apenas quando necessárias pela primeira vez, resultando em menos espaço usado, mas num maior atraso na exibição das mesmas. Algumas imagens poderão não estar disponíveis offline. Fechar automaticamente clientes de terceiros após sair do jogo Atraso para o encerramento do Cliente (em segundos) Não fechar após sessões de jogo de menos de (em segundos) Fechar automaticamente os seguintes clientes: Fechar Clientes Automaticamente Importar Lista de Exclusão Mostrar aviso quando for atribuída a jogos mídia de dimensões demasiado grandes Comando de abrir pasta Atualizar tamanho da instalação de jogos ao atualizar a biblioteca Verifica e atualiza o tamanho de instalação dos jogos se for detetado que os seus ficheiros foram modificados desde a última verificação Nenhum Preencher Uniforme Preenchimento uniforme Esquerda Direita Topo Fundo Erro de Importação Autenticação necessária Falha na autenticação Modo alternativo de renderização da visualização da Web Use quando tiver problemas com visualizações da Web, por exemplo, diálogos de autenticação de integração Carregamento parcial de grandes descrições do jogo Grandes descrições podem causar um atraso visível ao selecionar os jogos. Quando ativado, somente parte do texto da descrição será inicialmente carregado com uma opção para carregar o resto sob demanda. Importar Metadados Transferir Metadados Define uma configuração para ser usada em futuras transferências de metadados. Esta opção também pode ser alterada nas configurações da aplicação. Assistente de Importação de Emulação Este feiticeiro irá guiá-lo pelo processo de transferência e importação de emuladores de consola e importação de jogos emulados Tem em atenção que podes sempre adicionar outros emuladores e/ou jogos mais tarde, através do menu principal (na opção "Biblioteca" para configuração de emuladores e na opção "Adicionar jogos" para jogos emulados). Abaixo está uma lista de emuladores que o Playnite consegue reconhecer e configurar automaticamente. Pode descarregar instaladores dos emuladores a partir das respetivas páginas Web. Após instalar os emuladores (manualmente), pode importá-los na caixa de diálogo de configuração do emulador. Pode importar quaisquer emuladores instalados no seu computador clicando no botão "Detetar automaticamente a partir da pasta… ". O Playnite irá procurar na pasta selecionada quaisquer emuladores conhecidos e fornecerá a opção de os importar. Pode usar este botão várias vezes para importar emuladores de diferentes pastas. Os emuladores serão adicionados ao final da lista atual. Não há emuladores selecionados para importação. Não poderás importar automaticamente nenhum jogo emulado sem antes configurar os emuladores. Tens a certeza que pretendes continuar e sair do processo de importação? Examinar pasta usando Emulador Selecionar ficheiros Configurar Emuladores… A examinar… Configuração Inicial Integração de Bibliotecas Configuração concluída A configuração inicial foi concluída. Lembra-te que podes modificar todas as opções mais tarde no menu "Definições". Podes também adicionar quaisquer outros jogos mais tarde clicando no menu com o logótipo do Playnite. Configurar Plataformas e Emuladores Configurar Emuladores Plataformas Plataforma Emuladores Emulador Adicionar Plataforma Selecionar Ícone Selecionar Capa Selecionar Imagem Selecionar Item Selecionar fundo Selecionar Ficheiro Selecionar Endereço Adicionar Emulador Plataforma(s) suportada(s) Pretendes guardar as alterações da plataforma? Pretendes guardar as alterações do emulador? Executável Argumentos Pasta de Trabalho Tipos de Ficheiro Suportados Importar Emuladores… Transferir Emuladores… Carregar argumentos predefinidos do perfil de um emulador conhecido Tens a certeza de que pretendes remover o emulador {0}? Está atualmente a ser usado por {1} jogo(s). Tens a certeza de que pretendes remover a plataforma {0}? Está atualmente a ser usada por {1} jogo(s) e {2} emulador(es). Ajuda de configurações Ordenar Por Sentido da ordenação Agrupar Por Ascendente Descendente Não agrupar Agrupar por Biblioteca Agrupar por categoria Agrupar por Plataforma Ver Tipo Ver Painel do Explorador Painel de Filtros Ícone Ícone de Biblioteca Imagem de Capa Imagem de Fundo Nome para Ordenação Biblioteca Manual Nome Unidade de Instalação Nome da Conta Plataforma Categoria Género Data de Lançamento Ano de Lançamento Desenvolvedor Etiqueta Editora Estado da Instalação Combinar todos os filtros Se ativado, apenas jogos que usam todos os itens em todos os filtros serão incluídos na exibição. Se desativado, jogos que usam qualquer item em qualquer filtro serão incluídos na exibição. Instalado Instalado Não instalado Escondido Favorito Ativar o suporte HDR Se ativado, o HDR será ativado no display primário antes de iniciar o jogo. Note que o HDR não é suportado no seu monitor principal. Última sessão de jogo Categoria Descrição Pasta de instalação Imagem de Capa Ligações Caminho da Imagem, ROM ou ISO Género Géneros Empresa Empresas Desenvolvedor Desenvolvedores Editora Editoras Categoria Categorias Etiqueta Etiquetas Funcionalidade Funcionalidades Classificação Etária Classificações Etárias Região Regiões Fonte Fontes Actividade Recente Erro na Base de Dados Falha ao abrir a base de dados da biblioteca. Base de dados não está aberta. Não foi possível aceder à base de dados da biblioteca. O ficheiro "{0}" está a ser usado por outro processo ou está numa localização inacessível. Falha ao criar pacote de diagnóstico. Falha ao transferir automaticamente pacote de diagnóstico. A informação de diagnóstico foi enviada com sucesso. O pacote de diagnóstico foi criado e submetido com sucesso. Por favor, anexe o seguinte ID ao seu problema reportado: Falha ao importar jogos de {0}. Falha ao importar os jogos emulados de {0}. Não foi possível procurar jogos usando o perfil do emulador selecionado. O perfil não contém nenhuma extensão de ficheiro ou plataformas. Não foi possível iniciar o Playnite. Por favor, fecha todas as instâncias do programa e tenta de novo. Falha ao aplicar tema "{0}", perfil de cor "{1}" {2} Não foi possível abrir a ligação, o endereço não tem um formato válido. Falha ao iniciar a aplicação. Falha ao inicializar a visualização do componente da web. Playnite não pode continuar com o processo de inicialização. Mais informações em https://playnite.link/cefstartup Não foi possível importar os emuladores devido a ficheiros de definições em falta ou corrompidos. Falha ao executar ação do menu. Editar Detalhes do Jogo Endereço da Imagem Adicionar Ligação Adicionar ROM Guardar Alterações Aplicar alterações de campo ao(s) jogo(s) sendo editado. Adicionar Ação Apagar Ação Remover Ação de Jogo Adicionar Jogos Examinar Pasta… Detetar Instalações Procurar… Abrir Playnite Definições de Perfil O nome do jogo não pode estar vazio. Nome do jogo não pode estar vazio antes de procurar metadados. Dados de jogo inválidos Escreve um endereço web válido, iniciado com http:// ou https:// Selecionar Endereço Falha ao transferir metadados: {0} Erro na transferência Limpar Filtros Conta Privada Conta Pública Chave de API Erro de Inicialização Erro no Tema Limpar Tudo A instalar A desinstalar A iniciar Em execução Endereço Inválido Não fazer nada Minimizar Restaurar janela Fechar Alterar Avançado Nunca Estado de Conclusão Avaliação do Utilizador Avaliação da Crítica Avaliação da Comunidade Scripts de jogo Scripts de aplicação Scripts Componentes Fontes de Metadados Extensões ID da extensão Recarregar Scripts SDK Interativo PowerShell Todos os scripts recarregados com sucesso. Nenhum jogo encontrado com os critérios de pesquisa/filtragem especificados Nenhum item encontrado Alterar para o Modo Desktop Sair do Playnite Bibliotecas Atualizar Tudo Criado Por: Versão: Atualizado: Módulo: Biblioteca Estatísticas Tudo Nenhum Notificações Largura Altura Tamanho Pequeno Normal Grande Maior Ainda Maior Predefinido Selecionar Seleccionar Todos Desmarcar tudo Primeiro Aleatório Seleção do utilizador Carregar mais Transparente Ocultar Abrir Ocultar tudo Expandir tudo Outros Temas Argumentos do Emulador Argumentos integrados Argumentos Personalizados Argumentos de Emulador Adicionais Substituir Argumentos de Emulador Executar Seleccionar itens para importar Selecionar Jogos para Importar Pesquisa de metadados Atualização Disponível Alterações desde a última atualização Transferir e Instalar Atualização Procurar Atualizações Erro de Atualização Falha ao verificar atualizações. Nenhuma nova versão encontrada, estás atualizado. Falha ao transferir e instalar atualização. Uma tarefa em segundo plano encontra-se em progresso. Quer cancelá-la e proceder à atualização? Uma tarefa em segundo plano está atualmente em execução. Pretendes cancelá-la e fechar o Playnite? Uma tarefa em segundo plano está atualmente em execução. Mudar de modo vai cancelar a tarefa, pretendes mudar de qualquer modo? Uma atualização do Playnite está disponível Recarregar lista de temas Aplicar tema selecionado Vigiar alterações de ficheiro Aplicar automaticamente o tema quando o ficheiro fonte for alterado Execução do script Executar antes de iniciar um jogo Executar depois de sair de um jogo Executar depois que um jogo for iniciado Executar ao inicia a aplicação Executar ao fechar a aplicação Global Filtrado Actual Novo Testar script Mostrar apenas os itens selecionados. Guardar como predefinição Adicionar aos Favoritos Remover dos Favoritos Ativar o suporte HDR Desativar o suporte HDR Editar… Definir Categoria… Definir Estado de Conclusão Remover Executar Instalar Opções de Jogo Detalhes Desinstalar Abrir Local de Instalação Criar Atalho na Área de Trabalho Abrir Manual Mais Dica: Podes usar um processo mais avançado de transferência de metadados ao editar um jogo individualmente, através da opção "Editar". Não disponível quando existe alguma ação em curso. O texto de descrição é sensível a sintaxe HTML Valores de 0 a 100, ou vazio para ausência de avaliação. Código, tradução e outros contribuidores sem nenhuma ordem em particular: Cancelar monitorização de jogo? Tempo Jogado Última sessão de jogo {0}d {1}h {2}m {0}h {1}m {0} minutos {0} segundos Não Jogado A abrir o modo Desktop… A abrir o modo de Ecrã Inteiro… A carregar biblioteca de jogos… Calculando tamanho da instalação… Calculando tamanho de instalação de {0}… Falha ao instalar o ficheiro de script. Script instalado com sucesso. Instalar Script Erro no script Falha ao executar a função da extensão. Abrir a pasta dos metadados O cliente {0} não está instalado. O cliente {0} irá agora abrir. Por favor, inicia sessão e, de seguida, fecha esta mensagem. À espera de início de sessão do utilizador, por favor fecha esta mensagem quando terminares… Pasta de instalação do jogo não encontrada. Configuração de ação de jogo inválida. Resolução de problemas de sincronização de conta Resolução de problemas Renomear item Adicionar novo item Introduzir nome Digita novo nome Menos de uma hora 1 a 10 horas 10 a 100 horas 100 a 500 horas 500 a 1000 horas 1000+ Tema/Extensão está a utilizar uma versão da API não suportada. Instalação bem sucedida. Instalar extensão? Falha ao instalar extensão. {0} Falha ao instalar tema. {0} Alerta de Desempenho Não Mostrar Novamente A imagem selecionada pode ser demasiado grande para um desempenho ideal. Diretório d'instalação Diretório de dados O que é isto? Tens a certeza de que pretendes fazer isto? Tamanho total da instalação Barra lateral Mostrar na barra lateral Para desenvolvedores Extensões externas Troféus Fórum Notícias Página da Loja Jogado Recentemente Favoritos Mais Jogado Tudo Existem filtros aplicados. Procurar resultados para: Já existe um item com o mesmo nome. Limitar seleção ao filtro atual Escolher outro Complementos… Instalado Atualizações Atualizações ({0}) Instalar Desinstalar Não foi possível transferir Aceitar Recusar Caminho de rastreamento Ficheiro Emulador Predefinido Processo Pasta Velocidade da animação Remover item? Nos últimos 7 dias Nos últimos 31 dias Nos últimos 365 dias Mais de 365 dias atrás Configurar Tamanho da Letra Áudio Menus {0} está iniciando… {0} está executando… Equilibrado Qualidade Selecionar ficheiro... Seleccionar pasta... Perfil personalizado Combinar num jogo Combinar arquivos relacionados Combine arquivos de jogos relacionados, como jogos individuais, numa entrada de jogo. Adicionar scanner O serviço Nahimic foi detetado a correr no seu sistema. Este serviço é conhecido por causar problemas de renderização no Playnite (e outras aplicações). Se encontrar qualquer corrupção de gráficos ou outros problemas de renderização no Playnite, recomendamos que desabilite ou desinstale por completo o serviço Nahimic. Mais informação em https://playnite.link/nahimicsucks Aplicar a todos Apenas manualmente Uma vez por dia Uma vez por semana Em cada inicialização Incluir jogos ocultos Abrir Incluir jogos desinstalados Incluir jogos ocultos Ir para detalhes Editar jogo Abrir pesquisa Caixa de pesquisa Exclusões Usar correspondência incerta no filtro de nome Quando ativado, o filtro de nomes vai corresponder aos nomes de jogos da mesma maneira que a pesquisa global. A correspondência estrita pode ser imposta em um caso individual, prefixando o filtro com ! Definições Extensões instaladas Temas instalados Automático Sempre ligado Sempre desligado Pasta do programa Diretório do utilizador Corrupção no arquivo da biblioteca foi detectada. O Playnite será desligado. Abra um novo problema na página do GitHub do Playnite com um pedido para corrigir seus arquivos. Deseja guardar as alterações efetuadas? ================================================ FILE: source/Playnite/Localization/ro_RO.xaml ================================================  Română Limbă Playnite Iesire Filtru activ Filtru dezactivat Filtre adiționale Filtre Filtru Date invalide Salvați schimbările? Pagină home la www.playnite.link Codul sursă pe GitHub Creează pachet de diagnostic Trimite informaţiile despre sistem Despre Playnite Creat de Josef Němec Alocă categorie Setează Categorii Adaugă Categorie Bifat - Alocă categorie Nebifat - Șterge categorie Nedeterminat - Nicio schimbare (când editezi jocuri multiple) Nicio Categorie Nicio Platformă Ooops! A apărut o problemă... A apărut o eroare nerecuperabilă. Dacă doriţi să ne ajutaţi să rezolvăm această problemă, vă rugăm să descrieţi pe scurt acţiunile întreprinse înainte de eroare şi apoi să trimiteţi informaţiile de diagnosticare. Dacă sunteți online, acestea vor fi încărcat pe serverul Playnite pentru analiză. Alternativ, puteți face clic pe butonul "Raportare eroare" pentru a raporta eroarea manual pe GitHub. Vă mulțumim pentru ajutor. Extensia "{0}" a cauzat o eroare nerecuperabilă. Recomandăm salvarea fișierului jurnal și raportarea problemei la dezvoltatorul extensiei. Dacă problema continuă să reapară, dezactivați extensia. Extensia "{0}" a cauzat o eroare nerecuperabilă. Recomandăm raportarea problemei la dezvoltatorul extensiei. Dacă problema continuă să reapară, dezactivați extensia. Extensie necunoscută sau temă a cauzat o eroare nerecuperabilă. Vă recomandăm să dezactivați extensiile de la terțe părți, să izolați extensia problematică și să raportați problema la dezvoltatorului extensiei. A apărut o eroare nerecuperabilă. Dacă doriți să ne ajutați să rezolvăm această problemă, vă rugăm să trimiteți informații de diagnosticare. Vă mulțumim. Oprire extensie Salvează fișierul jurnal... Trimite informațiile de diagnosticare Raportează o eroare Restartează Playnite Repornire în Safe Mode Dezactivând toate extensiile 3rd party și folosind tema prestabilită Ieșire Playnite Acțiuni luate înaintea crash-ului (în engleză): Managerul Librăriei Înlătură Joc(uri)? Nu se poate șterge - Un joc sau instalator rulează. Nu se poate dezinstala - Un joc rulează. Sunteți sigur că doriți să eliminați {0}? Sigur doriți să ștergeți {0} jocuri? Sunteți sigur că doriți să eliminați {0}? Selectarea opțiunii „adaugă la lista de excludere” va împiedica jocul să fie importat din nou atunci când biblioteca este actualizată. Sunteți sigur că doriți să eliminați {0} (de) jocuri? Selectarea opțiunii „adaugă la lista de excludere” va împiedica jocurile să fie importate din nou atunci când biblioteca este actualizată. Sunteți sigur că doriți să eliminați {0} (de) intrări care nu sunt utilizate în prezent? Nu s-au gasit câmpuri nefolosite Da (și adaugă la lista de excluderi) Există modificări nesalvate în această secțiune Se actualizează formatul librăriei de jocuri... Update-ul la baza de date a eșuat. Nu se poate face update la librăria de jocuri. {0} MB de spațiu liber este necesar. Eroare de joc Nu se poate porni jocul. {0} nu a fost găsit în baza de date. Nu poate fi pornită camera: {0} Nu se poate incepe actiunea: {0} Nu se poate deschide locatia jocului: {0} Nu s-a putut detecta dimensiunea de instalarea a jocului: {0} Eroarea la scanarea dimensiunii de instalare Au fost {0} erori în timpul scanării dimensiunii de instalare Nu se poate crea scurtătura: {0} Nu s-a putut deschide manualul: {0} Nu se poate instala jocul: {0} Nu se poate dezinstala jocul: {0} Nu s-au găsit acțiuni valide de pornire a jocului. Atunci când utilizați acțiuni de emulator, asigurați-vă că definițiile platformei se potrivesc între configurația jocului și emulatorului. Implementarea instalației nu este disponibilă. Plugin-ul de bibliotecă responsabil pentru acest joc este dezactivat sau nu este instalat. Download-ul oficial pentru informații metadata nu este disponibil. Nu a fost selectat jocul. Executarea scriptului jocului a eșuat. Executarea scriptului de aplicație a eșuat. Executarea scriptului global a eșuat. Executarea scriptului de emulator a eșuat. Executarea scriptului de acțiune de rulare a eșuat. PowerShell 3.0 (sau mai nou) nu este instalat. Nu s-a putut determina cum se pornește jocul. Activat Dezactivat Elimină Șterge titlul nefolosit Redenumire Copiază Adaugă Pictogramă Implicită Imagine Cover implicită Imagine implicită de fundal Finalizare Următorul Înapoi EFECTUAT ÎNAPOI Eliberează Curăță Destituie Destituie Toate Importaţi Nume Autor Modul Serie Versiune Ultimul rulat Cele mai jucate Total redat Dimensiunea instalării Dosar Notițe Adăugat Data Adăugării Modificat Data Modificării Website Locație OK Salvează Închide Anulează Confirmare Restabilire Da Nu Bine ați venit Utilizator local General Media Link-uri Instalare Acțiuni Se descarcă... Se descarcă metadata... Se încarcă... Tip Profil Profile Elimină Descarca Căutare Rezoluție: Oricare Zoom Afișaj mod Listă Coperte Afișare mod Grilă Afișare mod Detaliu Personalizat URL Mulțumiri speciale Licenţă Contribuitori Playnite se închide... Astăzi Ieri Luni Marţi Miercuri Joi Vineri Sâmbătă Duminică Săptămâna trecută Luna trecută Anul trecut Mai mult de un an 0 Mo la 100 Mo 100 Mo la 1 GiO 1 GiO la 5 GiO 5 GiO la 10 GiO 10 GiO la 20 GiO 20 GiO la 40 GiO 40 GiO la 100 GiO 100 GiO sau mai mult Import realizat cu succes. Toate jocurile Id de joc Id de baza de date Setări predefinite Coloană Coloane Rând Rânduri Nu s-a putut obține pictograma de la acțiunea de rulare. Nu este prezentă nicio acțiune de tip „ tip fișier”. Descărcare doar metadatele lipsă Activarea acestei opțiuni va omite descărcarea metadatelor pentru câmpurile de date care conțin deja informații. Selecția de jocuri Selectați ce jocuri ar trebui actualizate cu metadate noi: Toate jocurile din baza de date Toate jocurile filtrare la moment Numai jocurile selectate Magazin Oficial IGDB Selectați câmpurile dorite pentru ca Playnite să le populeze automat și ce surse vreți să utilizați pentru a obține datele. Te rog consideră să apeși sigla de mai sus și să contribui cu actualizări la data de baze igdb.com ca să îmbunătătim funcționalițatea Playnite. Se descarcă metadata... Se importă jocurile instalate... Se importă {0} jocuri... Importare jocuri emulate din {0}… Se descarcă actualizările librăriilor... Se scanează dimensiunea jocurilor din bibliotecă… Se scanează dimensiunea jocurilor importate… Actualizarea librăriei s-a finalizat Se eliberează resursele... Configurare Setări… Platforme și Emulatoare Configurați Emulatoarele... Managerul Librăriei... Utilitare Descărcare Metadata... Ustensile Software Configurare integrări... Deschideți lansator terț Deschideți platformele terț Actualizare Librărie Anulare actualizare bibliotecă Actualizare dosare emulate Adaugare Joc Adaugare Manuală... Scanare Automată... Joc Emulat... Aplicație Microsoft Store… Despre Playnite Exprimați-vă Părerea Schimbă în mod Fullscreen Link-uri Ajutor Ajutați-ne pe Patreon Susțineți-ne pe Ko-fi Manual de utilizare Documentaţie Trusă de Dezvoltare Repornire Sistem Închidere Sistem Suspendare Sistem Hibernează Sistemul Blocare sistem Deconectare utilizator Alege un joc la întâmplare Câmpurile de jocu care vor fi afișate pe panoul de detalii: Spațiere item Arată craiorajul fundalului articolului Lățimea marginii craiorajului articolului Sursa iconiței jocului este lipsă Sursa coperții jocului este lipsă Sursa fundalului jocului este lipsă Spațiere verticală la detaliile jocului Poziția panoului de detalii în modul de vizualizare copertă Poziția listei cu jocuri în modul de detalii Desenează separator între panouri Înălțimea copertei de joc Înălțimea pictogramei listei de jocuri Fontul aplicației Font monospațiat Poziția Panoului de Filtre Poziția Panoului de Căutare Randează Coperta Jocului Corectați raportul de aspect Mod lărgit Cutie DVD Epic Games Store GOG Galaxy 2.0 IGDB Pătrat Banner Steam Copertă vericală Steam Twitch * Repornirea este necesară pentru a aplica Setări General Panoul de sus Aspect vizual Detalii joc Orientare Avansat Ecran complet Intrare Performanță Metadata Se actualizează Căutare Copie de rezervă Fă o copie de rezervă a bibliotecii Restaurare copie de rezervă Importați schimbările în librărie în mod automat Locație către baza de date este invalidă, trebuie setată în mod corect. Numele contului nu poate fi gol. Descarcă metadata după ce imporți jocurile Lansează Playnite la bară Lansează Playnite la pornirea calculatorului Deschidere în bara de procese Nu s-a putut seta Playnite pentru pornirea deodată cu calculatorul. Lansează în mod Fullscreen Încărcarea asincronă a imaginilor Îmbunătățește performanța în scroll-ul listelor de jocuri, dar imaginile se vor încărca mai incet. Arată numele jocului dacă coperta lipsește Arata numele jocurile in modul "Grid" de redare Întunecați jocurile neinstalate Arata iconitele jocurilor in modul de redare "Detalii" Arata numarul de obiecte in descrierile grupurilor Arată doar câmpurile atribuite în panourile de filtru și explorare Dezactivează accelerarea hardware Foloseste daca intalneste sacadare sau alte probleme legate de interfata Arată jocurile ascunse în liste lansare rapidă Număr de obiecte lansare rapidă Folosește imaginea de fundal a jocului ca fundal al ferestrei Blurare fundal Calitate înaltă Întunecare fundal Arată în vizualizarea tip grilă Temă Profilul temei Tema pentru Fulscreen Profilul Temei Fullscreen Locaţia bazei de date Stare de conectare: Setari Playnite Goliţi cache-ul web Ar putea rezolva probleme întâlnite la legarea conturilor. Arata iconița în bara de sistem Minimizează Playnite în bara de sistem Minimizează Playnite în bara de sistem când aplicația este închisă Când jocul pornește: După ce jocul se închide: Formatează timpul jucat pentru a indica numărul de zile jucate Formatul datei: Opțiunea aceasta vă v-a deloga de pe toate serviciile conectate. Repornirea aplicației este necesară, doriți să continuați? Curățați Cache-ul? Repornirea programului este necesară pentru aplicarea noii teme Obțineți mai multe teme Creează o temă nouă Caută mai multe extensii Creați o extensie nouă Ajută-ne să traducem programul PIaynite Playnite trebuie să fie repornit pentru a aplica noile setări. Reporniți acum? Repornirea va anula orice sarcini active (descărcări) în curs de desfășurare. Reporniți Playnite? Playnite nu poate muta fișierele bibliotecii automat. Trebuie să mutați manual/să copiați fișierele înainte de a schimba locația. Dacă nu există nici o bibliotecă în locația țintă, va fi creată una nouă. Noua locație a bazei de date nu va fi utilizată până când Playnite nu va fi repornit. Durata jocului nu va fi înregistrată dacă este setată acțiunea de "Închidere". Număr de rânduri Număr de coloane Număr de rânduri în modul de vizualizare îndetaliat Arată imaginea de fundal pe ecranul principal Nu se aplică retroactiv jocurilor existente fără re-descărcarea metadatelor. Importă timpul jucat al jocurilor în bibliotecă: Configurează când trebuie ca Playnite să importe timpul jucat de plugin-urile bibliotecii pentru jocurile din baza de date Playnite. Pentru a putea utiliza această caracteristică, este nevoie de suportul plugin-urilor ce gestionează biblioteca de jocuri. Întotdeauna: Importați timpul jucat pentru jocurile noi importate și existente în baza de date Playnite. Doar pentru jocurile nou importate: Importați timpul jucat doar pentru jocuri noi importate. Niciodată: Nu importa niciodată timpul jucat în nicio circumstanță. Întotdeauna Doar pentru jocurile nou importate Niciodată Activare suport pentru manetă în modul desktop Butonul Guide al gamepad-ului deschide modul Fullscreen Setări pentru descărcarea automată a Metadata pentru jocurile nou importate. Ecran țintă Utilizare întotdeauna ecranul principal Afișează Titlurile Jocului Afișează starea bateriei Afișează procentajul bateriei Afişaţi ceasul Ascunde cursorul mouse-ului Instalat doar în filtre rapide Butoane Gamepad Orientare Derulare orizontală Selectați una dintre subsecțiuni Nici o setare valabilă Încărcarea setărilor eșuată Aceste scripturi sunt executate pentru fiecare joc din bibliotecă. Scripturile individuale pot fi atribuite pentru fiecare joc separat în timp ce editați detaliile jocului. Animează tranzițiile imaginii de fundal Dimensiuni font Automat Greyscale (tonuri de gri) ClearType (font) Ideal Ecran Mod formatare text Mod randare text Metodele de redare și formatare a textului nu sunt utilizate în prezent pentru descrierile jocurilor. Preîncarcă imaginile de fundal Dacă este activat, Playnite va descărca ilustrații de fundal în timp ce descarcă metadate, utilizând mai mult spațiu pe disc și făcând ilustrațiile disponibile atunci când sunteți offline. Dacă este dezactivată, ilustrația de fundal este descărcată numai atunci când este nevoie prima dată, folosind mai puțin spațiu, dar poate duce la o întârziere înainte ca ilustrația să fie afișată și este posibil ca unele imagini să nu fie disponibile când sunteți offline. Închide automat clientul terț după ieșirea din joc Întârziere închidere client (în secunde) Nu închide după o sesiune de joc mai scurtă decât (în secunde) Inchiderea automată a următoarelor clienturi: Închidere automată a platformelor terț Importă lista de excluderi Afișează avertisment atunci când se atribuie fișiere media prea mari pentru joc Comandă deschidere dosar Organizația preferată de evaluare a vârstei Actualizați dimensiunea de instalare a jocurilor la actualizarea bibliotecii Scanează și actualizează dimensiunea de instalare a jocurilor, dacă este detectat că fișierele lor au fost modificate de la ultima scanare Nimic Umplere Uniform Stânga Dreapta Vârf Fund Eroare de import Autentificare necesară Autentificare eșuată Mod alternativ de redare web Utilizați atunci când întâmpinați probleme cu vizualizările web, de exemplu casetele de dialog de autentificare pentru integrări. Încărcare parțială a descrierilor mari de jocuri Descrierile mari pot provoca o întârziere vizibilă la selectarea jocurilor. Când este activată, doar o parte din textul descrierii va fi inițial încărcată cu o opțiune pentru a încărca restul la cerere. Import Metadata Descărcați metadate Setați configurația selectată pentru a fi utilizată pentru orice viitoare descărcări de metadate. Poate fi modificată, de asemenea, în setările aplicației. Procedura Import Emulare Acest asistent vă va ghida prin procesul de descărcare și import de emulatoare de consolă și de import de jocuri emulate. Rețineți că puteți adăuga oricând emulatoare și/sau jocuri suplimentare ulterior prin meniul principal (sub meniul „Bibliotecă” pentru setările emulatorului și meniul „Adăugați jocuri” pentru jocurile emulate). Mai jos este o listă de emulatoare pe care Playnite îi poate recunoaște și configura automat. Puteți descărca programe de instalare a emulatoarelor de pe site-urile lor web. După ce aveți emulatoarele instalate (manual), le puteți importa în dialogul de configurare a emulatorului. Puteți importa orice emulator care este instalat pe computer făcând clic pe butonul „Detectează automat din folder...”. Playnite va căuta în folderul selectat orice emulator cunoscut și va oferi opțiunea de a le importa. Puteți folosi acest buton de mai multe ori pentru a importa emulatoare din foldere diferite. Emulatoarele vor fi adăugate în partea de jos a listei curente. Scanează folder-ul ce conține Emulatorul Selectare fișiere Setări Emulatoare... Se caută... Setare pentru prima dată Integrare Librărie Setare Finalizată Setări Platforme și Emulatoare Setări Emulatoare Platforme Platformă Emulatoare Emulator Adăugați Platformă Selectați Iconiță Selectați Copertă Selectați Imaginea Selectaţi Item Selectați Fundal Selectați Fișier Selectați URL Adăugați Emulator Platforme Suportate Doriţi să salvaţi modificările la platformă? Doriţi să salvaţi modificările la emulator? Executabil Parametrii Directorul de lucru Tipuri de fișiere suportate Importă Emulatoare... Descarcă Emulatoare... Încarcă argumentele prestabilite de la profilul emulatorului cunoscut Ajutor Setări Sortează după Sortare direcție Grupare după Ascendent Descendent Nu grupa Grupează după Librărie Grupează după Categorie Grupează după Platformă Tip de Vedere Vizualizare Panou Explorator Panou Filtre Iconiță Iconiță bibliotecă Imagine Copertă Imagine Fundal Sortează după Nume Librărie Manualul Jocului Nume Nume Cont Platformă Categorie Gen Data Lansării Anul Lansării Dezvoltator Etichetă Publicist Starea instalării Instalat Instalat Neinstalat Ascuns Favorit Activare suport HDR Ultima Dată Jucat Categorie Descriere Directorul de instalare Imagine Copertă Link-uri Calea către Imagine, ROM sau ISO Gen Genuri Companie Companii Dezvoltator Dezvoltatori Publicare Producători Categorie Categorii Etichetă Etichete Caracteristică Caracteristici Rating Vârstă Rating Vârstă Regiune Regiuni Sursă Surse Activitate recentă Eroare bază de date Nu s-a putut deschide baza de date a librăriei. Baza de date nu a fost deschisă. Nu puteți accesa baza de date a librăriei. Fișierul "{0}" este utilizat de un alt proces sau este într-o locație inaccesibilă. Nu s-a reușit crearea arhiva de diagnosticare. Nu s-a reușit trimiterea automată a arhivei de diagnosticare. Nu s-a putut importa jocuri de la {0}. Link-ul nu a putut fi deschis, URL-ul nu are un format valid. Aplicația nu s-a putut lansa. Editare Detalii Joc Imagine URL Adaugă Link Salvează Modificările Adaugă Acţiune Șterge Acțiunea Ștergere buton de lansare Adăugare Jocuri Caută Folder-ul... Detectează instalările Navigare... Deschide Playnite Setări de Profil Nume jocului nu poate fi gol. Nume jocului nu poate fi gol înainte de căutarea metadatei. Data jocului invalidă Introduceți o adresă URL validă începând cu http:// sau https:// Selectați URL Nu s-a putut descărca metadata: {0} Eroare la descărcare Curăță Filtrele Cont Privat Cont Public API Key Eroare la Pornire Eroare Temă Curăță Tot Se instalează Se dezinstalează Se lansează Rulează URL invalid Repaus Minimizați Restaurați Fereastra Închideţi Schimbați Avansat Niciodată Stare de Finalizare Scorul Utilizatorului Scorul Criticilor Scorul Comunității Script-uri Plugin-uri Surse de metadata Extensii ID-ul extensiei Reîncarcă Scripturile Toate script-urile au fost reîncărcate cu succes. Niciun element găsit Revenire la modul Desktop Ieșire Playnite Librării Actualizează Tot Creat de: Versiune: Modul: Librărie Statistici Toate Nimic Notificări Lățime Înălţime Mărime Micșorat Normal Mărit Mai mare Cel mai mare Prestabilit Selectare Selectează tot Primul Aleator Încărcați mai multe Transparent Restrângere Extinde Restrânge tot Extinde tot Altele Teme Argumente Emulator Argumente adiționale la Emulator Suprascriere Argumentele Emulatorului Selectați Jocurile pentru Importare Căutare metadata Versiune nouă disponibilă Schimbări de la ultima actualizare Descarcă şi actualizează programul Verificați dacă sunt disponibile Actualizări Eroare la actualizare Nu s-a putut căuta versiunea nouă. Sunteți la curent cu cea mai nouă versiune. Actualizarea nu a putut fi descărcată și instalată. Reîncarcă lista de teme Aplicați tema selectată Urmărește schimbări în fișier Aplică automat tema pentru orice modificare la fișierului sursă Durata script-ului Execută script-ul global Global Filtrat Curent Nou Salvați ca setare implicită Adaugă la Favorite Șterge de la Favorite Ascunde acest joc Șterge din lista jocurilor ascunse Editare... Setează Categoria... Setează starea de finalizare Şterge Jucați Instalaţi Opțiuni de joc Detalii Dezinstalați Deschide Directorul de Instalare Creează scurtătură pe Desktop Deschide Manual Aflați mai multe Controlat de plugin-ul librăriei Sfat: Puteți folosi un proces mai avansat de descărcare a metadatelor în timpul editării unui joc prin opțiunea de "Editare" din meniu. Nedisponibil când se execută o acțiune. Textul descrierii trebuie să conțină o sintaxă HTML senzitivă Timpul jucat este înregistrat în secunde. Pentru a nu acorda un scor folosiți valori între 0 și 100 sau lăsați gol. Cod, localizare și alți contribuabili în nici o ordine particulară: Încetați monitorizarea jocului? Timpul Jucat Ultimul Joc {0}oră {1}minut {0} minute {0} secunde Nu a fost jucat Deschideți în modul Desktop... Deschideți în modul Fullscreen... Se încarcă librăria de jocuri... Nu s-a putut instala fișierul script. Script instalat cu succes. Instalează Script Eroare la script Nu s-a putut executa funcția extensiei. Deschideți folderul de metadate Programul {0} nu este instalat. Se va deschide clientul {0}. Vă rugăm să vă logați și să închideți mesajul acesta. Calea de instalare a jocului nu a fost gasită. Setarea acțiunii jocului este invalidă. Depanarea problemelor de sincronizare a contului Probleme de troubleshooting Redenumește articol Adauga articol nou Introduceţi numele Introduceţi numele nou În mai puțin de o oră Între 1 și 10 ore Între 10 și 100 ore Între 100 și 500 ore Între 500 și 1000 ore Mai mult de 1000 ore Playnite trebuie repornit pentru a putea termina instalarea. Doriți să reporniți acum? Instalare cu succes Instalați Extensia? Generic Instalarea temei a eșuat. {0} Doriți să instalați o nouă temă? {0} De {1} Versiunea {2} Avertisment de performanță Nu mai afișa Temele integrate nu pot fi dezinstalate. Extensiile integrate nu pot fi dezinstalate. Această extensie nu suportă versiunea actuală de Playnite. Directorul de instalare Importă fișireul... Ce-i asta? Sunteți sigur? Timpul total de joc Timpul mediu de joc Timpul maxim de joc Vedere de ansamblu Bara Laterală Realizari Forum Ştiri Pagina magazinului Jucat Recent Favorite Cel mai jucat Toate Sunt aplicate filtrele. Rezultatele căutării pentru: Alege altul ================================================ FILE: source/Playnite/Localization/ru_RU.xaml ================================================  Русский Язык Playnite Выход Фильтр активен Фильтр отключен Доп. фильтры Фильтры Фильтр Неверные данные Сохранить изменения? Домашняя страница: www.playnite.link Исходный код на GitHub Собрать файл для диагностики Отправить информацию для диагностики О Playnite Создано Йозефом Немеком (Josef Němec) Задать категорию Выбрать категорию Добавить категорию Выбрано - Категория назначена Не выбрано - Категория не назначена Не определено - Без изменений (когда редактируется несколько игр) Нет категории Нет платформы Ой! Что-то пошло не так… Произошла неисправимая ошибка. Если вы хотите помочь нам исправить эту проблему, кратко опишите действия, предпринятые перед сбоем, а затем отправьте сведения для диагностики. Если вы подключены к Интернету, диагностический пакет будет сам загружен на сервер Playnite для анализа. Кроме того, вы можете нажать на кнопку «Отчёт о сбое», чтобы самостоятельно указать на проблему в разделе «Issues» репозитория на GitHub, приложив туда этот отчёт. Спасибо вам за вашу помощь. Расширение "{0}" вызвало неисправимую ошибку. Мы рекомендуем сохранить лог-файл и сообщить о проблеме разработчику расширения. Если проблема снова возникнет, отключите расширение. Расширение "{0}" вызвало неустранимую ошибку. Рекомендуем сообщить о проблеме разработчику расширения. Если она повторится, отключите расширение. Неизвестный компонент (расширение или тема) вызвал неустранимую ошибку. Рекомендуем поочередно отключать сторонние дополнения, чтобы выявить проблемное и затем рассказать о проблеме его разработчику. Произошла неустранимая ошибка. Чтобы помочь решить проблему, пожалуйста, вышлите нам данные для диагностики. Спасибо. Отключить расширение Сохранить лог файл Выслать диагн. данные Сообщить о сбое Перезапустить Playnite Перейти в безопас. режим Отключает все сторонние расширение и применяет тему по умолчанию. Выйти из Playnite Порядок действий перед сбоем (опишите на английском языке): Менеджер библиотеки Убрать игру(ы) из списка? Невозможно убрать - запущена игра или установщик. Невозможно удалить - запущена игра. Вы уверены, что хотите убрать из списка {0}? Вы уверены, что хотите убрать из списка эти игры ({0} шт.)? Вы уверены, что хотите убрать из списка {0}? Опция "добавить в список исключений" предотвратит автоматический импорт игры при следующем обновлении библиотеки. Вы уверены, что хотите убрать игры ({0} шт.) из списка? Опция «добавить в исключения» предотвратит их повторное появление в списке при следующем обновлении библиотеки. Вы уверены, что хотите удалить значения неиспользуемых полей ({0} шт.)? Неиспользуемые поля не найдены. Да (добавить в исключения) В этой вкладке есть несохраненные изменения Обновление формата базы данных… Сбой обновления базы данных. Не удалось обновить список игр. Требуется {0} МБ свободного места на диске. Ошибка игры Не удалось запустить игру. '{0}' не найдена в базе данных. Не удалось запустить игру: {0} Не удалось начать действие: {0} Не удалось открыть расположение игры: {0} Не удалось определить размер игры: {0} Ошибка сканирования размера игры Ошибок при сканировании размера игры: {0} Не удалось создать ярлык: {0} Не удалось открыть руководство: {0} Не удалось установить игру: {0} Не удалось удалить игру: {0} Не найдено действительных действий запуска игры. При использовании действий эмулятора убедитесь, что определения платформы совпадают в конфигурации игры и эмулятора. Проведение установки недоступно. Плагин для клиента, отвечающий за эту игру, отключен или не установлен. Получение официальных метаданных невозможно. Ни одна игра не была выбрана. Не удалось выполнить скрипт игры. Не удалось выполнить скрипт приложения. Не удалось выполнить глобальный скрипт. Не удалось выполнить скрипт эмулятора Не удалось выполнить действие скрипта игры. PowerShell 3.0 или новее не установлен. Не удалось определить, как запустить игру. Включено Откл. Убрать Удалить неиспользуемые Переименовать Копировать Добавить Иконка по умолчанию Обложка по умолчанию Фоновое изображение по умолчанию Завершить Далее Назад ГОТОВО НАЗАД ОЧИСТИТЬ Снять выбор Скрыть Скрыть все Выбрать Название Автор Модуль Серия игр Версия Запускалась Самая запускаемая Запускалась раз(а) Размер игры Папка Заметки Добавлено Дата добавления Изменено Дата изменения Сайт Путь Окей Сохранить Закрыть Отменить Подтвердить Сбросить Да Нет Добро пожаловать Локальная учетка Основное Изображения Ссылки Место установки Действия Скачивание… Загрузка картинок… Загрузка… Тип Профиль Профили Убрать Скачать Поиск Разрешение: Любая Масштаб Отображение списком Обложка В виде сетки Детали Свой Ссылка Особая благодарность Лицензия Участники Выходим из Playnite... Сегодня Вчера Понедельник Вторник Среда Четверг Пятница Суббота Воскресенье На прошлой неделе В прошлом месяце В прошлом году Больше года назад от 0 до 100МБ от 100 МБ до 1 ГБ от 1 Гб до 5 Гб от 5 ГБ до 10 ГБ от 10 Гб до 20 Гб от 20Гб до 40ГБ от 40ГБ до 100ГБ от 100 ГБ или больше Импорт успешно завершен. Все игры ID игры ID базы данных Шаблоны Столбец Столбцы Строка Строки Не удалось получить значок действия «Играть». К действию не привязано ни одного типа файла. Докачать только недостающие метаданные Включение этой опции пропустит загрузку метаданных для полей данных, которые уже содержат информацию. Выбор игр Выберите, к каким играм следует обновить метаданные: Все игры из базы данных Все отфильтрованные игры Только выбранные Поля метаданных не выбраны Поля метаданных для загрузки не выбраны. Пожалуйста, выберите хотя бы одно и включите хотя бы один источник метаданных. Офиц. магазин IGDB Выберите, какие поля должны быть автоматически заполнены Playnite и какие источники должны использоваться для получения данных. Пожалуйста, нажмите на логотип выше и внесите изменения в базу данных IGDB.com, чтобы улучшить данные, используемые Playnite. Загружаем метаданные... Импортируем установленные игры… Импортируем игры из {0}… Импортируем эмулированные игры из {0}… Загружаем обновления библиотеки… Сканирование размера игр в библиотеке… Сканирование размера импортируемых игр… Обновление библиотеки завершено Освобождаем ресурсы… Конфигурация Настройки… Платформы и эмуляторы Настроить эмуляторы… Менеджер библиотеки... Инструменты Загрузить метаданные… Программные средства... Настроить интеграции… Открыть сторонний клиент Сторонние клиенты Обновить список игр Отменить обновление библиотеки Обновить папку Эмуляторов Добавить игру Вручную… Найти установленные... Эмулированная игра... Приложение из MS Store… О Playnite Отправить отзыв Перейти в полноэкранный режим Ссылки Справка Поддержать на Patreon Поддержать на Ko-fi Руководство пользователя Документация по SDK Перезагрузка Завершение работы Спящий режим Гибернация Заблокировать систему Выйти из аккаунта Подобрать игру наугад Что показывать на панели подробностей: Отступ между элементами Рисовать фон элемента сетки Толщина рамки Источник при отсутствии иконки игры Источник при отсутствии обложки игры Источник при отсутствии фона игры Расстояние по вертикали до описания игр Положение панели подробностей Положение списка игр Рисовать разделитель между панелями Высота обложки Высота значка списка игр Шрифт программы Моноширинный шрифт Положение панели фильтра Положение панели проводника Отрисовка обложки Желаемое соотношение сторон След. параметры также влияют и на отрисовку макета в полноэкранном режиме! Подогнать под соотношение DVD Box Epic Games Store GOG Galaxy 2.0 IGDB Квадратное Баннер Steam Вертикальная обложка Steam Twitch * Требуется перезапуск для применения Настройки Основные Верхняя панель Внешний вид Подробнее об игре Макет Расширенные Полноэкранный режим Ввод Производительность Метаданные Обновление Поиск Резервное копирование Резервное копирование данных библиотеки Восстановить резервную копию данных Автоматически импортировать изменения Не найдено расположение базы данных, должен быть указан верный путь. Имя учетной записи не может быть пустым. Загрузка метаданных после импорта игр Запускать Playnite свернутым Запускать Playnite вместе с системой Запускать спрятанным в обл. уведомлений Не удалось добавить Playnite в автозагрузку вместе с системой. Запускать сразу в полноэкранном режиме Отложенная загрузка изображений Может улучшить плавность прокрутки списков игр за счет увеличения времени загрузки изображений. Показывать название для игр без обложки Показывать название игры внизу обложки Затемнять неустановленные игры Показ. иконку игры на панели подробностей Показ. кол-во элементов в описании групп Отображать только указанные поля на панелях Фильтра и Проводника Отключить аппаратное ускорение Используйте в случае заиканий и других проблем с интерфейсом. Отображать скрытые игры в контекстном меню Влияет на контекстное меню значка на панели задач и в трее. Кол-во элементов контекстного меню Использовать фоновое изображение игры в качестве фона окна Размытие фона Высокое качество Затемнение фона Показать в виде сетки Тема Профиль темы Полноэкранная тема Цвет полноэкранной темы Путь к базе данных Статус: Настройки Playnite Очистить кэш Может решить проблемы, возникающие при привязке учетных записей. Показать значок в трее Сворачивать Playnite в трей Сворачивать Playnite в трей при закрытии окна приложения При запуске игры: После закрытия игры: Преобразовывать "время в игре" в количество сыгранных дней Формат дат: После этого вы выйдете из всех связанных служб. Требуется перезапуск программы, продолжить? Очистить кэш? Для применения новой темы требуется перезапуск Playnite Найти больше тем Создать свою Больше расширений Создать свое Помогите нам перевести Playnite Для применения новых настроек необходимо перезапустить Playnite. Перезапустить сейчас? В случае перезапуска все активные задачи (загрузки), которые выполняются в данный момент, будут отменены. Перезапустить Playnite? Playnite не может перемещать файлы вашей библиотеки автоматически. Перед изменением расположения необходимо будет вручную переместить/скопировать файлы. Если в указанном расположении нет библиотеки, в таком случае будет создана новая. Новое расположение базы данных будет использоваться только после перезапуска Playnite. Время игры не будет засчитано, если выбрать действие «Закрыть». Кол-во строк Кол-во столбцов Кол-во строк в режиме подробностей Показ. фон игры на главном экране Playnite будет использовать доступные игровые скриншоты как фон, взамен фоновых изображений из магазина. Не применяется ретроспективно к существующим играм. Импортировать время игры в библиотеку: Определяет, когда Playnite должен импортировать время игры, указанное плагинами библиотек для игр в базе данных Playnite. Для использования этой функции необходима поддержка плагинов библиотеки, отвечающих за обработку одной или нескольких игр. Всегда: Импортирует время игры для недавно импортированных и уже имеющихся игр в базе данных Playnite. Только для недавно импортированных игр: Импортирует время игры только для недавно импортированных игр. Никогда: Никогда не импортирует время игры ни при каких обстоятельствах. Всегда Только для недавно импортированных игр Никогда Вкл. поддержку геймпада в режиме рабочего стола Кнопка Guide переводит в полноэкр. режим Автозагрузка метаданных для недавно импортированных игр. Приоритетный экран Всегда использовать основной дисплей Показ. заголовки игр Показ. состояние батареи Показ. % заряда батареи Показывать время Скрывать курсор мыши Во вкладках только установленные Подсказки кнопок геймпада Макет Горизонтальная прокрутка Выберите один из подразделов Нет доступных настроек Не удалось загрузить настройки Эти скрипты выполняются к каждой игре в библиотеке. Скрипты для конкретной игры можно настроить при редактировании данных об игре. Анимировать смену фонового изображения Размеры текста Авто Без сглаживания Градации серого ClearType Оптимальный Обычный Режим форматирования текста Режим отрисовки текста Настройки режимов отрисовки и форматирования текста не применяются к тексту описания игры. Предзагрузка фоновых изображений Если включено, Playnite будет загружать фоновые изображения при загрузке метаданных. Используется больше дискового пространства, но обеспечивается доступность изображений в автономном режиме. Если выключено, фоновые изображения загружаются только при необходимости. Используется меньше дискового пространства, но может привести к задержке перед отображением изображения, а также некоторые изображения могут быть недоступны в автономном режиме. Автоматически закрывать сторонний клиент после выхода из игры Время отключения клиента (в секундах) Не закрывать после игровой сессии короче чем (в секундах) Автоматически закрывать следующие клиенты: Автовыход из клиентов Импорт списка исключений Предупреждать, если медиаданные игры много весят Команда открытия папки Предпочт-я организация, определ. возрастные рамки Обновить размер игр при обновлении библиотеки Если было обнаружено, что с момента последнего сканирования файлы игр были изменены, то в таком случае запускается повторный процесс сканирования и обновления размера игр Нет Растянуть По размеру Заполнение Слева Справа Сверху Снизу Ошибка при импортировании Требуется авторизация Не удалось авторизоваться Альтернативный режим отображения веб-просмотра Используйте при возникновении проблем с веб-просмотром, например, с диалогами авторизации интеграции. Частичная загрузка длинных описаний игр Длинные описания игр могут вызывать заметные зависания при их выборе в интерфейсе. Если включено, вначале будет загружаться только часть описания, с возможностью загрузить продолжение по запросу. Импорт метаданных Скачать метаданные Установите выбранную конфигурацию для загрузки метаданных в будущем. может быть изменен в настройках приложения. Мастер импорта эмуляторов и игр к ним Этот мастер проведет вас через процесс загрузки и импортирования эмулятор и игр к ним. Имейте в виду, что вы всегда сможете добавить другие эмуляторы и/или игры позже через главное меню (в разделе «Библиотека» для настроек эмулятора и «Добавить игру» для эмулируемых игр). Ниже приведен список эмуляторов, которые Playnite может распознавать и автоматически настраивать. Вы можете самостоятельно скачать и установить их с официальных сайтов. После установки эмуляторов (вручную), вы можете импортировать их в диалоге настройки эмуляторов. Вы можете импортировать любые эмуляторы, установленные на вашем ПК, нажав кнопку "Найти в папке…". Playnite поищет в выбранной папке любые известные эмуляторы и затем предложит их импортировать. Вы можете импортировать из разных папок, воспользовавшись кнопкой неограниченное количество раз. Эмуляторы будут добавлены в конец текущего списка. Вы можете импортировать игры, нажав кнопку "Сканировать папку эмулятором". Выбранный эмулятор сообщит Playnite, какие типы файлов следует просканировать и импортировать. Вы можете импортировать из разных папок, воспользовавшись кнопкой неограниченное количество раз. Игры будут добавлены в конец текущего списка. Нет подходящих эмуляторов для импортирования. Вы не сможете автоматически импортировать какие-либо эмулированные игры, предварительно не настроив эмулятор. Вы действительно хотите продолжить и выйти из процесса импортирования? В Playnite нет настроенных эмуляторов. Вы не можете импортировать игры без предварительной настройки эмулятора и выбора соответствующих типов файлов. Вы хотите добавить эмуляторы сейчас? Сканировать папку эмулятором Выбрать файлы Найти в папке… Настроить эмуляторы… Сканирование… Сканирование {0}… Первая настройка Этот помощник поможет вам пройти через автоматический процесс импорта игр и настройку внешних игровых библиотек. Playnite может автоматически импортировать игры из множества игровых клиентов, таких как Steam или GOG, а также обновлять свою библиотеку, автоматически обновляя ее во время запуска приложения. Не забывайте, что вы всегда сможете добавить какую-либо игру для любой платформы вручную из главного меню, нажав кнопку "Playnite". Интеграция библиотеки Ниже приведен список некоторых интеграций с клиентами, официально поддерживаемых Playnite. Выберите те, которые хотели бы установить. Другие интеграции могут быть установлены позже из меню "Дополнения". Конфигурация завершена Начальная настройка завершена. Помните, что вы можете изменить все позже, в меню «Настройки». Вы также можете добавить любую игру позже, щелкнув на логотип Playnite. Не удалось скачать дополнение(я). Можете попробовать снова загрузить интеграции из меню аддонов по завершению начальной настройки. Загрузка интеграции {0} … Загрузка списка рекомендованных интеграций… Не удалось загрузить список рекомендованных интеграций. Вы можете попробовать снова загрузить интеграции позже через меню Дополнений. Настройка платформы и эмулятора Настроить эмуляторы Платформы Платформа Эмуляторы Эмулятор Добавление платформы Выбрать иконку Выбрать обложку Выберите изображение Выберите элемент Выбрать фон Выберите файл Выбрать URL Добавление эмулятора Поддерживаемые платформы Вы хотите сохранить изменения платформы? Вы хотите сохранить изменения эмулятора? Исполняемый файл Аргументы Рабочая директория Поддерживаемые типы файлов Импортировать эмуляторы… Скачать эмуляторы… Загрузка шаблона с аргументами из профиля известного эмулятора Вы уверены, что хотите удалить эмулятор {0}? Он используется в играх ({1} шт.). Вы уверены, что хотите удалить платформу {0}? Она используется в играх ({1} шт.) и эмуляторах ({2} шт.) Настройки справки Сортировать Порядок сортировки Группировать По возрастанию По убыванию Не группировать По стороннему клиенту По категории По платформе Структура Вид Панель проводника Панель фильтров Иконка Значок библиотеки Обложка Фон Сортировка Библиотека Руководство пользователя Название игры Путь установки Имя пользователя Платформа Категория Жанр Дата выхода Год выхода Разработчик Метки Издатель Состояние установки Соотв. всем фильтрам Если включено, то только игры, использующие все предметы во всех фильтрах, будут включены в просмотр. Если выключено, то игры, использующие любой предмет в любом фильтре, будут включены в просмотр. Установлено Установлена Не установлено Скрыто В избранном Включить поддержку HDR Если включено, перед запуском игры HDR будет включаться на основном экране. Обратите внимание, что HDR не поддерживается на вашем основном экране. Последний запуск Категория Описание Путь установки Обложка Ссылки Путь к образу, ROM или ISO Жанр Жанры Компания Компании Разработчик Разработчики Издатель Издатели Категория Категории Метка Метки Особенность Особенности Возрастной рейтинг Возрастной рейтинг Регион Регионы Источник Источники Недавняя активность Ошибка базы данных Не удалось открыть базу данных библиотеки. База данных не открыта. Отсутствует доступ к базе данных библиотеки. Файл "{0}" используется другим процессом или находится в недоступном месте. Не удалось создать файл с диагностикой. Не удалось автоматически отправить пакет диагностики. Диагностическая информация была успешно отправлена. Файл с диагностикой был успешно создан и загружен. Пожалуйста, приложите следующий ID к вашему отчету о проблеме: Не удалось импортировать игры из {0}. Не удалось импортировать эмулированные игры из {0}. Невозможно выполнить поиск игр по выбранному профилю эмулятора. Профиль не содержит расширений или платформ. Не удалось запустить Playnite. Закройте все остальные запущенные копии и повторите ещё раз. Не удалось применить тему "{0}", цветовой профиль "{1}" {2} Не удалось перейти по ссылке, проверьте правильность URL. Не удалось запустить приложение. Не удалось инициализировать компонент WebView. Playnite не может продолжить процесс запуска. Подробнее на сайте https://playnite.link/cefstartup. Не получается импортировать эмуляторы из-за отсутствующего или поврежденного файла со скриптами. Не удалсь выполнить действие меню. Редактировать данные игры Ссылка на изображение Добавить ссылку Добавить ROM Сохранить изменения Применить изменения в полях к редактируемым играм. Добавить действие Удалить Убрать действие «Играть» Добавить Сканировать папку... Обнаружить установленные Обзор... Открыть Playnite Настройки профиля Имя игры не может быть пустым. Папка для выполнения действия «Играть» не может быть пустой. Имя игры не может быть пустым перед поиском метаданных. Неверные игровые данные Введите действительный URL-адрес, начинающийся с http:// или https:// Выбрать URL Не удалось скачать метаданные: {0} Ошибка загрузки Очистить фильтры Закрытый профиль Открытый профиль API-ключ Ошибка при запуске Ошибка темы Очистить всё Установка Удаление Запускается Запущена Неверный URL Ничего не делать Свернуть Развернуть окно Восстановить окно только при запуске из пользовательского интерфейса Закрыть Изменить Дополнительное Никогда Статус Статус завершения Оценка пользователей Оценка критиков Оценка сообщества Скрипты игры Скрипты приложений Скрипты Плагины Источники метаданных Расширения ID расширения Перезагрузить скрипты Интерактивный SDK PowerShell Все скрипты успешно перезагружены. Не найдено игр по заданным критериям поиска/фильтра Ничего не найдено Выйти из полноэкранного режима Закрыть Playnite Библиотеки Обновить всё Создано: Версия: Обновлено: Модуль: Библиотека Статистика Все Нет Уведомления Ширина Высота Размер Маленький Средний Большой Оч. большой Огромный По умолчанию Выбрать Выбрать все Снять выделения Первое Случайное Выбрать Загрузить ещё Прозрачность Свернуть Развернуть Свернуть всё Развернуть всё Другое Темы Параметры для эмулятора Предустан. аргументы Свои аргументы Дополн. аргументы для эмулятора Перезаписать аргументы для эмулятора Действие «Играть» Выберите объекты для импорта Выбрать игры для импорта Поиск метаданных Вышла новая версия Изменения с момента последнего обновления Скачать и обновить Проверить наличие обновлений Ошибка обновления Не удалось проверить наличие новой версии. Обновления не найдены, у вас установлена актуальная версия. Не удалось скачать и установить обновление. В данный момент выполняется какая-то фоновая задача. Вы хотите отменить ее и продолжить обновление? В данный момент выполняется какая-то фоновая задача. Вы хотите отменить ее и выйти из Playnite? В данный момент выполняется какая-то фоновая задача. Переключение режимов отменит эту задачу. Вы все равно хотите переключиться? Доступно обновление для Playnite Перезагрузить список тем Применить выбранную тему Посмотреть изменения файла Автоприменение темы при изменениях исходного файла Тип скрипта Выполнить перед запуском игры Выполнить после выхода из игры Выполнить после запуска игры Выполнить при запуске приложения Выполнить при закрытии приложения Скрипт запуска игры Скрипт запущенной игры Скрипт при выходе из игры Выполнение глобальных скриптов Общее Отфильтровано Текущее Создать Проверить скрипт Показывать только выбранные элементы. Сохранить по умолчанию Добавить в избранное Убрать из избранных Скрыть эту игру Убрать из скрытых Включить поддержку HDR Отключить поддержку HDR Изменить… Вычислить размер игры Вычислить размер игры (Все) Вычислить размер установки (Только недостающих) Размер игры Выбрать категорию… Статус игры Убрать из списка Играть Установить Настройки игры Подробная информация Удалить с устройства Открыть расположение игры Создать ярлык на рабочем столе Открыть руководство Другое Управляется плагином для клиента Процесс запуска игры будет осуществляться плагином для клиента, ответственным за эту игру. Не найдено никакой информации об игре '{0}'. Совет: Вы можете использовать более продвинутый процесс скачивания метаданных во время редактирования данных одной игры с помощью опцию меню "Изменить". Недоступно, когда выполняется какое-либо действие. Текст описания опирается на синтаксис HTML Время в игре записывается в секундах. Размер игры указывается в байтах Дата выхода должна быть установлена в формате "год-месяц-день". Значения месяца и дня могут быть пропущены. Введите значение от 0 до 100, либо оставьте без оценки. Разработка Playnite поддерживается этими патронами и подписчиками Ko-fi: Люди, принимавшие участие в написании кода, переводе на другие языки и прочем: Отменить мониторинг игры? В настоящее время запущен мониторинг установки, вы хотите отменить процесс и вернуть игру в предыдущее состояние? В настоящее время ведется мониторинг запуска игры. Вы хотите отменить процесс и вернуть игру в предыдущее состояние? Времени в игре Последний запуск {0} д. {1} ч. {2} м. {0} ч {1} мин {0} мин {0} сек Не запускалась Переход в обычный режим… Переход в полноэкранный режим… Загрузка библиотеки игр… Вычисление размера игры… Вычисление размера игры {0}… Не удалось установить файл скрипта. Скрипт успешно установлен. Установить скрипт Ошибка скрипта Не удалось выполнить функцию расширения. Открыть папку метаданных Вычислить Автоматически вычисляет размер игры, используя диски, если игра была не установлена, или каталог установки, если он был задан. Клиент {0} не установлен. Клиент {0} сейчас будет открыт. Войдите в свою учетную запись и закройте это сообщение. Ожидание входа в учетную запись, закройте сообщение, когда завершите процедуру… Папка с установленной игрой не найдена. Неправильная конфигурация для действия «Играть». Устранение неполадок синхронизации учетной записи Устранение неполадок Переименовать элемент Добавить новый Введите имя Введите новое имя Менее часа От 1 до 10 часов От 10 до 100 часов От 100 до 500 часов От 500 до 1000 часов 1000+ Playnite необходимо перезапустить, чтобы завершить установку. Перезапустить сейчас? Расширение упаковано неправильно. Тема не упакована должным образом. Не удалось загрузить должным образом расширение "{0}". Невозможно загрузить расширение "{0}", текущая версия Playnite не поддерживается. Не удалось загрузить должным образом тему "{0}". Невозможно загрузить тему "{0}", текущая версия Playnite не поддерживается. Не удалось загрузить должным образом расширение. Не удалось загрузить должным образом тему. Тема/Расширение использует неподдерживаемую версию API. Установка прошла успешно. Установить дополнение? Основные Не удалось установить дополнение "{0}". Не удалось установить расширение {0} Вы хотите установить новое расширение? {0} Автор: {1} Версия {2} Вы хотите обновить расширение "{0}"? Ваша версия: {1} Новая версия: {2} Не удалось установить тему. {0} Вы хотите установить новую тему? {0} Автор: {1} Версия {2} Вы хотите обновить тему "{0}"? Ваша версия: {1} Новая версия: {2} Вы собираетесь покинуть Playnite и перейти на следующую веб-страницу, используя свой веб-браузер по умолчанию. Вы хотите продолжить? {0} Выбранные изображения могут оказаться слишком большими для оптимальной производительности. Использование таких изображений может привести к ухудшению отзывчивости интерфейса и повышенному потреблению памяти. Максимальное рекомендуемое разрешение: Иконки: {0} МП Обложки: {1} МП Фоны: {2} МП Предупреждение о падении производительности Больше не показывать Файл с расширением {0} не поддерживается. Неподдерживаемое расширение файла Выбранный файл изображения может быть слишком большим для оптимальной производительности. Вы уверены, что хотите удалить выбранную тему? Операция будет поставлена в очередь до следующего запуска приложения. Встроенные темы нельзя удалять. Выбранная тема не поддерживает эту версию Playnite. Вы уверены, что хотите удалить выбранное расширение? Операция будет поставлена в очередь до следующего запуска приложения. Встроенные расширения нельзя удалять. Это расширение не поддерживает данную версию Playnite. Путь установки Папка с данными Сбор сведений для диагностики… Отправка сведений для диагностики… Импорт файла… Что это? Вы уверены, что хотите это сделать? Общее время Время в среднем Рекордное время Общий размер установки Обзор Боковая панель Показать на боковой панели Сбросить настройки Все настройки приложения будут сброшены к значениям по умолчанию, исключая: - Расположение базы данных - Импорт списка исключений - Настройки расширения, включая интеграцию библиотек Для завершения процесса требуется перезапуск приложения. Вы хотите сбросить настройки? Для разработчиков Внешние расширения Введите полный путь к папке. Достижения Форум Новости Страница в магазине Первичная настройка не завершена. Playnite будет запущен в обычном режиме, чтобы закончить процедуру. Недавно сыгранные Избранные Частые Все Применена сортировка. Применены другие фильтры. Результаты поиска для: Элемент с таким именем уже существует. Ограничить подбор текущим фильтром Подобрать другую Дополнения... Установленные Настройки расширений Обзор Обновления Обновления ({0}) Управление установленными расширениями и темами, включая их настройки, перенесено в новое меню «Дополнения». Здесь можно настроить все установленные интеграции с клиентами. Если вы хотите установить или удалить другие интеграции, используйте опцию «Дополнения…» в главном меню. Темы для стандартного режима Темы для полноэкранного режима Поиск… Дополнение не поддерживается в этой версии Playnite. Не удалось загрузить установочные файлы дополнения. Не удалось получить ссылку для установки дополнения. Требуется перезапуск приложения для применения изменений. Это дополнение запланировано к установке. Установить Переустановить Удалить Уже установлено Обновления для дополнений не найдены. Обновить дополнения Список изменений недоступен Запланировано к установке Ошибка загрузки Лицензия отклонена Загрузка {0}… Проверка наличия новых версий дополнений… Поиск обновлений программы… Доступны новые версии дополнений. Выберите элементы для обновления Экземпляр разработки расширения Лицензионное соглашение {0} Принять Отклонить Включить действие «Играть» из плагина для клиента Выберите действие Режим поиска Путь отслеживания Задержка отслеживания Частота отслеживания Ссылка Файл Эмулятор Скрипт По умолчанию Процесс Папка Исходный процесс Название процесса Сообщения о трассировке логов Последующие изменения перезаписывают данные для всех выбранных в данный момент игр! Ничего По размеру Только предметы Только начало и конец Чувствительность скроллинга Плавный скроллинг Скорость анимации Убрать объект? Вы действительно хотите убрать этот объект? Отображать кнопки на верхней панели: Настройки отображения Настройки группы Настройка сортировки Пресеты фильтров Положение элементов плагина Ширина разделителя секции Отображать кнопку главного меню на боковой панели Панель проводника Подбор случайной игры Выбрать случайную игру из списка Выбрать случайную игру из списка Сохранить настройки группировки и сортировки Показывать как быстрый фильтр в полноэкранном режиме За последние 7 дней За последний 31 день За последние 365 дней Более 365 дней назад Настроить Сохр. шаблон Свернуть после запуска игры Сворачивать Playnite после запуска игры. Отключение этой опции может привести к проблемам, так как на игры не будет переводиться фокус системы при их запуске! Размер текста Размер подсказок Поддержка игровых контроллеров Если этот параметр отключен, Playnite не будет принимать данные любых игровых контроллеров. Отключите, если используете сторонние инструменты, которые преобразуют входные данные игровых контроллеров в нажатия клавиатуры/мыши, иначе столкнетесь с проблемой двойного срабатывания в Playnite. Отображать иконки в основном меню: Инвертировать привязку кнопок X/A на главном экране Меняет местами привязки кнопок для запуска игры и отображения страницы сведений об игре на главном экране. Поменять местами кнопки подтверждения/отмены привязки Инвертирует привязки кнопок A/B для подтверждения и отмены. Только основной геймпад Когда опция включена, принимаются входные сигналы только от основного геймпада. Кнопка Guide фокусирует Playnite Громкость в полноэк. режиме Громкость фоновой музыки Заглушать звук при сворачивании Не удалось инициализировать аудио интерфейс. API вывода API, используемый для вывода звука. Поменяйте на другое, если у вас возникли проблемы со звуком. Основные Внешний вид Аудио Расположение Меню Ввод {0} запускается… {0} запущена… Caps Пробел Режим отрисовки изображений Альтернатива Баланс Качество Качество: Лучшее качество изображения, медленная скорость, высокая загрузка памяти. Баланс: Хорошее качество, быстрая скорость, низкая загрузка памяти. Альтернатива: Лучшее качество, средняя скорость, низкая загрузка памяти. Выбрать файл… Выбрать папку… Скрипт запуска Обратите внимание, что как расширения, так и темы могут сильно повлиять на производительность, стабильность и безопасность Playnite. Если у вас возникли проблемы после установки темы или расширения, попробуйте сначала отключить/удалить их, чтобы убедиться, не являются ли они корнем проблемы. Выбрать при запуске Выбрать при запуске Предустан. профили Предустан. профиль Свои профили Пользовательский профиль Управляется предустан. скриптом Уточнение эмулятора Уточнение платформы Уточнение региона Выполнить перед запуском эмулятора Выполнить после запуска эмулятора Выполнить после закрытия эмулятора Исполняемый файл эмулятора не найден. Характеристика эмулятора не найдена. Сценарий запуска эмулятора не найден. Разделить на отдельные игры Объединить в одну игру Установить платформу Установить регион Сканировать папку Параметры сканирования Шаблон для исключения из сверки контрольной суммы Файлы, соответствующие указанным шаблонам, перестанут сверяться по контрольной сумме и будут сопоставляться по имени файла. См. страницу справки эмулятора для получения дополнительной информации. Сканировать эмулятором Имя должно быть установлено при сохранении новой конфигурации. Эмулятор или профиль эмулятора не задан. Папка для сканирования не указана или не существует. Конфигурация сканирования настроена неправильно. Включить автоматическое сканирование при массовом сканировании Не удалось просканировать папку на наличие эмуляторов. Не удалось просканировать папку(и) на наличие игр для эмулятора. Скрыть импортированные Профили для импорта: Параметры автосканирования Сохр. как параметры автосканирования Сохраняет конфигурацию для последующего использования во время обновления библиотеки. Сохраненными конфигурациями можно управлять через меню «Настройка эмуляторов». Импорт с использованием относительных путей По возможности импортировать файлы игр, используя пути относительно папки установки Playnite или папки установки эмулятора. Сканировать подпапки Сканировать внутри архивов Объединить связанные файлы Объедининить связанные игровые файлы, такие как, например, отдельные игровые диски, под одной записью. Добавить ещё Добавить сохр. условия Начать сканирование Добавить конфигурацию сканирования с помощью эмулятора для сканирования определенных папок. Перед импортированием игр убедитесь, что эмуляторы правильно настроены (через меню «Библиотека» -> «Настройка эмуляторов...»). Статус по умолчанию, присваиваемый при добавлении новых игр Статус, присваиваемый играм, которые запущены впервые Не удалось инициализировать среду выполнения сценария PowerShell. Если вы являетесь пользователем Windows 7, попробуйте (пере)установить PowerShell 5.1, чтобы устранить проблему. Шаблон фильтра с указанным именем уже существует. Обновить шаблон с новыми настройками? Эти слова будут удалены из начала автоматически заполняемого значения "Имя сортировки": Используется для игнорирования слов в начале строки в целях сортировки. По умолчанию используются слова "The", "An" и "A". Заполнять "Имя Сортировки" в играх без него Сортировка Заполнение значений "Имя Сортировки"... Похоже, что в вашей системе запущена служба Nahimic. Она печально известна тем, что вызывает проблемы с отрисовкой в Playnite (и прочих приложениях). Если вы столкнулись с графическими глюками или другими проблемами с отрисовкой в Playnite, рекомендуем отключить или полностью удалить с устройства службу Nahimic. Больше информации на https://playnite.link/nahimicsucks Playnite запущен с повышенными привилегиями (от имени администратора). Делать это не рекомендуется, поскольку повышенные привилегии даются всем установленным расширениям и всем играм/приложениям, запускаемым из Playnite! Подробнее на https://playnite.link/adminfaq Предупреждать о запуске Playnite с повышенными правами доступа Получать реальный размер на диске при вычислении размера игр Если этот параметр включен, сканирование будет выполняться медленнее и при этом будет отображаться реальный размер файлов на диске. Если отключен, сканирование будет выполняться быстрее и при этом будет использоваться собственный размер файлов. Дополнение(я) из списка ниже помечены как потенциально проблемные либо из-за большой нагрузки на железо и нестабильной работы, либо из-за проблем с безопасностью. Мы настоятельно рекомендуем удалить их: {0} Исключать онлайн-файлы из сканирования Файлы из облачного хранилища не будут сканироваться и импортироваться, если недоступны локально. Поддерживается только для Google Диска, Dropbox, OneDrive Упрощенное сканирование без учета содержимого файлов Файлы будут импортированы, но с использованием менее точного метода, который не требует загрузки содержимого файла и его локального представления. Применить ко всем Переопределить состояние установки Если опция включена, Playnite будет игнорировать состояние установки (включая каталог установки), заданное плагином интеграции, который импортирует эту игру. Эта опция может не полностью работать с плагинами, использующими специфический метод импорта игр, если они также не учитывают эту опцию переопределения. Только вручную Раз в день Раз в неделю При каждом запуске Проверить наличие обновлений Проверить наличие обновлений дополнений Обновить библиотеки Сканирование папок эмуляции Включая скрытые игры Редактировать поля Выделить / отменить все Открыть Активировать Назначить Начните вводить для поиска игры… [F1] для получения справки При вводе символа # появляется список доступных команд. При вводе символа / появляется список доступных поисковых поставщиков/плагинов. При вводе ключевого слова поиска и завершении его пробелом происходит немедленный переход к этому поиску. TAB: переключение действия ENTER: активировать выбранное действие SHIFT-ENTER: открыть меню элементов Включая удалённые игры Включая скрытые игры Удаленные игры включены Удаленные игры исключены Скрытые игры включены Скрытые игры исключены Играть или установить Сведения Меню игры Изменить игру Открыть поиск Поисковая строка Кнопка поиска Основное действие игры Дополнительное действие игры CTRL-F открывает глобальный поиск вместо окна фокусировки поиска Сохранить настройки фильтра игр между поисковыми сессиями Провайдеры поиска Ключевое слово по умолчанию Пользовательское ключевое слово Общесистемный ярлык Поиск Playnite Настройки расширения Исключения Исключенные файлы относительно папки сканирования Исключенные папки относительно папки сканирования Добавить файл в список исключений Добавить папку в список исключений Исключения могут быть добавлены только в сохраненные конфигурации сканера. Исключения были добавлены в сканер "{0}". Переопределить платформу При включении сканер привяжет эту платформу ко всем играм, заменив все автоматически определенные платформы. Включить команды в поиск по умолчанию При отключении команды не будут включены в поиск по умолчанию, пока не будет использован префикс #. Использовать нечеткое сопоставление в фильтре по имени Если включено, фильтр по имени будет отбирать игры таким же образом, как и глобальный поиск. Можно включить строгое сопоставление для конкретного поиска, введя префикс "!" перед вашим запросом. Отобр. поля в результатах поиска игры: Скрытый статус Резервное копирование данных было отменено. Ошибка резервного копирования. Ошибка резервного копирования Выполняется резервное копирование данных... Восстановление данных из резервной копии... Не удалось восстановить данные из резервной копии. Настройки Библиотека игр Материалы библиотеки игр Установленные расширения Данные расширений Установленные темы Выберите данные, которые будут восстановлены из указанного файла резервной копии. Playnite будет автоматически перезапущен для начала процесса восстановления резервной копии. Выберите элементы, которые будут включены в резервную копию. Настройки приложения и данные игровой библиотеки включены по умолчанию. Playnite будет автоматически перезапущен для начала процесса резервного копирования. Автоматическое резервное копирование данных Частота автоматического резервного копирования Папка резервного копирования Чередование резервных копий Включить дополнительные данные: Если автоматическое резервное копирование включено, потребуется задать папку резервного копирования. Уведомлять только о вышедших патчах Если включено, то в уведомлении придут только обновления для текущего установленного релиза. Новые крупные релизы будут проигнорированы. Использовать условные даты для событий прошедшей недели Использовать условные даты в формате "Сегодня", "Вчера" и т.д., если срок давности события меньше недели. Для всех остальных дат будет использоваться указанный формат даты. Поиск по фото в интернете Иконка в строке поиска фото Строка поиска обложки Строка поиска фонового изображения Получение информации о дополнении… Нет доступного источника данных Настройки действия Играть Использовать настройки сканирования Выбрать профиль при запуске Выбрать эмулятор при запуске Автоматически Всегда вкл. Всегда выкл. Поддержка спец. возможностей (чтения с экрана) Меню приложения Меню игры Папка программы Папка пользовательских данных Обнаружен поврежденный файл библиотеки, Playnite вынужденно завершит работу. Откройте новый тикет с проблемой ("issue") на странице GitHub Playnite и запросите исправление поврежденных файлов. Сохранить сделанные вами изменения? Портативная установка Контроллеры не обнаружены ================================================ FILE: source/Playnite/Localization/si_LK.xaml ================================================  සිංහල ප්ලේනයිට් භාෂාව පිටවන්න පෙරහන ක්‍රියාත්මකයි පෙරහන අබල කර ඇත අතිරේක පෙරහන් පෙරහන් පෙරහන වලංගු නොවන දත්තයකි වෙනස්කම් සුරකින්නද? මුල් පිටුව www.playnite.link ප්ලේනයිට් පිළිබඳව ප්‍රවර්ග සකසන්න ප්‍රවර්ගය එකතු කරන්න ප්‍රවර්ග නැත ප්ලේනයිට් නැවතඅරඹන්න ප්ලේනයිට් න් පිටවන්න ක්‍රීඩා(ව) ඉවත් කරන්නද? අස්ථාපනය කළ නොහැකියි - ක්‍රීඩාව ධාවනය වෙමින්. ඉවත් කරන්න නැවත නම් කරන්න පිටපත් එකතු ආපසු ආපසු ආයාත කරන්න නම සටහන් වියමන අඩවිය මාර්ගය හරි සුරකින්න වසන්න අවලංගු කරන්න ඔව් නැහැ සාදරයෙන් පිළිගනිමු ස්ථානීය පරිශීලක මාධ්‍යය සබැඳි ක්‍රියාමාර්ග බාගත වෙමින්… මාධ්‍ය බාගත වෙමින්… පූරණය වෙමින්… ඉරිදා පසුගිය සතිය පසුගිය මාසය පසුගිය අවුරුද්ද සියලුම ක්‍රීඩා ක්‍රීඩාවේ හැඳු. දත්තසමුදායේ හැඳු. තීරුව තීරු පේළිය පේළි නිල ගබඩාව වින්‍යාසය සැකසුම්… මෙවලම් මෘදුකාංග මෙවලම්… සැකසුම් ================================================ FILE: source/Playnite/Localization/sk_SK.xaml ================================================  Slovenčina Jazyk Playnite Ukončiť Filter je aktívny Filter je vypnutý Dodatočné filtre Filtre Filter Neplatné údaje Uložiť zmeny? Stránka aplikácie na www.playnite.link Zdrojový kód na GitHub Vytvoriť diagnostický balíček Odoslať diagnostický balíček O Playnite Vytvoril Josef Němec Priradiť kategóriu Nastaviť kategórie Pridať kategóriu Zaškrtnuté - priraďte kategóriu Nezačiarknuté - Odobrať kategóriu Neurčité - Žiadne zmeny (pri úprave viacerých hier) Bez kategórie Bez platformy Ojoj! Niečo sa pokazilo… Stala sa závažná chyba. Ak by ste nám chceli pomôcť chybu opraviť, opíšte prosím v skratke vaše akcie, ktoré viedli k pádu programu a následne odošlite diagnostický balíček. Ak ste pripojený na internet, tento balíček sa nahrá na servery Playnite a bude podrobený analýze. Prípadne môžete tiež použiť tlačítko 'Nahlásiť pád' a tým sa vytvorí nový záznam o chybe na GitHub kde budete môcť popísať problém sami. Ďakujeme za pomoc. Rozšírenie "{0}" spôsobilo neodstrániteľnú chybu. Odporúčame uložiť súbor so záznamami (log) a nahlásiť tento problém vývojárovi rozšírenia. Ak problém pretrváva, tak toto rozšírenie vypnite. Rozšírenie "{0}" spôsobilo neodstrániteľnú chybu. Odporúčame to nahlásiť vývojárovi rozšírenia. Ak sa táto chyba opakuje, rozšírenie vypnite. Neznáme rozšírenie alebo téma spôsobila neodstrániteľnú chybu. Odporúčame zakázať doplnky tretej strany, izolovanie problematického doplnku a nahlásenie chyby vývojárovi doplnku. Vyskytla sa neopraviteľná chyba. Ak nám chcete pomôcť tento problém opraviť, odošlite prosím diagnostický balíček. Ďakujeme. Vypnúť rozšírenie Uložiť záznamy (log) Odoslať diagnostické informácie Nahlásiť chybu Reštartovať Playnite Reštartovať v bezpečnom režime Vypínajú sa všetky rozšírenia tretích strán a nastavuje sa predvolený motív vzhľadu. Ukončiť Playnite Akcie uskutočnené pred pádom programu (v angličtine): Správca knižnice Odstrániť Hru(y)? Nedá sa odstrnáť - je spustená hra alebo inštalátor. Nie je možné odinštalovať - hra je spustená. Ste si istí, že chcete odstrániť {0}? Naozaj chcete odstrániť {0} hier? Naozaj chcete odstrániť {0} hier? Výber možnosti „pridať do zoznamu vylúčených“ zabráni ďalšiemu importu hier pri nasledujúcej aktualizácii knižnice. Naozaj chcete odstrániť {0} hier? Výber možnosti „pridať do zoznamu vylúčených“ zabráni ďalšiemu importu hier pri nasledujúcej aktualizácii knižnice. Naozaj chcete odstrániť {0} záznamov, ktoré sa momentálne nepoužívajú? Nenašli sa žiadne nepoužívané polia. Áno (pridať do zoznamu vylúčených) V tejto časti sa nachádzajú neuložené zmeny Aktualizuje sa formát knižnice hier… Aktualizácie databázy zlyhala. Nie je možné aktualizovať knižnicu hier. Je potrebných {0} MB voľného priestoru na disku. Chyba hry Hra sa nedá spustiť. '{0}' sa nenachádza v databáze. Hra sa neda spustiť: {0} Akcia sa nedá spustiť: {0} Nie je možné otvoriť umiestnenie hry: {0} Nie je možné zistiť veľkosť inštalačného súboru hry: {0} Zisťovanie veľkosti hry sa nepodarilo {0} Chýb bolo zistených počas zisťovania veľkosti súboru Nepodarilo sa vytvoriť odkaz: {0} Nepodarilo sa otvoriť manuál: {0} Hra sa nedá nainštalovať: {0} Hra sa nedá odinštalovať: {0} Žiadne nájdené platné akcie štartu hry. Keď používate akcie emulátora, uistite sa, že definície medzi konfiguráciou hry a konfiguráciou platformy sú totožné. Implementácia inštalácie nie je dostupná. Doplnok knižnice zodpovedajúci k tejto hre je vypnutý alebo nenainštalovaný. Prevzatie oficiálnych metadát nie je k dispozícii. Nie je vybraná žiadna hra. Nepodarilo sa spustiť herný skript. Nepodarilo sa spustiť skript aplikácie. Nepodarilo sa spustiť globálny skript. Nepodarilo sa spustiť skript emulátora. Spustenie skriptu zlyhalo. Nie je nainštalovaný PowerShell 3.0 alebo novší. Nepodarilo sa určiť, ako začať hru. Povolené Zakázané Odstrániť Odstrániť nepoužité Premenovať Kopírovať Pridať Predvolená ikona Predvolený obrázok obalu Predvolený obrázok pozadia Dokončiť Ďalej Späť HOTOVO SPÄŤ VYČISTIŤ Vyčistiť Zamietnuť Odmietnuť všetko Importovať Názov Autor Modul Séria Verzia Naposledný hrané Najhranejšie Počet zahratí Veľkosť inštalácie Priečinok Poznámky Pridané Dátum pridania Zmenené Dátum zmeny Webové stránky Cesta OK Uložiť Zavrieť Zrušiť Potvrdiť Resetovať Áno Nie Vitajte Miestny použivateľ Všeobecné Médiá Odkazy Inštalácia Akcie Preberá sa… Preberajú sa médiá… Načítava sa… Typ Profil Profily Odstrániť Prevziať Hľadať Rozlíšenie: akýkoľvek Priblíženie Zobrazenie zoznamu Obaly Zobrazenie mriežky Zobrazenie podrobností Vlastné URL Špeciálne poďakovanie Licencia Prispievatelia Ukončuje sa Playnite… Dnes Včera Pondelok Utorok Streda Štvrtok Piatok Sobota Nedeľa Minulý týždeň Minulý mesiac Minulý rok Pred viac než rokom 0 až 100MB 100MB až 1GB 1GB až 5GB 5GB až 10GB 10GB až 20GB 20GB až 40GB 40GB až 100GB 100GB alebo viac Import bol úspešne dokončený. Všetky hry ID hry ID databázy Prednastavenia Stĺpec Stĺpce Riadok Riadky Nepodarilo sa získať ikonu z akcie Hrať Prevziať len chýbajúce metadáta Povolenie tejto možnosti spôsobí preskočenie preberania metadát pre polia, ktoré už obsahujú informácie. Výber hier Vyberte, ktoré hry by sa mali aktualizovať pomocou nových metadát: Všetky hry v databáze Všetky aktuálne zobrazené hry Iba vybrané hry Žiadne polia metadát nie sú vybrané Žiadne polia metadát nie sú vybrané na stiahnutie. Prosíme vyberte aspoň jedno a povoľte pre ne aspoň jedného poskytovateľa metadát. Oficiálny bbchod IGDB Vyberte, ktoré polia by mali byť automaticky doplnené apkou Playnite a ktoré zdroje by sa mali použiť pre získanie údajov. Zváže prosím ručnú aktualizáciu databázy igdb.com kliknutím na logo vyššie, čím pomôžete skvalitniť dáta, ktoré používa Playnite. Preberajú sa metadáta… Importujú sa nainštalované hry… Importujú sa hry z platformy {0}… Importujú sa emulované hry z {0}… Preberajú sa aktualizácie knižníc… Skenovanie veľkosti hry v knižnici… Skenovanie veľkosti importovaných hier… Aktualizácia knižnice dokončená Uvoľnujú sa zdroje… Konfigurácia Nastavenia… Platformy a emulátory Konfigurovať emulátory… Správca knižnice… Nástroje Prevziať metadáta… Softvérové nástroje… Konfigurovať integrácie Otvoriť externého klienta Klienti tretích strán Aktualizovať hernú knižnicu Zrušiť Aktualizáciu Knižnice Aktualizovať Emulované Priečinky Pridať hru Ručne… Automaticky prehľadať… Emulovaná hra… Apka Microsoft Store… O Playnite Odoslať spätnú väzbu Prepnúť do režimu celej obrazovky Odkazy Nápoveda Podporiť na Patreone Podporiť na Ko-fi Používateľský manuál Dokumentácia SDK Reštartovať systém Vypnúť systém Uspať systém Hibernovať systém Uzamknúť Systém Odhlásiť Používateľa Vybrať náhodnú hru Polia, ktoré budú viditeľné v zobrazení detailov: Medzery medzi položkami Vykresľovať pozadie položiek v mriežke Šírka ohraničenia položky v mriežke Chýbajúci zdroj ikon hier Chýbajúci zdroj obalov hier Chýbajúci zdroj pozadí hier Vertikálne odsadenie podrobností o hre Pozícia detailov v zobrazení mriežky Pozícia zoznamu hier v zobrazení detailov Vykresľovať oddelovač medzi panelmi Výška obrázku obalu hry Výška ikony v zozname hier Písmo aplikácie Monospaced font Pozícia panelu s filtrami Pozícia panelu prieskumníka Vykresľovanie obrázkov obalov Cieľový pomer strán Nasledujúce možnosti tiež ovplyvnia vykreslenie dlaždíc v režime celej obrazovky. Spôsob roztiahnutia DVD krabička Epic Games Store GOG Galaxy 2.0 IGDB Štvorec Steam baner Vertikálne obaly Steam Twitch * Toto nastavenie sa prejaví až po reštarte Playnite Nastavenia Všeobecné Horný panel Vzhľad Detaily hry Rozloženie Pokročilé Celá obrazovka Vstup Výkon Metadáta Aktualizovanie Vyhľadať Záloha Zálohovanie Dát Knižnice Obnoviť Zálohované Dáta Importovať zmeny v knižnici automaticky Chybné umiestnenie súboru databázy, musíte zadať správne umiestnenie súboru. Názov účtu nesmie byť prázdny. Prevziať metadáta po importe hier Spustiť Playnite minimalizovaný Spustiť Playnite po štarte systému Štart zatvorený do trayeu Nepodarilo sa zaregistrovať Playnite pre štart pri spustení počítača. Spustiť v režime celej obrazovky Asynchrónne načítanie obrázkov Vylepšuje plynulosť skrolovania zoznamu hier na úkor pomalšieho načítavania obrázkov. Ak chýba obal hry, tak zobraziť názov hry V zobrazení mriežky zobrazovať názvy hier Stmaviť nenainštalované hry Zobraziť ikonku hry v režime zobrazenia podrobností V popisoch skupín zobraziť počet položiek Zobraziť len priradené polia vo filtri a paneloch prieskumníka Vypnúť hardvérovú akceleráciu Použite v prípade, že zaznamenávate problémy ako je zasekávanie, prípadne iné grafické artefakty Zobraziť skryté hry v zoznamoch rýchleho spustenia Ovplyvňuje zoznam odkazov a zoznamy ponúk v systémovej lište. Počet položiek pre rýchle spustenie Použiť pozadie hry ako pozadie okna Rozostriť pozadie Vysoká kvalita Stmaviť pozadie Zobraziť v zobrazení mriežky Motív vzhľadu Profil motívu vzhľadu Motív vzhľadu v režime celej obrazovky Profil motívu vzhľadu v režime celej obrazovky Umiestnenie databázy Stav prihlásenia: Nastavenia Playnite Vymazať vyrovnávaciu pamäť Môže vyriešiť problémy pri pripájaní účtov. Zobraziť ikonu na systémovej lište Minimalizovať Playnite do systémovej lišty Po zatvorení okna aplikácie minimalizovať Playnite do systémovej lišty Pri spustení hry: Po ukončení hry: Formátovanie času stráveného v hre pre indikáciu nahraných dní Formát dátumu: Táto akcia vás odhlási zo všetkých pripojených služieb (účtov). Vyžaduje sa reštart aplikácie. Chcete pokračovať? Vymazať vyrovnávaciu pamäť? Pre zmenu vzhľadu je potrebné reštartovať Playnite Získať viac motívov vzhľadu Vytvoriť nový motív vzhľadu Získať viac rozšírení Vytvoriť nové rozšírenie Pomôžte nám s prekladom Playnite Ak chcete použiť nové nastavenia, je potrebné reštartovať Playnite. Reštartovať teraz? Reštartovaním sa zrušia všetky aktívne úlohy (preberania), ktoré práve prebiehajú. Reštartovať Playnite? Playnite nemôže automaticky presúvať vaše súbory z knižnice. Pred zmenou umiestnenia musíte súbory ručne presunúť / skopírovať. Ak v cieľovom umiestnení nie je žiadna knižnica, vytvorí sa nová. Nové umiestnenie databázy sa použije až po reštartovaní Playnite. Ak je nastavená akcia "Zavrieť", nebude sa zaznamenávať čas hrania. Počet riadkov Počet stĺpcov Počet riadkov v zobrazení podrobností Zobraziť obrázok pozadia na hlavnej obrazovke Nevzťahuje sa na hry, ktoré už existujú - pre tie je potrebné opätovne prebrať metadáta. Importovať čas strávený v hre do knižnice: Vždy Iba pre novo naimportované hry Nikdy Povoliť podporu ovládača v režime plochy Tlačidlo Sprievodca otvorí režim celej obrazovky Nastavenia automatického preberania metadát pre novo importované hry. Zobrazenie cieľu Vždy použiť hlavný displej Zobraziť názvy hier Zobraziť stav batérie Zobraziť stav batérie v percentách Zobraziť hodiny Skryť Kurzor Myši Len nainštalované v Rýchlych filtroch Popisy tlačidiel Rozloženie Horizontálne skrolovanie Vyberte jednu z podkategórií Nie sú dostupné žiadne nastavenia Nepodarilo sa načítať nastavenia Tieto skripty sú spustené pre každú hru v knižnici. Jednotlivé skripty môžu byť priradené ku každej hre zvlášť v úpravách podrobností o hre. Animovať prechody medzi obrázkami na pozadí Veľkosti písma Automaticky Vyhladené Odtiene šedej ClearType Ideálne Obrazovka Režim formátovania textu Režim vykresľovania textu Metódy pre renderovanie a formátovanie textu sa momentálne nepoužívaju pre popisy hier. Predbežne načítať obrázky pozadí Ak je povolené, Playnite prevezme obrázky pozadia počas preberania metadát. Obrázky budú dostupné aj v režime offline, avšak použije sa viac miesta na disku. Ak nie je povolené, obrázky pozadia sa budú preberať len vtedy ak sú potrebné. Ušetrí sa tým miesto na disku, avšak ich zobrazenie môže byť oneskorené a niektoré obrázky nemusia byť k dispozícii v režime offline. Po ukončení hry automaticky strany zatvárať klientov tretích strán Oneskorenie vypnutia klienta (v sekundách) Nezatvárať ak sa hralo menej než (v sekundách) Automaticky zatvoriť nasledovných klientov: Automaticky zavrieť klientov Importovať zoznam vylúčených Pri priradení príliš veľkých herných médií zobrazovať upozornenie Príkaz pre otvorenie adresára Preferovaná organizácia pre vekové obmedzenia Aktualizovať veľkosť inštalácie v aktualizácií knižnice Žiadne Vyplniť Rovnomerne Rovnomerne vyplniť Vľavo Vpravo Hore Dole Chyba importu Vyžaduje sa overenie Overenie zlyhalo Import metadát Prevziať metadáta Nastaviť vybratú konfiguráciu, ktorá sa použije pri ďalšom preberaní metadát. Je to tiež možné zmeniť v nastaveniach. Sprievodca importom emulátorov Tento sprievodca vás prevedie procesom prevzatia a importovania konzolových emulátorov a importovaním emulovaných hier. Nezabudnite, že ďalšie emulátory a/alebo hry, môžete kedykoľvek pridať prostredníctvom hlavnej ponuky (v ponuke "Knižnica" pre nastavenie emulátorov a "Pridať hry" pre emulované hry). Kliknutím na 'Automaticky zistiť z priečinka…' môžete importovať akýkoľvek emulátor nainštalovaný vo vašom PC. Playnite prehľadá zvolený priečinok a skúsi importovať akýkoľvek známy emulátor. Toto tlačidlo na import emulátorov môžete napoužiť viacnásobne. Nové emulátory sa pridaju na spodok aktuálneho zoznamu. Importovať hry môžete kliknutím na tlačidlo 'Prehľadať priečinok pomocou emulátora'. Vybraním príslušného emulátora bude Playnite vedieť, ktoré typy súborov by mali byť skenované a následne importované. Toto tlačidlo môžete použiť viacnásobne pre importovanie hier z rôznych priečinkov. Hry sa pridajú na spodok aktuálneho zoznamu. Nie sú nastavené žiadne emulátory pre import. Bez konfigurácie emulátorov nebudete môcť importovať žiadne emulované hry. Naozaj chcete pokračovať a ukončiť import? V Playnite nie sú nakonfigurované žiadne emulátory. Hry nemôžete importovať bez toho, aby ste najskôr nakonfigurovali emulátor a vybrali príslušné typy súborov. Chcete pridať nejaké emulátory teraz? Prehľadať priečinok pomocou emulátora Vybrať súbory Automaticky detegovať z priečinka… Konfigurovať emulátory… Prehľadáva sa… Úvodné nastavenie Tento sprievodca vás prevedie procesom automatického importu a konfigurácie externých knižníc hier. Playnite môže automaticky importovať hry z viacerých herných služieb ako napr. Steam alebo GOG, a tiež udržovať knižnicu aktuálnu automatickou aktualizáciou pri každom spustení aplikácie. Nezabudnite, že ľubovoľnú vlastnú hru pre ľubovoľnú platformu môžete pridať aj neskôr ručne kliknutím na tlačidlo 'Playnite' v hlavnej ponuke. Integrácia knižnice Automaticky importovať hry zo služieb uvedených nižšie. Všetky neskoršie zmeny hry (stav inštalácie) sa aktualizujú automaticky pri spustení Playnite alebo pri manuálnom spustení. Vybraté nastavenia ovplyvnia počiatočný a všetky nasledujúce importy. Konfigurácia dokončená Počiatočné nastavenie bolo dokončené. Nezabudnite, že všetky nastavenia môžete neskôr zmeniť v ponuke „Nastavenia“. Nastaviť platformy a emulátory Konfigurovať emulátory Platformy Platforma Emulátory Emulátor Pridať platformu Vybrať ikonu Vybrať obal Vybrať obrázok Vybrať položku Vybrať pozadie Vybrať súbor Vybrať adresu URL Pridať emulátor Podporované platformy Chcete uložiť zmeny v platformách? Chcete uložiť zmeny v emulátoroch? Spustiteľný súbor Parametre Pracovný priečinok Podporované typy súborov Importovať emulátory… Prevziať emulátory… Načítať nastavenie parametrov zo známych profilov emulátorov Naozaj chcete odstrániť emulátor {0}? Momentálne ho používa {1} hier. Naozaj chcete odstrániť platformu {0}? Monetálne ju používa {1} hier a {2} emulátorov. Nápoveda k nastaveniam Zoradiť podľa Smer zoradenia Zoskupiť podľa Vzostupne Zostupne Nezoskupovať Zoskupiť podľa knižnice Zoskupiť podľa kategórie Zoskupiť podľa platformy Typ zobrazenia Zobrazenie Panel prieskumníka Panel filtrov Ikona Ikona knižnice Obrázok obalu Obrázok pozadia Názov, podľa ktorého radiť Knižnica Manuál Meno Názov účtu Platforma Kategórie Žáner Dátum vydania Rok vydania Vývojár Štítok Vydavateľ Stav inštalácie Nainštalované Nainštalované Nenainštalované Skryté Obľúbené Povoliť podporu HDR Ak povolené, HDR bude povolené na primárnom displeji pred spustením hry. Naposledy hrané Kategória Popis Inštalačný priečinok Obrázok na obale Odkazy Cesta k obrazu, ROM alebo ISO Žáner Žánre Spoločnosť Spoločnosti Vývojár Vývojári Vydavateľ Vydavatelia Kategória Kategórie Štítok Štítky Funkcia Funkcie Vekové obmedzenie Vekové obmedzenia Región Regióny Zdroj Zdroje Nedávna aktivita Chyba databázy Nepodarilo sa otvoriť databázu knižnice. Databáza nie je otvorená. Nemožno získať prístup k databáze knižnice. Súbor "{0}" je používaný iným procesom alebo nie je k dispozícii. Nepodarilo sa vytvoriť diagnostický balíček. Nepodarilo sa automaticky nahrať diagnostický balíček. Diganostické informácie sa úspešne odoslali. Diagnostický balíček sa úspešne vytvoril a nahral. Pri nahlasovaní problému pripojte nasledujúce ID: Nepodarilo sa importovať hry z {0}. Nepodarilo sa importovať emulované hry z {0}. Nie je možné hľadať hry podľa vybratého profilu emulátora, pretože neobsahuje žiadne prípony súborov alebo platformy. Playnite sa nepodarilo spustiť. Zavrite prosím všetky inštancie aplikácie Playnite a skúste to znovu. Nepodarilo sa použiť motív "{0}", farebný profil "{1}" {2} Odkaz sa nedá otvoriť, adresa URL nie je v platnom formáte. Aplikáciu sa nepodarilo spustiť. Emulátory nie je možné importovať z dôvodu chýbajúceho alebo poškodeného definičného súboru. Nepodarilo sa spustiť ponuku akcie. Upraviť podrobnosti o hre URL obrázka Pridať odkaz Pridať ROM Uložiť zmeny Pridať akciu Vymazať akciu Odstrániť akciu Hrať Pridať hry Prehľadať priečinok… Detegovať nainštalované Prechádzať… Otvoriť Playnite Nastavenia profilu Názov hry nemôže byť prázdny. Pred vyhľadávaním metadát musíte doplniť názov hry. Neplatné údaje o hre Zadajte platnú webovú adresu URL začínajúcu reťazcom http:// alebo https:// Vybrať webovú adresu Nepodarilo sa prevziať metadáta: {0} Chyba preberania Zrušiť filtre Súkromný účet Verejný účet Kľúč API Chyba pri spustení Chyba motívu vzhľadu Zmazať všetko Prebieha inštalácia Prebieha odinštalácia Spúšťa sa Spustené Neplatná adresa URL Nerobiť nič Minimalizovať Obnoviť okno Zavrieť Zmeniť Pokročilé Nikdy Stav dokončenia Hodnotenie používateľov Novinárske hodnotenie Komunitné hodnotenie Skripty Doplnky Zdroje metadát Rozšírenia ID rozšírenia Znovu načítať skripty Všetky skripty sa úspešne znovu načítali. Pre zadané kritériá vyhľadávania sa nenašli žiadne hry Nenašli sa žiadne položky Prepnúť na režim plochy Ukončiť Playnite Knižnice Aktualizovať všetko Vytvoril: Verzia: Modul: Knižnica Štatistiky Všetko Nič Oznámenia Šírka Výška Veľkosť Malé Normálne Veľké Väčšie Najväčšie Predvolené Vybrať Vybrať všetko Prvé Náhodne Zadať používateľom Načítať viac Priehľadné Zbaliť Rozbaliť Zbaliť všetko Rozbaliť všetko Ostatné Motívy vzhľadu Parametre emulátora Dodatočné parametre emulátora Nahradiť parametre pre emulátor Vyberte metadáta pre import Vyberte hry pre import Vyhľadávanie metadát Dostupná aktualizácia Zmeny od poslednej aktualizácie Prevziať a nainštalovať aktualizáciu Skontrolovať aktualizácie Chyba aktualizácie Nepodarilo sa skontrolovať aktualizácie. Nenašla sa žiadna nová verzia, Playnite je aktuálny. Nepodarilo sa prevziať a nainštalovat aktualizáciu. Je k dispozícii aktualizácia pre Playnite Obnoviť zoznam motívov vzhľadu Použiť vybratý motív vzhľadu Sledovať zmeny súboru Automaticky použiť motív vzhľadu pri zmenách súboru Runtime skriptov Skript, ktorý sa má vykonať pred spustením hry Skript, ktorý sa má vykonať po ukončení hry Skript, ktorý sa má vykonať po spustení hry Spustiť globálny skript Globálne Terajšie Nové Uložiť ako predvolené Pridať do obľúbených Odstrániť z obľúbenýchOdobrať z obľúbených Skryť túto hru Odstrániť zo skrytých Povoliť podporu HDR Nepovoliť podporu HDR Upraviť… Veľkosť inštalácie Nastaviť kategóriu… Nastaviť stav dokončenia Odstrániť Hrať Inštalovať Možnosti hry Podrobnosti Odinštalovať Otvoriť umiestnenie inštalácie Vytvoriť odkaz na ploche Otvoriť manuál Viac Spravované doplnkom knižnice Proces spustenia hry bude spravovaný doplnkom knižnice zodpovedným za túto hru. Na uvedenej stránke neboli nájdené žiadne relevantné informácie o hre '{0}'. Tip: Ak editujete hru pomocou položky "Upraviť", môžete použiť pokročilé preberanie metadát. Nie je dostupné keď práve prebiehajú iné akcie. V popise je možné použiť značky HTML Odohraný čas sa zaznamenáva v sekundách. Veľkosť inštalácie je uvedená v bajtoch. Hodnoty od 0 do 100, prípadne prázdne ak skóre neexistuje. Do slovenského jazyka preložil Michal Žila. Ďalší prispievatelia do kódu (bez poradia): Zrušiť monitorovanie hry? Prebieha monitorovanie inštalácie, chcete ho ukončiť a vrátiť hru do predchádzajúceho stavu? Prebieha monitorovanie behu hry, chcete ho ukončiť a vrátiť hru do predchádzajúceho stavu? Odohraný čas Naposledy hrané {0} d {1} h {2} m {0}h {1}m {0} minút {0} sekúnd Zatiaľ nehrané Spúšťanie režimu plochy… Spúšťanie režimu celej obrazovky… Načítanie hernej knižnice… Inštalácia súboru skriptu zlyhala. Skrypt bol úspešne nainštalovaný. Nainštalovať skript Chyba skriptu Nepodarilo sa spustiť funkciu rozšírenia. Otvoriť priečinok s metadátami Vypočítať Klient {0} nie je nainštalovaný. Klient {0} sa teraz otvorí, prihláste sa a potom túto správu zavrite. Čaká sa na prihlásenie používateľa, keď sa tak stane, toto okno zavrite… Priečinok s inštaláciou hry sa nenašiel. Neplatná konfigurácia hry. Riešenie problémov so synchronizáciou účtu Riešenie problémov Premenovať položku Pridať novú položku Zadajte názov Zadajte nový názov Menej než hodina 1 až 10 hodín 10 až 100 hodín 100 až 500 hodín 500 až 1000 hodín 1000+ Na dokončenie inštalácie je potrebné Playnite reštartovať. Chcete teraz reštartovať? Rozšírenie nie je správne zabalené. Motív vzhľadu nie je správne zabalený. Rozšírenie "{0}" sa nepodarilo správne načítať Motív vzhľadu "{0}" sa nepodarilo správne načítať. Rozšítenie sa nepodarilo správne načítať Motív vzhľadu sa nepodarilo správne načítať. Motív vzhľadu/rozširenie používa nepodporovanu verziu API. Inštalácia bola úspešná. Nainštalovať rozšírenie? Obecné Inštalácia rozšírenia zlyhala. {0} Chcete nainštalovať nové rozšírenie? {0} Vytvoril {1} Verzia {2} Chcete aktualizovať rozšírenie "{0}"? Aktuálna verzia: {1} Nová verzia: {2} Inštalácia motívu zlyhala. {0} Chcete nainštalovať nový motív vzhľadu? {0} Vytvoril {1} Verzia {2} Chcete aktualizovať motív vzhľadu "{0}"? Aktuálna verzia: {1} Nová verzia: {2} Chystáte sa opustiť Playnite a otvoriť nasledujúcu web stránku vo vašom predvolenom webovom prehliadači. Chcete pokračovať? {0} Vybratý obrázok (obrázky) môže byť pre optimálny výkon príliš veľký. Použitie veľmi veľkých obrázkov môže mať za následok pomalšiu odozvu používateľského rozhrania a zvýšené využitie pamäte. Maximálne odporúčané rozlíšenia: Ikony: {0} mega pixelov Obaly: {1} mega pixelov Pozadia: {2} mega pixelov Varovanie o výkone Nabudúce nezobrazovať Nekompatibilná koncovka súboru Vybratý súbor obrázka môže byť príliš veľký na dosiahnutie optimálneho výkonu. Naozaj chcete odinštalovať vybraný motív vzhľadu? Odinštalácia bude sa vykoná pri ďalšom štarte aplikácie. Vstavané motívy vzhľadu nemožno odinštalovať. Tento motív vzhľadu nepodporuje túto verziu Playnite. Naozaj chcete odinštalovať vybrané rozšírenie? Odinštalácia sa vykoná pri ďalšom štarte aplikácie. Vstavané rozšírenia nemožno odinštalovať. Toto rozšírenie nepodporuje túto verziu Playnite. Inštalačný priečinok Adresár s dátami Vytvára sa diagnostický balíček… Nahráva sa diagnostický balíček… Importovať súbor… Čo je toto? Naozaj to chcete urobiť? Celkový čas hrania Priemerný čas hrania Najväčší čas hrania Celková veľkosť inštalácie Prehľad Bočný panel Zobraziť na bočnom panely Resetovať nastavenia Pre vývojárov Zadajte úplnú cestu k priečinku. Úspechy Fórum Novinky Stránka v obchode Prvotné nastavenie nie je dokončené. Playnite sa preto prepne do režimu plochy, aby sa toto nastavenie mohlo dokončiť. Nedávno hrané Obľúbené Najviac hrané Všetko Sú použité nejaké filtre. Výsledky vyhľadávania pre: Položka s týmto názvom už existuje. Obmedziť výber na aktuálny filter Vybrať dalšiu Vyhľadáva sa… Inštalovať Odinštalovať Zoznam zmien nie je dostupný Sťahovanie zlyhalo Prijať Odmietnuť Odkaz Súbor Emulátor Skript Priečinok Pôvodný proces Názov procesu Iba položky Iba začiatok a koniec Rýchlosť animácie Odstrániť položku? Ste si istí, že chcete odstrániť túto položku? Zobraziť tlačidlá na hornom panely: Nastavenia všeobecného pohľadu Nastavenia zoskupovania Nastavenia zoradenia Panel prieskumníka Vyberač náhodnej hry Vybrať náhodnú hru z pohľadu Za posledných 7 dní Za posledných 31 dní Za posledných 365 dní Pred viac než 365 dňami Nastaviť Uložiť predvoľbu Minimalizovať po spustení hry Veľkosť písma Malá veľkosť písma Rozdeliť do rozličných hier Zlúčiť do jednej hry Nastaviť platformu Nastaviť región Skenovať priečinok Konfigurácie skenovania Vylúčiť vzorce z prehľadávania kontrolného súčtu Skryť importované Profily na importovanie: Skenovať podpriečinky Skenovať vnútri archívov Pridať skener Pridať uložený skener Spustiť skenovanie Nastavenia Knižnica hier Médiá knižnice hier Nainštalované rozšírenia Dáta rozšírení Nainštalované motívy Priečinok záloh Rotujúce zálohy Zahrnúť ďalšie dáta: Hľadanie obrázkov na webe Menu hry Priečinok programu Prenosná inštalácia Nenašli sa žiadne ovládače ================================================ FILE: source/Playnite/Localization/sl_SI.xaml ================================================  ================================================ FILE: source/Playnite/Localization/sr_SP.xaml ================================================  Српски Playnite језик Излаз Филтер активан Филтер искључен Додатни филтери Филтери Филтер Неважећи подаци Сачувај промене? Вебсајт: www.playnite.link Изворни код на GitHub-у Направи нови дијагностички пакет Пошаљи дијагностичке информације О Playnite-у Направио Јозеф Њемец Додели категорију Постави категорије Додај категорију Чекирано - Додели категорију Нечекирано - Обриши категорију Неодређено - Без промена (када се измењује више игара) Нема категорије Нема платформе Упс! Нешто је пошло наопако… Непоправљива грешка се догодила. Ако желите да нам помогнете да поправимо овај проблем, молимо да кратко опишете све што сте радили пре грешке и онда пошаљете дијагностичке информације. Ако тренутно имате интернет конекцију, информације ће бити отпремљене на Playnite сервер за анализу. Алтернативно, можете користити "Пријави проблем" дугме да бисте пријавили нови проблем на GitHub-у и ручно пријавили проблем. Хвала вам за помоћ. Екстензија "{0}" је проузроковала непоправљиву грешку. Препоручујемо вам да сачувате записни фајл и пријавите проблем девелоперу екстензије. Ако се проблем поново појављује, искључите екстензију. Екстензија "{0}" је проузроковала непоправљиву грешку. Препоручујемо вам да пријавите проблем девелоперу екстензије. Ако се проблем поново појављује, искључите екстензију. Непозната екстензија или тема је изазвала непоправљиву грешку. Препоручујемо да онемогућите додатке треће стране, изолујете проблематични и пријавите проблем програмеру додатка. Непоправљива грешка се догодила. Ако желите да нам помогнете да поправимо овај проблем, молимо да нам пошаљете дијагностичке информације. Хвала. Искључи екстензију Сачувај записни фајл Пошаљи дијагностичке информације Пријави проблем Рестартуј Playnite Поново покрени у сигурном моду Искључује све екстензије треће стране и користи подразумевану тему. Изађи из Playnite-а Шта сте радили пре грешке (на енглеском): Менаџер библиотеке Обриши игре? Не може да се обрише - Игра или инсталација је тренутно покренута. Не може да се деинсталира - Игра је тренутно покренута. Да ли сте сигурни да желите да уклоните {0}? Јесте ли сигурни да хоћете да обришете {0} игара? Да ли сте сигурни да желите да уклоните {0}? Изаберите „додај на листу искључења“ да спречите поновни увоз игре следећи пут када се библиотека ажурира. Јесте ли сигурни да хоћете да обришете {0} игара? Селектовање "Додај на листу изузетака" опције ће спречити да се исте игре поново уведу следећи пут када је библиотека ажурирана. Јесте ли сигурни да хоћете да обришете {0} ставки које се тренутно не користе? Ниједно некоришћено поље није нађено. Да (Додај на листу изузетака) Имате несачуване промене у овој секцији Ажурирање формата библиотеке игара… Неуспешно ажурирање базе података. Не може да се ажурира библиотека игара. {0} мегабајта простора је потребно за то. ГрешкаУИгри Не може да се покрене игра. "{0}" није пронађено у бази података. Не може да се покрене игра: {0} Не може да се уради радња: {0} Не може да се отвори локација игре: {0} Није могуће открити величину инсталације игре: {0} Грешка при скенирању величине инсталације Било је {0} грешака током скенирања величине инсталације Неуспешно прављење пречице: {0} Неуспешно отварање упутсва за коришћење: {0} Не може да се инсталира игра: {0} Не може да се деинсталира игра: {0} Нису пронађене важеће радње покретања игре. Када користите радње емулатора, уверите се да дефиниције платформе одговарају конфигурацији игре и емулатора. Имплементација за инсталацију није доступна. Додатак за библиотеку одговоран за ову игру није инсталиран или није омогућен. Преузимање званичних метаподатака није доступно. Ниједна игра није изабрана. Неуспешно извршење игрине скрипте. Неуспешно извршење скрипте апликације. Неуспешно извршење глобалне скрипте. Неуспешно извршење скрипте емулатора. Неуспешно извршење глобалне скрипте. PowerShell 3.0 или новији није инсталиран. Неуспешно одређивање начина покретања игре Омогућено Онемогућено Обриши Обриши све некоришћено Преименуј Копирај Додај Подразумевана икона Подразумевана насловница Подразумевана позадина Заврши Следеће Назад ГОТОВО НАЗАД ОЧИСТИ Очисти Одбаци Одбаци све Увези Назив Аутор Модул Серија Верзија Последњи пут играно Највише играно Време играња Величина инсталације Фолдер Напомене Додато Датум додавања Измењено Датум измене Вебсајт Путања ОК Сачувај Затвори Откажи Потврди Ресетуј Да Не Добродошли Локални корисник Опште Медији Линкови Инсталација Радње Преузимање… Преузимање медија… Учитавање... Врста Профил Профили Обриши Преузми Претрага Резолуција: Било који Зумирај Приказ листе Насловнице Приказ мреже Приказ детаља Прилагођено URL Специјална захвалност Лиценца Сарадници Излажење из Playnite-а… Данас Јуче Понедељак Уторак Среда Четвртак Петак Субота Недеља Претходна недеља Претходни месец Претходна година Пре више од годину дана 0 до 100MB 100MB до 1GB 1GB до 5GB 5GB до 10GB 10GB до 20GB 20GB до 40GB 40GB до 100GB Више од 100GB Увоз је успешно завршен. Све игре ID игре ID базе података Пресети Колона Колоне Ред Редови Није могуће преузети икону из Play радње. Нема присутне радње типа датотеке. Само преузми недостале метаподатке Омогућавање ове опције ће прескочити преузимање метаподатака за поља података која већ садрже информације. Одабир игара Молимо изаберите које игре хоћете да су ажурирају новим метаподацима: Све игре из базе података Све тренутно филтиране игре Само изабране игре Званична продавница IGDB Молимо изаберите која поља ће се аутоматски попунити од стране Playnite-а и који извори ће се користити да се добију информације. Молимо ако желите кликните на логотип горе и допринесите ажурирања сајту igdb.com да бисте побољшали податке које Playnite користи. Преузимање метаподатака… Увођење инсталираних игара… Увођење {0} игара… Увоз емулираних игара из {0}... Преузимање ажурирања библиотеке… Скенирање величине игрица у библиотеци… Скениранје величине увезених игара… Завршено ажурирање библиотеке Ослобађање ресурса… Конфигурација Подешавања… Платформе и емулатори Конфигуриши емулаторе… Менаџер библиотеке… Алати Преузми метаподатке… Софтверске алатке… Конфигуриши интеграције… Отвори клијент треће стране Клијенти треће стране Ажурирај библиотеку игара Поништи ажурирање библиотеке Ажурирај емулиране фолдере Додај игру Ручно… Скенирај аутоматски… Емулирана игра… Апликација Microsoft продавнице… О Playnite-у Пошаљи повратне информације Промени у мод целог екрана Линкови Помоћ Подржите на Patreon-у Подржите на Ko-fi Упутство за кориснике SDK документација Рестартуј систем Искључи систем Суспендуј систем Хибернирај систем Закључај Систем Одјави корисника Изабери игру насумице Поља игре која се приказују на панелу детаља: Размак између ставки Нацртај позадину ставке у мрежи Ширина границе ставке у мрежи Извор у случају да недостаје икона игре Извор у случају да недостаје насловница игре Извор у случају да недостаје позадина игре Вертикални размак између детаља игре Позиција детаља у приказу мреже Позиција листе игара у приказу детаља Нацртај сеператор између панела Висина насловнице Висина иконе на листи игара Апликацијски фонт Једнако размакнут фонт Позиција филтерског панела Позиција истраживачког панела Рендерирање насловнице Циљана пропорција Следеће опције такође утичу и на рендерирање слика у моду целог екрана! Мод растезања DVD кутија Epic Games Store GOG Galaxy 2.0 IGDB Квадрат Steam банер Steam вертикална насловница Twitch * Потребно рестартовање да би се применило Подешавања Опште Горњи панел Изглед Детаљи игре Распоред Напредно Цео екран Унос Перформансе Метаподаци Ажурирање Претрага Резервна копија Направи резервну копију података Врати резервну копију података Уведи промене у библиотеку аутоматски Неважећа локација фајла базе података, правилна локација се мора поставити. Назив налога не може бити празан. Преузми метаподатке након увођења игара Покрени Playnite минимизиран Покрени Playnite када се компјутер укључи Покрени затворено у траци Неуспешно постављање Playnite-а да се покрене при покретању компјутера Покрени у моду целог екрана Асинхроно учитавање слика Побољшава глаткост скроловања листи игара у замену за спорије учитавање слика. Прикажи назив игре ако насловница недостаје Прикажи називе игара у приказу мреже Потамни неисталиране игре Прикажи иконе игара на листи приказа детаља Прикажи број ставки у описима група Прикажи само додељена поља на филтерском и истраживачком панелу Искључи хардверско убрзање Користите ако доживљавате муцање или сличне проблеме с интерфејсом Прикажи скривене игре у листама за брзо покретање Утиче на скочну листу и листу на траци са алатима. Број ставки за брзо покретање Користи позадину игре као позадину прозора Замагли позадину Високи квалитет Затамни позадину Прикажи у приказу мреже Тема Профил теме Тема за цео екран Профил теме за цео екран Локација базе података Статус пријављивања: Playnite подешавања Обриши веб кеширане податке Можда поправи проблеме при повезивању налога. Прикажи икону на траци са алатима Минимизирај Playnite на траку са алатима Минимизирај Playnite на траку са алатима када је апликацијски прозор затворен Када се игра покрене: Након што се игра затвори: Форматирај играно време да би означило број одиграних дана Формат датума: Ово ће вас одјавити од свих повезаних сервиса. Рестартовање апликације је потребно за ово, хоћете ли да наставите? Очисти кеширане податке? Playnite треба да се рестартује да би се применила нова тема Преузми још тема Направи нову тему Преузми још екстензија Направи нову екстензију Помозите нам да преведемо Playnite Playnite треба да се поново покрене да би се применила нова подешавања. Поново покрени сада? Поновно покретање ће прекинути све тренутно активне задатке (преузимања). Рестартуј Playnite? Playnite не премешта податке библиотеке аутоматски, ви морате да их преместите/копирате пре него што се локација промени. Ако се ниједна библиотека не налази у циљној локацији, нова ће се направити. Нова локација базе података ће се користити само након што је апликација рестартована. Време играња се неће пратити ако је "Затвори" радња подешена. Број редова Број колона Број редова у приказу детаља Прикажи позадину на главном екрану Не примењује се ретроспективно већ постојећим играма без поновног преузимања метаподатака. Увези играно време игара у библиотеци: Конфигурише када би Playnite требао да увезе време играња пријављено од стране додатака библиотеке за игре у Playnite бази података. Потребна је подршка библиотечких додатака задужених за руковање игрицама да бисте могли да користите ову функцију. Увек: Увози време играња за нове увезене и постојеће игре у Playnite базу података. Само за новоувезене игре: Увози време играња само за нове увезене игре. Никада: Никада не увози време играња ни под којим околностима. Увек Само за ново увезене игре Никад Омогући подршку за контролер у десктоп моду Guide дугме отвара мод за цео екран Подешавања аутоматског преузимања метаподатака за ново уведене игре. Циљани екран Увек користи главни екран Прикажи називе игара Прикажи статус батерије Прикажи проценат напуњености батерије Прикажи сат Сакриј курсор Инсталирано само у брзим филтерима Слике дугмића Распоред Хоризонтално скроловање Изабери једну од подсекција Нема доступних подешавања Неуспешно учитавање подешавања Ове скрипте важе за све игре у библиотеци. Индивидуалне скрипте могу се доделити свакој игри одвојено током измењивања игриних детаља. Анимирај транзиције позадинских слика Величине фонтова Аутоматски Са другим именом Сиви тонови ОчистиВрсту Идеално Приказ Мод форматирања текста Мод рендерирања текста Рендерирање текста и опције форматирања се тренутно не односе на описни текст игре. Раније учитај позадинске слике Ако је омогућено, Playnite ће да преузима позадинске слике током преузимања метаподатака, самим тим користиће и више простора на диску и слике ће бити доступне и када сте офлајн. Ако је укључено, позадинске слике преузимаће се само када су прво потребне, самим тим користиће се и мање простора, али то ће можда резултирати у кашњењу приказивања слика и неке слике можда не буду доступне офлајн. Аутоматски затвори клијент треће стране након што се из игре изађе Период пре искључивања клијента (у секундама) Не излази након сесија играња краћих од (у секундама) Аутоматски затварај следеће клијенте: Аутоматски затварај клијенте Уведи листу изузетака Прикажи упозорење када се додељују превелики фајлови за игру Команда за отварање директоријума Преферирана организација за старосна доба Ажурирај инсталацијску величину игара при ажурирању библиотеке Скенира и ажурира величину инсталације игара ако се открије да су њени фајлови промењени од прошлог скенирања Ништа Попуни Униформа Униформа која се попуњава Лево Десно Врх Дно Грешка приликом увоза Потребна је провера Неуспела провера Алтернативни режим исцртавања приказа са мреже Користите када имате проблема са веб приказима, на пример дијалозима за потврду идентитета интеграције. Делимично учитавање великих описа игара Велики описи могу знатно успорити апликацију при избору игрица. Када је омогућено, само део текста описа ће се иницијално учитати са опцијом да се остатак учита на захтев. Увоз метаподатака Преузми метаподатке Подеси одабрану конфигурацију да се користи за сва будућа преузимања метаподатака. Може да се промени и у подешавањима апликације. Чаробњак за увоз емулатора Овај чаробњак ће вас наводити кроз процес преузимања и увожења конзолашких емулатора као и увожења емулираних игара. Увек можете да додате додатне емулаторе и/или игре касније користећи главни мени (испод "Библиотека" менија за емулаторска подешавања и "Додај игре" менија за емулиране игре). Следи листа емулатора које Playnite може да препозна и аутоматски конфигурише. Можете да преузмете ове емулаторе и инсталирате са њихових вебсајтова. Када инсталирате емулаторе (ручно), можете их увести у дијалогу за конфигурацију емулатора. Можете да уведете било који емулатор који је инсталиран на вашем компјутеру тако што кликнете на "Аутоматски детектуј из фолдера…" дугме. Playnite ће онда да претражи тај фолдер за било које познате емулаторе и даће вам опцију да их уведете. Можете увести више емулатора тако што користите исто то дугме више пута. Емулатори ће се додавати на дно тренутне листе. Можете увести игре користећи "Скенирај фолдер користећи емулатор" дугме. Бирање прикладног емулатора ће рећи Playnite-у које врсте фајлова треба да скенира и уведе. Можете увести више фолдера тако што користите исто дугме више пута. Игре ће се додавати на дно тренутне листе. Ниједан емулатор није изабран за увоз. Нећете моћи да аутоматски уведете било које емулиране игре без прво конфигурисања емулатора. Хоћете ли да наставите и изађете из процеса увода? Ниједан емулатор није конфигурисан у Playnite-у. Не можете да уведете игре без прво конфигурисања емулатора и бирања прикладне врсте фајла. Хоћете ли да додате емулаторе сада? Скенирај фолдер користећи емулатор Изаберите фајлове Аутоматски детектуј из фолдера… Конфигуриши емулаторе… Скенирање… Скенирам {0}... Почетна конфигурација Овај чаробњак ће вас наводити кроз процес аутоматског увоза игара и конфигурације екстерних библиотека игара. Playnite може да аутоматски уведе игре са више сервиса за играње игара као што су Steam или GOG, и такође може и да одржава вашу библитеку игара ажурном тако што је ажурира при сваком покретању апликације. Увек можете да додате ручно било коју другу игру касније тако што кликнете на "Playnite" дугме. Интеграција библиотеке Аутоматски уведи игре са следећих сервиса. Било које касније промене (статус инсталације) ће се аутоматски ажурирати при покретању Playnite-а или када се исти процес покрене ручно. Изабрана подешавања утичу на почетне и све друге уводе. Завршена конфигурација Почетна инсталација је завршена. Запамтите да увек можете да промените сва подешавања касније у "Подешавања" менију. Такође можете и да додате било коју другу игру касније тако што кликнете на логотип Playnite-а. Преузимање једне или више екстензија није успело. Можете покушати да поново преузмете интеграције из менија додатака након што се чаробњак за прво покретање заврши. Преузимам {0} интеграцију… Преузимање листе препоручених интеграција… Преузимање листе препоручених интеграција није успело. Можете пробати и поново преузети интеграције касније преко менија додатака Конфигуриши платформе и емулаторе Конфигуриши емулаторе Платформе Платформа Емулатори Емулатор Додај платформу Изабери икону Изабери насловницу Изабери слику Изабери ставку Изабери позадину Изабери фајл Изабери URL Додај емулатор Подржана(е) платформа(е) Хоћете ли да сачувате измене платформе? Хоћете ли да сачувате измене емулатора? Покретни фајл Аргументи Радни директоријум Подржане врсте фајлова Уведи емулаторе… Преузми емулаторе… Учитај пресете аргумената из познатог емулаторског профила Јесте ли сигурни да хоћете да обришете {0} емулатор(а)? Тренутно га(их) користи(е) {1} игра(е). Јесте ли сигурни да хоћете да обришете {0} платформу(е)? Тренутно је(их) користи(е) {1} игра(е) и {2} емулатор(а). Помоћ око подешавања Сортирај по Смер сортирања Групиши по Растући редослед Опадајући редослед Не групиши Групиши по библиотеци Групиши по категорији Групиши по платформи Врста приказа Приказ Истраживачки панел Филтерски панел Икона Иконица библиотеке Насловница Позадинска слика Сортирајуће име Библиотека Упутство за коришћење Назив Драјв инсталације Назив налога Платформа Категорија Жанр Датум издавања Година издавања Девелопер Ознака Издавач Инсталациони статус Задовољи све критеријуме Ако је омогућено, у приказ ће бити укључене само игре које користе све ставке у свим филтерима. Ако је онемогућено, игре које користе било коју ставку у било ком филтеру биће укључене у приказ. Инсталирано Инсталирано Није инсталирано Сакривено Омиљено Укључи подршку HDR-а Ако укључено, HDR ће бити укључен на главном екрану пре покретања игре. Ваш главни екран не подржава HDR. Последњи пут играно Категорија Опис Инсталациони директоријум Насловница Линкови Путања за Image, ROM или ISO Жанр Жанрови Компанија Компаније Девелопер Девелопери Издавач Издавачи Категорија Категорије Ознака Ознаке Својство Својства Старосно доба Старосна доба Регион Региони Извор Извори Скорашња активност Грешка у бази података Неуспешно отварање базе података библиотеке. База података није отворена. Не можемо да приступимо бази података библиотеке. Фајл "{0}" већ користи неки други процес или је у неприступачној локацији. Неуспешно прављење дијагностичког пакета. Неуспешно аутоматско отпремљивање дијагностичког пакета. Дијагностичке информације су успешно послате. Дијагностички пакет је направљен и успешно је отпремљен. Молимо додајте следећи ID на вашу пријаву проблема: Неуспешно увођење игара из {0}. Неуспешно увођење игара из {0}. Не може да се одради претрага игара са изабраним емулаторским профилом. Профил не садржи ниједну екстензију фајла или платформу. Playnite није успео да се покрене. Молимо затворите све програме и пробајте поново. Неуспешна примена теме "{0}", профил боје "{1}" {2} Не може да се отвори линк, URL није у валидном формату. Неуспешно покретање апликације. Иницијализација компоненте прегледа веба није успела. Playnite не може наставити са покретањем. Више информација на https://playnite.link/cefstartup Не могу да се уведу емулатори због фајлова који недостају или су корумпирани. Неуспешно покретање радње из менија. Измени детаље игре URL слике Додај линк Додај РОМ Сачувај промене Примени измене поља на игре које се мењају Додај радњу "Обриши" радња Обриши "Играј" радњу Додај игре Скенирај фолдер… Детектуј инсталиране игре Претражи… Отвори Playnite Подешавања профила Назив игре не може да буде празан. Фолдер за праћење акција игре не може бити празан. Назив игре не може да буде празан пре претраге метаподатака. Неважећи подаци о игри Унесите валидни URL који почиње са http:// или https:// Изабери URL Неуспело преузимање метаподатака: {0} Грешка приликом преузимања Очисти филтере Приватни налог Јавни налог API кључ Грешка при покретању Грешка теме Очисти све Инсталирање Деинсталирање Покретање Покренуто Неважећи URL Не ради ништа Минимизирај Поврати прозор Врати прозор само кад је покренут са интерфејса Затвори Измени Напредно Никад Статус завршености Статуси завршености Корисникова оцена Оцена критичара Оцена других корисника Скрипте игара Скрипте апликација Скрипте Додаци Извори за метаподатке Екстензије ID екстензије Поново учитај скрипте Интерактивни SDK PowerShell Све скрипте су успешно поново покренуте. Ниједна игра није пронађена користећи критеријуме које сте навели током филтрирања/претраге Није пронађена ниједна ставка Промени у десктоп мод Изађи из Playnite-а Библиотеке Ажурирај све Направио: Верзија: Ажурирано: Модул: Библиотека Статистика Све Ништа Обавештења Ширина Висина Величина Мали Нормалан Велики Већи Највећи Подразумевано Изабери Изабери све Поништи селекцију Прво Насумице Корисников избор Учитај више Транспарентно Скупи Прошири Скупи све Прошири све Остало Теме Аргументи за емулатор Уграђени аргументи Посебни аргументи Додатни емулаторски аргументи Надгласај емулаторске аргументе Акција "играј" Изабери метаподатке за увоз Изабери игре за увоз Претрага метаподатака Доступно је ажурирање Промене од прошлог ажурирања Преузми и инсталирај ажурирање Провери да ли постоје ажурирања Грешка при ажурирању Неуспешна провера нових ажурирања. Нема нових ажурирања, ажурни сте. Неуспешно преузимање и инсталирање ажурирања. Позадински задатак је тренутно у току. Прекинути и наставити са ажурирањем? Позадински задатак је тренутно у току. Прекинути и напустити апликацију? Позадински задатак је тренутно у току. Прекинути и променити начин рада? Ажурирање за Playnite је доступно Поново учитај листу тема Примени изабрану тему Посматрај промене фајла Аутоматски примени тему када се изворни фајл промени Време скрипте Скрипта за покренути пре покретања игре Скрипта за покренути после излажења из игре Скрипта за покренути након покретања игре Извршите при покретању апликације Извршите при гашењу апликације Скрипта када се игра покреће Скрипта када је игра покренута Скрипта када је игра стопирана Покрени глобалну скрипту Глобално Филтрирано Тренутно Ново Тестна скрипта Прикажи сам обележено Сачувај као подразумевано Додај у омиљене Уклони из омиљених Сакриј ову игру Уклони из скривених игара Укључи подршку HDR-а Искључи подршку HDR-а Измени… Израчунај величину инсталације Израчунај величину инсталације (све игре) Израчунај величину инсталације (само недостајући подаци) Величина инсталације Подеси категорију… Подеси статус завршености Обриши Играј Инсталирај Опције игре Детаљи Деинсталирај Отвори локацију инсталације Направи пречицу на десктопу Отвори упутства за коришћење Више Руковођено од стране додатка за библиотеку Процес покретања игре ће руководити додатак за библиотеку одговоран за ову игру. Ниједна релевантна информација о игри "{0}" није пронађена на наведеној страници. Савет: Можете користити напреднији процес за преузимање метаподатака ако измењујете игру користећи "Измени" опцију у менију. Није доступно када је нека радња у току. Описни текст је сензитиван на HTML синтаксе. Време играња се бележи у секундама. Величина инсталације је обележена у бајтовима. Датум објаве мора бити у формату година-месец-дан. Месец и дан се могу изоставити. Бројеви од 0 до 100 или празно да би остало без оцене. Развој Playnite-а је подржан од стране следећих патрона и Ko-fi чланова: Кодирање, преводи и други контрибутори у никаквом посебном редоследу: Прекини праћење игара? Праћење инсталације је тренутно у току. Хоћете ли да прекините процес и вратите игру у прошло стање? Праћење покретања игре је тренутно у току. Хоћете ли да прекините процес и вратите игру у прошло стање? Време играња Последњи пут играно {0}д {1}ч {2}м {0}h {1}m {0} минут(а) {0} секунд(и) Не играно Отварање десктоп мода… Отварање мода целог екрана… Учитавање библиотеке игара… Израчунавање величине инсталације… Израчунавање величине инсталације {0}… Неуспешно инсталирање фајла скрипте. Скрипта успешно инсталирана. Инсталирај скрипту Грешка у скрипти Неуспешно покретање функције екстензије. Отвори фолдер метаподатака Израчунај Аутоматски израчунава величину инсталације користећи ROM-ове ако игра има било који или инсталациони директоријум ако је подешен {0} клијент није инсталиран. {0} клијент ће се сада отворити. Молимо пријавите се и затворите ову поруку. Чекање да се корисник пријави, молимо затворите ову поруку када сте готови… Инсталациони директоријум игре није пронађен. Неважећа конфигурација радње игре. Решавање проблема са синхронизацијом налога Решавање проблема Преименуј ставку Додај нову ставку Унеси назив Унеси нов назив Мање од сата Од 1 до 10 сати Од 10 до 100 сати Од 100 до 500 сати Од 500 до 1000 сати 1000+ Playnite треба да се поново покрене да би се инсталација завршила. Хоћете ли да поново покренете сада? Екстензија није правилно упакована. Тема није правилно упакована. Неуспешно учитавање екстензије "{0}." Неуспешно учитавање "{0}" екстензије, тренутна верзија апликације није подржана. Неуспешно учитавање теме "{0}." Неуспешно учитавање "{0}" теме, тренутна верзија апликације није подржана. Неуспешно учитавање екстензије. Неуспешно учитавање теме. Тема/екстензија користи неподржану API верзију. Инсталација је успешна. Инсталирај екстензију? Опште Неуспешна инсталација "{0}" додатка. Неуспешно инсталирање екстензије. {0} Хоћете ли да инсталирате нову екстензију? {0} Од {1} Верзија {2} Хоћете ли да ажурирате "{0}" екстензију? Тренутна верзија: {1} Нова верзија: {2} Неуспешно инсталирање теме. {0} Хоћете ли да инсталирате нову тему? {0} Од {1} Верзија {2} Хоћете ли да ажурирате "{0}" тему? Тренутна верзија: {1} Нова верзија: {2} Управо сада ће те да изађете из Playnite-а и да одете на следећи вебсајт користећи ваш подразумевани веб прегледач. Хоћете ли да наставите? {0} Изабрана(е) слика(е) је(су) можда превелика(е) за оптималне перформансе. Коришћење превеликих слика може да резултира у проблемима са интерфејсом и повећаном коришћењу меморије. Максималне препоручене резолуције: Иконе: {0} мегапиксела Насловнице: {1} мегапиксела Позадине: {2} мегапиксела Упозорење за перформансе Не приказуј поново Фајл са екстензијом {0} није компатибилан. Неисправан тип датотеке Изабрана слика је можда превелика за оптималне перформансе. Јесте ли сигурни да хоћете да обришете одабрану тему? Уграђене теме не могу да се обришу. Ова тема није подржана од стране тренутне верзије Playnite-а. Јесте ли сигурни да хоћете да обришете одабрану екстензију? Уграђене екстензије не могу да се обришу. Ова екстензија није подржана од стране тренутне верзије Playnite-а. Инсталациони директоријум Директоријум података Генерисање дијагностичког пакета… Отпремање дијагностичког пакета… Уведи фајл… Шта је ово? Да ли сте сигурни да желите да урадите ово? Укупно време играња Просечно време играња Најдуже време играња Укупна величина инсталације Преглед Мени са стране Прикажи у бочној траци Враћање подешавања Сва подешавања апликације ће бити враћена на подразумеване вредности, осим: - Локација базе података - Листа искључења увоза - Подешавања екстензија, укључујући интеграције библиотеке За завршетак процеса потребно је поновно покретање апликације. Да ли желите да ресетујете подешавања? За програмере Екстерне екстензије Унесите путању до фолдера Достигнућа Форум Вести Страница у продавници Почетна инсталација није довршена. Playnite ће се сада рестартовати у десктоп мод да бисте завршили процедуру. Недавно играно Омиљено Највише играно Све Неки филтери су примењени. Примењени су додатни филтери. Резултати претраге за: Ставка са истим називом већ постоји. Лимитирај селекцију на тренутни филтер Изабери другу Додаци... Инсталирано Подешавања екстензија Претражи Ажурирања Ажурирања ({0}) Управљање инсталираним екстензијама и темама, укључујући њихова подешавања, премештено је у нови мени „Додаци“. Овде се могу конфигурисати све тренутно инсталиране екстензије за интеграцију библиотеке. Ако желите да инсталирате или деинсталирате додатне интеграције, користите опцију „Додаци“ из главног менија. Декстоп теме Теме приказа преко целог екрана Претражујем… Додатак није компатибилан са овом верзијом апликације. Неуспешно преузимање инсталационог пакета додатка. Неуспешно преузимање инсталационог манифеста додатка. Неопходно је поново покренутри апликацију да би измене биле примењене. Овај додатак има заказано ажурирање Инсталирај Деинсталирај Већ инсталирано Нису пронађена нова ажурирања Ажурирај додатке Дневник измена није доступан Заказани за инсталацију Преузимање неуспешно Одбијена лиценца Преузимам {0}… Тражим ажурирања додатака... Доступно једно или више ажурирања додатака. Одаберите ствари за ажурирање: Развојна инстанца екстензије {0} уговор о лиценци Прихвати Одбиј Одабрати акцију Режим праћења Фреквенција праћења Линк Фајл Емулатор Скрипта Подразумевано Процес Фолдер Оригинални процес Забележи причвршћене поруке Следеће измене преписују податке за све тренутно одабране игре! Ниједан Униформно Само између ставки Само почетно и завршно Осетљивост скроловања Глатко скроловање Брзина анимација Уклонити ставку? Да ли сте сигурни да желите да уклоните ову ставку? Прикажи дугмад у горњем панелу: Општа подешавања Подешавања груписања Подешавања сортирања Унапред подешени филтери Позиција уметака Ширина размака између секција Помери дугме за главни мени у бочни мени. Претраживач панел Бирач насумичне игре Сачувеј подешавања за груписање и сортирање Прикажи као брзи филтер у приказу целог екрана Последњих 7 дана Последњих 31 дан Последњих 365 дана Пре више од 365 дана Конфигуриши Сачувај подешавање Смањи после покретања игре Умањи апликацију након покретања игре. Онемогућавање овог може изазвати проблеме да игре не буду у фокусу након покретања! Величине фонта Мали фонт Прикажи ставке у главном менију: Мења места дугмади за покретање игре и приказ странице детаља игре у главном приказу. Гласност интерфејса Гласноћа позадине Мутирај кад је у позадини Неуспешно покретање Х-унос звучног интерфејса. Излазни АПИ АПИ који се користи за репродукцију звука. Измените ако имате проблема са звуком. Општи Изгледи Звук Распоред Менији Унос {0} се покреће… {0} је у току… Велика/мала слова Размак Алтернатива Уравнотежено Квалитет Квалитет: Најбољи квалитет слике, спор, највише користи меморију. Избалансирано: Добар квалитет, брз, мало искоришћене меморије. Алтернативно: Бољи квалитет, средња брзина, мало искоришћене меморије. Изаберите фајл... Изаберите фасциклу... Скрипта за покретање Имајте на уму да и екстензије и теме могу у великој мери утицати на перформансе, стабилност и безбедност Playnite-а. Ако почнете да имате проблема након инсталирања теме или екстензије, покушајте прво да их онемогућите/деинсталирате да бисте видели да ли су оне узрок проблема. Одабери при покретању Одабери при покретању Уграђени профил Уграђени профил Посебни профили Посебни профил Руковођен уграђеном скриптом Спецификација емулатора Спецификација платформе Спецификација регије Скрипта за покренути пре покретања игре Скрипта за покренути након покретања игре Покрени након излажења из игре Извршна датотека емулатора није пронађена Спецификација емулатора није пронађена Скрипта за покретање емулатора није пронађена Растави у одвојене игре Повежи у једну игру Постави платформу Постави регион Скенирај фолдер Скенирај конфигурације Искључи обрасце из скенирања контролног збира. Контролни збир фајлова који одговарају наведеном обрасцу неће бити проверен и биће упоређен само по имену фајла. Погледати помоћну страницу емулатора за информације. Скенирај са емулатором Име мора бити постављено када се чува нова конфигурација. Емулатор или њехов профил нису подешени. Путања за скенирање није подешена или је празна. Конфигурација скенера није правилно постављена. Укључи у групно скенирање аутоматског скенирања Неусешно скенирање емулатора унутар фолдера Неуспешно скенирање фолдера за игре емулатора. Сакриј увезено Профили за увоз: Аутоматски скенирај конфигурације Сачувај као ауто-скен конфигурацију Чува конфигурацију за коришћење приликом ажурирања библиотеке. Сачуване конфигурације се могу подешавати преко менија "Конфигурација емулатора" Увези користећи релативне путање Ако је могуће, увези датотеке игара користећи путање у односу на Playnite-ов инсталациони директоријум или инсталациони директоријум емулатора. Скенирај подфолдере Скенирај у архивама Споји повезане фајлове Споји повезане фајлове игара, нпр. појединачних дискова игара, под једним уносом игре. Додај скенер Додај сачуван скенер Почни скен Подразумеван статус додељен ново-додатим играма Статус додељен играма које се покрећу први пут Иницијализација PowerShell скрипте није успело. Ако користите Windows 7, пробајте да (ре)инсталирате PowerShell 5.1 да поправите грешку. Унапред подешен филтер са задатим именом већ постоји. Ажурирати постојећи филтер са новим подешавањима? Сортирање Апликација ради са повишеним привилегијама (администратор). Ово се не препоручује јер се тако дају повишене привилегије свим додацима и играма/апликацијама покренутим од стране апликације. Више информација на https://playnite.link/adminfaq Прикажи упозорење ако апликација ради са повишеним привилегијама Следећи додаци су пријављени као можда проблематични, или због високог утицаја на стабилност/перформансе, или због сигурности. Топло препоручујемо да их уклоните: {0} Изузми онлајн фајлове из скена Фајлови смештени на серверу неће бити скенирани и увезерни ако нису доступни локално. Подржани су само: Гугл Диск, Дропбокс и Онедриве Скенирај користећи поједностављен метод без садржаја фајлова. Фајлови ће бити увезени користећи мање прецизну методу која не гарантује преузимање садржаја Примени на све Само ручно Једном дневно Једном недељно Приликом сваког покретања Провери за ажурирања програма Провери за ажурирања додатака Ажурирај библиотеке Скенирај фолдере емулације Укључи скривене игре Уреди поља Изабери / Поништи све Отвори Активирај Додели Почните да куцате да бисте тражили игре… [F1] за помоћ. Почетак са # приказује листу доступних команди. Почетак са / приказује листу доступних добављача/додатака за претрагу. Уношење кључне речи за претрагу и завршавање са РАЗМАКОМ, одмах вас пребацује на ту претрагу. ТАБУЛАТОР: промени акцију ЕНТЕР: активирај изабрану акцију ШИФТ-ЕНТЕР: отвори мени ставки Укључи неинсталиране игре Укључи скривене игре Неисталиране игре су укључене Неисталиране игре су искључене Скривене игре су укључене Скривене игре су искључене Враћање података из резервне копије… Враћање података из резервне копије није успело. Подешавања Библиотека игара Медији игара библиотеке Инсталиране екстензије Подаци екстензије Инсталиране теме Изабери податке за враћање из изабране резервне копије. Playnite ће се аутоматски поновно покренути да покрене процес повратка резервне копије. Изаберите ставке које ће бити укључене у резервну копију података. Подешавања апликације и подаци библиотеке игара су подразумевано укључени. Playnite ће се аутоматски поновно покренути да покрене процес резервне копије. Аутоматска резервна копија података Фреквенција аутоматске резервне копије Фасцикла резеервних копија Ротирајуће резервне копије Укључи додатне податке: Фасцикла резервних копија мора бити одређена ако су аутоматске резервне копије укључене. Прикажи обавештења само за новости Када је омогућено, само ажурирања доступна за тренутно инсталирано главно издање ће резултирати обавештењем о ажурирању. Нова велика издања неће резултирати обавештењем о ажурирању. Користи релативне датуме за прошлу недељу Користи формат релативног датума нпр. "Данас", "Јуче"... ако је датум стар мање од недељу дана. Наведени формат датума ће се користити за све остале датуме. Потражи слику на интернету Добијање информација о додатку… Није доступан извор метаподатака Изабери профил при покретању Изабери емулатор при покретању Аутоматски Увек укључено Увек искључено Подршка за приступачност (читач екрана) Мени апликација Мени игара Фасцикла програма Директоријум корисничких података Откривена је корупција библиотеке, Playnite ће се сада угасити. Отворите ново питање (issue) на Playnite-овој GitHub страници са захтевом да поправите оштећење у вашим датотекама. Да ли желите да сачувате промене које сте направили? Преносива инсталација Ниједан контролер није препознат ================================================ FILE: source/Playnite/Localization/sv_SE.xaml ================================================  Svenska Playnite språk Avsluta Filtret aktivt Filtret inaktiverat Ytterligare filter Filter Filter Ogiltiga data Spara ändring? Hemsida: www.playnite.link Källkod: GitHub Skapa diag. paket Skicka felsökningsinformation Om Playnite Skapad av Josef Němec Tilldela Kategori Sätt Kategori Ny Kategori Bockad - Tilldela kategori Avbockad - Ta bort kategori Obestämd - Inga ändringar (vid redigering av flera spel) Ingen kategori Ingen plattform Hoppsan! Något gick fel. Ett oåterkallelig fel har inträffat. Om du vill hjälpa oss att åtgärda problemet, beskriv kortfattat vad som hände före kraschen, och skicka sedan diagnosinformationen. Om du är uppkopplad laddas paketet upp till Playnite-servern för analys. Alternativt kan du klicka på knappen 'Rapportera krasch' för att skapa ett nytt GitHub issue och rapportera kraschen manuellt. Tack för din hjälp. Tillägget "{0}" orsakade ett oåterkalleligt fel. Vi rekommenderar att du sparar loggfilen och rapporterar problemet till tilläggets utvecklare. Om problemet fortsätter att återkomma, inaktivera tillägget. Tillägget "{0}" orsakade ett oåterkalleligt fel. Vi rekommenderar att du rapporterar problemet till tilläggets utvecklare. Om problemet fortsätter att återkomma, inaktivera tillägget. Okänt tillägg eller tema orsakade ett fel som inte kan åtgärdas. Vi rekommenderar att stänga av tredjeparts-tillägg, isolera den problematiska och rapportera problemet till tilläggets utvecklare. Ett oåterkallelig fel har inträffat. Om du vill hjälpa oss att lösa problemet, skicka diagnostisk information. Tack. Inaktivera tillägg Spara loggfil Send diag. info Skicka diagnosinfo Rapportera en krasch Starta om Playnite Starta om i felsäkert läge Inaktivera alla tillägg från tredje part och använda standardtema. Avsluta Playnite Åtgärder som vidtagits före kraschen (på engelska): Bibliotekshanterare Ta bort Spel? Kan inte ta bort - Spelet eller installationsprogrammet körs. Kan inte avinstallera - Spelet körs. Är du säker på att du vill ta bort {0}? Är du säker på att du vill ta bort {0} spel? Är du säker på att du vill ta bort {0}? Om du väljer inställningen "lägg till på uteslutnings listan" kommer spel inte kunna bli importerade igen för än nästa gång biblioteket blir uppdaterat. Är du säker på att du vill ta bort {0} spel? Om du väljer alternativet "lägg till i uteslutningslistan" förhindras spel att importeras igen nästa gång biblioteket uppdateras. Är du säker på att du vill ta bort {0} poster som för närvarande inte används? Inga oanvända fält hittades. Ja (lägg till i undantagslistan) Det finns osparade ändringar i denna sektion Uppdaterar databasversionen… Databasuppdatering misslyckades, kan inte öppna databasfilen. Det går inte att uppdatera spelbiblioteket. {0} MB ledigt diskutrymme krävs. SpelFel Kan inte starta spelet. '{0}' hittades inte i databasen. Kan inte starta spelet: {0} Kan inte starta åtgärd: {0} Kan inte öppna spelplats: {0} Kunde inte upptäcka spelets installationsstorlek: {0} Fel vid skanning av installationsstorlek {0} fel uppstod vid skanning av installationsstorlek Misslyckades med att skapa genväg: {0} Det gick inte att öppna manualen: {0} Kan inte installera spel: {0} Kan inte avinstallera spel: {0} Inga giltiga startåtgärder för spelet hittades. När du använder emulatoråtgärder, se till att plattformsdefinitionerna matchar mellan spelet och emulatorkonfigurationen. Installationsimplementering är inte tillgänglig. Biblioteksplugin som ansvarar för detta spel är inaktiverat eller inte installerat. Officiell nedladdningskälla för metadata är inte tällgänglig. Inget spel är markerat. Körning av spelskript misslyckades. Körning av programskript misslyckades. Global skriptkörning misslyckades. Körning av emulatorskript misslyckades. Körningen av uppspelningsskript misslyckades. PowerShell 3.0 eller senare är inte installerat. Kunde inte avgöra hur spelet ska startas. Aktiverad AV Ta bort Ta bort oanvända Byt namn Kopiera Lägg till Standard Ikon Standard Omslag Standard bakgrundsbild Klart Nästa Tillbaka KLART TILLBAKA RENSA Rensa Avfärda Avfärda alla Importera Namn Skapare Modul Serie Version Senast spelad Mest spelade Antal speltillfällen Installerad storlek Mapp Noteringar Tillagd Tillagd datum Ändrad Ändrad datum Hemsida Sökväg Ok Spara Stäng Avbryt Bekräfta Återställ Ja Nej Välkommen Lokal Användare Allmän Media Länkar Installation Handlingar Laddar ner… Laddar ner media Laddar… Typ Profil Profiler Ta bort Ladda ner Sök Upplösning: Alla Zoom Listvy Omslag Rutnätsvy Detaljvy Manuell URL Tack till Licens Bidragsgivare Avslutar Playnite… Idag Igår Måndag Tisdag Onsdag Torsdag Fredag Lördag Söndag Senaste veckan Senaste månaden Senaste året Mer än 1 år sedan 0 till 100 MB 100MB till 1GB 1GB till 5GB 5GB till 10GB 10GB till 20GB 20GB till 40GB 40GB till 100GB 100GB eller mer Import slutfördes framgångsrikt. Alla spel Spel-id Databas-id Förinställningar Kolumn Kolumner Rad Rader Det gick inte att hämta ikonen från spelåtgärden. Det finns ingen åtgärd av filtypen närvarande. Ladda bara ner saknade metadata Om detta alternativ är aktiverat så kommer nedladdning av metadata skippas helt för fält som redan innehåller information. Spelval Vänligen välj vilka spel som ska uppdateras med nya metadata: Alla spel från databasen Alla filtrerade spel Endast utvalda spel Officiell Butiken IGDB Vänligen välj vilka fält som ska fyllas automatiskt av Playnite och vilka källor ska användas för att få data från. Vänligen överväg att klicka på logotypen ovan för att bidra med uppdateringar till databasen på igdb.com för att förbättra den data som Playnite använder. Hämtar metadata… Importerar installerade spel… Importerar {0} spel… Importerar emulerade spel från {0} Hämtar biblioteksuppdateringar… Skannar spelens storlek i biblioteket… Skannar storleken på importerade spel… Bibliotekets uppdatering slutförd Frigör resurser… Konfiguration Inställningar… Plattformar och Emulatorer Konfigurera emulatorer… Bibliotekshanterare… Verktyg Hämta Metadata… Programvaruverktyg Konfigurera integrationer Öppna extern Plattform Tredjepartsklienter Uppdatera Bibliotek Avbryt biblioteksuppdatering Uppdatera emulerade mappar Lägg till Spel Manuellt… Installerade Spel… Emulerade spel… Microsoft Store applikation… Om Playnite Skicka Feedback Ändra till Fullskärmsläge Länkar Hjälp Stöd Playnite på Patreon Stöd oss på Ko-fi Användarmanual SDK-dokumentation Starta om systemet Stäng ner systemet Sätt systemet i viloläge Viloläge Låsningssystem Logga ut användare Välj ett Slumpmässigt Spel Infofält som visas på detaljpanelen: Marginal mellan rutorna Rutmönstrad bakgrund Tjocklek på ram för rutnätet Saknar källa för spelikoner Saknar källa för spelomslagsbilder Bakgrundskälla för spel saknas Lodrätt mellanrum för speldetaljer Plats för speldetaljer i rutnätsvy Plats för spellistan i detaljvyn Separator mellan paneler Höjd för spelomslag Ikonhöjd på spellistan Font Monospacetypsnitt Plats för filterpanelen Plats för utforskarpanelen Visning spelomslagsbilder Målbildförhållande Följande alternativ påverkar även brickors rendering i helskärmsläge! Tänj DVD Låda Epic Games Store GOG Galaxy 2.0 IGDB Kvadrat Steam Omslag Verrtikalt Steam-omslag Twitch * Kräver omstart för att tillämpa Inställningar Allmänt Övre panel Utseende Speldetaljer Layout Avancerat Helskärm Inmatning Prestanda Metadata Uppdaterar Sök Säkerhetskopiera Säkerhetskopiera biblioteksdata Återställ säkerhetskopia av data Importera ändringar i bibliotek automatiskt Ogiltig databas sökväg, rätt filväg måste ställas in. Kontonamnet får inte vara tomt. Ladda ner metadata efter import av spel Kör Playnite minimerat Kör Playnite när du startar datorn Starta minimerad till aktivitetsfältet Misslyckades att registrera Playnite att starta automatiskt när datorn startar. Kör i Fullskärmsläge Asynkron laddning av bilder Förbättrar rullningsläge i spellistor i utbyte mot långsammare bildbelastningstider. Visa spelnamn när spelomslag saknas Visa spelnamn i rutnätsvyn Gör avinstallerade spel mörkare Visa spelikoner i listvy Visa räknare på titelfältet för gruppen Visa endast valda fält på filter- och utforskarpaneler Inaktivera hårdvaruacceleration Använd när du upplever hackande användargränssnitt eller liknande problem Visa gömda spel i snabbstartslistor Påverkar aktivitetsfältets och och skrivbordets snabbmenyer Antal snabbstartsobjekt Använd spelets bakgrundsbild som bakgrund för fönstret Sudda bakgrundsbild Hög kvalitet Förmörka bakgrundsbild Visa i rutnätsvyn Tema Tema Profiler Fullskärms Teman Fullskärms Tema Profil Sökväg till databas Inloggningsstatus: Playnite Inställningar Rensa webbcache Kan lösa problem som uppstått vid länkning av konton. Visa systemfältsikon Minimera Playnite till systemfältet Minimera Playnite till systemfältet när programmet stängs ner När spel startar: Efter att ett spel avslutas: Visa tid spelad i dagar Datumformat: Det här loggar ut dig från alla länkade tjänster. Omstart av program krävs, vill du fortsätta? Rensa Cache? Playnite omstart krävs för att tillämpa nytt tema Skaffa fler teman Skapa nytt tema Skaffa fler tillägg Skapa nya tillägg Hjälp oss översätta Playnite Playnite behöver startas om för att tillämpa nya inställningar. Starta om nu? Omstarten kommer avbryta alla aktiva processer (nedladdningar) som körs just nu. Starta om Playnite? Playnite kan inte flytta dina filer automatiskt. Du måste manuellt flytta/kopiera över filerna innan du byter lagringsplatsen. Om det inte finns någon mapp vid den valda platsen, kommer en ny skapas. Den nya mappen kommer inte börja användas förens Playnite startas om. Speltid kommer inte registreras om "Stäng" är valt. Antal rader Antal kolumner Antalet rader i detaljvyn Visa Bakgrundsbild på Hemskärmen Gäller ej retroaktivt för existerande spel i biblioteket utan att ladda ner metadata på nytt. Importera speltid för spel i biblioteket: Bestämmer när Playnite ska importera den speltid som rapporteras av bibliotekets tillägg för spel i Playnite-databasen. Stöd av bibliotekets tillägg, som ansvarar för hanteringen av spelet, behövs för att kunna använda denna funktion. Alltid: Importerar speltid för nya importerade och befintliga spel i Playnite-databasen. Endast för nyimporterade spel: Importerar endast speltid för nya importerade spel. Aldrig: Importera aldrig speltid under några omständigheter. Alltid Endast för nyimporterade spel Aldrig Aktivera stöd för spelkontroller i Skrivbordsläge Guide-knappen öppnar Fullskärmsläget Inställningar för automatisk metadata-nedladdning för nyimporterade spel. Målskärm Använd alltid primärskärm Visa speltitlar Visa batteristatus Visa batteriprocent Visa klocka Dölj musmarkören Visa enbart installerade spel i snabbfilter Anpassade knapptexter Layout Skrolla vågrätt Välj en av undermenyerna Inga inställningar tillgängliga Misslyckades att ladda inställningar Dessa skript körs för varje spel i biblioteket. Individuella skript kan tilldelas varje spel separat medan du redigerar speldetaljer. Animera bakgrundsbildsövergångar Typsnittsstorlekar Automatisk Utjämnad Gråskala ClearType Idealisk Skärm Textformateringsläge Textrenderingsläge Textrendering och formateringsmetoder används för närvarande inte för spelbeskrivningar. Förladda bakgrundsbilder Om aktiverad, kommer Playnite ladda ner bakgrundsbilder när du laddar ner metadata, detta kräver mer diskutrymme och gör bakgrunden tillgänglig offline. Om inaktiverad, laddas bakgrundsbilder endast ner när det behövs för första gången, detta kräver mindre utrymme, men kan resultera i en fördröjning innan bilder visas och vissa bilder kanske inte är tillgängliga offline. Stäng automatiskt tredjepartsklient när spelet har avslutats Fördröjning för klientavstängning (i sekunder) Stäng inte efter spelsessioner kortare än (i sekunder) Stäng automatiskt följande klienter: Stäng klienter automatiskt Importera uteslutningslista Visa varning vid tilldelning av för stora spelmedia Kommando för mappöppning Föredragen åldersbedömningsorganisation Uppdatera installationsstorleken på spel vid uppdatering av bibliotek Skannar och uppdaterar installationsstorleken på spel om det upptäcks att deras filer har ändrats sedan den senaste skanningen Ingen Fyll ut Enhetlig Fyll likformigt Vänster Höger Toppen Botten Importerings fel Autentisering krävs Autentisering misslyckades Alternativt renderingsläge för webbvy Använd när du upplever problem med webbvyer, till exempel integreringsautentiseringsdialoger. Delvis laddning av långa spelbeskrivningar Långa spelbeskrivningar kan orsaka märkbart lagg när man väljer spel. Om du sätter på inställningen kommer det bara laddas in en liten del av beskrivningstexten och ett alternativ för att ladda in mer. Importera metadata Hämta Metadata Anger att den valda konfigurationen kommer att användas för alla framtida nedladdningar av metadata. Detta kan också konfigureras i applikationsinställningarna. Emulerings Import Assistent Den här guiden kommer att guida dig genom processen att ladda ner och importera konsolemulatorer och importera emulerade spel. Tänk på att du alltid kan lägga till ytterligare emulatorer och/eller spel senare via huvudmenyn (under "Bibliotek"-menyn för Emulatorinställningar och "Lägg till spel"-menyn för emulerade spel). Nedan är en lista över emulatorer som Playnite kan känna igen och konfigurera automatiskt. Du kan ladda ner emulatorinstallatörer från deras webbplatser. När du har emulatorerna installerade (manuellt) kan du importera dem på emulatorkonfigurationsdialogrutan. Du kan importera emulatorer som är installerade på din dator genom att klicka på knappen "Autodetect From Folder…". Playnite kommer att söka i den valda mappen efter alla kända emulatorer och ge möjlighet att importera dem. Du kan använda denna knapp flera gånger för att importera emulatorer från olika mappar. Emulatorer kommer att läggas till i botten av den aktuella listan. Du kan importera spel genom att klicka på knappen "Skanna mapp med Emulator". Att välja lämplig emulator kommer att tala om för Playnite vilka filtyper som ska skannas och importeras. Du kan använda denna knapp flera gånger för att importera spel från olika mappar. Spel kommer att läggas till i botten av den aktuella listan. Det finns inga emulatorer för import. Du kommer inte att kunna importera emulerade spel automatiskt utan att konfigurera emulatorer först. Är du säker på att du vill fortsätta och avsluta importprocessen? Det finns inga emulatorer konfigurerade i Playnite. Du kan inte importera spel utan att först konfigurera emulatorn och välja lämpliga filtyper. Vill du lägga till några emulatorer nu? Skanna katalog med Emulator Välj filer Identifiera automatiskt från mapp… Konfigurera emulatorer… Skannar… Skannar {0}... Första Gången Konfiguration Denna process kommer att vägleda dig genom en automatisk import och konfiguration av externa spelbibliotek. Playnite kan automatiskt importera spel från flera speltjänster, som Steam eller GOG. Tänk på att du också manuellt kan lägga till ett anpassat eller emulerat spel för någon plattform senare från huvudmenyn. Bibliotek Integration Följande är listan över några kurerade bibliotek integrationer Playnite stöder. Välj de du vill installera. Fler integrationer kan installeras senare från menyn "Tillägg". Konfiguration Avslutad Den första installationen har slutförts. Kom ihåg att du kan ändra alla inställningar senare samt lägga till ytterligare integreringar från huvudmenyn. Det gick inte att ladda ner ett eller flera tillägg. Du kan försöka ladda om integrationer från tilläggsmenyn efter att guiden först körts. Laddar ner {0} integration… Laddar ner lista över rekommenderade integrationer… Det gick inte att ladda ner listan över rekommenderade integrationer. Du kan försöka ladda ner integrationer igen senare via Addons menyn. Konfigurera Plattformar och Emulatorer Konfigurera emulatorer Plattformar Plattform Emulatorer Emulator Lägg till Plattform Välj Ikon Välj Omslag Välj bild Välj Objekt Välj Bakgrund Välj Fil Välj URL Lägg Till Emulator Platform(ar) Vill du spara plattformsändringar? Vill du spara emulatorändringar? Kör fil Argument Katalog Filtyper Importera Emulatorer… Hämta Emulatorer… Ladda argumenter förinställda från känd emulatorprofil Är du säker på att du vill ta bort {0} emulator? Den används för tillfället av {1} spel. Är du säker på att du vill ta bort {0} emulator? Den används för tillfället av {1} spel och {2} emulator(er). Inställningar hjälp Sortera efter Sorteringsordning Gruppera efter Stigande Fallande Gruppera inte Gruppera efter Distribuerare Gruppera efter Kategori Gruppera efter Plattform Typ av vy Vy Utforskar-panel Filter-panel Ikon Biblioteketsikon Omslagsbild Bakgrundsbild Sorteringsnamn Leverantör Handbok Titel Installationsenhet Kontonamn Plattform Kategori(er) Genre(r) Utgivningsdatum Släpptes år Utvecklare Tag(s) Utgivare Installationsstatus Matcha alla filter Om aktiverad, kommer endast spel som använder alla objekt i alla filter att inkluderas i vyn. Om inaktiverad kommer spel som använder något objekt i något filter att inkluderas i vyn. Installerad Installerad Inte installerad Dold Favorit Sätt på HDR stöd Tänk på att din primära skärm inte stödjer HDR. Senast Aktivitet Kategori Beskrivning Installationskatalog Omslagsbild Länk Image/ISO Sökväg Genre(r) Genrer Företag Bolag Utvecklare Utvecklare Utgivare Utgivare Kategori Kategorier Etikett Etiketter Funktion Funktioner Åldersgränser Åldersgränser Region Regioner Källkod Källor Senaste aktivitet Databas fel Misslyckades med att öppna bibliotekets databas. Databasen är inte öppen. Kan inte komma åt biblioteksdatabasen. Fil "{0}" används av en annan process eller är otillgängligt. Misslyckades med att skapa diagnostikpaket. Misslyckades med att automatiskt ladda upp paket med felsökningsinformation. Lyckades skicka felsökningsinformationen. Diagnostikpaketet har skapats och skickats. Var vänlig bifoga följande ID i din felanmälan: Misslyckades med att importera spel från {0}. Misslyckades med att importera spel från {0}. Kan inte söka efter spel med den valda emulatorns profil. Profilen innehåller inga filtillägg eller plattformar. Playnite kunde inte starta. Var vänlig stäng alla andra flikar och försök igen. Kunde inte tillämpa temat "{0}", färgprofil "{1}" {2} Kan inte öppna länk, URL är inte i giltigt format. Kunde inte starta programmet. Det gick inte att initiera webbvykomponent. Playnite kan inte fortsätta med startprocessen. Mer information på https://playnite.link/cefstartup Kan inte importera emulatorer på grund av borttappade eller korrupta definitionsfiler. Kunde inte köra menyåtgärd. Redigera Speluppgifter Bild URL Lägg till Länk Lägg till ROM Spara Ändringar Tillämpa fältändringar på spel(en) som redigeras. Lägg till Åtgärd Ta bort Åtgärd Ta bort Startalternativ Lägg till Spel Skanna katalog… Uptäck installerade Bläddra… Öpnna Playnite Profilinställningar Spelnamnet kan inte vara tomt. Spårningskatalog för spel kan inte vara tom. Spelnamnet kan inte vara tomt innan du söker metadata. Ogiltig spelinformation Ange giltig webbadress http:// eller https:// Välj URL Det gick inte att hämta metadata: {0} Hämtings fel Rensa Filter Privat konto Allmän användare API Nyckel Startfel Tema Fel Rensa allt Installation pågår... Avinstallerar Startar Körs Ogiltig URL Gör inget Minimera Återställ fönster Återställ fönstret endast när det startats från UI. Stäng Ändra Avancerat Aldrig Status för avklarande Status för slutförandet Användarbetyg Betyg från kritiker Övriga användares betyg Spelskript Applikationsskript Skript Plugins Källor för metadata Tillägg Tilläggs-ID Ladda om skript Interaktivt SDK PowerShell Alla skript blev omladdade. Inga spel hittades för angivna sök-/filterkriterier Inga objekt hittades Växla till skrivbordsläge Avsluta Playnite Bibliotek Uppdatera alla Skapad av: Version: Uppdaterad: Modul: Bibliotek Statistik Alla Ingen Aviseringar Bredd Höjd Storlek Liten Normal Stor Större Störst Standard Välj Välj alla Avmarkera alla Första Slumpmässigt Välj användare Ladda mer Genomskinlig Stäng Expandera Stäng allt Öppna allt Annat Teman Argument till Emulatorn Inbyggda argument Anpassade argument Ytterligare Emulator Arguments Skriv över Emulator Arguments Välj metadata att importera Välj Spel för Import Metadata sök Uppdatering tillgänglig Ändringar sedan senaste uppdateringen Installera uppdatering Sök efter uppdateringar Uppdateringsfel Misslyckades med att söka efter en ny version. Ingen ny version hittat, du har den senaste. Misslyckades med att ladda ner och installera uppdatering. Någon bakgrundsprocess håller fortfarande på. Är du säker på att du vill avbryta den och fortsätta med uppdateringen? Någon bakgrundsprocess håller fortfarande på. Är du säker på att du vill avbryta den och stänga av Playnite? Någon bakgrundsprocess håller fortfarande på. Att byta läge kommer att avbryta den, vill du byta läge ändå? En uppdatering för Playnite finns tillgänglig Uppdatera temalista Tillämpa valt tema Titta på filändringar Använd automatiskt tema när källfilen ändras Körtid för skript Kör innan du startar ett spel Kör efter avslutat spel Kör efter ett spel har startats Kör vid programstart Kör vid programstängning Spelet startar skript Spelet startade skript Spelet stoppades skript Kör globalt skript Global Filtrerad Nuvarande Ny Testa skript Visa endast valda. Spara som standard Lägg till i Favoriter Ta bort från Favoriter Dölj detta spel Ta bort från dolda Sätt på HDR stöd Stäng av HDR stöd Ändra… Beräkna installationsstorlek Beräkna installationsstorlek (alla spel) Beräkna installationsstorlek (endast saknade data) Installerad storlek Ange Kategori… Ange status för slutförandet Ta Bort Spela Installera Spelinställningar Detaljer Avinstallera Bläddra bland Lokalafiler Skapa genväg på Skrivbordet Öppna manuellt Mer Hanteras av biblioteketstillägget Spelets startprocess kommer att hanteras av det biblioteketstillägg som ansvarar för detta spel. Ingen relevant information om spelet '{0}' har hittats på den angivna sidan. Tips! Du kan använda mer avancerad metadatahämtningsprocess medan du redigerar ett spel via menyn "Redigera". Ej tillgänglig när annan process pågår. Beskrivning text är HTML-syntax känslig Speltid registreras i sekunder. Installationsstorleken är angiven i bytes. Publiceringsdatum måste anges i "år-månad-dag" format. Månad och Dag värden kan utelämnas. Värde mellan 0 till 100 eller tom för inget betyg. Kod, lokalisering och andra bidragsgivare i ingen särskild ordning: Avbryt övervakning av spel? Installationsövervakning pågår för närvarande. Vill du avbryta processen och returnera spelet till föregående tillstånd? Övervakningen av spelet körs för närvarande. Vill du avbryta processen och returnera spelet till föregående tillstånd? Speltid Senast spelat {0}d {1}h {2}m {0}h {1}m {0} minuter {0} sekunder Aldrig spelat Öppnar skrivbordsläge… Öppnar helskrärmsläge… Laddar spelbibeliotek… Beräknar installationsstorlek… Beräknar installationsstorlek för {0}… Det gick inte att installera skriptfilen. Skriptet har installerats. Installera skript Fel i skript Kunde inte köra aktuell funktion i utökningen. Öppna katalog för metadata Beräkna Beräknar automatiskt installationsstorleken med hjälp av ROMs om spelet har någon eller installationskatalogen om den har ställts in {0}-klienten är inte installerad. {0}-klienten kommer nu att öppnas. Var vänlig logga in där och stäng sedan detta meddelande. Väntar på inloggning, var vänlig stäng detta fönster när du är färdig… Kunde inte hitta installationskatalogen för spelet. Ogiltig konfiguration för spelhandlingar. Felsökning gällande synkronisering av konto Felsökning av problem Ändra namn på post Lägg till en ny post Ange namn Ange nytt namn Mindre än en timme 1 till 10 timmar 10 till 100 timmar 100 till 500 timmar 500 till 1000 timmar 1000+ Playnite måste startas om för att slutföra installationen. Vill du starta om nu? Tillägg är inte paketerat på rätt sätt. Tema är inte paketerat på rätt sätt. {0} tillägget lyckades inte ladda in korrekt. Installationen lyckades. Installera tillägg? Allmänt Misslyckades med att installera tillägget. {0} Är du säker på att du vill installera ett nytt tillägg? {0} Av {1} Version {2} Är du säker på att du vill uppdatera "{0}" tillägget? Nuvarande version: {1} Ny version: {2} Misslyckades med att installera temat. {0} Är du säker på att du vill installera ett nytt tema? {0} Av {1} Version {2} Är du säker på att du vill uppdatera "{0}" temat? Nuvarande version: {1} Ny version: {2} Prestandavarning Visa Inte Igen Fil med tillägget {0} är inte kompatibel. Inkompatibel filändelse Den valda bilden kan vara för stor för optimal prestanda Standard tema kan inte avinstalleras. Temat funkar inte med denna version av Playnite. Installationsmapp Datamapp Vad är det här? Är du säker på att du vill göra detta? Total speltid Genomsnittlig speltid Högsta speltid Total installations storlek. Överblick Sidofält Återställ inställningar Prestationer Forum Nyheter Butikssida Den inledande installationen är inte färdig. Playnite kommer nu starta om till Skrivbordsläge för att slutföra processen. Senast spelade Favoriter Mest spelade Alla Filter är aktiva. Sökresultat för: En post med samma namn existerar redan. Välj en annan Installerad Tilläggsinställningar Bläddra Uppdateringar Uppdateringar ({0}) Hantering av installerade tillägg och teman, inklusive deras inställningar, har flyttats till en ny "tillägg"-meny. Alla nu installerade biblioteksintegrationstillägg kan konfigureras här. Om du vill installera eller avinstallera ytterligare integreringar, använd alternativet "Tillägg" från huvudmenyn. Skrivbordsteman Helskärmsteman Söker… Tillägg är inte kompatibelt med denna version av Playnite. Det gick inte att ladda ner installationspaketet för tillägg. Det gick inte att ladda ner installationsmanifestet för tillägg. Omstart av programmet krävs för att tillämpa väntande ändringar. Det här tillägget är schemalagt för installation. Installera Ominstallera Avinstallera Redan installerad Inga nya tilläggsuppdateringar hittades. Uppdatera tillägg Ändringslogg är inte tillgänglig Schemalagd för installation Nedladdning misslyckad Licensen avvisades Hämtar {0}... Söker efter tilläggsuppdateringar… Letar efter uppdateringar. Ett eller flera tilläggsuppdateringar finns tillgängliga. Välj objekt att uppdatera Expansion för tilläggsutveckling {0} licensavtal Godkänn Avböj Inkludera aktiviteter för biblioteksintegrering Välj åtgärd Spårningsläge Länk Fil Emulator Skript Förinställd Bearbeta Mapp Ursprunglig process Logga spårningsmeddelanden Efter ändringar skriver du över data för alla valda spel! Ingen Enhetlig Endast objekt Endast start och slut Scrollkänslighet Mjuk rullning Flytta huvudmenyknappen till sidofältet Slumpmässig spelväljare Utrymme Bildrenderingsskalare Alternativ Balanserad Kvalitet Kvalitet: Bästa bildkvalitet, långsam, hög minnesanvändning. Balanserad: Bra kvalitet, snabb, låg minnesanvändning. Alternativ: Bättre kvalitet, medelhög hastighet, låg minnesanvändning. Välj fil… Välj mapp… Uppstartsskript Observera att både tillägg och teman i hög grad kan påverka Playnites prestanda, stabilitet och säkerhet. Om du börjar uppleva några problem efter att du installerat ett tema eller ett tillägg, försök avaktivera/avinstallera dem först för att se om de är roten till problemet. Välj vid uppstart Välj vid uppstart Inbyggda profiler Inbyggd profil Anpassade profiler Hanteras av inbyggt skript Specifikation för emulator Specifikation för plattformen Specifikation för region Kör innan emulatorn startas Kör efter emulatorn startad Kör när emulatorn avslutats Emulator: körbar fil hittades inte. Emulatorspecifikation hittades inte. Emulatorns startskript hittades inte. Dela som separata spel Slå ihop till ett spel Ange plattform Ange region Skanna mapp Skanna konfigurationer Uteslut mönster från checksummaskanning Filer som matchar angivna mönster kommer inte att skannas efter kontrollsumma och kommer att matchas med filnamn. Se emulatorns hjälpsida för mer information. Skanna med emulator Du måste ange ett namn när du sparar ny konfiguration. Emulator eller emulatorprofil är inte vald. Katalog att skanna är inte angiven eller så finns den inte. Skanning av konfigurationen är inte korrekt inställd. Inkludera i massskanning automatisk skanning Det gick inte att söka efter emulatorer. Det gick inte att skanna mapp(er) efter emulerade spel. Dölj importerade Profiler att importera: Autoskanna konfigurationer Spara som autoskannings-konfiguration Sparar konfiguration för senare användning under biblioteksuppdatering. Sparade konfigurationer kan hanteras via menyn "Konfigurera emulatorer". Importera med relativa sökvägar Om möjligt importera spelfiler med sökvägar i förhållande till Playnites installationsmapp eller emulatorns installationsmapp. Skanna undermappar Skanna inuti arkiv Lägg till skanner Gå till detaljer Spelmeny Redigera spel Öppna sök Sökruta Sökknapp Primär spelåtgärd Sekundär spelåtgärd CTRL-F öppnar global sökning istället för att fokusera sökruta Spara spelfilterinställningar mellan söksessioner Sökleverantörer Standard nyckelord Anpassat nyckelord Systemövergripande genväg Playnite-sökning Tilläggsinställningar Undantag Exkluderade filer i förhållande till skanningsmappen Uteslutna mappar i förhållande till skanningsmappen Lägg till fil i uteslutningslistan Lägg till mapp i uteslutningslistan Undantag kan endast läggas till i sparade skanner-konfigurationer. Undantag har lagts till i "{0}" skanningar. Åsidosätt plattform När den är inställd, kommer skannern tilldela denna plattform till alla spel, skriva över alla automatiskt upptäckta plattformar. Inkludera kommandon i standardsökningen När inaktiverad kommer kommandon inte att inkluderas i standardsökningen förrän # prefix används. Fält som ska visas för spelresultat: Dold status Säkerhetskopiering av data avbröts. Säkerhetskopiering av data misslyckades. Fel vid säkerhetskopiering Säkerhetskopiering av data pågår… Återställer data från säkerhetskopia… Det gick inte att återställa data från säkerhetskopian. Inställningar Spelbibliotek Media för spelbibliotek Installerade tillägg Tilläggsdata Installerade teman Välj data som ska återställas från angiven säkerhetskopia. Playnite startar automatiskt om för att starta säkerhetskopieringsprocessen. Välj objekt som ska inkluderas med säkerhetskopiering av data. Applikationsinställningar och spelbiblioteksdata ingår som standard. Playnite startar automatiskt om för att starta säkerhetskopieringsprocessen. Automatisk säkerhetskopiering av data Automatisk säkerhetskopieringsfrekvens Säkerhetskopieringsmapp Roterande säkerhetskopior Inkludera ytterligare data: ================================================ FILE: source/Playnite/Localization/tr_TR.xaml ================================================  Türkçe Playnite dili Çıkış Filtre Etkin Filtre Devre Dışı Ek filtreler Filtreler Filtrele Geçersiz Veri Değişiklikleri Kaydet? Web Adresi: www.playnite.link GitHub: Kaynak Kodu Tanılama paketi oluştur Tanılama bilgisini gönder Playnite Hakkında Josef Němec tarafından yapılmıştır Kategori Ata Kategorileri Ayarla Kategori Ekle İşaretli - Kategoriye ata İşaretsiz - Kategoriyi kaldır Belirsiz - Değişiklik yok (birden fazla oyun düzenlenirken) Kategori Yok Platform Yok Amanın! Bir şeyler ters gitti… Kurtarılamaz bir hata oluştu. Bu sorunu çözmemize yardımcı olmak istiyorsanız, lütfen çökmeden önce gerçekleştirilen eylemleri kısaca açıklayın ve ardından bir tanı bilgisi gönderin. Çevrimiçiyseniz, paket, analiz için Playnite sunucusuna yüklenecektir. Alternatif olarak, GitHub'da yeni bir sorun oluşturmak ve çöküşü elle bildirmek için 'Çökme Bildir' düğmesini kullanabilirsiniz. Yardımınız için teşekkürler. "{0}" uzantısı, kurtarılamaz bir hataya neden oldu. Günlük dosyasını kaydetmenizi ve sorunu uzantının geliştiricisine bildirmenizi öneririz. Sorun tekrarlanmaya devam ederse, uzantıyı devre dışı bırakın. "{0}" uzantısı, kurtarılamaz bir hataya neden oldu. Sorunu uzantının geliştiricisine bildirmenizi öneririz. Sorun tekrarlanmaya devam ederse, uzantıyı devre dışı bırakın. Bilinmeyen uzantı veya tema, kurtarılamaz bir hataya neden oldu. 3. taraf eklentileri devre dışı bırakmanızı, sorunlu olanı ayırmanızı ve sorunu eklentinin geliştiricisine bildirmenizi öneririz. Kurtarılamaz hata oluştu.          Bu sorunu çözmemize yardımcı olmak için lütfen tanılama bilgilerini gönderin. Teşekkürler. Uzantıyı devre dışı bırak Günlük dosyasını kaydet Tanılama bilgilerini gönder Çökmeyi Raporla Playnite'ı Yeniden Başlat Güvenli Modda Yeniden Başlat Tüm üçüncü parti uzantılarını devre dışı bırak ve varsayılan temayı kullan. Playnite Çıkış Çökmeden önce yapılan eylemler (İngilizce olarak açıklayın): Kütüphane Yöneticisi Oyun(lar) Kaldırılsın mı? Kaldırılamıyor - Oyun veya kurulum çalışıyor. Kaldırılamıyor - Oyun çalışıyor. {0} isimli oyunu kaldırmak istediğinize emin misiniz? {0} oyunu kaldırmak istediğinize emin misiniz? {0} adlı oyunu listeden çıkarmak istiyor musunuz? "Hariç tutulanlar listesine ekle" seçeneği, bir sonraki kütüphane güncellemesinde oyunun tekrar içeri aktarılmasını engelleyecektir. {0} oyunu kaldırmak istediğinizden emin misiniz? "Hariç tutulanlar listesine ekle" seçeneği, bir sonraki kütüphane güncellemesinde oyunun tekrar içeri aktarılmasını engelleyecektir. Şu anda kullanımda olmayan {0} girdiyi kaldırmak istediğinizden emin misiniz? Kullanılmayan alan bulunamadı. Evet (hariç tutulanlar listesine ekle) Bu bölümde kaydedilmemiş değişiklikler var Oyun kütüphanesi biçimi güncelleniyor… Veritabanı güncellemesi başarısız. Oyun kütüphanesi güncellenemiyor. {0} MB boş alan gerekiyor. OyunHatası Oyun başlatılamıyor. '{0}' veritabanı üstünde bulunamadı. Oyun başlatılamıyor: {0} Eylem başlatılamıyor: {0} Oyun konumu açılamıyor: {0} {0} isimli oyunun kurulum boyutu bulunamadı. İndirme boyutu taraması başarısız İndirme boyutu taranırken {0} hata bulundu Kısayol oluşturulamadı: {0} El ile açılamadı: {0} Oyun kurulamıyor: {0} Oyun kaldırılamıyor: {0} Geçerli oyun başlatma eylemi bulunamadı. Emülatör eylemlerini kullanırken, platform tanımlarının oyun ve emülatör ayarları arasında uyuştuğuna emin olun. Kurulum uygulaması mevcut değil. Bu oyundan sorumlu kütüphane eklentisi devre dışı bırakılmış veya kurulmamış. Resmi üst veri indirmesi mevcut değil. Hiçbir oyun seçilmedi. Oyun betiği çalıştırılamadı. Uygulama betik yürütme başarısız. Genel betik eylemi çalıştırılırken hata. Emülatör betik yürütme başarısız. Oynatma betik eylemi çalıştırılırken hata. PowerShell 3.0 veya yeni sürüm kurulu değil. Oyunun nasıl başlatılacağı belirlenemedi. Etkin Devre dışı Kaldır Kullanılmayanı kaldır Adlandır Kopyala Ekle Varsayılan Simge Varsayılan Kapak Resmi Varsayılan Arka Plan Bitir İleri Geri TAMAMLANDI GERİ TEMİZLE Temizle Yoksay Tümünü Yoksay İçe aktar İsim Yazar Modül Seriler Sürüm Son Oynanma Çok Oynanan Oynanma Sayısı Kurulu Boyut Klasör Notlar Eklenme Eklenme Tarihi Değiştirilme Değiştirilme Tarihi Web Sayfası Yol TAMAM Kaydet Kapat İptal Onayla Sıfırla Evet Hayır Hoş geldiniz Yerel Kullanıcı Genel Medya Bağlantılar Kurulum Eylemler İndiriliyor… Medya indiriliyor… Yükleniyor… Tür Profil Profiller Kaldır İndir Ara Çözünürlük: Herhangi Yakınlaştır Liste Görünümü Kapaklar Izgara Görünümü Ayrıntılı Görünüm Özel URL Özel teşekkürler Lisans Katkıda Bulunanlar Playnite'den Çıkılıyor… Bugün Dün Pazartesi Salı Çarşamba Perşembe Cuma Cumartesi Pazar Geçen Hafta Geçen Ay Geçen Yıl Bir yıldan fazla 0 ila 100MB 100MB ila 1GB 1GB ila 5GB 5GB ila 10GB 10GB ila 20GB 20GB ila 40GB 40GB ila 100GB 100GB veya daha fazla Aktarma başarıyla tamamlandı. Tüm Oyunlar Oyun Kimliği Veritabanı Kimliği Ön Ayarlar Sütun Sütunlar Satır Satırlar Oyna işleminden simge alınamadı. Mevcut Dosya türü eylemi yok. Sadece eksik olan üst veriyi indir Bu seçeneğin etkinleştirilmesi, zaten bilgi içeren veri alanları için üst verileri indirmeyi atlar. Oyun seçimi Lütfen hangi oyunların yeni üst verilerle güncellenmesi gerektiğini seç: Veritabanı üstündeki tüm oyunlar Filtrelenmiş tüm oyunlar Sadece seçili oyunlar Hiçbir üst veri alanı seçilmedi İndirme için hiçbir üst veri alanı seçilmedi. Lütfen en az birini seçin ve bunun için en az bir üst veri sağlayıcısını etkinleştirin. Resmi Mağaza IGDB Lütfen Playnite tarafından hangi alanların otomatik olarak doldurulacağını ve verileri almak için hangi kaynakların kullanılması gerektiğini seçin. Lütfen yukarıdaki logoya tıklayıp Playnite'ın kullandığı verileri geliştirmek için igdb.com veritabanı güncellemelerine katkıda bulun. Üst veriler indiriliyor… Kurulu oyunlar içeri aktarılıyor… {0} oyunları içe aktarılıyor… {0} kaynağından emülatör oyunları içe aktarılıyor… Kütüphane güncellemeleri indiriliyor… Kütüphanedeki oyunların boyutu hesaplanıyor… İçe aktarılan oyunların boyutu taranıyor… Kütüphane güncellemesi bitti Kaynaklar serbest bırakılıyor… Yapılandırma Ayarlar… Platform ve Emülatörler Emülatörleri Yapılandır… Kütüphane Yöneticisi… Araçlar Üst Verileri İndir… Yazılım Araçları… Bütünleşmeleri Yapılandır… Üçüncü Parti İstemci Aç Üçüncü Parti İstemciler Oyun Kütüphanesini Güncelle Kütüphane Güncellemesini İptal Et Emülatör Klasörleri Güncelle Oyun Ekle El İle… Otomatik Tara… Emülatör Oyunu… Microsoft Mağaza Uygulaması… Playnite Hakkında Geri Bildirim Gönder Tam Ekran Kipine Geç Bağlantılar Yardım Patreon'da Destekle Ko-fi üzerinden destekle Kullanım kılavuzu SDK Belgelendirmesi Sistemi Yeniden Başlat Sistemi Kapat Sistemi Askıya Al Sistemi Hazırda Beklet Sistemi Kilitle Oturumu Kapat Rastgele Bir Oyun Seç Ayrıntılar panelinde görüntülenecek oyun alanları: Öğe aralığı Izgara öğesi arka planını çiz Izgara öğesi sınır genişliği Eksik oyun simgesi kaynağı Eksik oyun kapak kaynağı Eksik oyun arka plan kaynağı Oyun ayrıntılarına dikey boşluk Izgara görünümü ayrıntı konumu Ayrıntılı görünüm oyun listesi konumu Paneller arası ayraç koy Oyun kapak görseli yüksekliği Oyun listesi simge yüksekliği Uygulama yazı tipi Eş aralıklı yazı tipi Filtre paneli konumu Gezgin paneli konumu Kapak fotoğrafı işleme Hedef en boy oranı Aşağıdaki seçenekler tam ekran kipinde kare işlemeyi etkiler! Uzatma kipi DVD Kutusu Epic Games Mağazası GOG Galaxy 2.0 IGDB Kare Steam Afişi Steam dikey kapak Twitch * Uygulanması için yeniden başlatılmalı Ayarlar Genel Üst Panel Görünüm Oyun Ayrıntıları Düzen Gelişmiş Tam Ekran Girdi Performans Üst Veri Güncellemeler Ara Yedekle Kütüphane Verisini Yedekle Veri Yedeğini Geri Yükle Kütüphanedeki değişiklikleri otomatik içeri aktar Geçersiz veritabanı dosyası konumu, uygun dosya yolu ayarlanmalıdır. Hesap adı boş bırakılamaz. Oyunları içeri aktardıktan sonra üst verilerini indir Simge durumunda başlat Playnite bilgisayar açıldığında başlasın Sistem tepsisinde başlat Bilgisayar başladığında başlatmak için Playnite kaydı yapılamadı. Tam ekran kipinde başlat Görüntüleri arka planda yükle Daha yavaş görüntü yükleme süreleri karşılığında oyun listelerinin kaydırma akıcılığını artırır. Kapak resmi eksik olduğunda oyunun adını göster Izgara görünümünde oyun adlarını göster Kurulu olmayan oyunları koyulaştır Oyun simgelerini Liste görünümünde göster Grup açıklamalarında öğe sayısını göster Filtre ve gezgin panellerinde yalnızca atanmış alanları göster Donanım hızlandırmayı devre dışı bırak Takılma veya benzeri kullanıcı arayüzü sorunları yaşarken kullanın Hızlı başlatma listelerinde gizli oyunları göster Sağ tıklama menüsü listesini ve sistem tepsisi listesini etkiler. Hızlı başlatma öğelerinin sayısı Oyun arka planını pencere arka planı olarak kullan Arka planı bulanıklaştır Yüksek Kalite Arka planı karart Izgara görünümünde göster Tema Tema Profili Tam Ekran Tema Tam Ekran Tema Profili Veritabanı Konumu Oturum durumu: Playnite Ayarları Web önbelleğini temizle Hesapları bağlarken karşılaşılan sorunları çözebilir. Sistem tepsisi simgesini göster Sistem tepsisine küçült Uygulama penceresi kapatıldığında sistem tepsisine küçült Oyun başladığında: Oyun kapandıktan sonra: Oynama süresini ifade etmek için oynanan gün sayısını biçimlendir Tarih biçimleri: Bu sizi bağlı tüm servislerden çıkarır. Uygulamanın yeniden başlatılması gerekiyor, devam etmek istiyor musunuz? Önbelleği Temizle? Yeni temayı uygulamak için playnite yeniden başlatılmalıdır Daha fazla tema edin Yeni tema oluştur Daha fazla uzantı al Yeni uzantı oluştur Playnite'ı çevirmemize yardımcı olun Yeni ayarların uygulanması için Playnite'ın yeniden başlatılması gerekiyor. Şimdi yeniden başlatılsın mı? Yeniden başlatmak, halihazırda devam etmekte olan tüm etkin görevleri (indirmeleri) iptal edecektir. Playnite yeniden başlatılsın mı? Playnite, kütüphane dosyalarınızı otomatik olarak taşıyamaz. Konumu değiştirmeden önce dosyaları el ile taşımanız/kopyalamanız gerekir. Hedef konumda kütüphane yoksa, yeni bir tane oluşturulacaktır. Playnite yeniden başlatılıncaya kadar yeni veritabanı konumu kullanılmayacaktır. "Kapat" eylemi ayarlı ise, oynama zamanı kaydedilmeyecek. Satır sayısı Sütun sayısı Ayrıntı görünümü satır sayısı Arkaplan Resmini Ana Ekranda Göster Üst verileri yeniden indirmeden mevcut oyunlara geriye dönük olarak uygulanmaz. Kütüphanedeki oyunların oynanış sürelerini aktar: Playnite veritabanındaki oyunlar için kütüphane eklentileri tarafından raporlanan oynatma süresini Playnite tarafından ne zaman içe aktaracağını yapılandırır. Bu özelliğin kullanılabilmesi için oyunlardan sorumlu kütüphane eklentilerinin desteği gereklidir. Her Zaman: Playnite veritabanındaki yeni içe aktarılan ve mevcut oyunlar için oyun süresini içe aktarır. Yalnızca yeni içe aktarılan oyunlar için: Oynatma süresini yalnızca yeni içe aktarılan oyunlar için içe aktarır. Asla: Hiçbir koşulda oyun süresini içe aktarmaz. Her zaman Sadece yeni aktarılan oyunlar için Asla Masaüstü modunda oyun kolu desteğini etkinleştirin Rehber düğmesi Tam ekran kipi açar Yeni içe aktarılan oyunlar için otomatik üst veri indirme ayarları. Hedef ekran Her zaman birincil ekranı kullan Oyun Başlıklarını Göster Pil Durumunu Göster Pil Yüzdesini Göster Saati Göster İmleci gizle Hızlı Filtrelerde Sadece Kurulu Oyunlar Düğme İstemleri Düzen Yatay Kaydırma Alt bölümlerden birini seçin Kullanılabilir ayar yok Ayarlar yüklenemedi Bu betikler kütüphanedeki her oyun için çalıştırılır. Oyun ayrıntıları değiştirilerek, her oyuna ayrı bir betik ataması da yapılabilir. Arka plan resim geçişini canlandır Yazı tipi boyutları Otomatik Takma Adlı Gri tonlama ClearType İdeal Ekran Metin biçimlendirme kipi Yazı işleme kipi Metin oluşturma ve biçimlendirme yöntemleri şu anda oyun açıklamaları için kullanılmamaktadır. Arka plan resimlerini önceden yükle Etkinleştirilirse, Playnite üst verileri indirirken arka plan resmini indirir. Bu şekilde daha fazla disk alanı kullanır ancak resmi çevrimdışı kullanılabilir. Devre dışı bırakılırsa, arka plan resmi yalnızca ilk ihtiyaç duyulduğunda indirilir. Daha az alan kullanır, ancak resim gösterilmeden önce bir gecikmeye neden olabilir ve bazı resimler çevrimdışı olunduğunda kullanılamayabilir. Oyundan çıktıktan sonra üçüncü parti istemciyi otomatik kapat İstemci otomatik kapatma gecikmesi (saniye) Oyun oturumu bu süreden kısa ise kapatılmasın (saniye) Aşağıdaki istemcileri otomatik kapat: İstemcileri Otomatik Kapat Hariç Tutulanlar Listesi Çok büyük oyun ortamı atandığında uyarı göster Klasör açma komutu Tercih edilen yaş derecelendirme kuruluşu Kütüphane güncellenirken oyunların kurulum boyutunu güncelle Son taramadan bu yana değişen dosyalar tespit edildiğinde oyunların kurulum büyüklükleri tekrar hesaplanır Hiçbiri Doldur Üniforma Doldurmak için oranı koru Sol Sağ Üst Alt İçe Aktarım Hatası Doğrulama gerekli Kimlik doğrulama başarısız oldu Alternatif web görünümü oluşturma modu Web görünümleriyle ilgili sorunlarla karşılaştığınızda kullanın, örneğin entegrasyon kimlik doğrulama iletişim kutuları. Çok uzun oyun tanımlarını kısmen yükle Çok uzun açıklamalar, oyun seçimini değiştirirken fark edilir ölçüde gecikmelere yol açabilir. Etkinleştirildiğinde, ilk başta açıklama metninin bir kısmı yüklenecek, metnin geri kalanı istendiğinde ekrana getirilebilecektir. Üst Veriyi İçe Aktar Üst Veri İndir Gelecekteki üst veri indirme işlemi için kullanılacak seçili yapılandırmayı ayarlayın. Aynı zamanda uygulama ayarlarından da değiştirilebilir. Emülatör İçe Aktarma Sihirbazı Bu sihirbaz, hem konsol emülatörlerini indirme ve içe aktarma hem de emülatör oyunlarını içe aktarma süreçlerinde size rehberlik edecektir. Her zaman ek emülatör ve/veya oyunları ana menüden (Emülatör ayarları için "Kütüphane" menüsü altında ve emülatör oyunları için "Oyun Ekle" menüsü) ekleyebileceğinizi unutmayın. Playnite tarafından otomatik olarak tanınan ve yapılandırılabilen emülatörlerin bir listesi bulunmaktadır. Kurulumları web sitelerinden indirebilirsiniz. Emülatörler kurulduktan sonra (el ile), emülatör yapılandırma menüsünden bunları içe aktarabilirsiniz. 'Klasörden Otomatik Algıla…' düğmesini tıklayarak PC'nizde kurulu olan tüm emülatörleri içe aktarabilirsiniz. Playnite, seçilen klasörde bilinen emülatörler için arama yapacak ve bunları içe aktarma seçeneği sunacaktır. Emülatörleri farklı klasörlerden içe aktarmak için bu düğmeyi birden çok kez kullanabilirsiniz. Emülatörler mevcut listenin altına eklenecektir. 'Emülatörü Kullanarak Klasörü Tara' düğmesini tıklayarak oyunları içe aktarabilirsiniz. Uygun emülatörün seçilmesi, Playnite'a hangi dosya türlerinin taranması ve içe aktarılması gerektiğini söyleyecektir. Oyunları farklı klasörlerden içe aktarmak için bu düğmeyi birden çok kez kullanabilirsiniz. Oyunlar, mevcut listenin altına eklenecektir. İçe aktarma için seçilen emülatör yok. Emülatörü yapılandırmadan, oyunları otomatik olarak içe aktaramazsınız. İçe aktarma işlemini iptal ederek, devam etmek istediğinizden emin misiniz? Playnite'da yapılandırılmış emülatör yok. Emülatörü yapılandırmadan ve uygun dosya türlerini seçmeden önce oyunları içe aktaramazsınız. Şimdi emülatör eklemek ister misiniz? Emülatör kullanarak klasörü tara Dosyaları seç Klasörden Otomatik Algıla… Emülatörleri Yapılandır… Taranıyor… Taranıyor {0}… İlk Kez Yapılandırma Bu sihirbaz, harici oyun kütüphanelerinin otomatik olarak içe aktarılması ve yapılandırılması sürecinde size yol gösterecektir. Playnite, Steam veya GOG gibi birden çok oyun hizmetinden oyunları otomatik olarak içe aktarabilir ve ayrıca uygulama başlatıldığında otomatik olarak güncelleyerek kütüphanenizi güncel tutabilir. Daha sonra ana menüdeki 'Playnite' düğmesine tıklayarak herhangi bir platform için herhangi bir özel oyunu el ile ekleyebileceğinizi unutmayın. Kütüphane Entegrasyonu Aşağıda listelenen hizmetlerden oyunları otomatik olarak içe aktarın. Daha sonraki herhangi bir oyun değişikliği (Kurulum Durumu) Playnite başlangıcında veya manuel olarak tetiklendiğinde otomatik olarak güncellenecektir. Seçilen ayarlar ilk ve sonraki tüm içe aktarımları etkileyecektir. Yapılandırma Tamamlandı İlk kurulum tamamlandı. Tüm ayarları daha sonra 'Ayarlar' menüsünden değiştirebileceğinizi unutmayın. Playnite logo menüsünü tıklayarak daha sonra başka bir oyun da ekleyebilirsiniz. Bir veya daha fazla uzantı indirilemedi. İlk çalıştırma sihirbazı tamamlandıktan sonra eklentiler menüsünden uzantıları yeniden indirmeyi deneyebilirsiniz. {0} tümleştirmesi indiriliyor… Önerilen tümleştirmelerin listesi indiriliyor… Önerilen tümleştirmelerin listesi indirilemedi. Tümleştirmeleri daha sonra Eklentiler menüsü aracılığıyla deneyebilir ve yeniden indirebilirsiniz. Platformları ve Emülatörleri Yapılandır Emülatörleri yapılandır Platformlar Platform Emülatörler Emülatör Platform Ekle Simge Seç Kapak Seç Resim Seç Öğe Seç Arka Plan Seç Dosya Seç URL Seç Emülatör Ekle Desteklenen Platform(lar) Platform değişikliklerini kaydetmek ister misiniz? Emülatör değişikliklerini kaydetmek ister misiniz? Uygulama Değişkenler Çalışma Dizini Desteklenen Dosya Türleri Emülatörleri İçe Aktar … Emülatörleri İndir… Bilinen emülatör profilinden önceden belirlenmiş değişkenleri yükleyin {0} emülatörünü kaldırmak istediğinizden emin misiniz?? Şu anda {1} oyun tarafından kullanılıyor. {0} platformunu kaldırmak istediğinize emin misiniz? Şu anda {1} oyun ve {2} emülatör tarafından kullanılıyor. Ayarlar yardım Sırala Sıralama Yönü Gruplandır Artan Azalan Gruplama Kütüphaneye göre grupla Kategoriye göre grupla Platforma göre grupla Görünüm Türü Görünüm Keşif Paneli Filtre Paneli Simge Kütüphane Simgesi Kapak Resmi Arka Plan Resmi Sıralama İsmi Kütüphane Kılavuz İsim Kurulum Sürücüsü Hesap Adı Platform Kategori Tür Çıkış Tarihi Çıkış Yılı Geliştirici Etiket(ler) Yayımcı Kurulum Durumu Tüm filtreleri eşleştir Etkinleştirilirse, yalnızca tüm filtrelerdeki tüm öğeleri kullanan oyunlar görünüme dahil edilir. Devre dışı bırakılırsa, herhangi bir filtredeki herhangi bir öğeyi kullanan oyunlar görünüme dahil edilir. Kurulu Kurulu Kurulu değil Gizli Favori HDR Desteğini Aç Aktif olduğunda, bir oyun başlatmadan önce ana ekranda HDR etkinleştirilecektir. Ana ekranınızda HDR desteği mevcut değil. Son Oynanma Kategori Açıklama Kurulum Klasörü Kapak Resmi Bağlantılar Kalıp, ROM veya ISO Dizini Tür Türler Şirket Şirketler Geliştirici Geliştiriciler Yayımcı Yayımcılar Kategori Kategoriler Etiket Etiketler Özellik Özellikler Yaş Sınırı Yaş Sınırı Bölge Bölgeler Kaynak Kaynaklar Son Etkinlik Veritabanı Hatası Kütüphane veritabanı açılamadı. Veritabanı açılmadı. Kütüphane veritabanına erişilemiyor. "{0}" dosyası başka bir işlem tarafından kullanılıyor veya erişilemeyen bir konumda. Tanılama paketi oluşturulamadı. Tanılama paketi otomatik olarak yüklenemedi. Form bilgisi başarılı bir şekilde gönderildi. Tanılama paketi başarıyla oluşturuldu ve gönderildi. Lütfen sorun raporunuza aşağıdaki kimliği ekleyin: {0} konumundan oyun içe aktarılamadı. Emülatör oyunları {0}'den içe aktarılamadı. Seçilen emülatör profiline göre oyunlar aranamıyor. Profilde herhangi bir dosya uzantısı veya platform bulunmuyor. Playnite başlatılamadı. Lütfen diğer tüm örnekleri kapatın ve tekrar deneyin. "{0}" teması, renkli profil "{1}" uygulanamadı {2} Bağlantı açılamıyor, URL geçerli biçimde değil. Uygulama başlatılamadı. Web görüntüleyici bileşeni başlatılamadı. Playnite, başlangıç sürecini devam ettiremiyor. Bilgi için: https://playnite.link/cefstartup Eksik veya bozuk tanım dosyası nedeniyle emülatörler içe aktarılamıyor. Menü eylemi gerçekleştirilemedi. Oyun Ayrıntılarını Düzenle Resim URL’si Bağlantı Ekle ROM ekle Değişiklikleri Kaydet Alan değişikliklerini, düzenlenmekte olan oyun(lar)a uygulayın. Eylem Ekle Kaldır Oynatma Eylemini Kaldır Oyunları Ekle Klasörü Tara… Kurulu Olanları Algıla Gözat… Playnite Arayüzü Profil Ayarları Oyun adı boş bırakılamaz. Oyun eylem takip dizini boş olamaz. Üst verileri aramalarında oyun adı boş bırakılamaz. Geçersiz oyun verileri http:// veya https:// ile başlayan geçerli web URL'si girin URL Seç Üst veriler indirilemedi: {0} İndirme Hatası Filtreleri Temizle Özel Hesap Genel Hesap API Anahtarı Başlangıç Hatası Tema Hatası Tümünü Temizle Kuruluyor Kaldırılıyor Başlatılıyor Çalışıyor Geçersiz URL Hiçbir şey yapma Küçült Pencereyi geri yükle Pencereyi kullanıcı arayüzünden başlatıldığında geri yükle Kapat Değiştir Gelişmiş Asla Tamamlanma Durumu Tamamlanma Durumu Kullanıcı Puanı Eleştirmen Puanı Topluluk Puanı Oyun betikleri Uygulama betikleri Betikler Eklentiler Üst Veri Kaynakları Uzantılar Uzantı kimliği Betikleri Yeniden Yükle İnteraktif SDK PowerShell Tüm betikler başarıyla yeniden yüklendi. Seçili arama/filtre kriterinde oyun bulunamadı Öğe bulunamadı Masaüstü Moduna Geç Playnite'ı Kapat Kütüphaneler Tümünü Güncelle Oluşturan: Sürüm: Güncellenme: Modül: Kütüphane İstatistikler Tümü Hiçbiri Bildirimler Genişlik Yükseklik Boyut Küçük Normal Büyük Daha büyük En büyük Varsayılan Seç Tümünü Seç Tüm Seçimleri Kaldır İlk Rastgele Kullanıcı seçimi Daha fazla Şeffaf Daralt Genişlet Tümünü Daralt Tümünü Genişlet Diğer Temalar Emülatör Değişkenleri Dahili Bağımsız Değişkenler Özel Bağımsız Değişkenler Ek Emülatör Değişkenleri Emülatör Değişkenlerini Geçersiz Kıl Oyna Eylemi İçe aktarılacak üst verileri seçin İçe Aktarılacak Oyunları Seçin Üst veri ara Güncelleme Mevcut Son güncellemeden bu yana yapılan değişiklikler Güncellemeyi İndir ve Kur Güncelleştirmeleri Denetle Güncelleme Hatası Yeni sürüm kontrol edilemedi. Şu anda son sürümü kullanıyorsunuz. Güncelleme indirilemediği için kurulamadı. Birkaç arka plan görevi şu anda devam ediyor. İptal edip güncellemeye devam etmek istiyor musunuz? Birkaç arka plan görevi şu anda devam ediyor. İptal edip Playnite'tan çıkmak istiyor musunuz? Birkaç arka plan görevi şu anda devam ediyor. Modlar arasında geçiş yapmak görevi iptal edecek. Yine de geçiş yapmak istiyor musunuz? Playnite için bir güncelleme mevcut Tema listesini yeniden yükle Seçilen temayı uygula Dosya değişikliklerini izle Kaynak dosya değiştiğinde temayı otomatik olarak uygula Betik çalışma zamanı Oyun başlatılmadan önce çalıştırılacak betik Oyundan çıkış yapıldıktan sonra çalıştırılacak betik Oyun başlatıldıktan sonra çalıştırılacak betik Uygulama başlangıcında çalıştır Uygulama kapanışında çalıştır Oyun betiği başlatıyor Oyun betiği başlatıldı Oyun betiği durduruldu Genel betiği çalıştır Genel Filtrelenmiş Mevcut Yeni Betiği test et Yalnızca seçili öğeleri göster. Varsayılan olarak kaydet Favorilere ekle Favorilerden kaldır Bu oyunu gizle Gizlenenlerden Kaldır HDR Desteğini Aç HDR Desteğini Kapat Düzenle… Kurulum boyutunu hesapla Kurulum boyutunu hesapla (Tüm oyunlar) Kurulum boyutunu hesapla (Sadece eksik veriler) Kurulum boyutu Kategori Ayarla… Tamamlanma Durumu Ayarla Listeden Kaldır Oyna Kur Oyun Seçenekleri Ayrıntılar Kurulumu Kaldır Kurulum Konumunu Aç Masaüstü Kısayolu Oluştur Kılavuzu Aç Diğer Kütüphane eklentisi tarafından yönetiliyor Oyunun başlama süreci, bu oyundan sorumlu kütüphane eklentisi tarafından yönetilecektir. Belirtilen sayfada '{0}' oyunuyla ilgili hiçbir bilgi bulunamadı. İpucu: "Düzenle" menü seçeneğiyle tekli oyun düzenlerken daha gelişmiş üst veri indirme işlemini kullanabilirsiniz. Bazı işlem devam ederken kullanılamaz. Açıklama metni HTML sözdizimine duyarlıdır Oyun süresi saniye cinsinden kaydedilir. İndirme boyutu byte olarak gösterilir. Çıkış tarihi 'yıl-ay-gün' formatında ayarlanmalıdır. Ay ve Gün değerleri atlanabilir. 0 ile 100 arası bir değer girin ya da boş bırakın. Playnite gelişimi bu Patron'lar ve Ko-fi üyeleri tarafından desteklenmektedir: Kod, yerelleştirme ve diğer katkıda bulunanlar özel bir düzende değildir: Oyun izleme iptal edilsin mi? Kurulum izleme şu anda çalışıyor. İşlemi iptal edip oyunu önceki durumuna döndürmek istiyor musunuz? Oyun yürütme izleme şu anda çalışıyor. İşlemi iptal edip oyunu önceki durumuna döndürmek istiyor musunuz? Oynama Süresi Son Oynanma {0}g {1}sa {2}dk {0}s {1}dk {0} dakika {0} saniye Oynanmadı Masaüstü modunu açıyor… Tam ekran kipi açılıyor… Oyun kütüphanesi yükleniyor… İndirme boyutu ölçülüyor… {0} isimli oyunun indirme boyutu ölçülüyor… Betik dosyası kurulamadı. Betik dosyası başarıyla kuruldu. Betik Dosyası Kur Betik hatası Uzantı işlevi yürütülemedi. Üst veri klasörünü aç Hesapla Eğer oyuna ait bir ROM mevcutsa ya da kurulum dizini belirlenmişse, kurulum büyüklüğü otomatik hesaplanır {0} istemcisi kurulmamış. {0} müşteri şimdi açılacak. Lütfen oturum açın ve ardından bu mesajı kapatın. Kullanıcının oturum açmasını bekleyin, tamamladığınızda lütfen bunu kapatın… Oyunun kurulum klasörü bulunamadı. Geçersiz oyun eylemi yapılandırması. Hesap eşitleme sorunlarını gider Sorun gider Öğeyi yeniden adlandır Yeni öğe ekle İsim gir Yeni isim gir Bir saatten az 1 ila 10 saat 10 ila 100 saat 100-500 saat 500-1000 saat 1000 saatten fazla Kurulumu tamamlamak için Playnite yeniden başlatılmalıdır. Şimdi yeniden başlatmak istiyor musunuz? Uzantı düzgün paketlenmemiş. Tema düzgün paketlenmemiş. "{0}" uzantısı düzgün yüklenemedi. "{0}" uzantısı yüklenemiyor, mevcut Playnite sürümü desteklenmiyor. "{0}" teması düzgün yüklenemedi. "{0}" teması yüklenemiyor, mevcut Playnite sürümü desteklenmiyor. Uzantı düzgün yüklenemedi. Tema düzgün yüklenemedi. Tema/Uzantı, desteklenmeyen API sürümü kullanıyor. Kurulum başarılı oldu. Eklenti kur? Genel "{0}" eklentisi kurulamadı. Uzantı kurulumu başarısız. {0} Yeni bir uzantı yüklemek istiyor musunuz? {0} Geliştirici: {1} Sürüm: {2} "{0}" uzantısını güncellemek istiyor musunuz? Geçerli sürüm: {1} Yeni sürüm: {2} Tema yüklenemedi. {0} Yeni bir tema yüklemek istiyor musunuz? {0} Geliştirici: {1} Sürüm: {2} "{0}" temasını güncellemek istiyor musunuz? Geçerli sürüm: {1} Yeni sürüm: {2} Playnite'ten ayrılmak ve varsayılan web tarayıcınızı kullanarak aşağıdaki web sayfasına gitmek üzeresiniz. Devam etmek istiyor musun? {0} Seçili görüntüler, orantılı performans için çok büyük olabilir. Çok büyük görüntülerin kullanılması, daha kötü UI (Kullanıcı Arayüzü) yanıt süresine ve artan bellek kullanımına neden olabilir. Önerilen en fazla çözünürlükler: Simgeler: {0} mega piksel Kapaklar: {1} mega piksel Arka planlar: {2} mega piksel Performans Uyarısı Bir Daha Gösterme Dosya, {0} uzantısı ile uyumlu değil. Uyumsuz dosya uzantısı Seçilen resim dosyası en iyi performans için çok büyük olabilir. Seçili temayı kaldırmak istediğinizden emin misiniz? Kaldırma işlemi, bir sonraki uygulama başlangıcına kadar sıraya alınacaktır. Yerleşik temalar kaldırılamaz. Bu tema Playnite'ın bu sürümünü desteklemiyor. Seçili uzantıyı kaldırmak istediğinizden emin misiniz? Kaldırma işlemi, bir sonraki uygulama başlangıcına kadar sıraya alınacaktır. Yerleşik uzantılar kaldırılamaz. Bu uzantı, Playnite'ın bu sürümünü desteklemiyor. Kurulum klasörü Veri klasörü Tanılama paketi oluşturuluyor… Tanılama paketi yükleniyor… Dosyayı içe aktar… Bu nedir? Bunu yapmak istediğinize emin misiniz? Toplam oynama süresi Ortalama oynama süresi En uzun oynama süresi Toplam indirme boyutu Genel bakış Kenar Çubuğu Кenar Çubuğunda Göster Ayarları sıfırla Aşağıdakiler hariç tüm uygulama ayarları varsayılan değerlere sıfırlanacaktır: - Veritabanı konumu - Hariç tutulan içe aktarma listesi - Kütüphane entegrasyonları dahil olmak üzere uzantı ayarları İşlemi bitirmek için uygulamanın yeniden başlatılması gerekir. Ayarları sıfırlamak istiyor musunuz? Geliştiriciler Harici uzantılar Tam klasör yolunu girin. Başarımlar Forum Haberler Mağaza Sayfası İlk kurulum tamamlanmadı. Playnite, prosedürü bitirmek için şimdi Masaüstü Moduna yeniden başlayacaktır. En Son Oynananlar Favoriler Çok Oynanan Tümü Uygulanan filtreler var. Uygulanan ek filtreler var. Arama Sonuçları: Aynı ada sahip bir öğe zaten var. Seçimi geçerli filtreyle kısıtla Başka seç Eklentiler… Kurulu Uzantı Ayarları Gözat Güncellemeler Güncelleme ({0}) Kurulu uzantıların ve temaların yönetimi, ayarları dahil, yeni olan "Eklentiler" menüsüne taşındı. Şu anda kurulu olan tüm kütüphane bütünleşmesi uzantıları buradan yapılandırılabilir. Ek bütünleştirmeler kurmak veya kaldırmak istiyorsanız ana menüden "Eklentiler" seçeneğini kullanın. Masaüstü Temaları Tam Ekran Temaları Aranıyor… Eklenti, Playnite'ın bu sürümüyle uyumlu değil. Eklenti yükleme paketi indirilemedi. Eklenti yükleme paketi indirilemedi. Yeni ayarları uygulamak için yeniden başlatma gerekiyor. Bu eklenti kurulum için planlandı. Kur Təzədən Quraşdırmaq Kaldır Zaten kurulmuş Yeni eklenti güncellemesi bulunamadı. Eklentileri güncelle Değişiklikler mevcut değil Kurulum için planlandı İndirme başarısız Lisans reddedildi {0} indiriliyor… Eklenti güncellemeleri kontrol ediliyor… Program güncellemeleri kontrol ediliyor… Bir veya daha fazla eklenti güncellemesi mevcut. Güncellenecek öğeleri seçin Uzantı geliştirme örneği {0} lisans sözleşmesi Onayla Reddet Kütüphane bütünleşmesine oynatma eylemlerini dahil et Eylem seç Takip modu Takip Dizini İlk izleme gecikmesi İzleme sıklığı Bağlantı Dosya Emülatör Betik Varsayılan İşlem Klasör Özgün işlem İşlem adı İzleme mesajlarını günlüğe kaydet Yapılacak olan değişiklikler seçili olan tüm oyunların verilerinin üzerine yazılacaktır! Hiçbiri Tekdüze Sadece Öğeler Yalnızca başlangıç ve bitiş Kaydırma hassasiyeti Akıcı kaydırma Animasyon hızı Öğeyi kaldır? Bu ögeyi kaldırmak istediğinize emin misiniz? Düğmeleri üst panelde göster: Genel görünüm ayarları Gruplandırma ayarları Sıralama ayarları Filtre ön ayarları Eklenti öğeleri konumu Bölüm ayırıcı genişliği Ana menü düğmesini kenar çubuğuna taşı Keşif Paneli Rastgele oyun seçici Rastgele oyun seçiciyi görüntüler Görünümden rastgele oyun seç Gruplandırma ve sıralama ayarlarını kaydet Tam ekran kipinde hızlı filtre olarak göster Son 7 gün içinde Son31 gün içinde Son 365 gün içinde 365 günden daha fazla Yapılandır Önayarı kaydet Oyuna başladıktan sonra simge durumuna küçült Oyun başladıktan sonra Playnite'ı simge durumuna küçültün. Bunu devre dışı bırakmak, oyunların başlangıçta girdiye odaklanmamasıyla ilgili sorunlara yol açabilir! Yazı Tipi Boyutu Yazı Tipi Boyutu Küçük Oyun kontrolcüsü API desteğini etkinleştir Oyun kolu desteği Devre dışı bırakılırsa Playnite hiçbir oyun kolu girişini kabul etmez. Oyun kolu girişlerini fare/klavye girişlerine çeviren araçlar kullanıyorsanız ve Playnite ile çift giriş alıyorsanız devre dışı bırakın. Öğeleri ana menüde göster: Ters X/A ana görünüm düğmesi bağlama Bir oyunu başlatmak ve ana görünüm sayfasında oyun ayrıntılarını göstermek için düğme bağlantılarını değiştirir. Onay/iptal düğme eşlemelerini değiştir Onay ve iptal için A/B düğme eşlemelerini tersine çevirir. Sadece ana oyun kolu Aktif olduğunda, sadece ana oyun kolundan gelen girdiler dikkate alınır. Rehber düğmesi Playnite'ı öne getirir Arayüz ses seviyesi Arka plan ses seviyesi Arka plandayken sessize al Ses arabirimi başlatılamadı. Çıkış API'sı Ses çıkışı için kullanılan API. Sesle ilgili sorunlar yaşıyorsanız değiştirin. Genel Görseller Ses Düzen Menüler Girdi {0} başlıyor… {0} çalışıyor… Büyük Harfler Boşluk Görüntü işleme ölçekleyicisi Alternatif Dengeli Kalite Kalite: En iyi görüntü kalitesi, yavaş, yüksek bellek kullanımı. Dengeli: Kaliteli, hızlı, düşük bellek kullanımı. Alternatif: Daha iyi kalite, orta hız, düşük bellek kullanımı. Dosya seç… Klasör seç… Başlangıç ​​betiği Lütfen hem uzantıların hem de temaların Playnite'ın performansını, kararlılığını ve güvenliğini büyük ölçüde etkileyebileceğini unutmayın. Bir tema veya uzantı yükledikten sonra bazı sorunlar yaşamaya başlarsanız, sorunun kaynağı olup olmadığını görmek için önce bunları devre dışı bırakmayı/kaldırmayı deneyin. Başlangıçta seç Başlangıçta seç Gömülü modüller Gömülü profiller Özel profiller Özel profil Dahili betik tarafından işleniyor Emülatör özellikleri Platform özellikleri Bölge özellikleri Emülatörü başlatmadan önce çalıştır Emülatör başlatıldıktan sonra yürüt Emülatörden çıktıktan sonra yürüt Emülatör yürütülebilir dosyası bulunamadı. Emülatör özellikleri bulunamadı. Emülatör başlangıç betiği bulunamadı. Ayrı oyunlar olarak böl Tek bir oyunda birleştir Platformu ayarla Bölge ayarla Klasörleri tara Yapılandırmaları tara Kalıpları sağlama toplamı taramasından hariç tut Belirtilen desen(ler)le eşleşen dosyalar sağlama toplamı için taranmayacak ve dosya adıyla eşleştirilecektir. Daha fazla bilgi için öykünücü yardım sayfasına bakın. Emülatörle tara Yeni yapılandırma kaydedilirken ad belirtilmeli. Öykünücü veya öykünücü profili ayarlanmadı. Taranacak dizin belirtilmemiş veya mevcut değil. Tarama yapılandırması düzgün ayarlanmamış. Toplu tarama içinde otomatik-tarama içerir Emülatörler için klasör taranamadı. Emülatör oyunları için klasör taranamadı. İçe aktarılanları gizle İçe aktarılacak profiller: Otomatik tarama ayarları Otomatik tarama ayarları olarak kaydet Daha sonra kütüphane güncellemesi sırasında kullanılmak üzere yapılandırmayı kaydeder. Kaydedilen yapılandırmalar "Emülatörleri Yapılandır" menüsünden yönetilebilir. Göreceli dizinler kullanarak aktar Mümkün olduğunda oyun dosyaları, Playnite ya da emülatör kurulum klasörlerine bağıntılı dizinler altından içe aktarılır. Alt klasörleri tara Arşivlerin içini tara İlişkili dosyaları birleştir Tekil oyun diskleri gibi birbiriyle ilişkili dosyaları tek bir oyun kaydı altında birleştir. Tarayıcı ekle Kaydedilmiş tarayıcı ekle Taramayı başlat Belirli klasörleri taramak için emülatörlerle tarama yapılandırmaları ekleyin. Oyunları içe aktarmadan önce emülatörlerin doğru şekilde yapılandırıldığından emin olun (Kütüphane -> Emülatör Yapılandır menüsü aracılığıyla). Yeni eklenen oyunlara atanan varsayılan durum İlk kez oynanan oyunlara atanan statü PowerShell betiği çalışma zamanı başlatılamadı. Windows 7 kullanıcısıysanız, çözüm için PowerShell 5.1 (yeniden) kurmayı deneyin. Belirtilen ada sahip filtre ön ayarı zaten var. Ön ayar yeni ayarlarla güncellensin mi? Toplu eklenen veya düzenlenen oyunlar için eksik sıralama adlarını otomatik olarak doldur Bir oyunu düzenlediğinizde, kütüphane güncellemesi, emülatör klasörü taraması veya normal klasör taraması yoluyla oyun eklediğinizde, "Sıralama Adı" alanını oyunun adının daha iyi sıralanabilir bir temsilini otomatik olarak doldurun. Örneğin, "The Witcher 3" için Sıralama Adı "Witcher 03" olacaktır. Bu, oyun adından farklı olmayan bir sıralama adı belirlemez ve yalnızca boş olan sıralama adlarını otomatik olarak günceller. Şu kelimeler, otomatik doldurulan Sıralama İsmi bilgisinin başlangıcından silinecektir: Sıralama esnasında terimlerin başındaki kelimeleri göz ardı etmek için kullanın. Varsayılanlar, "The", "An", ve "A" kelimeleridir. Sıralama İsmi boş olduğunda doldur Sıralama İsim Sıralama Değerini Doldur… Nahimic hizmetinin sisteminizde çalıştığı tespit edildi. Bu hizmetin Playnite'ta (ve diğer uygulamalarda) ciddi işleme sorunlarına neden olduğu bilinmektedir. Playnite'ta grafiksel bozulmalar veya başka işleme sorunları ile karşılaşırsanız, Nahimic servisini devre dışı bırakmanızı veya tamamen kaldırmanızı öneririz. https://playnite.link/nahimicsucks adresinden daha fazla bilgi edinin. Playnite, yükseltilmiş ayrıcalıklarla (yönetici olarak) çalışıyor. Bu, Playnite'tan başlatılan tüm kurulu uzantılara ve tüm oyunlara/uygulamalara yükseltilmiş ayrıcalıklar sağladığı için önerilmez! https://playnite.link/adminfaq adresinde daha fazla bilgi alabilirsiniz. Playnite yükseltilmiş ayrıcalıklarla çalışıyorsa uyarı göster Oyunların boyutunu belirlerken disk üzerindeki gerçek boyutu hesapla Aktif olduğunda, tarama daha uzun sürer ve dosyaların disk üzerinde kapladığı gerçek kullanım alanı hesaplanır. Pasif olduğunda, tarama daha hızlı tamamlanır ve dosyaların kendi boyutları hesaba dahil edilir. Aşağıdaki eklentinin/eklentilerin, yüksek kararlılık/performans etkisi veya güvenlik sorunları nedeniyle potansiyel olarak sorunlu olduğu bildirildi. Bunları kaldırmanızı şiddetle öneririz: {0} Çevrimiçi dosyaları taramadan hariç tut Bulut depolamada depolanan dosyalar yerel olarak mevcut değilse taranmaz ve içe aktarılmaz. Yalnızca şunlar için desteklenir: Google Drive, DropBox, OneDrive Dosya içeriği olmadan basitleştirilmiş yöntemi kullanarak tara Dosyalar içe aktarılacak, ancak dosya içeriğinin indirilmesini ve yerel olarak sunulmasını gerektirmeyen daha az doğru bir yöntem kullanılacaktır. Tümüne uygula Esas alınan kurulum durumu Tanımlı olduğunda Playnite, entegrasyon eklentisi tarafından belirlenen kurulum durumunu (kurulum dizini de dahil olmak üzere) göz ardı edecektir. Bu seçenek, belirli oyun aktarım metodunu kullanan ve bu ayarı dikkate almayan eklentiler için tam çalışmayabilir. Sadece el ile Günde bir Haftada bir Her açılışta Program güncellemelerini kontrol et Eklenti güncellemelerini kontrol et Kütüphaneleri güncelle Emülatör klasörlerini tara Gizli oyunları dahil et Alanları düzenle Tümünü seç / Seçimi kaldır Etkinleştir Ata Oyun aramak için yazın… Yardım için [F1] # karakteriyle başlayarak mevcut komutlar listelenir. / karakteriyle başlayarak mevcut arama sağlayıcıları/eklentiler listelenir. Anahtar kelime yazdıktan sonra BOŞLUK karakteriyle bitirerek derhal ilgili aramaya geçilir. TAB: eylem değiştir ENTER: seçili eylemi yürüt SHIFT-ENTER: işlem menüsünü görüntüle Kurulmamış oyunları dahil et Gizli oyunları dahil et Kurulmamış oyunları dahil et Kurulmamış oyunları hariç tut Gizli oyunlar dahil Gizli oyunlar hariç Oyna veya Kur Ayrıntıları görüntüle Oyun menüsü Oyunu düzenle Aramayı Aç Arama kutusu Arama düğmesi Birincil oyun eylemi İkincil oyun eylemi CTRL-F, arama kutucuğuna odaklanmak yerine genel arama ekranını açar Oyun filtreleme seçeneklerini her arama için hatırla Arama sağlayıcıları Varsayılan Özel anahtar kelime Sistem kısayolu Playnite arama Uzantı Ayarları İstisnalar Taranacak klasöre göre hariç tutulacak dosyalar Taranacak klasöre göre hariç tutulacak klasörler Dosyayı istisna listesine ekle Klasörü hariç tut İstisnalar sadece kayıtlı tarayıcı yapılandırmalarına eklenebilir. İstisnalar "{0}" adlı tarayıcıya eklendi. Esas alınan platform Tarayıcı, tüm yeni oyunlar için, otomatik tespit edilen platform bilgisi yerine bu platform bilgisini kullanacaktır. Varsayılan arama sonuçlarında komutları da dahil et Devre dışı olduğunda, # öneki kullanılmadığı sürece komutlar, varsayılan arama sonuçlarına dahil edilmeyecektir. İsim filtresi için esnek eşleştirme kullan Etkin olduğunda, isim filtesi, oyun isimleriyle genel arama ile aynı biçimde karşılaştırılacaktır. Tekil aramalarda katı karşılaştırma yapmak için arama terimlerinin başına ! karakterini ekleyebilirsiniz. Oyun sonuçları için görüntülenecek alanlar: Gizlilik Durumu Veri yedekleme iptal edildi. Veri yedekleme başarısız oldu. Veri yedekleme hatası Veri yedekleme devam ediyor… Veriler yedekten geri yükleniyor… Yedekten geri yükleme başarısız oldu. Ayarlar Oyun kütüphanesi Oyun kütüphane medyası Kurulu uzantılar Uzantı verileri Kurulu temalar Seçili yedek dosyasından geri yüklenecek verileri seçin. Playnite, otomatik olarak geri yükleme sürecini tekrar başlatacaktır. Veri yedeğine dahil edilecek kalemleri seçin. Uygulama ayarları ve oyun kütüphane verilerinin dahil edileceği varsayılır. Playnite, otomatik olarak yedekleme sürecini tekrar başlatacaktır. Otomatik veri yedekleme Otomatik yedekleme sıklığı Yedekleme klasörü Dönüşümlü yedekler İlave verileri dahil et: Otomatik yedekleme için yedek klasörü tanımlanmış olmalıdır. Sadece yama sürümleri için bildirim görüntüle Aktif olduğunda, sadece kurulu olan ana sürüm için yayınlanan güncellemelere dair bildirim görüntülenecektir. Yeni ana sürümler için bildirim görüntülenmeyecektir. Geçen hafta için göreceli tarih kullan Tarih bir haftadan daha eskiyse göreceli tarihleri ​​"Bugün", "Dün" vb. biçiminde kullanın. Diğer tüm tarihler için belirtilen tarih biçimi kullanılacaktır. Web resim araması Simge arama terimleri Kapak resmi arama terimleri Arka plan resmi arama terimleri Eklenti bilgisi alınıyor… Üstveri kaynağı yok Oyna eylemi ayarları Tarayıcı ayarlarını kullan Başlangıçta profili seç Başlangıçta emülatör seç Otomatik Her zaman açık Her zaman kapalı Erişilebilirlik (ekran okuyucu) desteği Uygulama menüsü Oyun menüsü Program klasörü Kullanıcı veri dizini Kütüphane dosyasında arıza tespit edildi, Playnite şimdi sonlandırılacak. Playnite GitHub sayfasında yeni bir hata kaydı oluşturarak arızalı dosyalarınızın tamir edilmesini talep edin. Yaptığınız değişiklikleri kaydetmek istiyor musunuz? Taşınabilir kurulum Hiçbir oyun kolu algılanmadı ================================================ FILE: source/Playnite/Localization/uk_UA.xaml ================================================  Українська Мова Playnite Вихід Фільтр Активний Фільтр Вимкнений Додаткові фільтри Фільтри Фільтр Неправильні дані Зберегти зміни? Домашня сторінка: www.playnite.link Вихідний код на GitHub Створити пакунок для діагностики Надіслати інформацію для діагностики Про Playnite Розробник: Josef Němec Призначити категорію Обрати категорію Додати категорію Вибрано - Категорію призначено Не вибрано - Прибрати категорію Невизначено - Без змін (коли редагується кілька ігор) Немає категорії Немає платформи Дідько! Щось пішло не так... Сталася невиправна помилка. Якщо ви хочете допомогти нам виправити цю проблему, будь ласка коротко опишіть ваші дії до збою, після чого вишліть інформацію для діагностики. Якщо ви в мережі, пакунок буде надіслано на сервер Playnite для аналізу. Як альтернатива, ви можете натиснути на кнопку "Повідомити про Збій" щоб створити нову проблему на GitHub і повідомити про збій вручну. Дякуємо за вашу допомогу. Розширення "{0}" призвело до невиправної помилки. Ми рекомендуємо зберегти лог-файл і повідомити про проблему розробнику розширення. Якщо проблема повторюється знову і знову, вимкніть розширення. Розширення "{0}" призвело до невиправної помилки. Ми рекомендуємо повідомити про проблему розробнику розширення. Якщо проблема повторюється знову і знову, вимкніть розширення. Невідоме розширення або тема спричинили непоправну помилку. Ми рекомендуємо відключити доповнення сторонніх розробників, виділити проблемне та повідомити про проблему розробнику доповнення. Сталася невиправна помилка. Якщо ви хочете допомогти нам виправити цю проблему, будь ласка відправте інформацію для діагностики. Дякуємо. Вимкнути розширення Зберегти лог-файл Вислати діаг. інформацію Повідомити про збій Перезапустити Playnite Перезапустити в безпечному режимі Вимкнення всіх сторонніх розширень та використання теми за замовчуванням. Вийти з Playnite Дії вчинені перед збоєм (англійською мовою): Менеджер Бібліотеки Прибрати ігри з Playnite? Неможливо прибрати - гра запущена або встановлюється. Неможливо видалити - гра запущена. Ви дійсно бажаєте прибрати {0}? Ви впевнені, що хочете прибрати {0} ігор? Ви дійсно бажаєте прибрати {0} з Playnite? Якщо вибрати "додати до списку виключень", тоді гру не буде додано знову наступного разу з оновленням бібліотеки. Ви впевнені, що хочете прибрати {0} ігор? Якщо вибрати "додати до списку виключень", ці ігри не буде імпортовано знову з наступним оновленням бібліотеки. Ви впевнені, що хочете прибрати {0} записів, що наразі не використовуються? Не знайдено невикористаних полів. Так (додати у список виключень) У цьому розділі є незбережені зміни Оновлення формату ігрової бібліотеки... Не вдалося оновити базу даних. Неможливо оновити ігрову бібліотеку. Потрібно {0} МБ вільного простору. Помилка гри Неможливо запустити гру. '{0}' не знайдено в базі даних. Неможливо запустити гру: {0} Не вдалося розпочати дію: {0} Не вдалося відкрити розташування гри: {0} Не вдалося виявити розмір встановлення гри: {0} Помилка сканування розміру встановлення Під час сканування розміру встановлення сталося {0} помилок Не вдалося створити ярлик: {0} Не вдалося відкрити посібник: {0} Не вдається встановити гру: {0} Не вдається видалити гру: {0} Не знайдено дійсних дій при запуску гри. Використовуючи дії емулятора, переконайтеся, що визначення платформи збігаються між грою та конфігурацією емулятора. Реалізація установки недоступна. Плагін для бібліотеки відповідальний за запуск гри вимкнено або не встановлено. Офіційне завантаження метаданих недоступне. Не вибрано жодної гри. Виконання призначеного грі сценарію не вдалося. Не вдалося виконати сценарій застосунку. Виконання призначеного глобально сценарію не вдалося. Не вдалося виконати сценарій емулятора. Виконання сценарію дії "Грати" не вдалося. PowerShell 3.0 або новіше не встановлено. Не вдалося визначити, як розпочати гру. Увімкнено Вимкнено Прибрати Прибрати невикористані Перейменувати Копіювати Додати Значок за замовчуванням Типова обкладинка Фонове зображення за замовчуванням Завершити Далі Назад ГОТОВО НАЗАД ОЧИСТИТИ Очистити Відхилити Відхилити Все Імпортувати Назва Автор Модуль Серія Версія Востаннє зіграно Найбільше зіграно Разів зіграно Розмір встановлення Тека Примітки Додано Додано Змінено Дата зміни Веб-сайт Шлях ОК Зберегти Закрити Скасувати Підтвердити Скинути Так Ні Ласкаво просимо Локальний користувач Загальні Медіа Посилання Встановлення Дії Завантаження... Завантаження медіа... Завантаження... Тип Профіль Профілі Прибрати Завантажити Пошук Роздільна здатність: Будь-яка Масштабування Список Обкладинка Сітка Деталі Власні URL-адреса Окремі подяки Ліцензія Автори Вихід з Playnite... Сьогодні Вчора Понеділок Вівторок Середа Четвер П'ятниця Субота Неділя Минулий тиждень Минулий місяць Минулий рік Більше року тому 0 до 100 МБ 100МБ до 1 ГБ 1 ГБ до 5 ГБ 5 ГБ до 10 ГБ 10 ГБ до 20 ГБ 20 ГБ до 40 ГБ 40 ГБ до 100 ГБ 100 ГБ або більше Імпортування успішно завершено. Всі ігри Ігровий Id Id бази даних Шаблони Колонка Колонки Рядок Рядки Не вдалося отримати значок із дії "Грати". До дії не прив'язано жодного типу файлу. Завантажити лише відсутні метадані Включення цієї опції пропустить завантажування метаданих для вже заповнених полів. Вибір ігор Будь ласка, оберіть які ігри потребують оновлення метаданих: Всі ігри з бази даних Всі відфільтровані ігри Лише обрані ігри Не вибрано жодного поля метаданих Не обрано жодного поля метаданих для завантаження. Будь ласка, оберіть хоча б одне, та виберіть принаймні одне джерело метаданих. Офіційний магазин IGDB Виберіть, які поля повинні бути автоматично заповнені Playnite і які джерела повинні використовуватися для отримання даних. Будь ласка розгляньте можливість тощо, щоб клацнути на логотип вище і внести оновлення в базу igdb.com, дані з якої використовує Playnite. Завантаження метаданих... Імпортування встановлених ігор... Імпортування ігор {0}... Імпортування емульованих ігор з {0}… Завантаження оновлень бібліотеки... Сканування розміру ігор у бібліотеці… Сканування розміру імпортованих ігор… Оновлення бібліотеки завершено Звільнення ресурсів... Конфігурація Налаштування... Платформи та емулятори Налаштування емуляторів... Менеджер Бібліотеки... Інструменти Завантажити метадані... Програмні інструменти... Налаштування інтеграцій... Відкрити сторонній клієнт Сторонні клієнти Оновити ігрову бібліотеку Скасувати оновлення бібліотеки Оновити емульовані теки Додати гру Вручну... Сканувати автоматично... Емульована Гра... Застосунок Microsoft Store... Про Playnite Надіслати відгук Запустити повноекранний режим Посилання Допомога Підтримати на Patreon Підтримка в Ko-fi Посібник користувача SDK Документація Перезавантажити систему Вимкнути систему Призупинити систему Режим глибокого сну Заблокувати систему Вийти з облікового запису Вибрати Випадкову Гру Ігрові поля, що будуть відображатися на панелі деталей: Відстань між елементами Малювати фон елемента Сітки Ширина рамки елемента Сітки Відсутнє джерело значка гри Відсутнє джерело обкладинки гри Відсутнє джерело фонового зображення гри Вертикальний відступ до деталей гри Розташування деталей у Сітці Розташування списку ігор в детальному перегляді Малювати розділювач між панелями Висота обкладинки гри Висота значків в списку ігор Шрифт застосунку Моноширинний шрифт Позиція панелі фільтрів Позиція панелі провідника Відтворення обкладинок Бажане співвідношення сторін Наступні налаштування також впливають на відображення плитки в повноекранному режимі! Режим розтягнення DVD Box Epic Games Store GOG Galaxy 2.0 IGDB Квадрат Банер Steam Вертикальна обкладинка Steam Twitch * Необхідний перезапуск для застосування Налаштування Загальні Верхня панель Зовнішній вигляд Подробиці гри Розташування Додатково Повноекранний режим Ввід Швидкодія Метадані Оновлення Пошук Резервна копія Створити резервну копію даних бібліотеки Відновити резервну копію даних Автоматично імпортувати зміни в бібліотеці Неправильне розташування бази даних, має бути вказано правильний шлях. Ім'я облікового запису не може бути пустим. Завантажити метадані після імпортування ігор Запускати Playnite згорнутим Запускати Playnite при вмиканні комп'ютера Запускати згорнутим в панель завдань Не вдалося зареєструвати Playnite для запуску при ввімкненні комп'ютера. Запустити в повноекранному режимі Асинхронне завантаження зображень Покращує плавність прокручування списків ігор в обмін на повільніший час завантаження зображення. Показувати назву гри, якщо відсутня обкладинка Відображати назви ігор у Сітці Затемнювати не встановлені ігри Відображати значки ігор у списку детального перегляду Показувати кількість елементів в описі груп Показувати лише визначені поля на панелях фільтрів і провідника Вимкнути апаратне прискорення Використовуйте під час блимання або подібних проблем інтерфейсу користувача Показувати приховані ігри в списках швидкого запуску Впливає на перехідний список і список в меню області сповіщень. Кількість елементів швидкого запуску Використовувати фонове зображення гри як фонове зображення вікна Розмити фон Висока якість Затемнити фон Показати у Сітці Тема Тема профілю Повноекранна тема Повноекранна тема Розташування бази даних Статус входу: Налаштування Playnite Очистити веб-кеш Може вирішити проблеми під час зв'язування облікових записів. Показувати значок в області сповіщень Згорнути Playnite в область сповіщень Згорнути Playnite в область сповіщень, якщо закрити вікно застосунку Коли гра запускається: Коли гра закривається: Форматувати зіграний час, щоб показувати кількість зіграних днів Формати дат: Ви вийдете з усіх пов'язаних сервісів. Потрібно перезапустити застосунок, хочете продовжити? Очистити кеш? Щоб застосувати нову тему, потрібно перезапустити Playnite Отримати більше тем Створити нову тему Отримати більше розширень Створити нове розширення Допоможіть нам перекласти Playnite Playnite потрібно перезапустити щоб застосувати нові налаштування. Перезапустити зараз? Перезапуск скасує всі поточні активні задачі (завантаження). Перезапустити Playnite? Playnite не може переносити файли вашої бібліотеки автоматично. Ви повинні вручну перемістити/скопіювати файли перед зміною розташування. У разі відсутності бібліотеки в новому місті розташування, буде створена нова бібліотека. Нове розташування бази даних не буде використано до перезапуску Playnite. Час гри не буде записано, якщо обрати параметр "Закрити". Кількість рядків Кількість стовпців Кількість рядків детального перегляду Показувати фонове зображення на головному екрані Не застосовується ретроспективно до ігор, що існують, без повторного завантаження метаданих. Імпорт часу у грі до бібліотеки: Налаштовує, коли Playnite має імпортувати час у грі, який надається плагінами бібліотек для ігор у базі даних Playnite. Для використання цієї функції необхідна підтримка плагінів бібліотек, відповідальних за обробку ігор. Завжди: Імпортує час у грі для нових імпортованих і існуючих ігор у базі даних Playnite. Тільки для нещодавно імпортованих ігор: Імпортує час відтворення лише для нових імпортованих ігор. Ніколи: Ніколи й за жодних обставин не імпортувати час у грі. Завжди Тільки для нещодавно імпортованих ігор Ніколи Увімкнути підтримку контролера в режимі робочого столу Кнопка Guide відкриває повноекранний режим Автоматичне завантаження метаданих для нещодавно імпортованих ігор. Цільовий дисплей Завжди використовувати основний дисплей Показувати заголовки ігор Показувати стан батареї Показувати процент заряду батареї Показувати годинник Сховати курсор Лише встановлені у Швидких Фільтрах Підказки кнопок контролера Розташування Горизонтальна прокрутка Виберіть один з підрозділів Немає доступних налаштувань Не вдалося завантажити налаштування Ці сценарії застосовуються до кожної гри в бібліотеці. Індивідуальні сценарії можуть бути призначені кожній грі окремо під час редагування деталей гри. Анімація зміни фонового зображення Розміри шрифту Авто Без згладжування Відтінки сірого ClearType Оптимальний Дисплей Режим форматування тексту Режим відтворення тексту Методи відтворення і формату тексту наразі не застосовуються до опису гри. Попередньо завантажувати фонові зображення Якщо увімкнено, Playnite завантажуватиме фонові зображення під час завантаження метаданих. Це вимагає більше місця на диску і надає доступ до зображень в автономному режимі. Якщо вимкнено, фонові зображення завантажуватимуться лише при першій необхідності. Це вимагає менше місця, але може викликати затримку перед відображенням зображення. Крім того деякі зображення можуть бути не доступні в автономному режимі. Автоматично закривати сторонній клієнт після виходу з гри Час завершення роботи клієнта (в секундах) Не закривати після ігрових сесій коротших ніж (в секундах) Автоматично закривати наступні клієнти: Автоматичне закриття клієнтів Список виключень при імпорті Відображати попередження при призначенні грі завеликих медіа ресурсів Команда відкриття теки Бажана організація, що визначає віковий рейтинг Оновлювати розмір встановлення ігор при оновленні бібліотеки Сканування та оновлення розміру встановлення ігор, якщо виявлено, що їх файли були змінені з часу останнього сканування Нічого Залити Вписати Вписати зберігаючи співвідношення сторін Зліва Справа Зверху Внизу Помилка імпортування Необхідна автентифікація Автентифікація не виконана Альтернативний режим відображення веб-перегляду Використовуйте, коли виникають проблеми з веб-переглядами, наприклад, вбудованими діалоговими вікнами автентифікації. Часткове завантаження великих описів гри Великі описи можуть викликати помітну затримку при виборі ігор. Коли увімкнено, лише частина тексту опису буде завантажена автоматично, решту можна буде завантажити за запитом. Імпортування метаданих Завантажити метадані Задати вибрану конфігурацію для використання під час майбутніх завантажень метаданих. Також можна змінити в налаштування застосунку. Майстер імпортування емуляторів Цей майстер проведе вас через процес завантаження та імпорту консольних емуляторів та імпорту емульованих ігор. Не забувайте, що ви можете завжди додати додаткові емулятори та/або ігри пізніше через головне меню (в меню "Бібліотека" для налаштувань емулятора і меню "Додати гру" для емульованих ігор). Нижче наведено список емуляторів які Playnite може розпізнавати і налаштовувати автоматично. Ви можете завантажити програми встановлення з їх веб-сайтів. Після встановлення емуляторів (вручну) ви можете імпортувати їх у діалоговому вікні налаштування емуляторів. Ви можете імпортувати будь-які емулятори, встановлені на вашому комп'ютері, натиснувши кнопку "Автовизначення з теки...". Playnite буде шукати відомі йому емулятори у заданій теці і дасть можливість імпортувати їх. Ви можете імпортувати з декількох тек, використовуючи цю кнопку кілька разів. Емулятори будуть додані в нижній частині поточного списку. Ігри можна імпортувати, натиснувши кнопку "Сканувати теку за допомогою емулятора". Вибір відповідного емулятора дасть Playnite знати які типи файлів слід сканувати та імпортувати. Ви можете імпортувати з декількох тек, використовуючи цю кнопку кілька разів, ігри будуть додані в нижній частині поточного списку. Немає емуляторів, вибраних для імпорту. Ви не зможете автоматично імпортувати будь-які емульовані ігри без попереднього налаштування емуляторів. Дійсно продовжити та вийти з процесу імпорту? У Playnite немає налаштованих емуляторів. Не можна імпортувати ігри без попереднього налаштування емулятора та вибору відповідних типів файлів. Ви хочете додати якісь емулятори зараз? Сканувати теку за допомогою емулятора Вибір файлів Автовизначення з теки... Налаштування емуляторів... Сканування… Сканування {0}… Налаштування при першому запуску Цей майстер проведе вас через автоматичний процес імпортування і налаштування зовнішніх бібліотек. Playnite може автоматично імпортувати ігри з декількох ігрових сервісів таких як Steam чи GOG, а також тримати вашу бібліотеку в актуальному стані, автоматично оновлюючи її під час запуску програми. Майте на увазі, що ви можете додати будь-яку власну гру для будь-якої платформи пізніше, натиснувши кнопку "Playnite" в головному меню. Інтеграція бібліотеки Нижче наведено список деяких відібраних бібліотечних інтеграцій, які підтримує Playnite. Виберіть ті, які потрібно встановити. Інші інтеграції можна встановити пізніше з меню "Надбудови". Конфігурацію завершено Першочергове налаштування завершено. Пам'ятайте, що ви можете змінити будь-які налаштування пізніше в меню "Налаштування". Також ви маєте можливість додати будь-яку іншу гру згодом натиснувши на меню у вигляді логотипа Playnite. Не вдалося завантажити одне або кілька розширень. Ви можете спробувати повторно завантажити інтеграції з меню надбудов після завершення майстра першого запуску. Завантаження інтеграції {0}… Завантаження списку рекомендованих інтеграцій… Не вдалося завантажити список рекомендованих інтеграцій. Ви можете спробувати повторно завантажити інтеграції пізніше через меню Надбудови. Налаштування платформ та емуляторів Налаштування емуляторів Платформи Платформа Емулятори Емулятор Додати платформу Обрати значок Обрати обкладинку Обрати зображення Вибрати елемент Обрати фон Обрати файл Обрати URL Додати емулятор Підтримувані платформи Ви хочете зберегти зміни платформи? Ви хочете зберегти зміни емулятора? Виконуваний файл Аргументи Робочий каталог Підтримувані типи файлів Імпортувати емулятори... Завантажити емулятори... Завантажити аргументи з відомого профілю емулятора Ви дійсно хочете видалити емулятор {0}? Наразі він використовується {1} грою(ами). Дійсно видалити платформу {0}? Наразі вона використовується {1} грою(ами) та {2} емулятором(ами). Довідка про налаштування Сортувати за Напрямок сортування Групування Зростанням Спаданням Не групувати Групувати за постачальником Групувати за категорією Групувати за платформою Тип перегляду Вигляд Панель провідника Панель фільтрів Значок Значок бібліотеки Обкладинка Фонове зображення Сортування Постачальник Посібник Назва Диск встановлення Ім'я облікового запису Платформа Категорія Жанр Дата випуску Рік випуску Розробник Теги Видавець Стан установки Відповідність всім фільтрам Якщо увімкнено, будуть показані тільки ігри що відповідають всім пунктам у всіх фільтрах. Якщо вимкнено, будуть показані ігри що відповідають будь-яким пунктам у будь-яких фільтрах. Встановлено Встановлено Не встановлено Приховано Обране Увімкнути підтримку HDR Якщо увімкнено, HDR буде увімкнено на основному дисплеї перед запуском гри. Зверніть увагу, що HDR не підтримується на вашому основному дисплеї. Востаннє зіграно Категорія Опис Тека встановлення Обкладинка Посилання Образ/ISO шлях Жанр Жанри Компанія Компанії Розробник Розробники Видавець Видавці Категорія Категорії Тег Теги Особливість Особливості Віковий рейтинг Віковий рейтинг Регіон Регіони Джерело Джерела Останні дії Помилка бази даних Не вдалося відкрити базу даних бібліотеки. Базу даних не відкрито. Не вдалося отримати доступ до бази даних бібліотек. Файл "{0}" використовується іншим процесом, або він недосяжний. Не вдалося створити пакунок діагностики. Не вдалося автоматично завантажити пакунок діагностики. Інформацію для діагностики успішно відіслано. Пакунок діагностики був створений та висланий успішно. Будь ласка, додайте наступний ID до звіту про проблему: Не вдалося імпортувати ігри з {0}. Не вдалося імпортувати емульовані ігри з {0}. Неможливо виконати пошук ігор за обраним профілем емулятора. Профіль не містить розширень файлів або платформ. Не вдалося запустити Playnite. Закрийте всі запущені екземпляри та повторіть спробу. Не вдалося задіяти тему "{0}", кольоровий профіль "{1}" {2} Не вдається відкрити посилання, URL-адреса має неприпустимий формат. Не вдалося запустити застосунок. Не вдалося ініціалізувати компонент вебперегляду. Playnite не може продовжити процес запуску. Більше інформації на https://playnite.link/cefstartup Неможливо імпортувати емулятори через відсутність або пошкодження файлу визначення. Не вдалося виконати дію меню. Змінити відомості про гру Зображення URL Додати посилання Додати ROM Зберегти зміни Застосувати зміни поля гри/ігор, що редагуються. Додати дію Видалити дію Прибрати дію "Грати" Додати Ігри Сканувати теку... Виявити встановлені Огляд... Відкрити Playnite Налаштування профілю Назва гри не може бути порожньою. Каталог відстеження дій гри не може бути порожнім. Назва гри не може бути порожньою перед пошуком метаданих. Неприпустимі ігрові дані Введіть припустимий URL що починається з http:// або https:// Обрати URL Не вдалося завантажити метадані: {0} Помилка завантаження Очистити фільтри Приватний обліковий запис Загальнодоступний обліковий запис API ключ Помилка запуску Помилка теми Очистити все Встановлення Видалення Запуск Запущено Неприпустимий URL Нічого не робити Мінімізувати Відновити вікно Відновити вікно лише при запуску з інтерфейсу Закрити Змінити Розширені Ніколи Статус завершення Статуси завершення Моя оцінка Оцінка критиків Оцінка спільноти Сценарії гри Сценарії застосунку Сценарії Плагіни Джерела метаданих Розширення ID розширення Перезавантажити сценарії Інтерактивна SDK PowerShell Всі сценарії перезавантажено успішно. Не знайдено жодної гри за заданими критеріями пошуку/фільтру. Нічого не знайдено Повернутися до режиму робочого столу Вийти з Playnite Бібліотеки Оновити все Створено: Версія: Оновлено: Модуль: Бібліотека Статистика Все Нічого Сповіщення Ширина Висота Розмір Малий Звичайний Великий Більший Найбільший Типово Вибрати Обрати все Скасувати вибір Перше Випадкове Вибір користувача Завантажити ще Прозорість Згорнути Розгорнути Згорнути все Розгорнути все Інше Теми Аргументи емулятора Вбудовані параметри Користувацькі параметри Додаткові аргументи емулятора Перевизначити аргументи емулятора Дія "Грати" Виберіть метадані для імпорту Обрати ігри для імпортування Пошук метаданих Доступне оновлення Зміни після останнього оновлення Завантажити та встановити оновлення Перевірити наявність оновлень Помилка оновлення Не вдалося перевірити наявність нової версії. Нову версію не знайдено, ви користуєтеся найновішою. Не вдалося завантажити та інсталювати оновлення. Наразі виконується фонове завдання. Скасувати його та продовжити з оновленням? Наразі виконується фонове завдання. Скасувати його і вийти з Playnite? Наразі виконується фонове завдання. Зміна режимів скасує завдання, хочете все одно змінити режим? Доступне оновлення Playnite Перезавантажити список тем Застосувати вибрану тему Дивитися зміни файлу Автоматично застосовувати тему при зміні вихідного файлу Тип сценарію Сценарій для виконання перед початком гри Сценарій для виконання після виходу з гри Сценарій для виконання після запуску гри Виконати під час запуску застосунку Виконати під час закриття застосунку Гра запускає сценарій Гра запустила сценарій Гра зупинила сценарій Виконати глобальний сценарій Глобально Відфільтровано Поточні Нові Випробувати сценарій Показувати лише вибрані елементи Зберегти за замовчуванням Додати в Обране Прибрати з Обраного Приховати цю гру Прибрати з Прихованих Увімкнути підтримку HDR Вимкнути підтримку HDR Змінити... Обчислити розмір встановлення Обчислити розмір встановлення (всі ігри) Обчислити розмір встановлення (лише відсутні дані) Розмір встановлення Задати категорію... Задати статус завершення Прибрати Грати Встановити Налаштування гри Деталі Видалити Відкрити розташування гри Створити ярлик на робочому столі Відкрити посібник Більше Управляється плагіном бібліотеки Процес запуску гри буде керуватися бібліотечним плагіном, відповідальним за цю гру. На зазначеній сторінці не знайдено відповідної інформації про гру "{0}". Порада: Ви можете використовувати більш просунутий процес завантаження метаданих під час редагування однієї гри за допомогою опції меню "Редагувати". Недоступно, коли виконується деяка дія. Текст опису чутливий до синтаксису HTML Час гри записується в секундах. Розмір встановлення вказується в байтах. Дата випуску має бути встановлена у форматі "рік-місяць-день". Значення місяця та дня можна опустити. Значення від 0 до 100 або порожнє, якщо нема оцінки. Розробка Playnite підтримується цими патронами та членами Ko-fi: Ті, хто брали участь в написанні коду, локалізації і т.д.: Скасувати моніторинг гри? Працює моніторинг установки. Ви хочете скасувати процес і повернути гру в попередній стан? Виконується моніторинг виконання гри. Ви хочете скасувати процес і повернути гру в попередній стан? Часу зіграно Останній запуск {0}д {1}г {2}хв {0}г {1}хв {0} хв {0} сек Не запускалася Відкриття режиму робочого столу... Відкриття повноекранного режиму... Завантаження бібліотеки ігор... Обчислення розміру встановлення... Обчислення розміру встановлення {0}… Не вдалося встановити файл сценарію. Сценарій успішно встановлений. Встановити сценарій Помилка сценарію Не вдалося виконати функцію розширення. Відкрити теку з метаданими Обчислити Автоматично розраховує або розмір встановлення за допомогою ромів, якщо гра їх має, або директорію установки, якщо її було задано {0} клієнт не встановлено. Клієнт {0} зараз буде відкрито. Будь ласка авторизуйтеся після чого закрийте це повідомлення. Очікування на вхід користувача, будь ласка закрийте це коли завершите... Теку встановлення гри не знайдено Невірна конфігурація дії "Грати" Вирішення проблем із синхронізацією облікового запису Вирішення проблем Перейменувати Додати елемент Введіть ім'я Введіть нове ім'я Менше години Від 1 до 10 годин Від 10 до 100 годин Від 100 до 500 годин Від 500 до 1000 годин 1000+ Щоб завершити встановлення, Playnite потрібно перезапустити. Хочете перезапустити зараз? Розширення не упаковано належним чином. Тема не упакована належним чином. Розширення "{0}" не змогло завантажитися коректно. Не вдається завантажити розширення "{0}", поточна версія Playnite не підтримується. Тема "{0}" не змогла завантажитися коректно. Не вдається завантажити тему "{0}", поточна версія Playnite не підтримується. Розширення не змогло завантажитися коректно. Тема не змогла завантажитися коректно. Тема/Розширення використовує непідтримувану версію API. Встановлення завершилося успішно. Встановити надбудову? Загальні Не вдалося встановити доповнення "{0}". Не вдалося встановити розширення. {0} Ви хочете встановити нове розширення? {0} Автор: {1} Версія {2} Ви хочете оновити розширення "{0}"? Поточна версія: {1} Нова версія: {2} Не вдалося встановити тему. {0} Ви хочете встановити нову тему? {0} Автор: {1} Версія {2} Ви хочете оновити тему "{0}"? Поточна версія: {1} Нова версія: {2} Ви збираєтеся покинути Playnite і перейти на наступну веб-сторінку, використовуючи ваш основний веб-браузер. Хочете продовжити? {0} Обрані зображення можуть бути завеликі для оптимальної роботи. Використання завеликих зображень може призвести до гіршої чутливості інтерфейсу та збільшення використання пам'яті. Максимальна рекомендована роздільна здатність: Значки: {0} мегапікселів Обкладинки: {1} мегапікселів Фони: {2} мегапікселів Попередження щодо швидкодії Не показувати знову Файл із розширенням {0} несумісний. Несумісне розширення файлу Обране зображення може бути завеликим для оптимальної швидкодії. Ви впевнені що хочете видалити вибрану тему? Видалення буде заплановано при наступному запуску застосунку. Вбудовані теми неможливо видалити. Ця тема не підтримує цю версію Playnite. Ви впевнені що хочете видалити вибране розширення? Видалення буде заплановано при наступному запуску застосунку. Вбудовані розширення неможливо видалити. Це розширення не підтримує цю версію Playnite. Тека встановлення Тека даних Створення пакунку діагностики... Надсилання пакунку діагностики... Завантажити з файлу Що це? Ви впевнені, що ви хочете зробити це? Загальний час у грі Середній час у грі Найдовший час у грі Загальний розмір встановлення Огляд Бічна панель Показати на бічній панелі Скинути налаштування Усі налаштування застосунку будуть скинуті до значень за замовчуванням, за винятком: - Розташування бази даних - Списку виключень імпорту - Налаштувань розширень, включаючи інтеграції з бібліотекою Щоб завершити процес, потрібно перезавантажити застосунок. Скинути налаштування? Для розробників Зовнішні розширення Введіть повний шлях до теки. Досягнення Форум Новини Сторінка в крамниці Першочергове налаштування не завершено. Playnite перезапуститься в режимі робочого столу щоб завершити процедуру. Нещодавно зіграні Обране Найбільше награні Всі Є використані фільтри. Застосовуються додаткові фільтри. Результати пошуку для: Елемент з таким іменем вже існує. Обмежити вибір поточним фільтром Обрати іншу Надбудови... Встановлені Налаштування розширень Огляд Оновлення Оновлення ({0}) Управління встановленими розширеннями та темами, включаючи їх налаштування, було переміщено до нового меню "Надбудови". Усі встановлені розширення для інтеграції бібліотек можна налаштувати тут. Якщо ви хочете встановити або видалити додаткові інтеграції, скористайтеся опцією "Надбудови" з головного меню. Теми режиму робочого столу Теми повноекранного режиму Пошук.. Надбудова не сумісна з цією версією Playnite. Не вдалося завантажити інсталяційний пакет надбудови. Не вдалося завантажити маніфест інсталяції надбудови. Щоб застосувати очікувані зміни потрібно перезапустити застосунок. Ця надбудова запланована для встановлення. Встановити Перевстановити Видалити Вже встановлено Нових оновлень надбудов не знайдено. Оновити надбудови Журнал змін не доступний Заплановано до встановлення Помилка завантаження Ліцензію відхилено Завантаження: {0}... Пошук оновлень надбудов… Перевірка оновлень програми... Доступно одне або кілька оновлень надбудов. Виберіть елементи для оновлення Екземпляр розширення Ліцензійна угода {0} Прийняти Відхилити Включити дії "Грати" інтеграції бібліотеки Виберіть дію Режим стеження Шлях відстеження Затримка відстеження Частота відстеження Посилання Файл Емулятор Сценарій Типово Процес Тека Оригінальний процес Ім'я процесу Журналювати повідомлення відстеження Наступні зміни перепишуть дані для всіх вибраних ігор! Відсутня Вписати Лише елементи Тільки початок і кінець Чутливість прокручування Плавне прокручування Швидкість анімації Видалити елемент? Ви впевнені, що хочете видалити цей елемент? Показати кнопки на верхній панелі: Загальні налаштування перегляду Налаштування групування Параметри сортування Шаблони фільтрів Позиція елементів плагіну Ширина роздільника секцій Перемістити кнопку головного меню на бічну панель Панель провідника Вибір випадкових ігор Вибір випадкової гри зі списку Вибрати випадкову гру з поточного списку Зберегти налаштування групування та сортування Показувати як швидкий фільтр у повноекранному режимі За минулі 7 днів За минулий 31день За минулі 365 днів Більше 365 днів тому Настроїти Зберегти шаблон Згорнути після початку гри Згорнути Playnite після початку гри. Вимкнення цього може призвести до проблем, коли фокус не перемикається на ігри при запуску! Розмір шрифту Маленький розмір шрифту Увімкнути API-підтримку контролера Підтримка ігрового контролера Якщо вимкнено, Playnite ігноруватиме контролер. Вимкніть, якщо ви використовуєте інструменти, які імітують мишу/клавіатуру з контролера і ви спостерігаєте подвійні натискання в Playnite. Показати пункти в головному меню: Поміняти місцями кнопки X/A на головному екрані Міняє прив’язки кнопок для початку гри та показу сторінки з детальною інформацією про гру на головному екрані. Поміняти місцями кнопки підтвердження/скасування Міняє місцями прив’язку кнопок A/B для підтвердження та скасування. Тільки основний контролер Приймати дані лише з основного контролера, коли увімкнено. Кнопка Guide зміщує фокус на Playnite Гучність інтерфейсу Фонова гучність Вимкнути звук у фоновому режимі Не вдалося ініціалізувати аудіо інтерфейс. API виводу API, що використовується для виведення звуку. Змініть, якщо у вас виникли проблеми зі звуком. Загальні Зображення Аудіо Розташування Меню Ввід {0} починається… {0} працює… Верхній регістр Пробіл Масштабувальник візуалізації зображень Альтернативний Збалансований Якість Якість: Найкраща якість зображення, повільний, велике використання пам’яті. Збалансований: Якісний, швидкий, низьке використання пам’яті. Альтернативний: Краща якість, середня швидкість, низьке використання пам’яті. Обрати файл... Обрати теку... Сценарій при запуску Зверніть увагу, що розширення та теми можуть значно вплинути на продуктивність, стабільність та безпеку Playnite. Якщо після встановлення теми або розширення у вас виникають проблеми, спершу спробуйте вимкнути/видалити їх, щоб перевірити, чи вони є причиною проблеми. Вибрати при запуску Вибрати при запуску Вбудовані профілі Вбудований профіль Користувацькі профілі Користувацький профіль Обробляється за допомогою вбудованого сценарію Специфікація емулятора Специфікація платформи Специфікація регіону Виконати перед запуском емулятора Виконати після запуску емулятора Виконати після виходу з емулятора Виконуваний файл емулятора не знайдено. Специфікацію емулятора не знайдено. Сценарій запуску емулятора не знайдено. Розділити як окремі ігри Об’єднати в одну гру Встановити платформу Встановити регіон Сканувати теку Конфігурації сканування Виключити шаблони зі сканування контрольної суми Файли, що відповідають зазначеним шаблонам, не перевірятимуться на наявність контрольної суми, і будуть вибрані за назвою файлу. Додаткову інформацію див. на сторінці довідки емулятора. Сканувати емулятором Під час збереження нової конфігурації потрібно встановити назву. Емулятор або профіль емулятора не встановлено. Каталог для сканування не вказано або він не існує. Конфігурація сканування встановлена неправильно. Включити в автоматичне сканування масове сканування Не вдалося просканувати теку на наявність емуляторів. Не вдалося просканувати теку(и) на наявність емульованих ігор. Приховати імпортовані Профілі для імпорту: Конфігурації автоматичного сканування Зберегти як конфігурацію автоматичного сканування Зберігає конфігурацію для подальшого використання під час оновлення бібліотеки. Збереженими конфігураціями можна керувати за допомогою меню "Налаштування емуляторів". Імпортувати з використанням відносних шляхів По можливості імпортувати файли гри, використовуючи відносні шляхи до теки встановлення Playnite або емулятора. Сканувати підтеки Сканувати всередині архівів Об'єднати пов'язані файли Об'єднати пов'язані ігровими файли, наприклад окремі диски, під одним записом гри. Додати сканер Додати збережений сканер Почати сканування Додати конфігурацію(ї) сканування з емуляторами щоб сканувати конкретні теки. Переконайтесь що емулятори правильно налаштовані до того, як імпортувати ігри (через меню Бібліотека -> Налаштування емуляторів) Стан за замовчуванням, призначений для нещодавно доданих ігор Статус, призначений іграм, які граються вперше Не вдалося ініціалізувати середовище виконання сценарію PowerShell. Якщо ви користуєтесь Windows 7, спробуйте (повторно) встановити PowerShell 5.1, щоб вирішити проблему. Шаблон фільтрів із зазначеною назвою вже існує. Оновити шаблон з новими налаштуваннями? Автоматично заповнювати пусті поля "Сортування" для ігор, що додаються масово Коли ви редагуєте гру, додайте ігри за допомогою оновлення бібліотеки, скануванням тек емуляторів або нормальним скануванням, автоматично заповнювати поле "Сортування" для кращого сортування ігор за назвами. Наприклад "The Witcher 3" отримає сортувальне ім'я "Witcher 03". Сортувальне ім'я не буде присвоєно якщо воно не відрізняється від звичайної назви гри, і лише пусті поля сортування будуть автоматично заповнені. Ці слова буде видалено з початку автоматично заповненого поля "Сортування": Використовуйте це для ігнорування слів на початку рядка з метою сортування. За замовчуванням це "The", "An" і "A". Заповнити поле "Сортування" для ігор з пустим полем Сортування Заповнення полів "Сортування"… 344 / 5000 Результати перекладу Було виявлено, що служба Nahimic запущена у вашій системі. Відомо, що ця служба викликає проблеми з рендерингом у Playnite (та інших програмах). Якщо ви зіткнулися з будь-яким графічними проблемами в Playnite, ми рекомендуємо вимкнути або повністю видалити службу Nahimic. Більше інформації на https://playnite.link/nahimicsucks Playnite працює з правами адміністратора. Це не рекомендується, оскільки це дає підвищені привілеї всім встановленим розширенням і всім іграм/додаткам запущеним з Playnite! Більше інформації - https://playnite.link/adminfaq Показувати попередження, якщо Playnite запущено з правами адміністратора Отримати реальний розмір на диску під час розрахунку розміру ігор Якщо ввімкнено, сканування відбуватиметься повільніше та отримуватиме реальний розмір файлів на диску. Якщо вимкнено, сканування відбуватиметься швидше та використовуватиме розмір самих файлів. Повідомлялося, що наступні надбудови є потенційно проблемними або через високий вплив на стабільність/продуктивність або через проблеми з безпекою. Ми настійно рекомендуємо видалити їх: {0} Виключити онлайн-файли зі сканування Файли, що зберігаються в хмарному сховищі, не будуть скануватися та імпортуватися, якщо вони недоступні локально. Підтримується лише для: Google Drive, DropBox, OneDrive Сканувати, але використовуючи спрощений метод без вмісту файлу Файли будуть імпортовані, але за допомогою менш точного методу, який не вимагає завантаження вмісту файлу та його присутності локально. Застосувати до всіх Перевизначити стан встановлення Коли задано, Playnite ігноруватиме стан встановлення (включно з каталогом встановлення), встановлений плагіном, який імпортує цю гру. Цей параметр може не повністю працювати з плагінами, які використовують специфічні методи імпорту, хіба що вони також беруть цей параметр до уваги. Тільки вручну Раз на день Раз на тиждень При кожному запуску Перевірка оновлень програми Перевірка оновлень надбудов Оновлення бібліотек Сканування теки емуляції Включати приховані ігри Редагувати поля Вибрати все / Зняти вибір з усього Відкрити Активувати Призначити Почніть вводити текст для пошуку ігор… [F1] для допомоги # на початку відкриває список доступних команд. / на початку відкриває список доступних пошукових систем/плагінів. Введення ключового слова для пошуку із ПРОБІЛОМ в кінці негайно перемикає на цей пошук. TAB: змінити дію ENTER: активувати вибрану дію SHIFT-ENTER: відкрити меню елементу Включити не встановлені ігри Включати приховані ігри Не встановлені ігри включені Не встановлені ігри виключені Приховані ігри включені Приховані ігри виключені Грати або Встановити До подробиць Меню гри Редагувати гру Відкрити пошук Поле пошуку Кнопка пошуку Основна дія гри Вторинна дія гри CTRL-F відкриває глобальний пошук замість фокусування на полі пошуку Зберігати налаштування фільтру ігор між пошуковими сеансами Пошукові системи Стандартне ключове слово Власне ключове слово Загальносистемний ярлик Пошук Playnite Параметри розширення Виключення Виключені файли під час сканування теки Виключені теки під час сканування теки Додати файл до списку виключень Додати теку до списку виключень Виключення можуть бути додані лише для збережених налаштувань сканера. Винятки були додані до сканера "{0}". Перевизначити платформу Коли задано, сканер призначить цю платформу всім іграм, перезаписуючи будь-які автоматично виявлені платформи. Включити команди до стандартного пошуку Коли вимкнено, команди не будуть включені в пошук за умовчанням, доки не буде використано префікс #. Використовувати нечітку відповідність у фільтрі назв Якщо ввімкнено, фільтр назв буде знаходити ігри за назвами так само як і глобальний пошук. При необхідності можливо задати сувору відповідність додавши символ ! на початку. Показувати поля для результатів пошуку гри: Прихований статус Резервне копіювання даних скасовано. Помилка резервного копіювання даних. Помилка резервного копіювання даних Триває резервне копіювання даних… Відновлення даних з резервної копії… Не вдалося відновити дані з резервної копії. Налаштування Бібліотека ігор Медіа бібліотеки ігор Встановлені розширення Дані розширень Встановлені теми Виберіть дані для відновлення з указаного файлу резервної копії. Playnite автоматично перезапуститься та почне процес відновлення з резервної копії. Виберіть елементи, які будуть включені в резервну копію. Параметри застосунку та дані бібліотеки ігор включені за замовчуванням. Playnite автоматично перезапуститься та почне процес резервного копіювання. Автоматичне резервне копіювання даних Частота автоматичного резервного копіювання Тека резервних копій Циклічні резервні копії Включити додаткові дані: Якщо ввімкнено автоматичне резервне копіювання необхідно задати теку для його файлів. Показувати сповіщення лише про нові патчі Якщо ввімкнено, лише оновлення, доступні для наразі встановленого основного випуску, призведуть до сповіщення про оновлення. Нові основні випуски не призведуть до сповіщення про оновлення. Використовувати відносні дати за минулий тиждень Використовувати відносні дати у форматі «сьогодні», «вчора» тощо, якщо дата менше ніж тиждень. Зазначений формат дати буде використано для всіх інших дат. Пошук зображення в Інтернеті Рядок пошуку зображення значка Рядок пошуку зображення обкладинки Рядок пошуку фонового зображення Отримання інформації про надбудову… Немає доступних джерел метаданих Налаштування дії "Грати" Використовувати параметри сканера Вибирати профіль при запуску Вибирати емулятор при запуску Автоматично Завжди увімкнено Завжди вимкнено Підтримка спеціальних функцій (зчитування з екрана) Меню застосунку Меню гри Тека програми Каталог даних користувача Виявлено пошкодження файлу бібліотеки, Playnite вимкнеться. Відкрийте нову проблему на сторінці Playnite в GitHub із запитом на виправлення пошкодження у ваших файлах. Бажаєте зберегти внесені зміни? Портативна установка Контролерів не знайдено ================================================ FILE: source/Playnite/Localization/vi_VN.xaml ================================================  Tiếng Việt Ngôn ngữ của Playnite Thoát Bộ lọc đang được sử dụng Bộ lọc bị vô hiệu hóa Bộ lọc bổ sung Bộ lọc Lọc Dữ liệu không hợp lệ Lưu các thay đổi? Trang chủ: www.playnite.link Mã nguồn ở GitHub Tạo gói diag. Gửi thông tin diag. Thông tin về Playnite Được làm bởi Josef Němec Xếp danh mục Đặt danh mục Thêm danh mục Đã chọn - Chỉ định danh mục Bỏ chọn - Xóa danh mục Không xác định - Không thay đổi (khi chỉnh sửa nhiều trò chơi) Không có danh mục Không có nền tảng Rất tiếc! Đã xảy ra lỗi. Đã xảy ra lỗi không thể khôi phục. Nếu bạn muốn giúp chúng tôi khắc phục sự cố này, vui lòng mô tả ngắn gọn các hành động đã thực hiện trước khi xảy ra sự cố, sau đó gửi thông tin chẩn đoán cho chúng tôi. Nếu bạn đang trực tuyến, thông tin này sẽ được gửi lên máy chủ của Playnite để phân tích. Ngoài ra, bạn có thể nhấp vào nút 'Báo cáo sự cố' để tạo sự cố mới trên GitHub và báo cáo sự cố theo cách thủ công. Cám ơn sự giúp đỡ của bạn. Tiện ích mở rộng "{0}" đã gây ra lỗi không thể khôi phục. Chúng tôi khuyên bạn nên lưu tệp nhật ký và báo cáo vấn đề cho nhà phát triển của tiện ích. Nếu sự cố vẫn tái diễn, hãy tắt tiện ích mở rộng. Tiện ích mở rộng "{0}" đã gây ra lỗi không thể khôi phục. Chúng tôi khuyên bạn nên báo cáo vấn đề này cho nhà phát triển của tiện ích. Nếu sự cố vẫn tái diễn, hãy tắt tiện ích mở rộng. Tiện ích mở rộng không xác định hoặc chủ đề gây ra lỗi không thể khôi phục. Chúng tôi khuyên bạn nên tắt tiện ích bổ sung của bên thứ 3, cô lập tiện ích bổ sung có vấn đề và báo cáo vấn đề với nhà phát triển tiện ích bổ sung. Đã xảy ra lỗi không thể khôi phục. Nếu bạn muốn giúp chúng tôi khắc phục sự cố này, xin vui lòng gửi thông tin chẩn đoán đến cho chúng tôi. Xin cám ơn. Tắt tiện ích mở rộng Lưu tệp nhật ký Gửi thông tin chuẩn đoán Báo cáo lỗi Khởi động lại Playnite Khởi động lại ở Chế độ An toàn Tắt tất cả các tiện ích mở rộng của bên thứ 3 và sử dụng chủ đề mặc định. Thoát Playnite Các hành động đã thực hiện trước khi xảy ra sự cố (viết bằng tiếng Anh): Quản lý Thư viện Xóa (các) trò chơi? Không thể xóa - Trò chơi hoặc trình cài đặt đang chạy. Không thể gỡ cài đặt - Trò chơi đang chạy. Bạn có chắc rằng mình muốn xóa {0}? Bạn có chắc chắn muốn xóa {0} trò chơi này không? Bạn có chắc rằng muốn gỡ bỏ {0}? Lựa chọn "thêm vào danh sạch loại trừ" sẽ ngăn việc trò chơi được thêm lại vào lần sau khi cập nhật thư viện. Bạn có chắc chắn muốn xóa {0} trò chơi không? Việc chọn tùy chọn "thêm vào danh sách loại trừ" sẽ ngăn trò chơi được thêm lại vào lần sau khi cập nhật thư viện. Bạn có chắc chắn muốn xóa {0} mục hiện không được sử dụng không? Không tìm thấy được các trường không sử dụng. Có (thêm vào danh sách loại trừ) Có những thay đổi chưa được lưu trong phần này Đang cập nhật định dạng thư viện trò chơi… Cập nhật cơ sở dữ liệu không thành công. Không thể cập nhật thư viện trò chơi. Cần có {0} MB dung lượng trống. Lỗi Game Không thể bắt đầu trò chơi. '{0}' không được tìm thấy trong cơ sở dữ liệu. Không thể bắt đầu trò chơi: {0} Không thể thực hiện hành động: {0} Không thể mở vị trí trò chơi: {0} Không thể xác định dung lượng bộ cài game: {0} Lỗi khi truy quét dung lượng cài đặt Đã có {0} lỗi trong quá trình xác định dung lượng Không tạo được lối tắt: {0} Thất bại mở sách hướng dẫn: {0} Không thể cài đặt trò chơi: {0} Không thể gỡ cài đặt trò chơi: {0} Không tìm thấy thao tác nào phù hợp khi khởi động game. Khi sử dụng thao tác giả lập, hãy chắc chắn rằng nền tảng được khớp giữa game và cài đặt của trình giả lập. Phần triển khai cài đặt chưa khả dụng. Plugin thư viện chịu trách nhiệm cho trò chơi này đã bị vô hiệu hóa hoặc chưa được cài đặt. Dữ liệu chính thức để tải xuống không tồn tại Không có trò chơi nào được chọn. Thực hiện tập lệnh của trò chơi thất bại. Mã lệnh thực thi đã thất bại. Thực hiện tập lệnh toàn bộ thất bại Thực hiện tập lệnh giả lập thất bại. Thực hiện phát tập lện hành động thất bại. PowerShell 3.0 hoặc mới hơn chưa được cài đặt. Không thể tìm cách bắt đầu trò chơi. Đã kích hoạt Đã tắt Xóa bỏ Tẩy bỏ phần không sử dụng. Đổi Tên Sao chép Thêm Biểu tượng mặc định Ảnh bìa mặc định Ảnh nền mặc định Hoàn tất Tiếp theo Quay lại XONG MẶT SAU TRỐNG trống Bỏ qua Bỏ qua tất cả Nhập Tên Tác giả Mô-đun Chuỗi Phiên bản Chơi gần đây nhất Chơi nhiều nhất Số lần chơi Dung Lượng Cài Đặt Thư mục Ghi chú Đã thêm vào. Ngày được thêm vào Đã chỉnh sửa Ngày sửa đổi Địa chỉ web Đường dẫn OK Lưu Đóng Hủy bỏ Xác nhận Thiết lập lại Không Chào mừng Người dùng cục bộ Tổng quan Đa phương tiện Liên kết Thiết lập cài đặt Quản lý thao tác Đang tải xuống… Đang tải xuống đa phương tiện… Đang tải… Loại hình Hồ sơ Các hồ sơ Xóa bỏ Tải xuống Tìm kiếm Độ phân giải: Bất kỳ Phóng to Chế độ hiển thị dạng danh sách Bìa Chế độ hiển thị dạng lưới Chế độ hiển thị chi tiết Tùy chỉnh Đường dẫn (URL) Lời cảm ơn sâu sắc Giấy phép Cộng tác viên Thoát Playnite... Hôm nay Hôm qua Thứ Hai Thứ Ba Thứ Tư Thứ Năm Thứ Sáu Thứ Bảy Chủ Nhật Một tuần qua Một tháng qua Một năm qua Cách đây nhiều hơn một năm trước 0 tới 100MB 100MB tới 1GB 1GB tới 5GB 5GB tới 10GB 10GB tới 20GB 20GB tới 40GB 40GB tới 100GB 100GB hoặc nhiều hơn Đã nhập thành công. Tất cả Trò chơi ID Trò chơi ID Cơ sơ dữ liệu Mẫu thiết lặp Cột Các Cột Dòng Các Hàng Không thể tải biểu tượng từ Play action. Không có hành động nào của Loại tệp hiện tại. Chỉ tải các Siêu Dữ Liệu cò thiếu Bật tùy chọn này sẽ bỏ qua quá trình tải xuống siêu dữ liệu cho các trường dữ liệu mà đã chứa thông tin. Lựa chọn trò chơi. Xin lựa chọn các trò chơi nào để cập nhật siêu thông tin mới. Mọi trò chơi từ cơ sở dữ liệu. Tất cả các trò chơi hiện đang được chọn lọc. Chỉ các trò chơi đã chọn. Chưa chọn trường thông tin phụ Chưa chọn trường thông tin phụ để tải xuống. Vui long chọn ít nhất một, và kích hoạt ít nhất một nhà cung cấp cho nó. Cửa Hàng Chính thức. IGDB Vui lòng chọn trường nào sẽ được Playnite tự động điền và nguồn nào nên được sử dụng để lấy dữ liệu từ đó. Vui lòng xem xét nhấp vào biểu trưng ở trên và đóng góp các bản cập nhật cho cơ sở dữ liệu igdb.com để cải thiện các dữ liệu Playnite sử dụng. Đang tải xuống Siêu dữ liệu... Đang nhập các trò chơi cài đặt sẵn... Đang nhập {0} các trò chơi... Đang nhập các trò chơi giả lập từ {0} Đang tải xuống các bản cập nhập thư viện... Đang quét dung lượng trò chơi trong thư viện... Đang xác định dung lượng những game đã nhập… Cập nhật thư viện thành công Đang thả ra các tài nguyên Cấu hình Cài đặt... Nền tảng và Phần mềm giả lập Cài đặt giả lập... Quản lý Thư viện... Công cụ Đang tải xuống Siêu dữ liệu... Công cụ phần mềm... Cài đặt tích hợp... Mở Client bên thứ 3 Client bên thứ 3 Cập nhật Thư viện Game Hủy Cập nhật Thư viện Cập nhật các thư mục giả lập Thêm Trò chơi Tự tay thêm... Tự động quét... Trò chơi giả lập... Ứng dụng Microsoft Store... Về Playnite Gửi phản hồi Chuyển sang chế độ toàn màn hình Liên kết Trợ giúp Hỗ trợ trên Patreon Ủng hộ trên Ko-fi Hướng dẫn sử dụng Tài liệu về bộ công cụ phát triển phần mềm Khởi động lại hệ thống Tắt hệ thống Ngưng hệ thống Chế độ ngủ đông Hệ thống Khoá Đăng xuất Người dùng Chọn một trò chơi ngẫu nhiên Các lĩnh vực trò chơi được hiển thị trên bảng chi tiết: khoảng cách các mục Vẽ nền mục lưới Chiều rộng đường viền mục lưới Thiếu nguồn biểu tượng trò chơi Thiếu nguồn bìa game Thiếu nguồn nền trò chơi Khoảng cách dọc đến chi tiết trò chơi Vị trí chi tiết chế độ xem lưới Chi tiết xem vị trí danh sách trò chơi Vẽ dấu phân cách giữa các bảng Chiều cao ảnh bìa trò chơi Chiều cao biểu tượng danh sách trò chơi Phông chữ ứng dụng Phông chữ liền mạch Vị trí bảng điều khiển bộ lọc Vị trí bảng điều khiển Explorer Kết xuất ảnh bìa Tỷ lệ khung hình mục tiêu Các tùy chọn sau đây cũng ảnh hưởng đến kết xuất ô ở chế độ Toàn màn hình! Chế độ kéo dãn Hộp DVD Epic Games Store GOG Galaxy 2.0 IGDB Quảng trường Biểu ngữ Steam Steam nắp dọc hơi Twitch * Cần khởi động lại để áp dụng cài đặt Cài đặt Cài đặt chung Bảng điều khiển trên cùng Giao diện Chi tiết trò chơi Bố cục Nâng cao Toàn màn hình Đầu vào Hiệu suất Siêu dữ liệu Đang cập nhật Tìm kiếm Sao lưu Sao lưu Dữ liệu Thư viện Khôi phục Dữ liệu Sao lưu Tự động nhập các thay đổi trong thư viện Vị trí tệp cơ sở dữ liệu không hợp lệ, cần đặt đường dẫn tệp thích hợp Tên tài khoản không thể để trống Tải xuống siêu dữ liệu ngay sau khi nhập trò chơi Chạy Playnite dưới dạng thu nhỏ Chạy Playnite cùng lúc với máy tính Khởi động thu gọn ở system tray Thất bại trong việc đưa Playnite vào khởi động cùng lúc với máy tính Chạy ở chế độ toàn màn hình Tải hình ảnh không đồng bộ Cải thiện độ mượt mà khi cuộn danh sách trò chơi và đổi lấy thời gian tải hình ảnh chậm hơn Hiển thị tên trò chơi nếu thiếu ảnh bìa Hiển thị tên trò chơi ở chế độ xem dạng lưới Làm tối màu các trò chơi chưa được cài đặt Hiển thị biểu tượng trò chơi ở chế độ xem chi tiết Hiển thị số lượng mục trên mô tả nhóm Chỉ hiển thị các trường được chỉ định trên bảng bộ lọc và bảng khám phá Tắt tăng tốc phần cứng Sử dụng khi gặp sự cố nói lắp hoặc các sự cố giao diện người dùng tương tự Hiển thị các trò chơi ẩn trong danh sách khởi chạy nhanh Ảnh hưởng đến Danh sách nhảy và danh sách menu khay. Số lượng vật phẩm khởi chạy nhanh Sử dụng hình nền trò chơi làm nền cửa sổ Làm mở ảnh nền Chất lượng cao Làm tối ảnh nền Hiển thị trên chế độ lưới Chủ đề Hồ sơ chủ đề Chủ đề toàn màn hình Cấu hình chủ đề toàn màn hình Vị trí cơ sở dữ liệu Trạng thái đăng nhập: Cài đặt Playnite Xóa bộ nhớ đệm Có thể giải quyết các vấn đề gặp phải khi liên kết tài khoản. Hiển thị biểu tượng khay hệ thống Thu nhỏ Playnite vào khay hệ thống Thu nhỏ Playnite vào khay hệ thống khi đóng cửa sổ ứng dụng Khi trò chơi bắt đầu: Sau khi trò chơi kết thúc: Cấu trúc thời gian để hiển thị số ngày đã chơi Định dạng ngày tháng: Điều này sẽ đăng xuất bạn khỏi tất cả các dịch vụ được liên kết. Khởi động lại ứng dụng là bắt buộc, bạn có muốn tiếp tục không? Xóa bộ nhớ cache? Bắt buộc phải khởi động lại Playnite để áp dụng chủ đề mới Nhận thêm các chủ đề Tạo chủ đề mới Thêm tiện ích mở rộng tạo tiện ích mở rộng mới Giúp chúng tôi dịch Playnite Playnite cần được khởi động lại để áp dụng các cài đặt mới. Khởi động lại ngay bây giờ? Khởi động lại sẽ hủy mọi tác vụ đang hoạt động (tải xuống) hiện đang được thực hiện. Khởi động lại Playnite? Playnite không thể tự động di chuyển các tệp thư viện của bạn. Bạn phải di chuyển / sao chép các tệp theo cách thủ công trước khi thay đổi vị trí. Nếu không có thư viện ở vị trí đích, một thư viện mới sẽ được tạo. Vị trí cơ sở dữ liệu mới sẽ không được sử dụng cho đến khi Playnite được khởi động lại. Thời gian chơi sẽ không được ghi nếu hành động "Đóng" được đặt. Số hàng Số cột Số lượng hàng xem chi tiết Hiển thị hình nền trên màn hình chính Không áp dụng trở lại các trò chơi hiện có mà không cần tải xuống lại siêu dữ liệu. Nhập thời gian chơi của game vào thư viện: Thiết lập thời gian Playnite nhập thời gian chơi của game vào cơ sở dữ liệu. Để sử dụng tính năng này cần có sự hỗ trợ của plugin cho thư viện. Luôn luôn: Nhập thời gian chơi cho game mới nhập và game đã tồn tại trong cơ sở dữ liệu của Playnite database. Chỉ khi có game mới nhập: Chỉ nhập thời gian chơi của những game vừa thêm vào cơ sở dữ liệu của Playnite. Không bao giờ: Không bao giờ nhập thời gian chơi ở mọi hoàn cảnh. Luôn luôn Chỉ dành cho trò chơi mới được thêm vào Không bao giờ Bật hỗ trợ bộ điều khiển trong Chế độ máy tính để bàn Nút hướng dẫn mở chế độ Toàn màn hình Cài đặt tải xuống siêu dữ liệu tự động cho các trò chơi mới được nhập Mục tiêu hiển thị Luôn sử dụng màn hình chính Hiện tiêu đề game Hiển thị tình trạng pin Hiển thị phần trăm pin Hiển thị đồng hồ Ẩn con trỏ chuột Chỉ được cài đặt trong Bộ lọc nhanh Lời nhắc Nút Bố cục Cuộn ngang Chọn một trong các lựa chọn Tùy chỉnh khả dụng Không tải được cài đặt Các tập lệnh này được thực thi cho mọi trò chơi trong thư viện. Các kịch bản riêng lẻ có thể được chỉ định cho từng trò chơi riêng biệt trong khi chỉnh sửa chi tiết trò chơi. Tạo hiệu ứng chuyển đổi ảnh nền Cỡ chữ Tự động Bí danh Màu xám Loại rõ ràng Lý tưởng Hiển thị Chế độ định dạng văn bản Chế độ kết xuất văn bản Các phương pháp kết xuất và định dạng văn bản hiện không được sử dụng cho mô tả trò chơi. Tải trước hình nền Nếu được bật, Playnite sẽ tải xuống tác phẩm nghệ thuật nền trong khi tải siêu dữ liệu xuống, sử dụng thêm dung lượng đĩa và cung cấp tác phẩm nghệ thuật khi ngoại tuyến. Nếu bị tắt, tác phẩm nền chỉ được tải xuống khi cần thiết lần đầu tiên, sử dụng ít dung lượng hơn, nhưng có thể dẫn đến độ trễ trước khi tác phẩm nghệ thuật được hiển thị và một số hình ảnh có thể không khả dụng khi ngoại tuyến. Tự động đóng ứng dụng bên thứ ba sau khi trò chơi thoát Độ trễ tắt máy khách (tính bằng giây) Không đóng sau các phiên trò chơi ngắn hơn (tính bằng giây) Tự động đóng các ứng dụng khách sau: Tự động đóng khách hàng Nhập danh sách loại trừ Hiển thị cảnh báo khi chỉ định phương tiện trò chơi quá lớn Lệnh mở thư mục Tổ chức xếp hạng độ tuổi ưa thích Cập nhật dung lượng cài đặt của game khi cập nhật thư viện Quét và cập nhật kích thước cài đặt của trò chơi nếu như phát hiện ra tập tin của chúng được chỉnh sửa từ lần quét gần nhất Không có Lấp đầy Đồng phục Đồng phục để lấp đầy trái phải trên Dưới Lỗi nhập Yêu cầu xác thực Quá trình xác thực đã thất bại Chế độ hiển thị chế độ xem web thay thế Sử dụng khi gặp sự cố với chế độ xem web, ví dụ: hộp thoại xác thực tích hợp. Đang tải một phần mô tả game Mô tả chi tiết có thể gây nên độ lag đáng kể khi lựa chọn game. Khi được kích hoạt, chỉ một phần của chữ trong mô tả sẽ được nạp ban đầu với lựa chọn nạp phần còn lại khi bạn cần. Nhập siêu dữ liệu Tải xuống siêu dữ liệu Đặt cấu hình đã chọn sẽ được sử dụng cho mọi lần tải xuống siêu dữ liệu trong tương lai. Cũng có thể được thay đổi trong cài đặt ứng dụng. Trình hướng dẫn nhập mô phỏng Trình hướng dẫn này sẽ hướng dẫn bạn quá trình tải xuống và nhập trình giả lập bảng điều khiển và nhập trò chơi được mô phỏng. Hãy nhớ rằng bạn luôn có thể thêm trình giả lập và / hoặc trò chơi bổ sung sau này thông qua menu chính (trong menu "Thư viện" cho cài đặt Trình giả lập và menu "Thêm trò chơi" cho các trò chơi được mô phỏng). Dưới đây là danh sách các trình giả lập mà Playnite có thể nhận dạng và định cấu hình tự động. Bạn có thể tải xuống trình cài đặt giả lập từ trang web của họ. Sau khi bạn đã cài đặt trình giả lập (theo cách thủ công), bạn có thể nhập chúng vào hộp thoại cấu hình trình giả lập. Bạn có thể nhập bất kỳ trình giả lập nào được cài đặt trên PC của mình bằng cách nhấp vào nút 'Tự động phát hiện từ thư mục…'. Playnite sẽ tìm kiếm thư mục đã chọn cho bất kỳ trình giả lập nào đã biết và cung cấp tùy chọn để nhập chúng. Bạn có thể sử dụng nút này nhiều lần để nhập trình giả lập từ các thư mục khác nhau. Trình giả lập sẽ được thêm vào cuối danh sách hiện tại. Bạn có thể nhập trò chơi bằng cách nhấp vào nút 'Quét thư mục bằng trình giả lập'. Việc chọn trình giả lập thích hợp sẽ cho Playnite biết loại tệp nào nên được quét và nhập. Bạn có thể sử dụng nút này nhiều lần để nhập trò chơi từ các thư mục khác nhau. Trò chơi sẽ được thêm vào cuối danh sách hiện tại. Không có trình giả lập nào được chọn để nhập. Bạn sẽ không thể tự động nhập bất kỳ trò chơi giả lập nào nếu không định cấu hình trình giả lập trước. Bạn có chắc chắn muốn tiếp tục và thoát khỏi quá trình nhập không? Không có trình giả lập nào được định cấu hình trong Playnite. Bạn không thể nhập trò chơi mà không định cấu hình trình giả lập trước và chọn loại tệp thích hợp. Bạn có muốn thêm một số trình giả lập bây giờ không? Quét thư mục bằng Trình giả lập Chọn file Tự động phát hiện từ thư mục… Định cấu hình Trình giả lập… Đang quét... Đang quét {0}… Cấu hình lần đầu tiên Quá trình này sẽ hướng dẫn bạn cách nhập tự động và cấu hình các thư viện trò chơi bên ngoài. Playnite có thể tự động nhập trò chơi từ nhiều dịch vụ trò chơi, chẳng hạn như Steam hoặc GOG. Hãy nhớ rằng bạn cũng có thể thêm thủ công bất kỳ trò chơi tùy chỉnh hoặc giả lập nào cho bất kỳ nền tảng nào sau đó từ menu chính. Tích hợp Thư viện Sau đây là danh sách một số tích hợp thư viện được quản lý mà Playnite hỗ trợ. Vui lòng chọn những cái bạn muốn cài đặt. Các tích hợp khác có thể được cài đặt sau từ menu "Tiện ích bổ sung". Cấu hình đã hoàn thành Thiết lập ban đầu đã được hoàn thành. Hãy nhớ rằng bạn có thể thay đổi tất cả cài đặt sau này cũng như thêm các tích hợp bổ sung từ menu chính. Không thể tải xuống một hoặc nhiều tiện ích mở rộng. Bạn có thể thử tải xuống lại các tích hợp từ menu tiện ích bổ sung sau khi trình hướng dẫn chạy lần đầu tiên kết thúc. Đang tải xuống tích hợp {0}… Đang tải danh sách những tệp nhúng… Thất bại trong việc tải danh sách của những trình nhúng được đề xuất. Bạn có thể thử tải lại sau đó thông qua menu Addons. Định cấu hình nền tảng và trình giả lập Định cấu hình Trình giả lập Các nền tảng Nền tảng Các trình giả lập Trình giả lập Thêm nền tảng Chọn Biểu tượng Chọn bìa Chọn ảnh Chọn vật phẩm Lưa chọn nền Chọn tập tin Đặt URL Thêm trình giả lập Nền tảng được hỗ trợ Bạn có muốn lưu các thay đổi nền tảng không? Bạn có muốn lưu các thay đổi của trình giả lập không? Thực hiện Tham số Thư mục làm việc Các loại tập tin được hỗ trợ Nhập trình giả lập… Tải trình giả lập Tải đối số đặt trước từ cấu hình trình giả lập đã biết Bạn có chắc chắn muốn xóa trình mô phỏng {0} không? Nó hiện đang được sử dụng bởi {1} trò chơi. Bạn có chắc chắn muốn xóa nền tảng {0} không? Nó hiện đang được sử dụng bởi {1} (các) trò chơi và {2} trình mô phỏng. Trợ giúp cài đặt Sắp xếp theo Hướng Sắp xếp Nhóm theo Theo tăng dần Xếp giảm dần Đừng nhóm danh sách Nhóm theo Thư viện Nhóm theo danh mục Nhóm theo Thể loại Kiểu hiển thị: Hiển thị Bảng điều khiển Explorer Bảng điều khiển bộ lọc Biểu tượng Biểu tượng Thư viện Ảnh bìa Hình nền Tên sắp xếp Thư viện Thủ công Tên Ổ cài đặt Tên tài khoản Nền tảng Danh mục Thể Loại Ngày phát hành Năm phát hành Nhà phát triển Thẻ: Những nhà phát triển Trạng thái cài đặt Khớp tất cả các bộ lọc Nếu được bật, chỉ những trò chơi sử dụng tất cả các mục trong tất cả các bộ lọc mới được đưa vào chế độ xem. Nếu bị tắt, các trò chơi sử dụng bất kỳ mục nào trong bất kỳ bộ lọc nào sẽ được đưa vào chế độ xem. Đã cài đặt Đã cài đặt Chưa cài đặt Ẩn Sản phẩm yêu thích Kích hoạt Hỗ trợ HDR Khi được bật, HDR sẽ được kích hoạt ở màn hình chính trước khi mở game. Lưu ý rằng màn hình chính của bạn không hỗ trợ HDR. Lần chơi cuối Danh mục Mô tả Thư mục cài đặt cửa hàng Ảnh bìa Liên kết Đường dẫn hình ảnh, ROM hoặc ISO Thể loại Các thể loại Công ty các công ty Nhà phát triển Các nhà phát triển Nhà xuất bản Những nhà phát triển Danh mục Các danh mục Thẻ: Các thẻ Tính năng nổi bật Các tính năng Xếp hạng độ tuổi Xếp hạng độ tuổi Khu vực Các khu vực Mã nguồn Các đoạn mã nguồn Hoạt động gần đây Lỗi cơ sở dữ liệu Không mở được cơ sở dữ liệu thư viện. Cơ sở dữ liệu không thể mở được. Không thể truy cập cơ sở dữ liệu thư viện. Tệp "{0}" đang được sử dụng bởi một quy trình khác hoặc tệp ở vị trí không thể truy cập được. Không tạo được gói chẩn đoán. Không thể tự động tải lên gói chẩn đoán. Thông tin trong mẫu đã được gửi thành công. Gói chẩn đoán đã được tạo và gửi thành công. Vui lòng đính kèm ID sau vào báo cáo sự cố của bạn: Không thể nhập trò chơi từ {0}. Không thể nhập trò chơi mô phỏng từ {0}. Không thể tìm kiếm trò chơi theo cấu hình trình giả lập đã chọn. Hồ sơ không chứa bất kỳ phần mở rộng hoặc nền tảng tệp nào. Playnite không khởi động được. Vui lòng đóng tất cả các phiên bản khác và thử lại. Không áp dụng được chủ đề "{0}", cấu hình màu "{1}" {2} Không thể mở liên kết, URL không có định dạng hợp lệ. Không thể khởi động ứng dụng Thất bại khi khởi tạo màn hình web. Playnite không thể tiếp tục khởi động. Thông tin thêm tại https://playnite.link/cefstartup Không thể nhập trình mô phỏng do tệp định nghĩa bị thiếu hoặc bị hỏng. Không thực hiện được hành động trên menu. Sử Thông tin game URL ảnh Thêm liên kết Thêm ROM Lưu thay đổi Áp dụng các thay đổi trường cho trò chơi (đang) đang được chỉnh sửa. Thêm Hành động Xóa bỏ Xóa bỏ hành động Thêm Trò chơi Quét thư mục… Phát hiện đã cài đặt Duyệt qua... Mở Playnite Cài đặt cấu hình Tên trò chơi không được để trống. Thư mục theo dõi hành động trò chơi không được để trống. Không được để trống tên trò chơi trước khi tìm kiếm siêu dữ liệu. Dữ liệu không hợp lệ Nhập URL web hợp lệ bắt đầu bằng http: // hoặc https: // Đặt URL Không tải xuống được siêu dữ liệu: {0} Lỗi Tải Xuống Xoá bộ lọc Tài khoản cá nhân Tài khoản công khai Khoá API Lỗi khởi động Lỗi chủ đề Xóa tất cả Đang cài đặt Gỡ cài đặt Ra mắt Đang hoạt động URL không hợp lệ Không làm gì cả Thu nhỏ cửa sổ Khôi phục cửa sổ Khôi phục cửa sổ chỉ khi nào khởi động từ UI Đóng cửa sổ Thay đổi Nâng cao Không bao giờ Trạng thái Trạng thái hoàn thành Thành tích của Game thủ Điểm phê bình Điểm cộng đồng Mã lệnh game Mã lệnh chương trình Tập lệnh Bổ sung Nguồn siêu dữ liệu Tiện ích mở rộng ID tiện ích mở rộng Tải lại Tập tin Script Tương tác với PowerShell SDK Tất cả các tập lệnh đã được tải lại thành công. Không tìm thấy trò chơi nào cho tiêu chí tìm kiếm / bộ lọc được chỉ định không tìm thấy vật nào Chuyển sang Chế độ máy tính để bàn Thoát Playnite Các thư viện Cập nhật tất cả Tạo bởi: Phiên bản: Đã cập nhật: Mô-đun: Thư viện Số liệu thống kê Tất cả Không có Thông báo Rộng Cao Kích cỡ Nhỏ Bình thường Lớn Lớn hơn Lớn nhất Mặc định Chọn Chọn tất cả Bỏ chọn Tất cả Đầu tiên Ngẫu Nhiên Người dùng lựa chọn Tải thêm Trong suốt Thu nhỏ Mở rộng Thu gọn tất cả Mở rộng tất cả Khác Chủ đề Đối số của trình giả lập đổi số tích hợp Đối số của trình giả lập Đối số giả lập bổ xung Ghi đè các đối số của Trình mô phỏng Chơi hành động Chọn siêu dữ liệu để nhập Chọn trò chơi để nhập Tìm kiếm siêu dữ liệu Bản cập nhật có sẵn Những thay đổi kể từ lần cập nhật cuối cùng Tải xuống và cài đặt bản cập nhật Kiểm Tra Cập Nhật Cập nhật bị lỗi Không thể kiểm tra phiên bản mới. Không tìm thấy phiên bản mới, bạn đã cập nhật. Không thể tải xuống và cài đặt bản cập nhật. Một số tác vụ hiện đang chạy trong nền. Bạn có muốn hủy bỏ nó và tiếp tục cập nhật không? Một số tác vụ hiện đang chạy trong nền. Bạn có muốn hủy bỏ nó và thoát Playnite không? Một số tác vụ nền hiện đang được thực hiện. Chuyển đổi chế độ sẽ hủy tác vụ, bạn có muốn chuyển đổi không? Đã có bản cập nhật cho Playnite Tải lại danh sách chủ đề Áp dụng các chủ đề đã chọn Xem các thay đổi của tệp Tự động áp dụng chủ đề khi tệp nguồn thay đổi Thời gian chạy tập lệnh Thực thi trước khi bắt đầu trò chơi Thực thi sau khi thoát khỏi trò chơi Thực thi sau khi trò chơi được bắt đầu Thực thi khi ứng dụng được khởi động Thực thi khi ứng dụng bị thoát Trò chơi bắt đầu... Trò chơi bắt đầu... Lệnh trò chơi đã dừng Thực thi tập lệnh toàn cầu Toàn cầu Bộ lọc Đang tiến hành Mới Tập lệnh thử nghiệm Chỉ hiện thị các mục đã được chọn Lưu thành mặc định? Thêm vào mục yêu thích Loại bỏ khỏi mục ưa thích Ẩn khỏi mục ưa thích Xóa khỏi danh sách ẩn Kích hoạt Hỗ trợ HDR Tắt Hỗ trợ HDR chỉnh sửa Tính toán kích cỡ cài đặt Tính toán dung lượng cài đặt (Tất cả game) Tính toán dung lượng cài đặt (Chỉ đối với dữ liệu thiếu) Dung lượng cài đặt Đặt danh mục… Trạng thái hoàn thành Xóa bỏ Chơi Cài đặt Cài đặt game Thông tin chi tiết Gỡ cài đặt Mở vị trí cài đặt Tạo lối tắt trên Desktop Mở hướng dẫn sử dụng Thêm Được quản lý bởi đầu vào thư viện Quá trình bắt đầu trò chơi sẽ được quản lý bởi plugin thư viện chịu trách nhiệm về trò chơi này. Không có thông tin liên quan nào về trò chơi '{0}' được tìm thấy trên trang được chỉ định. Mẹo: Bạn có thể sử dụng quy trình tải xuống siêu dữ liệu nâng cao hơn trong khi chỉnh sửa trò chơi đơn lẻ thông qua tùy chọn menu "Chỉnh sửa". Không khả dụng khi một số hành động đang diễn ra. Văn bản mô tả nhạy cảm với cú pháp HTML Thời gian trò chơi được tính bằng giây. Dung lượng cài đặt được hiển thị dạng byte Ngày phát hành phải được đặt ở định dạng 'năm-tháng-ngày'. Giá trị Tháng và Ngày có thể bị bỏ qua Giá trị từ 0 đến 100 hoặc trống không có điểm. Sự phát triển của Playnite được hỗ trợ bởi những khách hàng quen thuộc và các thành viên của Ko-fi sau: Mã, bản địa hóa và những người đóng góp khác không theo thứ tự cụ thể: Hủy theo dõi trò chơi? Giám sát cài đặt hiện đang chạy. Bạn có muốn hủy quá trình và đưa trò chơi về trạng thái trước đó không? Giám sát thực thi trò chơi hiện đang chạy. Bạn có muốn hủy quá trình và đưa trò chơi về trạng thái trước đó không? Thời gian chơi Lần chơi cuối {0} ngày {1} giờ {2} phút {0} giờ {1} phút {0} phút {0} giây Chưa chơi Đang mở chế độ Máy tính để bàn… Đang mở chế độ Toàn màn hình…   Đang tính toán dung lượng cài đặt của … Đang tính toán dung lượng cài đặt của {0}… Không cài đặt được tệp tập lệnh. Tập lệnh đã được cài đặt thành công. Cài đặt tập lệnh tập lệnh lỗi Không thực hiện được chức năng tiện ích mở rộng. Mở thư mục siêu dữ liệu Tính toán Tự động tính toán kích thước cài đặt bằng Roms nếu trò chơi có hoặc tự động chọn nơi cài đặt game nếu như chưa được thiết lập Ứng dụng khách {0} chưa được cài đặt. Ứng dụng khách {0} bây giờ sẽ mở. Vui lòng đăng nhập và sau đó đóng thông báo này. Đang đợi người dùng đăng nhập, vui lòng đóng phần này khi bạn hoàn tất… Không tìm thấy thư mục cài đặt trò chơi. Cấu hình hành động trò chơi không hợp lệ. Khắc phục sự cố đồng bộ hóa tài khoản Khắc phục sự cố Đổi tên thư mục Thêm mục mới Nhập tên Nhập tên mới ít hơn một giờ 1 đến 10 giờ 10 đến 100 giờ 100 đến 500 giờ 500 đến 1000 giờ Hơn 1000 giờ Playnite phải được khởi động lại để hoàn tất quá trình cài đặt. Bạn có muốn khởi động lại bây giờ không? Tiện ích mở rộng không được đóng gói đúng cách. Chủ đề không được đóng gói đúng cách. Không thể tải đúng tiện ích "{0}". Không thể tải tiện ích mở rộng "{0}", phiên bản Playnite hiện tại không được hỗ trợ. Không thể tải đúng tiện ích "{0}". Không thể tải giao diện mở rộng "{0}", phiên bản Playnite hiện tại không được hỗ trợ. Không thể tải đúng tiện ích "". Không thể tải đúng tiện ích "". Chủ đề / Tiện ích mở rộng đang sử dụng phiên bản API không được hỗ trợ. Cài đặt thành công Cài đặt tiện ích bổ sung Cài đặt chung Không cài đặt được tiện ích bổ sung "{0}". Không cài đặt được tiện ích mở rộng. {0} Bạn có muốn cài đặt một tiện ích mở rộng mới không? {0} Bởi 1} Phiên bản {2} Bạn có muốn cập nhật tiện ích mở rộng "{0}" không? Phiên bản hiện tại: {1} Phiên bản mới: {2} Không cài đặt được tiện ích mở rộng. {0} Bạn có muốn cài đặt một tiện ích mở rộng mới không? {0} Bởi 1} Phiên bản {2} Bạn có muốn cập nhật tiện ích mở rộng "{0}" không? Phiên bản hiện tại: {1} Phiên bản mới: {2} Bạn sắp rời Playnite và điều hướng đến trang web sau bằng trình duyệt web mặc định của mình. Bạn có muốn tiếp tục? {0} (Các) hình ảnh đã chọn có thể quá lớn để có hiệu suất tối ưu. Việc sử dụng hình ảnh rất lớn có thể dẫn đến khả năng phản hồi giao diện người dùng kém hơn và tăng mức sử dụng bộ nhớ. Độ phân giải khuyến nghị tối đa: Biểu tượng: {0} mega pixel Bìa: {1} mega pixel Nền: {2} mega pixel Cảnh báo Hiệu suất Không hiện thị lại Tệp có phần mở rộng {0} không tương thích. Phần mở rộng tập tin không hợp lệ Tệp hình ảnh đã chọn có thể quá lớn để có hiệu suất tối ưu. Bạn có chắc chắn muốn gỡ cài đặt chủ đề đã chọn không? Quá trình gỡ cài đặt sẽ được xếp hàng để bắt đầu ứng dụng tiếp theo. Không thể gỡ cài đặt các chủ đề cài sẵn. Chủ đề này không hỗ trợ phiên bản Playnite này. Bạn có chắc chắn muốn gỡ cài đặt chủ đề đã chọn không? Quá trình gỡ cài đặt sẽ được xếp hàng để bắt đầu ứng dụng tiếp theo. Không thể gỡ cài đặt các tiện ích mở rộng tích hợp. Chủ đề này không hỗ trợ phiên bản Playnite này. Thư mục cài đặt cửa hàng Thư mục dữ liệu Đang tạo gói chẩn đoán… Đang tải lên gói chẩn đoán… Nhập tập tin... Đây là cái gì? Bạn có chắc bạn muốn thực hiện điều này? Tổng thời gian chơi Thời gian trung bình Tổng thời gian chơi Tổng dung lượng cài đặt Tổng quan Thanh lệnh bên cạnh Hiển thị thanh tiện ích bên trái Đặt lại cài đặt Tất cả cài đặt ứng dụng sẽ được đặt lại về giá trị mặc định, ngoại trừ: - Vị trí cơ sở dữ liệu - Nhập danh sách loại trừ - Cài đặt tiện ích mở rộng, bao gồm cả tích hợp thư viện Yêu cầu khởi động lại ứng dụng để kết thúc quá trình. Bạn có muốn đặt lại cài đặt không? Cho các lập trình viên Phần mở rộng bên ngoài Nhập đường dẫn thư mục đầy đủ. Thành tựu Diễn đàn Tin mới Trang cửa hàng Cài đặt lần đầu chưa hoàn tất. Playnite sẽ khởi động lại ở chế độ Desktop để hoàn tất thiết lập. Được chơi gần đây Ưa thích Được chơi nhiều nhất Toàn bộ Một số bộ lọc được áp dụng. Có các bộ lọc bổ sung được áp dụng Hiển thị kết quả tìm kiếm cho: Một mục có cùng tên đã tồn tại. Giới hạn lựa chọn đối với bộ lọc hiện tại Chọn cái khác Tiện ích bổ sung... Đã cài đặt Cài đặt phần mở rộng Duyệt Cập nhật Cập nhật ({0}) Việc quản lý các chủ đề và phần mở rộng đã cài đặt, bao gồm cả cài đặt trong đó, đã được chuyển sang menu "Tiện ích bổ sung". Tất cả các phần mở rộng tích hợp thư viện hiện được cài đặt có thể được định cấu hình tại đây. Nếu bạn muốn cài đặt hoặc gỡ cài đặt các tích hợp bổ sung, hãy sử dụng tùy chọn "Tiện ích bổ sung" từ menu chính. Chủ đề Desktop Chủ đề Toàn màn hình Đang tìm kiếm... Tiện ích bổ sung không tương thích với phiên bản Playnite này. Không thể tải xuống gói cài đặt tiện ích bổ sung. Không thể tải xuống tệp kê khai cài đặt tiện ích bổ sung. Khởi động lại ứng dụng là bắt buộc để áp dụng các thay đổi đang chờ xử lý. Tiện ích bổ sung này được lên lịch cài đặt. Cài đặt Cài đặt lại Gỡ cài đặt Đã được cài đặt Không tìm thấy bản cập nhật tiện ích bổ sung mới nào. Cập nhật tiện ích bổ sung Changelog không khả dụng Đã lên lịch để cài đặt Tải xuống thất bại Giấy phép bị từ chối Đang tải xuống {0}… Tìm kiếm bản cập nhật cho tiện ích bổ sung... Đang tìm cập nhật của app Một hoặc vài bản cập nhật cho tiện ích bổ sung đang khả dụng. Chọn mục để cập nhật Phiên bản phát triển tiện ích mở rộng {0} Điều khoản của thỏa thuận cấp phép Đồng ý Không đồng ý Bao gồm các hành động chơi tích hợp thư viện Chọn hành động Trang Theo Dõi Đường dẫn Đánh dấu Độ trễ theo dõi ban đầu Tần số theo dõi Đường link Tập tin Các trình giả lập Nội dung Mặc định Tiến trình Thư mục Tiến trình ban đầu Tên tiến trình Kênh tin nhắn Các thay đổi sau sẽ ghi đè dữ liệu cho tất cả các trò chơi hiện được chọn! Không có Đồng phục Chỉ các mặt hàng Thời gian bắt đầu và kết thúc Độ nhạy thanh cuộn Làm mượt thanh cuộn Tốc độ hoạt ảnh Loại bỏ mục Bạn có chắc rằng bạn muốn xóa sản phẩm này? Hiển thị các nút trên bảng điều khiển trên cùng: Thiết lập chung Cài đặt nhóm Cài đặt sắp xếp Lọc cài đặt trước Vị trí các mục plugin Chiều rộng phân cách phần Di chuyển nút menu chính sang thanh bên Bảng điều khiển Explorer Bộ chọn trò chơi ngẫu nhiên Chọn game ngẫu nhiên Chọn game ngẫu nhiên trong danh sách Lưu cài đặt nhóm và sắp xếp Hiển thị dưới dạng bộ lọc nhanh ở chế độ Toàn màn hình Trong 7 ngày qua Trong 31 ngày gần đây Trong 365 ngày qua Hơn 365 ngày trước Đặt lại cấu hình Lưu cài đặt trước Thu nhỏ sau khi bắt đầu trò chơi Thu nhỏ Playnite sau khi trò chơi được bắt đầu. Việc vô hiệu hóa tính năng này có thể dẫn đến sự cố game không nhận được đầu vào khi khởi động! Kích cỡ phông chữ Kích thước phông chữ nhỏ Bật hỗ trợ cho API tay cầm trò chơi Hỗ trợ tay cầm Nếu bị tắt, Playnite sẽ không chấp thuận bất kì đầu vào của thiết bị điều khiển nào. Tắt đi nếu bạn sử dụng công cụ để chuyển tín hiệu đầu vào của tay cầm sang chuột/bàn phím và bạn đang nhận được hai đầu vào trong Playnite Hiển thị các mục trên menu chính: Liên kết nút xem chính X / A đảo ngược Hoán đổi các ràng buộc nút để bắt đầu trò chơi và hiển thị trang chi tiết trò chơi trên giao diện chính. Đảo vị trí nút xác nhận/hủy bỏ Đảo ngược vị trí các nút A/B cho việc xác nhận và hủy bỏ. Chỉ tay cầm chính Chỉ cho phép nhập liệu từ tay cầm chính khi được bật. Nút Guide tập trung vào Playnite Giao diện âm thanh Âm lượng nền Tắt âm thanh khi ở chế độ nền Không thể khởi chạy giao diện âm thanh. Đầu ra API API được sử dụng cho đầu ra âm thanh. Thay đổi nếu bạn đang gặp sự cố với âm thanh. Tổng quan Hình ảnh Âm thanh Bố cục Bảng chọn Đầu vào {0} đang bắt đầu… {0} đang chạy… Caps Nút cách Bộ chia tỷ lệ kết xuất hình ảnh Thay thế Cân bằng Chất lượng Chất lượng: Chất lượng hình ảnh tốt nhất, chậm, sử dụng nhiều bộ nhớ. Cân bằng: Chất lượng tốt, nhanh, sử dụng ít bộ nhớ. Thay thế: Chất lượng tốt hơn, tốc độ trung bình, sử dụng bộ nhớ thấp. Chọn tập tin… Chọn thư mục… Tập lệnh khởi động Xin lưu ý rằng cả phần mở rộng và chủ đề đều có thể ảnh hưởng lớn đến hiệu suất, sự ổn định và bảo mật của Playnite. Nếu bạn bắt đầu gặp một số sự cố sau khi cài đặt chủ đề hoặc phần mở rộng, trước tiên hãy thử tắt/gỡ cài đặt chúng để xem chúng có phải là gốc của sự cố hay không. Chọn khi khởi động Chọn khi khởi động Cấu hình tích hợp Hồ sơ cài sẵn Hồ sơ tùy chỉnh Hỗ sơ tùy chỉnh Được xử lý bởi một tập lệnh cài sẵn Đặc điểm giả lập Đặc điểm kỹ thuật nền tảng Đặc điểm kỹ thuật khu vực Thực thi trước khi bắt đầu trình giả lập Thực thi sau khi trình giả lập được khởi động Thực thi sau khi thoát trình giả lập Không tìm thấy tệp thực thi giả lập. Không tìm thấy thông số kỹ thuật giả lập. Không tìm thấy tập lệnh khởi động trình giả lập. Chia thành các trò chơi riêng biệt Hợp nhất thành một trò chơi Đặt nền tảng Đặt khu vực Quét thư mục Quét cấu hình Loại trừ các mẫu khỏi quá trình quét tổng kiểm tra Các tệp khớp với (các) mẫu đã chỉ định sẽ không được quét để kiểm tra tổng và sẽ được khớp theo tên tệp. Xem trang trợ giúp của trình giả lập để biết thêm thông tin. Quét bằng trình giả lập Tên phải được đặt khi lưu cấu hình mới. Trình giả lập hoặc hồ sơ trình giả lập chưa được đặt. Thư mục để quét không được chỉ định hoặc nó không tồn tại. Cấu hình quét không được đặt đúng cách. Bao gồm tự động quét quét hàng loạt Không quét được thư mục cho trình giả lập. Không quét được (các) thư mục cho các trò chơi được mô phỏng. Ẩn đã nhập Hồ sơ cần nhập: Cấu hình tự động quét Lưu dưới dạng cấu hình tự động quét Lưu cấu hình để sử dụng sau này trong quá trình cập nhật thư viện. Các cấu hình đã lưu có thể được quản lý thông qua menu "Định cấu hình trình giả lập". Nhập liệu sử dụng đường dẫn tương đồng Nếu có thể, nhập file game cho thư mục cài đặt của Playnite (hoặc thư mục cài đặt của trình giả lập) bằng cách sử dụng đường dẫn tương đồng. Quét thư mục con Quét trong file nén Gộp những file liên quan Gộp các file tương đồng của game (như các đĩa rời) thành dạng một đầu vào. Thêm máy quét Thêm máy quét đã lưu Bắt đầu quét Thêm (các) cấu hình quét bằng trình giả lập để quét các thư mục cụ thể. Đảm bảo rằng trình giả lập được định cấu hình đúng cách trước khi nhập trò chơi (thông qua menu Thư viện -> Định cấu hình trình giả lập). Trạng thái mặc định được chỉ định cho các trò chơi mới được thêm vào Trạng thái được chỉ định cho các trò chơi được chơi lần đầu tiên Không thể khởi chạy thời gian chạy tập lệnh PowerShell. Nếu bạn là người dùng Windows 7, hãy thử cài đặt (lại) PowerShell 5.1 để khắc phục sự cố. Bộ lọc đặt trước với tên được chỉ định đã tồn tại. Cập nhật cài đặt trước với cài đặt mới? Tự động điền những tên bị thiếu cho game đã sửa thông tin hoặc thêm cả loạt Khi bạn sửa thông tin game, thêm game mới qua cập nhật của kho game, quét thư mục trình giả lập, hoặc quét thư mục thông thường, tự động điền trường 'Tên Sắp xếp' với tên dễ sắp xếp hơn của tên các game. Ví dụ, "The Witcher 3" sẽ có tên để xếp là "Witcher 03". Như thế sẽ không đặt tên để xếp mà khác với tên game, và app sẽ chỉ tự động cập nhật tên để xếp mà còn trống. Những từ sau sẽ bị gỡ bỏ khi khởi động tiến trình tự đặt Tên Sắp xếp: Sử dụng tính năng này để bỏ qua những từ nằm ở đầu chuỗi ký tự để phục vụ việc sắp xếp. Mặc định là "The", "An" và "A". Điền Tên Sắp xếp cho những game chưa có Sắp xếp Đang điền Tên Sắp xếp… Dịch vụ Nahimic đã được phát hiện đang chạy trên hệ thống của bạn. Dịch vụ này được biết là gây ra sự cố hiển thị cho Playnite (và các ứng dụng khác). Nếu bạn gặp phải bất kỳ lỗi đồ họa nào hoặc các sự cố hiển thị khác trong Playnite, chúng tôi khuyên bạn nên tắt hoặc gỡ cài đặt hoàn toàn dịch vụ Nahimic. Thông tin thêm tại https://playnite.link/nahimicsucks Playnite đang chạy với các đặc quyền nâng cao (với tư cách là quản trị viên). Điều này không được khuyến nghị vì nó cung cấp các đặc quyền nâng cao cho tất cả các phần mở rộng đã cài đặt và tất cả các trò chơi/ứng dụng bắt đầu từ Playnite! Thêm thông tin tại https://playnite.link/adminfaq Hiển thị cảnh báo nếu Playnite đang chạy với các đặc quyền nâng cao Lấy dung lượng thực tế trên ổ đĩa khi tính toán dung lượng của game Khi được bật, trình quét sẽ chậm hơn và tính toán dung lượng thực tế của các file trên ổ đĩa. Khi bị tắt, trình quét sẽ nhanh hơn và sử dụng dung lượng của file game. (Các) tiện ích bổ sung sau đây đã được báo cáo là có khả năng có vấn đề, do tác động đến độ ổn định/hiệu suất cao hoặc các vấn đề bảo mật. Chúng tôi thực sự khuyên bạn nên gỡ cài đặt chúng: {0} Loại trừ các tệp trực tuyến khỏi quá trình quét Các tệp được lưu trữ trên bộ nhớ đám mây sẽ không được quét và nhập nếu không có sẵn cục bộ. Chỉ hỗ trợ cho: Google Drive, DropBox, OneDrive Quét nhưng sử dụng phương pháp đơn giản hóa mà không có nội dung tệp Các tệp sẽ được nhập nhưng sử dụng phương pháp kém chính xác hơn, không yêu cầu nội dung tệp được tải xuống và hiển thị cục bộ. Áp dụng cho tất cả Ghi đè trạng thái cài đặt Khi được gán, Playnite sẽ bỏ qua trạng thái cài đặt (bao gồm thư mục cài đặt) được cấu hình bởi plugin nhúng phụ trách nhập game này. Lựa chọn này có thể không hoạt động tốt đối với những plugin sử dụng phương thức nhập game một cách cụ thể, trừ khi chúng có thể áp dụng lựa chọn ghi đè. Chỉ chạy thủ công Mỗi ngày một lần Mỗi tuần một lần Vào mỗi lần khởi động Kiểm tra cập nhật cho chương trình Kiểm tra cập nhật cho add-on Cập nhật thư viện Quét các thư mục giả lập Bao gồm cả game đã ẩn Sửa trường Chọn / Bỏ chọn tất cả Mở Kích hoạt Gán Bắt đầu gõ để tìm game… [F1] để được trợ giúp Bắt đầu với # sẽ hiện danh sách những mã lệnh có sẵn. Bắt đầu với / sẽ hiện danh sách những dịch vụ tìm kiếm/plugins. Gõ từ khóa tìm kiếm kết thúc bằng phím cách sẽ chuyển tới kết quả tìm kiếm ngay lập tức. TAB: chuyển hành động ENTER: kích hoạt hành động đang chọn SHIFT-ENTER: mở menu ngữ cảnh Bao gồm cả game đã gỡ cài đặt Bao gồm cả game đã ẩn Bao gồm cả những game đã gỡ bỏ Loại trừ những game đỡ bỏ Bao gồm cả game đã ẩn Loại trừ game đã ẩn. Chơi hay Cài đặt Tới mục chi tiết Menu game Chỉnh sửa game Mở tìm kiếm Hộp tìm kiếm Nút tìm kiếm Hành động chính Hành động thứ cấp CTRL-F mở hộp thoại tìm tổng thể thay vì hộp tìm kiếm Lưu thiết lập của bộ lọc game giữa các tiến trình tìm kiếm Tìm nhà cung cấp Từ khóa mặc định Tùy chỉnh từ khóa Phím tắt toàn hệ thống Tìm kiếm Playnite Cài đặt Phần mở rộng Loại trừ Bỏ qua thư mục tương tự khi quét Bỏ qua thư mục tương tự khi quét Thêm file vào danh sách loại trừ Thêm thư mục vào dach sách loại trừ Chỉ có thể thêm loại trừ vào những cài đặt đã lưu của trình quét. Những loại trừ đã được thêm vào "{0}" trình quét. Ghi đè nền tảng Khi được chọn, trình quét sẽ gán nền tảng này tới tất cả game, ghi đè lên mọi nền tảng nhận biết tự động. Bao gồm cả mã lệnh trong tìm kiếm mặc định Khi bị tắt, câu lệnh sẽ không hiển thị ở mục tìm kiếm mặc định cho đến khi # được sử dụng. Sử dụng tìm kiếm xấp xỉ khi lọc tên Khi được bật, bộ lọc tên sẽ khớp với tên của game giống như phương thức tìm kiếm toàn bộ. Bộ lọc chặt chẽ có thể được bật đối với những trường hợp riêng bằng cách dùng tiền tố ! khi lọc. Trường hiển thị với kết quả game Trạng thái Ẩn Sao lưu dữ liệu bị hủy. Sao lưu dữ liệu thất bại. Lỗi sao lưu dữ liệu Đang sao lưu dữ liệu… Khôi phục dữ liệu từ bản sao lưu... Không thể khôi phục dữ liệu từ bản sao lưu. Cài đặt Thư viện game Phương tiện thư viện game Các phần mở rộng đã cài đặt Dữ liệu Tiện ích mở rộng Các chủ đề đã cài đặt Lựa chọn dữ liệu để khôi phục từ tập tin sao lưu. Playnite sẽ tự động khởi động lại để bắt đầu quá trình khôi phục sao lưu Lựa chọn các mục được đi kèm với sao lưu dữ liệu. Các cài đặt của ứng dụng và dữ liệu thư viện game được bao gồm như mặc định. Playnite sẽ tự động khởi động lại để bắt đầu quá trình sao lưu dữ liệu. Tự động sao lưu dữ liệu Tần suất tự động sao lưu Thư mục sao lưu Sao lưu xoay chiều Bao gồm những dữ liệu bổ sung: Thư mục sao lưu cần được chỉ định khi bật tự động sao lưu. Chỉ hiện thông báo cập nhật đối với các bản vá Khi được bật, chỉ những cập nhật sẵn có cho những bản vá đã cài đặt được hiển thị. Những bản vá mới sẽ không hiển thị ở thông báo cập nhật. Sử dụng ngày tương đồng đối với tuần trước Sử dụng ngày tương đồng ở định dạng "Hôm nay", "Ngày hôm qua",...v.... nếu như mốc thời gian ngắn hơn một tuần. Định dạng ngày cụ thể sẽ được sử dụng đối với những ngày còn lại. Tìm kiếm hình ảnh trên web Chuỗi tìm kiếm ảnh icon Chuỗi tìm kiếm ảnh cover Chuỗi tìm kiếm ảnh nền Đang tải thông tin add-on… Không có nguồn dữ liệu khả dụng Khởi chạy cài đặt của hành động Sử dụng thiết lập của trình quét Chọn hồ sơ lúc khởi động Chọn trình giả lập lúc khởi động Tự động Luôn bật Luôn tắt Hỗ trợ Trợ năng (đọc màn hình) Menu ứng dụng Menu game Thư mục chương trình Thư mục dữ liệu người dùng Đã phát hiện file thư viện bị hư hại, sẽ thoát Playnite. Mở issue mới trên trang GitHub của Playnite để đề xuất sửa file bị hư hại của bạn. Bạn có muốn lưu các thay đổi không? Trình cài đặt di động Không phát hiện được thiết bị điều khiển nào ================================================ FILE: source/Playnite/Localization/zh_CN.xaml ================================================  中文(简体) Playnite语言 退出 筛选器激活 筛选器停用 额外筛选器 筛选 筛选 无效数据 保存更改? 主页位于 www.playnite.link 源代码位于 GitHub 创建诊断数据包 发送诊断信息 关于 Playnite 由 Josef Němec 制作 指定分类 设置分类 添加分类 已勾选 - 指定分类 不勾选 - 移除分类 不决定 - 不做更改(编辑多个游戏时) 没有分类 没有平台 糟糕,出错了! 发生了一个无法恢复的错误。 如果您想要协助我们修复这个问题,请简要描述在发生这个错误之前您做过的行为,并发送诊断信息。如果您现在有连接网络,诊断数据包将被上传至 Playnite 服务器以供分析。 或者,您可以点击“报告崩溃”按钮在 Github 上手动创建一个问题(issue)来反馈这次崩溃。 感谢您的帮助。 扩展“{0}”导致了一个无法恢复的错误。 我们建议保存日志文件并向扩展开发者报告该问题。若问题仍然发生,请禁用该扩展。 扩展“{0}”导致了一个无法恢复的错误。 我们建议向扩展开发者报告该问题。若问题仍然发生,请禁用该扩展。 未知的扩展或主题导致了一个无法恢复的错误。 我们建议禁用第三方附加组件,隔离其中导致问题的附加组件并向其开发者报告该问题。 发生了不可恢复的错误。 如果您想帮助我们解决此问题,请向我们发送诊断信息。 谢谢! 停用扩展 保存记录文件 发送诊断信息 报告崩溃 重开 Playnite 以安全模式重开 停用所有第三方扩展并使用默认主题。 退出Playnite 崩溃前做过的行为(请用英语): 库管理器 删除游戏? 无法移除 - 游戏或安装程序正在运行。 无法卸载 - 游戏正在运行。 您确定要删除 {0} 吗? 您确定要移除这 {0} 个游戏吗? 您确定要移除 {0} 吗? 选择“加入排除列表”选项可防止本游戏在下次更新库时被再次导入。 您确定要移除 {0} 个游戏吗? 选择“加入排除列表”选项可防止这些游戏在下次更新库时被再次导入。 您确定要移除这 {0} 个目前未被使用的条目吗? 没有找到未使用的字段。 是(加入排除列表) 此部分上有未保存的更改 正在更新游戏库格式… 数据库更新失败。 无法更新游戏库。需要{0}MB空余空间。 游戏错误 无法启动游戏,在数据库中找不到"{0}"。 无法启动游戏:{0} 无法启动指令:{0} 无法打开游戏位置:{0} 无法检测游戏占用空间: {0} 占用空间扫描错误 占用空间扫描时有 {0} 个错误 创建快捷方式失败:{0} 打开手册失败:{0} 无法安装游戏:{0} 无法卸载游戏:{0} 未找到有效的游戏启动指令。在使用模拟器指令时,请确保平台定义同时匹配游戏和模拟器的配置。 安装流程不可用。 未安装或启用负责此游戏的库插件。 官方资料数据下载不可用。 没有选择游戏。 游戏脚本执行失败。 应用程序脚本执行失败。 全局脚本执行失败。 模拟器脚本执行失败。 游玩脚本指令执行失败。 PowerShell 3.0 或更新版本未安装。 无法确定如何启动游戏。 启用 已停用 移除 移除未使用 重命名 复制 添加 默认图标 默认封面图像 默认背景图像 完成 下一步 上一步 完 成 返 回 清 除 清除 忽略 忽略全部 导入 名称 作者 组件 系列 版本 上次游玩 最常游玩 游玩次数 占用空间 文件夹 备注 添加日期 添加日期 修改日期 修改日期 网站 路径 好的 保存 关闭 取消 确认 重置 欢迎 本地用户 通用 媒体 链接 安装 指令 正在下载… 正在下载媒体… 正在加载… 类型 配置文件 配置文件 移除 下载 搜索 分辨率: 任何 缩放 列表视图 封面 网格视图 详情视图 自定义 网址 特别致谢 许可协议 贡献者 正在退出 Playnite… 今天 昨天 星期一 星期二 星期三 星期四 星期五 星期六 星期日 过去一周 过去一个月 过去一年 超过一年以前 0 到 100MB 100MB 到 1GB 1GB 到 5GB 5GB 到 10GB 10GB 到 20GB 20GB 到 40GB 40GB 到 100GB 100GB 或以上 导入已成功完成。 所有游戏 游戏 ID 数据库 ID 预设 无法通过游玩指令获取图标。不存在类型为文件的指令。 仅下载缺失的资料数据 启用该选项会跳过已经含有信息的数据字段的资料数据下载。 游戏选择 请选择要使用新资料数据更新资料的游戏: 数据库中的所有游戏 所有当前筛选出的游戏 仅所选游戏 未选择任何元数据字段 未选择任何元数据字段进行下载。请至少选择一个,并为其启用至少一个元数据提供程序。 官方商店 IGDB 请指定通过 Playnite 自动填充的字段,以及用于获取数据的源。 请您考虑点击上面的徽标,访问 igdb.com 数据库并协助更新信息,以帮助 Playnite 使用更完善的数据。 正在下载资料数据… 正在导入已安装的游戏… 正在导入 {0} 的游戏… 正在从 {0} 导入模拟端游戏… 正在下载库的更新… 正在扫描库中的游戏大小… 正在扫描导入的游戏的大小…… 库更新完成 正在释放资源… 配置 设置… 平台和模拟器 配置模拟器… 库管理器… 工具 下载资料数据… 软件工具… 配置集成… 打开第三方客户端 第三方客户端 更新游戏库 取消库更新 更新模拟端游戏文件夹 添加游戏 手动… 自动扫描… 模拟端游戏… Microsoft Store 应用程序… 关于 Playnite 发送反馈 切换至全屏模式 链接 帮助 在 Patreon 上支持我们 在 Ko-Fi 上支持我们 用户手册 SDK文档 重启系统 关闭系统 挂起系统 休眠系统 锁定系统 登出用户 挑选一个随机游戏 要在详情面板上显示的游戏字段: 项目间隔 绘制网格项目背景 网格项目边框宽度 缺少游戏图标源 缺少游戏封面源 缺少游戏背景源 到游戏详情的竖直间距 网格视图详情位置 详情视图游戏列表位置 在面板之间绘制分隔线 游戏封面图像高度 游戏列表图标高度 应用程序字体 等宽字体 筛选器面板位置 导航面板位置 封面艺术渲染 目标长宽比 以下选项也会影响全屏模式的图块渲染! 拉伸模式 DVD 盒 Epic 游戏商城 GOG Galaxy 2.0 IGDB 矩形 Steam 横幅 Steam 纵向封面 Twitch * 需要重开以应用 设置 通用 顶部面板 外观 游戏详情 布局 高级 全屏 输入 性能 资料数据 更新 搜索 备份 备份库数据 恢复数据备份 自动导入游戏库中的更改 无效的数据库文件位置,必须设置正确的文件路径。 账户名称不能为空。 导入游戏后下载资料数据 启动 Playnite 时最小化 随电脑开机启动 Playnite 启动时关闭至系统托盘 无法注册 Playnite 为开机启动项。 以全屏模式启动 异步图像正在加载 以较慢的图像加载时间换取较好的游戏列表滚动流畅度。 封面艺术缺失时显示游戏名 在网格视图中显示游戏名称 暗色显示未安装的游戏 在详情视图的列表上显示游戏图标 在分组描述中显示项目数量 在筛选器面板和导航面板上仅显示指定的字段 禁用硬件加速 可用于出现卡顿等界面问题的情形 在快速启动列表中显示隐藏游戏 影响任务栏图标的快捷菜单和托盘图标的菜单。 快速启动列表中项目数量 使用游戏背景图像作为窗口背景 模糊背景 高品质 暗背景 在网格视图中显示 主题 主题简介 全屏主题 全屏主题简介 数据库位置 登录状态: Playnite 设置 清除网页缓存 可能解决链接账户时遇到的问题。 显示系统托盘图标 最小化 Playnite 到系统托盘 当 Playnite 窗口被关闭时最小化到系统托盘 游戏启动时: 游戏结束后: 按游玩天数显示游玩时间 日期格式: 这将使您从所有已链接的服务中登出。应用程序需要重开,您想继续吗? 清除缓存? Playnite 需要重开以应用新主题 获取更多主题 创建新的主题 获取更多扩展 创建新扩展 帮助我们翻译 Playnite Playnite 需要重开来应用新的设置。现在重开吗? 重开将取消当前所有进行中的任务(例如下载)。 重新启动Playnite? Playnite 不能自动移动您的库文件。您必须在更改位置之前手动移动/复制这些文件。 若目标位置中没有库,则将创建一个新库。 新的数据库位置会在 Playnite 重开时启用。 若设置了“关闭”指令,游玩时间将不会被记录。 行数 列数 详情视图行数 在主界面显示背景图像 不重新下载资料数据,就不会追溯应用于已有游戏。 导入库中游戏的游玩时间: 配置 Playnite 应何时向 Playnite 数据库导入由库插件报告的游玩时间。需要库插件支持接管对游戏的处理,才能使用该特性。 总是:同时为 Playnite 数据库中新导入与已存在的游戏导入游玩时间。 仅新导入:仅为新导入的游戏导入游玩时间。 从不:无论何种情形都不导入游玩时间。 总是 仅新导入 从未 在桌面模式下启用控制器支持 导航按钮打开全屏模式 为新导入的游戏自动下载资料数据。 目标显示器 总是使用主显示器 显示游戏标题 显示电池状态 显示电量百分比 显示时钟 隐藏鼠标光标 快速筛选器中仅显示已安装 按钮提示 布局 水平滚动 选择其中一个子选项: 没有可用的设置 加载设置失败 对库中的每个游戏,这些脚本都会被执行。可在编辑游戏详情时为每个游戏指定单独的脚本。 切换背景图像时使用动效 字体大小 自动 锯齿化 灰度 ClearType Ideal(理想化) Display - GDI 兼容 文本格式化模式 文本渲染模式 文字渲染和格式化方法当前不会应用于游戏描述。 预加载背景图像 如果启用该选项,Playnite会在下载资料数据时下载背景图像,这会使用较多磁盘空间,背景图像在离线时可以显示。 如果停用该选项,背景图像仅在首次需要时下载,这会使用较少磁盘空间,但是可能导致背景图像显示延迟,并且某些背景图像在离线时不可以显示。 退出游戏后自动关闭第三方客户端 客户端延迟关闭时间(秒) 游玩时间短于该时间时不关闭(单位:秒) 自动关闭以下客户端: 自动关闭客户端 导入排除列表 当游戏媒体数据过大的时候显示警告 目录打开命令 首选游戏年龄评级机构 在更新库时更新游戏的占用空间 扫描并更新游戏的占用空间,如果检测到自上次扫描以来他们的文件已被修改 填充 均匀 均匀填充 导入错误 需要身份验证 身份验证失败 备选 WebView 渲染模式 可用于 WebView (如集成的身份验证对话框)遇到问题的情形。 部分加载过大的游戏描述 过大的描述可能会在选择游戏时造成明显的延迟。 启用时,最初只会加载部分描述文本,并可选择按需加载其余部分。 资料数据导入 下载资料数据 设置选中的配置用于任何未来的资料数据下载。 也可以在应用程序设置中修改。 模拟器导入向导 这个向导会引导您下载并导入游戏机模拟器,以及导入模拟端游戏。 记住您随时可以在主菜单添加另外的模拟器或游戏(在“库”菜单找到“配置模拟器…”,在“添加游戏”菜单添加模拟端游戏)。 以下是 Playnite 可识别和自动配置的模拟器列表。您可以前往他们的网站下载模拟器安装程序并手动安装。完成后,您可以在模拟器 配置对话框中导入它们。 您可以通过点击“从文件夹自动检测…”按钮来导入您的 PC 上已安装的任何模拟器。Playnite 将在所选文件夹中搜索任何已知的模拟器,并提供选项来导入它们。您可以多次使用该按钮从多个文件夹导入。模拟器将被添加到当前列表的底部。 您可以通过点击“扫描模拟器文件夹”按钮导入游戏。选择适当的模拟器将告诉 Playnite 哪些文件类型应该被扫描和导入。您可以多次使用该按钮从多个文件夹导入。游戏将被添加到当前列表的底部。 未选择要导入的模拟器。若不先配置模拟器,则无法自动导入任何模拟端游戏。您确定要继续并退出导入流程吗? Playnite 中没有已配置的模拟器。若不先配置模拟器并选择适当的文件类型,则无法导入游戏。您想现在添加一些模拟器吗? 扫描模拟器文件夹 选择文件 从文件夹自动检测… 配置模拟器… 正在扫描… 正在扫描 {0}… 首次配置 该向导将引导您完成自动导入和配置外部游戏库的流程。Playnite 可以自动从 Steam 或 GOG 等多种游戏服务导入游戏。 注意,您也可以通过主菜单手动添加任一平台的自定义或模拟端的游戏。 库集成 以下是一些Playnite支持集成库的列表。 请选择要安装的。 稍后可以从“插件”菜单中安装更多的集成。 配置完成 初始设置已经完成。请记住您稍后可从主菜单更改所有设置以及新增另外的集成。 一个或多个扩展下载失败。 您可以尝试先等运行向导完成,之后再从附加组件菜单中重新下载集成。 正在下载 {0} 集成… 正在下载推荐集成列表… 无法下载推荐集成列表。您可以稍后通过附加组件菜单尝试重新下载集成。 配置平台和模拟器 配置模拟器 平台 设备平台 模拟器 模拟器 添加平台 选择图标 选择封面 选择图像 选择项目 选择背景 选择文件 选择网址 添加模拟器 支持的平台 你想保存平台设置吗? 您想保存模拟器设置吗? 可执行文件 参数 工作目录 支持的文件类型 导入模拟器… 下载模拟器… 从已知的模拟器配置文件加载参数预设 您确定要移除模拟器“{0}”吗? 它目前正在被 {1} 个游戏使用。 您确定要移除平台“{0}”吗? 它目前正在被 {1} 个游戏和 {2} 个模拟器使用。 设置帮助 排序方式 排序方向 分组方式 升序 降序 不分组 按库分组 按分类分组 按平台分组 查看类型 查看 导航面板 筛选器面板 图标 游戏库图标 封面图像 背景图像 排序名称 手册 名称 安装盘 账户名称 平台 分类 流派 发行日期 发行年份 开发者 标签 发行机构 安装状态 匹配所有筛选器 若启用,仅将满足所有筛选项的游戏包含在视图中。 若禁用,将满足任一筛选项的游戏都包含在视图中。 已安装 已安装 未安装 隐藏 收藏 启用 HDR 支持 如果启用,HDR 将在开始游戏前在主显示器上启用。 请注意,您的主显示器不支持 HDR。 上次游玩 分类 描述 安装目录 封面图像 链接 图像、ROM 或 ISO 路径 流派 流派 公司 公司 开发者 开发者 发行商 发行机构 分类 分类 标签 标签 功能特色 功能特色 年龄分级 年龄分级 地区 国家 来源 来源 最近动态 数据库错误 无法打开库的数据库。 数据库未打开 无法访问库的数据库。文件“{0}”正在被另一个进程使用,或者它位于不可访问的位置。 创建诊断数据包失败。 无法自动上传诊断数据包。 诊断信息发送成功。 诊断数据包创建并提交成功。 请在您的问题(issue)报告中附上以下 ID: 从 {0} 导入游戏失败。 从 {0} 导入模拟端游戏失败。 无法通过选定的模拟器配置文件搜索游戏。 配置文件不包含任何文件扩展名或平台 Playnite 未能启动。请关闭其他所有运行实例再重试。 无法应用主题“{0}”,颜色配置文件“{1}” {2} 无法打开链接,网址格式无效。 无法启动该应用程序。 无法初始化 web view 组件。Playnite 无法继续启动。 更多信息参见 https://playnite.link/cefstartup 未能导入模拟器,因为定义文件缺失或损坏。 执行菜单动作失败。 编辑游戏详情 图像网址 添加链接 添加 ROM 保存更改 将字段更改应用于正在编辑的游戏。 添加指令 移除 移除游玩指令 添加游戏 扫描文件夹… 探测已安装 浏览… 打开 Playnite 配置文件设置 游戏名称不能为空。 游戏指令追踪目录不能为空。 搜索资料数据时,游戏名称不能为空。 游戏数据无效 输入以 http:// 或 https:// 开头的有效网址 选择网址 下载资料数据失败:{0} 下载错误 清除筛选器 私密账户 公开账户 API 密匙 启动错误 主题错误 全部清除 正在安装 正在卸载 正在启动 正在运行 无效的网址 什么也不做 最小化 恢复窗口 仅当从界面启动时还原窗口 关闭 更改 高级 从未 完成状态 完成状态 用户评分 媒体评分 社区评分 游戏脚本 应用程序脚本 脚本 插件 资料数据来源 扩展 扩展 ID 重载脚本 交互式 SDK PowerShell 已成功重载所有脚本 未找到符合指定搜索/筛选条件的游戏 未找到项目 切换至桌面模式 退出 Playnite 全部更新 作者: 版本: 已更新: 模块: 统计 全部 通知 宽度 高度 大小 标准 更大 最大 默认 选择 全选 取消全选 首项 随机 由用户选择 加载更多 透明 折叠 展开 全部折叠 全部展开 其他 主题 模拟器参数 内置参数 自定义参数 额外模拟器参数 覆盖模拟器参数 作为游玩指令,而非菜单指令 选择要导入的资料数据 选择要导入的游戏 资料数据搜索 有可用的更新 上次更新以来的变化 下载并安装更新 检查更新 更新错误 检查新版本失败。 未发现新版本,您已在最新版。 下载并安装更新失败。 有后台任务正在运行。您想要取消它并继续更新吗? 有后台任务正在运行。您想要取消它并退出 Playnite 吗? 有后台任务正在运行。切换模式将取消该任务,您仍然要切换吗? Playnite 有一个可用的更新 重载主题列表 应用所选主题 监视文件更改 源文件更改时自动应用主题 脚本运行时阶段 启动游戏前执行 退出游戏后执行 启动游戏后执行 应用程序启动时执行 应用程序关闭时执行 游戏启动前脚本 游戏启动后脚本 游戏停止后脚本 执行全局脚本 全局 已筛选 当前 新的 测试脚本 仅显示所选项目。 保存为默认 添加至收藏 从收藏中删除 隐藏此游戏 从隐藏中移除 启用 HDR 支持 禁用 HDR 支持 编辑… 计算占用空间 计算占用空间 (所有游戏) 计算占用空间 (仅缺少的数据) 占用空间 设置分类… 设置完成状态 移除 游玩 安装 游戏选项 详情 卸载 打开安装位置 创建桌面快捷方式 打开手册 更多 由库插件管理 游戏的启动过程将由负责此游戏的库插件管理。 在指定页面找不到游戏“{0}”的相关信息。 提示:你可以在编辑单个游戏时通过“编辑”菜单选项使用更多高级资料数据下载过程。 某些行动正在进行时不可用。 描述文本支持 HTML 语法 游玩时间按秒记录。 占用空间以字节表示。 发布日期必须以“年-月-日”格式设置。月和日的值可以省略。 分值范围为 0 至 100,留空为不评分。 Playnite 的开发得到了这些 Patreon 和 Ko-fi 赞助者的支持: 代码、本地化和其他贡献者(排名不分先后): 取消游戏监控? 正在监测安装状态。您是否要取消监测,并将游戏返回到之前的状态? 正在监测运行状态。您是否要取消监测,并将游戏返回到之前的状态? 游玩时间 最后游玩时间 {0}天{1}小时{2}分 {0} 小时 {1} 分钟 {0} 分钟 {0} 秒 没有玩过 正在打开桌面模式… 正在打开全屏模式… 正在加载游戏库… 正在计算占用空间… 正在计算 {0} 的占用空间… 无法安装脚本文件。 脚本安装成功。 安装脚本 脚本错误 无法执行扩展功能。 打开资料数据文件夹 计算 如果游戏有 ROM ,则使用 ROM 自动计算占用空间,否则使用设置的安装目录自动计算占用空间 {0} 客户端未安装。 {0} 客户端即将打开。请在登录后关闭此消息。 正在等待用户登录,请您在完成后关闭此消息… 未找到游戏的安装目录 无效的游戏指令配置。 账户同步疑难解答 疑难解答 重命名项目 添加新项目 输入名称 输入新名称 少于 1 小时 1 至 10 小时 10 至 100 小时 100 至 500 小时 500 至 1000 小时 超过 1000 小时 Playnite 必须重开以完成安装。您想要现在重开吗? 扩展未被正确打包。 主题未被正确打包。 扩展“{0}”载入失败。 无法加载扩展“{0}”,当前版本的 Playnite 不支持。 主题“{0}”载入失败。 无法加载主题“{0}”,当前版本的 Playnite 不支持。 扩展无法正确载入。 主题载入失败。 主题或扩展使用了不受支持的 API 版本。 安装成功。 安装附加组件? 通用 “{0}”附加组件安装失败。 安装扩展失败。 {0} 您想安装这个新扩展吗? {0} 来自 {1} 版本 {2} 您想更新扩展“{0}”吗? 当前版本:{1} 新版本:{2} 安装主题失败。 {0} 您想安装这个新主题吗? {0} 来自 {1} 版本 {2} 您想要更新主题“{0}”吗? 当前版本:{1} 新版本:{2} 您即将离开 Playnite 并使用您的默认网页浏览器来浏览以下网页。您想要继续吗? {0} 所选图像可能过大并导致无法达到最佳性能。使用非常大的图像可能导致界面卡顿并增加内存占用。 建议的最大分辨率: 图标: {0} 百万像素 封面: {1} 百万像素 背景: {2} 百万像素 性能警告 不再显示 不兼容扩展名为 {0} 的文件。 文件扩展名不兼容 所选图像文件过大,可能会影响最佳性能。 您确定要卸载选中的主题吗?卸载将被安排至下次程序启动时。 内置主题不能被卸载。 这个主题不支持当前版本的 Playnite。 您确定要卸载选中的扩展吗?卸载将被安排至下次程序启动时。 内置扩展不能被卸载。 这个扩展不支持当前版本的 Playnite。 安装目录 数据目录 正在生成诊断数据包… 正在上传诊断数据包… 导入文件… 这是什么? 您确定要这样做吗? 总游玩时间 平均游玩时间 最多游玩时间 总占用空间 总览 侧边栏 在侧边栏显示 重置设置 所有应用程序设置将重置为默认值,除了: -数据库位置 -导入排除列表 -扩展设置,包括库集成 完成此流程需要重开应用程序。您想重置设置吗? 面向开发者 外部扩展 输入完整的文件夹路径。 成就 论坛 新闻 商店页面 初始化设置未完成。Playnite 将重开到桌面模式以完成相关步骤。 最近玩过 收藏 最常游玩 所有 已应用筛选器。 已应用额外筛选器。 搜索结果: 已存在同名项目。 以当前筛选器限制选择范围 选择另一个 附加组件… 已安装 扩展设置 浏览 更新 更新({0}) 对已安装的扩展和主题的管理,包括它们的设置,已被移到一个新的“附加组件”菜单。 可以在此处配置所有当前安装的库集成扩展。 若您想安装或卸载另外的集成,请使用主菜单中的“附加组件”选项。 桌面模式主题 全屏模式主题 正在搜索… 附加组件与此版本 Playnite 不兼容。 无法下载附加组件安装包。 无法下载附加组件的 manifest 文件。 更改将在应用程序重开后生效。 该附加组件已被计划安装。 安装 重新安装 卸载 已安裝 未发现新的附加组件更新。 更新附加组件 更新日志不可用 已计划安装 下载失败 许可证被拒绝 正在下载 {0}… 寻找附加组件更新 正在查找程序更新… 有一个或多个附加组件更新可用。 选择项目以更新 扩展开发实例 {0} 许可协议 接受 拒绝 启用库集成的游玩指令 选择指令 追踪模式 追踪路径 初始追踪延迟 追踪频率 链接 文件 模拟器 脚本 默认 进程 文件夹 原进程 进程名称 记录追踪消息 以下变更将覆盖所有当前所选游戏的数据! 均匀 仅项目 仅开始和结束 滚动灵敏度 平滑滚动 动效速度 移除项目? 您确定要移除这个项目吗? 在顶部面板上显示按钮: 通用视图设置 分组设置 排序设置 筛选器预设 插件项目位置 分区间隔宽度 将主菜单按钮置于侧边栏 导航面板 随机游戏选择器 查看随机游戏选择器 从当前视图中选择随机游戏 保存分组和排序设置 在全屏模式下显示为快速筛选器 过去 7 天 过去 31 天 过去 365 天 超过365天前 配置 保存预设​​​​​​​ 启动游戏后最小化 在游戏开始后最小化 Playnite。 禁用此项可能会导致游戏在启动时无法获得输入焦点! 字体大小 小字体 启用游戏控制器API支持 游戏控制器支持 如果禁用,Playnite 将不接受任何游戏控制器输入。 如果您使用了将游戏控制器输入转换为鼠标/键盘输入的工具,且在 Playnite 中出现了重复输入的情况,禁用此选项。 在主菜单上显示项目: 反转 X/A 主视图按钮绑定 互换这两个按钮的绑定功能:“启动游戏”与“在主视图上显示游戏详情页面”。 交换确认/取消按钮绑定 反转 A/B 按钮绑定的确认和取消。 仅主控制器 启用时,仅接受主控制器的输入。 向导按钮聚焦 Playnite 界面音量 背景音量 在后台时静音 未能初始化音频接口。 输出API 用于音频输出的 API。若您遇到音频问题,请更改。 通用 画面 音频 布局 菜单 输入 {0}正在启动… {0}正在运行… 大写 空格 图像渲染缩放器 备选 均衡 质量 质量: 最佳图像质量,慢速,高内存占用。 均衡: 良好图像质量,快速,低内存占用。 备选: 更优图像质量,中速,低内存占用。 选择文件… 选择文件夹… 启动脚本 请注意,扩展和主题可能极大地影响 Playnite 的性能、稳定性和安全性。 若您在安装一个主题或扩展后开始遇到某些问题,请尝试先禁用或卸载它们,以观察它们是否是该问题的根源。 启动时选择 启动时选择 内置配置文件 内置配置文件 自定义配置文件 自定义配置文件 由内置脚本控制 模拟器规格 平台格式 地区格式 启动模拟器前执行 启动模拟器后执行 退出模拟器后执行 找不到模拟器可执行文件。 找不到模拟器规格。 找不到模拟器启动脚本。 拆分游戏 合并为一个游戏 设置平台 设置地区 扫描文件夹 扫描配置 不对特定模型校验扫描 匹配特定模型的文件不会被扫描校验,而是通过文件名进行匹配。更多信息请参见模拟器帮助页面。 通过模拟器扫描 在保存新配置时必须设置名称。 未设置模拟器或模拟器配置文件。 要扫描的目录未指定或不存在。 扫描配置未正确设置。 包含在批量扫描自动扫描中 无法扫描模拟器文件夹。 无法扫描模拟端游戏文件夹。 隐藏已导入内容 要导入的配置文件: 自动扫描配置 保存为自动扫描配置 保存配置以用于之后的库更新。保存的配置可以通过“配置模拟器”菜单管理。 使用相对路径导入 若可能,使用相对于 Playnite 安装文件夹或模拟器安装文件夹的路径导入游戏文件。 扫描子文件夹 扫描压缩包内部 合并相关文件 将相关的游戏文件(例如单独的游戏光盘)合并在一个游戏条目下。 添加扫描器 添加已保存的扫描器 开始扫描 添加模拟器的扫描配置以扫描特定文件夹。请将正确配置模拟器(通过库 -> 配置模拟器菜单)作为导入游戏的前提。 为新添加游戏指定的默认状态 已指定首次游玩的游戏状态 无法初始化 PowerShell 脚本运行时。如果您是 Windows 7 用户,尝试安装或重装 PowerShell 5.1 以解决这个问题。 已存在指定名称的筛选器预设。使用新的设置更新预设吗? 自动填写批量添加或者修改后游戏缺失的排序名称 当您编辑游戏时,通过库更新,模拟器筛选器扫描或一般筛选器扫描添加游戏,自动填写“排序名称”字段以便更好进行游戏名的排序。 例如,“The Witcher 3”将得到“Witcher 03”的排序名称。 这不会设置一个与游戏名称没有不同的排序名称,而且只会自动更新空的排序名称。 将在自动填充的排序名称的开头删除这些词: 就排序目的,利用它忽略掉字符串开头的一些字词。默认值为“The”、“An”和“A”。 填充游戏的为空的排序名称字段 排序 填写排序名称值… 已检测到 Nahimic 服务正在您的系统上运行。已知此服务会导致 Playnite(和其它应用程序)出现渲染问题。 若您在Playnite中遇到任何图形损坏或其它渲染问题,我们建议禁用或完全卸载 Nahimic 服务。 更多信息请访问 https://playnite.link/nahimicsucks Playnite 正在以高权限(作为管理员)运行。不推荐这样做,因为这将为所有安装的扩展与所有从 Playnite 启动的游戏/应用程序(如客户端等)赋予高权限! 更多信息参见 https://playnite.link/adminfaq 当 Playnite 以高权限运行时显示警告 计算游戏大小时获取在驱动器上实际占用的空间 如果启用,扫描速度较慢,将获取文件在驱动器上实际占用的空间。 如果禁用,扫描速度较快,将使用文件本身的大小。 以下附加组件已因潜在问题被报告,例如严重影响稳定性/性能或安全问题。我们强烈建议您卸载它们: {0} 扫描时排除在线文件 云端存储且本地不可用的文件不会被扫描和导入。 仅支持:Google Drive、DropBox、OneDrive 使用忽略文件内容的简单方法扫描 使用不严格的方法导入文件,无须下载文件内容到本地。 应用到全部 覆盖安装状态 设置时,Playnite 将忽略导入此游戏的集成插件设置的安装状态(包括安装目录)。 此选项可能无法完全适用于使用特定游戏导入方法的插件,除非它们也考虑到此覆盖选项。 仅手动 每天一次 每周一次 每次启动时 检查程序更新 检查附加组件更新 更新库 扫描模拟器文件夹 包含隐藏游戏 编辑字段 全选/全不选 打开 激活 指定 开始输入以搜索游戏… [F1] 获取帮助 以 # 开头展示可用命令列表。 以 / 开头展示可用搜索提供者/插件。 输入搜索关键字并以空格作结,将立即切换到该搜索。 TAB:切换指令 ENTER:激活所选指令 SHIFT-ENTER:打开项目菜单 包含未安装游戏 包含隐藏游戏 已包含未安装游戏 已排除未安装游戏 已包含隐藏游戏 已排除隐藏游戏 游玩或安装 转至详情 游戏菜单 编辑游戏 打开搜索 搜索框 搜索按钮 主指令 次指令 CTRL-F 打开全局搜索,而不是聚焦搜索框 在搜索会话之间保存游戏筛选设置 搜索供应者 默认关键字 自定义关键字 系统级快捷键 Playnite 搜索 扩展设置 排除项 已排除扫描文件夹的相关文件 已排除扫描文件夹的相关文件夹 添加文件到排除列表 添加文件夹到排除列表 排除项只能添加到已保存的扫描器配置中。 已添加排除项到扫描器"{0}"中。 覆盖平台 设置时,扫描器将为所有游戏指定此平台,覆盖任何自动检测到的平台。 在默认搜索中包含命令 禁用时,除非使用 # 前缀,否则在默认搜索中不包含命令。 在名称过滤器中使用模糊匹配 启用时,名称过滤器将以与全局搜索相同的方式匹配游戏名称。 可以通过在过滤器前加上 ! 字符来对个别情况强制执行严格匹配。 游戏搜索结果中显示的字段: 隐藏状态 数据备份已取消。 数据备份已失败。 数据备份错误 正在进行数据备份… 正在从备份恢复数据… 未能从备份恢复数据。 设置 游戏库 游戏库媒体 已安装扩展 扩展数据 已安装主题 从指定的备份文件中选择要还原的数据。 Playnite 将自动重启以开始备份还原过程。 选择数据备份要包含的项目。默认情况下包含应用程序设置和游戏库数据。 Playnite 将自动重启以开始备份过程。 自动数据备份 自动备份频率 备份文件夹 轮换备份 包含额外数据: 若要启用自动备份,须设置备份文件夹。 仅显示补丁发布通知 启用时,只有当前安装的大版本可用的更新将发出更新通知。 新的大版本不会发出更新通知。 为最近一周使用相对日期 为最近一周使用“今天”、“昨天”等格式的相对日期。 指定的日期格式将用于所有其他日期。 网页图像搜索 图标搜索字符串 封面图像搜索字符串 背景图像搜索字符串 正在获取附加组件信息… 没有可用的资料数据来源 游玩指令设置 使用扫描器设置 启动时选择配置文件 启动时选择模拟器 自动 总是开启 总是关闭 辅助功能 (屏幕阅读器) 支持 应用程序菜单 游戏菜单 程序文件夹 用户数据目录 已检测到库文件损坏,Playnite 现在将关闭。 在 Playnite 的 GitHub 页面创建新的问题(issue),请求修复您文件中的损坏问题。 您想要保存您所作的更改吗? 便携式安装 未检测到控制器 ================================================ FILE: source/Playnite/Localization/zh_TW.xaml ================================================  中文(繁體) 變更語言 結束 過濾器已啟用 過濾器已停用 更多過濾條件 過濾器 過濾器 資料無效 儲存變更? 官方網站 www.playnite.link 在GitHub上檢視原始碼 建立診斷資料包 發送診斷資料 關於Playnite 由Josef Němec製作 指定分類 設定分類 新增分類 勾選 - 指定分類 未勾選 - 移除分類 不確定 - 不更改(當編輯多個遊戲時) 沒有分類 沒有平台 哎喲! 出錯了。 發生無法復原的錯誤。 若您願意協助我們修復此問題,請簡短地描述當機前您所進行的動作並創建診斷包。若您有連網則診斷包將被上傳至Playnite伺服器進行分析。 除此之外,您也可使用"回報當機"按鈕,在Github上回報本次錯誤。 感謝您的協助。 擴充功能 "{0}" 導致無法復原的錯誤 建議您保存記錄檔並向該功能的開發者回報。如果本錯誤持續發生,請關閉該功能。 擴充功能 "{0}" 導致無法復原的錯誤 建議您保存記錄檔並向該功能的開發者回報。如果本錯誤持續發生,請關閉該功能。 有未知的擴充功能或佈景主題造成了無法回復的錯誤。 我們建議先停用所有第三方附加元件,移除造成問題的元件後向該元件開發者回報此錯誤 發生無法復原的錯誤。 如果您願意協助我們解決此問題,請傳送診斷資料報告給我們,謝謝。 停用擴充功能 保存記錄檔 傳送診斷報告 問題回報 重新啟動Playnite 在安全模式下重新啟動 停用所有非官方擴充功能並使用預設佈景主題 結束Playnite 當機之前進行的行為(以英文描述) 資料庫管理員 移除遊戲()? 無法移除 - 遊戲或安裝程式正在執行. 無法移除 - 遊戲正在執行. 你確定要移除{0}嗎? 您確定要移除{0}嗎? 您確定要移除{0}嗎? 點選"加入除外清單"可防止下次遊戲資料庫更新時再度匯入本遊戲。 您確定要移除{0}嗎? 點選"加入除外清單"可防止下次遊戲資料庫更新時再度匯入本遊戲。 你確定要刪除目前非使用中的 {0} 項目? 無未使用的欄位 是(加入除外清單) 這個頁面上有些變更尚未儲存 正在更新遊戲資料庫格式... 數據庫更新失敗 無法更新遊戲資料庫,需要 {0} MBs 的可用空間。 遊戲錯誤 無法開始遊戲。數據庫中找不到'{0}' 。 無法啟動遊戲: {0} 無法進行操作: {0} 無法開啟遊戲路徑: {0} 無法偵測遊戲佔用儲存容量: {0} 佔用儲存容量掃描錯誤 掃描佔用儲存容量期間出現 {0} 個錯誤 無法創建捷徑: {0} 無法開啟說明: {0} 無法安裝遊戲: {0} 無法移除遊戲: {0} 找不到有效的遊戲啟動指令。當你使用模擬器指令的時候,請確認遊戲以及模擬器設定檔之間的平台定義是相互符合的。 無法進行安裝 本遊戲的資料庫外掛程式已被停用或尚未安裝 無法下載官方元資料 未選擇任何項目。 遊戲腳本執行失敗 應用程式腳本執行失敗 全域腳本執行失敗 模擬器腳本執行失敗 遊戲腳本執行失敗 尚未安裝PowerShell 3.0 或更新版本 無法確定遊戲啟動方式 啟用 停用 移除 清除未用的 重新命名 複製 新增 預設圖示 預設封面圖片 預設背景圖片 完成 繼續 返回 完成 返回 清除 清除 關閉 全部關閉 匯入 名稱 作者 模組 系列 版本 最後遊玩 最常遊玩 遊玩次數 安裝大小 資料夾 備註 已新增 新增日期 已修改 修改日期 網站 路徑 確定 儲存 關閉 取消 確認 重置 歡迎 本地使用者 一般 媒體 連結 安裝 動作 下載中.... 下載資料中... 載入中... 類型 基本資料 基本資料 移除 下載 搜尋 解析度: 任意 放大 列表檢視 封面 網格檢視 詳細檢視 自訂 網址 特別感謝 授權 貢獻者 正在退出 Playnite… 今天 昨天 星期一 星期二 星期三 星期四 星期五 星期六 星期日 上週 上個月 去年 超過一年 0 到 100MB 100MB 到 1GB 1GB 到 5GB 5GB 到 10GB 10GB 到 20GB 20GB 到 40GB 40GB 到 100GB 100GB 以上 匯入成功 所有遊戲 遊戲 Id 數據庫ID 預設 無法從開始遊玩動作取得圖示。找不到對應的檔案類型。 只下載缺少的元資料 啟用此選項則已有資料的欄位不會從元資料更新數據。 遊戲選擇 請選擇需要從元資料更新訊息的遊戲: 數據庫中的所有遊戲 所有目前過濾出的遊戲 僅選中的遊戲 未選取任何元資料欄位 未選取任何需進行下載的元資料欄位。請至少選擇一個欄位並啟用至少一個元資料提供者以使用本功能。 官方商店 IGDB 請選擇透過Playnite自動輸入的資料範圍以及資料來源 請點擊上方圖示並協助更新Igdb.com數據庫幫助Playnite提供用戶更詳盡的資訊。 正在下載元資料... 正在匯入已安裝遊戲... 正在匯入 {0} 遊戲中... 正在從{0}...匯入模擬遊戲 正在下載資料庫更新... 正在掃描庫中的遊戲大小… 正在掃描已匯入的遊戲大小… 資料庫更新完成 正在釋放資源... 設定 設定... 平台與模擬器 設定模擬器... 資料庫管理員... 工具 下載元資料... 軟體工具... 設定整合功能... 開啟第三方軟體 第三方登入器 更新遊戲資料庫 取消資料庫更新 更新模擬器資料夾 新增遊戲 手動... 自動掃描... 模擬器遊戲... Microsoft Store 應用程式… 關於Playnite 意見回饋 進入全螢幕模式 連結 說明 在Patreon上支持我們 在 Ko-fi 上支持我們 使用說明 SDK文件 系統重啟 關閉系統 系統睡眠 系統休眠 鎖定系統 登出使用者 選擇隨機遊戲 會顯示在詳細面板上的遊戲資訊: 物件間隔 繪製網格化物件背景 網格化物件邊界寬度 缺少遊戲圖示時從此處抓取 找不到遊戲封面時從此處抓取 找不到遊戲背景圖片時從此處抓取 到遊戲詳細資訊的垂直間距 網格檢視詳細資訊位置 列表檢視遊戲列表位置 在面板間繪製分隔線 遊戲封面圖片高度 遊戲列表圖示高度 程式字體 等寬字體 過濾器面板位置 瀏覽面板位置 封面圖片渲染 目標長寬比 以下選項也會影響全螢幕模式下的渲染! 延展模式 DVD 盒 Epic Games Store GOG Galaxy 2.0 IGDB 方形 Steam 標題 Steam直式封面 Twitch * 需要重新啟動以套用變更 設定 一般 頂部面板 外觀 遊戲詳細資訊 版面配置 進階設定 全螢幕 輸入 性能 元資料 更新 搜尋 備份 備份資料庫檔案 還原備份 自動匯入資料庫的改動 無效的數據庫位置,必須設定正確的文件路徑 帳號名稱不能為空白 匯入遊戲後下載元資料 以最小化狀態啟動Playnite 開機時自動啟動Playnite 啟動後縮小至工具列 無法設定Playnite於開機後自動啟動 以全螢幕模式啟動 不即時載入縮圖 提升瀏覽遊戲列表時的滑鼠滾動順暢度,但圖片讀取速度相對較慢 如無封面圖片則顯示遊戲名稱 在網格檢視中顯示遊戲名稱 以暗色顯示未安裝遊戲 在詳細檢視中顯示遊戲圖示 在分組說明中顯示遊戲數量 在過濾器跟瀏覽面板中只顯示指定欄位 停用硬體加速 可用於改善畫面停頓等界面問題 在快速啟動列表中顯示隱藏遊戲 影響工作列捷徑清單與系統列選單 快速啟動列表中項目數量 使用遊戲背景圖片作為視窗背景 模糊背景 高畫質 背景加黑 在網格檢視中顯示 佈景主題 佈景主題簡介 全螢幕佈景主題 全螢幕佈景主題簡介 數據庫位置 登入狀態: Playnite設定 清除快取 可改善帳號連結問題 顯示系統列圖示 最小化時縮小至系統列 關閉Playnite時將視窗縮小至系統列 當遊戲啟動時: 當遊戲關閉時: 將遊玩時間改以天數顯示 日期格式: 即將登出所有已連結服務並重啟程式,是否繼續? 清除快取? Playnite需要重新啟動以套用新的佈景主題 取得更多佈景主題 建立新的佈景主題 取得更多擴充功能 添增新的附加元件 協助我們翻譯Playnite Playnite需要重新啟動以套用變更,是否重啟? 重新啟動將取消目前所有進行中的任務(例如下載) 重新啟動Playnite? Playnite不會自動搬移資料庫文件。您必須在更改位置前移動/複製這些資料。如果移動後的位置不存在資料庫,Playnite將會自動創建一個新的資料庫。 新的資料庫位置將會在下次重新啟動程式之後被套用。 如果設定了"關閉"指令,則遊玩時間將不會被記錄 列數 欄數 列表檢視列數 在主螢幕顯示背景圖片 未重新下載元資料時現有遊戲不會被覆蓋 匯入資料庫當中的遊玩時間: 設定 Playnite 應該在何時匯入由管理Playnite資料庫外掛所回報的遊玩時間。 負責管理遊戲資料庫的外掛必須要有支援才能使用這個功能。 總是: 將會對新匯入以及已存在於Playnite資料庫的遊戲匯入遊玩時間。 只針對新匯入的遊戲: 只會對新匯入的遊戲匯入遊玩時間。 永不: 在任何情況下都不匯入遊玩時間。 總是 只針對新匯入的遊戲 從未 在桌面模式中啟用手把 導航按鍵開啟全螢幕模式 設定新匯入的遊戲應該下載哪些資料 目標顯示器 總是使用主顯示器 顯示遊戲標題 顯示電池狀態 顯示剩餘電量百分比 顯示時鐘 隱藏滑鼠游標 在快速過濾器中只顯示已安裝遊戲 按鈕提示 配置 水平捲軸 選擇一個子選項 無可用的設定值 讀取設定失敗 這些腳本將供媒體庫中的所有遊戲運行。編輯遊戲詳細資訊時可針對不同遊戲各自分配獨立的腳本。 增加背景圖片動態轉場效果 字型大小 自動 鋸齒化 灰階 ClearType 最佳化 Display - GDI 兼容 文字格式化模式 文字渲染模式 文字渲染以及格式化選項目前不會對遊戲敘述造成影響 預先讀取背景圖片 若啟用本功能則Playnite會於下載元資料時一併下載背景圖片。會佔用額外的硬碟空間且可於離線時瀏覽圖片。 若禁用本功能則Playnite僅會在首次訪問時才下載背景圖片。會節省硬碟空間但可能造成圖片讀取速度緩慢, 且離線時將無法觀看部分圖片。 退出遊戲時自動關閉遊戲啟動器 延緩用戶端關閉時間(秒) 當遊戲時間低於(秒) 則不關閉 自動關閉以下登入器: 自動關閉啟動器 匯入例外清單 當遊戲媒體容量過大時顯示警告 資料夾開啟指令 預設年齡分級組織 更新資料庫時同步更新遊戲安裝容量 若系統偵測到上次掃描後檔案已被修改過,則掃瞄並更新遊戲安裝容量 填滿視窗 保留原長寬比縮放以符合視窗 保留原長寬比縮放以符合視窗並自動裁切超出視窗內容 匯入錯誤 需要驗證 驗證失敗 其他WebView渲染模式 在WebView中遇到問題(例如集成驗證對話框時)使用。 遇到較長的遊戲敘述時僅先讀取部分敘述 在選擇遊戲時,較長的遊戲敘述會導致明顯的延遲。 啟用本選項後系統只會先讀取部分敘述,之後用戶可以自己再手動操作讀取剩餘敘述。 匯入元資料 下載元資料 選擇未來元資料下載的設定。 之後可在設定中更改。 模擬器匯入精靈 這個精靈將帶領你完成下載以及匯入主機模擬器,模擬遊戲的流程。 請留意,您可隨時透過主選單新增模擬器及/或遊戲(在"媒體庫"選單中可找到模擬器設定,"新增遊戲"選單中可新增模擬器遊戲) 以下是Playnite可自動辨認及設定的模擬器清單。你可以到模擬器的網站下載安裝。當你完成安裝(手動)之後,你可以在模擬器設定畫面匯入模擬器。 您可以按下"從資料夾自動檢查..." 來匯入任何已安裝在您PC上的模擬器。Playnite會在點選的資料夾中搜尋任何已知的模擬器並提供匯入選項。您可以多次使用該按鈕從多個文件夾匯入,模擬器將會被新增在目前列表的底部 按下 "掃描模擬器資料夾" 按鈕,可自動匯入遊戲。如果選擇了適當的模擬器,Playnite將能自動辨識並匯入正確的檔案類型。您可以多次使用該按鈕從多個資料夾匯入,遊戲會被新增在目前清單的底部 未選擇要匯入的模擬器。如未設定模擬器則將無法自動匯入任何模擬器遊戲。是否確定要繼續並離開匯入程序? 尚未設定任何模擬器。如果未設定模擬器並選擇適當的檔案類型則無法匯入遊戲。是否新增模擬器? 掃描模擬器資料夾 選擇檔案 從資料夾自動檢查... 設定模擬器... 掃描中... 掃描中 {0}... 首次設定 本精靈將帶領您完成遊戲匯入以及外部遊戲資料庫的設定。Playnite可以自動從遊戲服務商如Steam或GOG匯入遊戲,並於啟動時自動更新保持遊戲狀態同步。 請留意,您可隨時點選"Playnite"按鈕,透過主選單手動新增任何平台上的自訂遊戲。 資料庫整合 自動從以下的服務匯入遊戲。以後Playnite啟動時將會自動更新遊戲狀態(安裝狀態) 或您可手動進行更新。此設定將會影響初次以及所有後續的匯入過程。 完成設定 已完成初始設定。請留意您可隨時於設定中更改配置。 您亦可隨時點選Playnite圖標來新增任何整合功能。 一個或以上的擴充功能下載失敗 你可以在首次執行精靈結束後嘗試從附加元件選單中重新下載整合功能 下載{0}整合功能中... 正在下載推薦整合功能清單… 無法下載整合功能清單。你可以稍後在附加元件選單中重新嘗試下載。 設定平台以及模擬器 設定模擬器 平台 平台 模擬器 模擬器 新增平台 選擇圖示 選擇封面 選擇圖片 選擇項目 選擇背景 選擇檔案 選擇網址 新增模擬器 支持的平台 是否儲存平台設定? 是否儲存模擬器設定? 執行檔 引數 工作目錄 支援的檔案類型 匯入模擬器... 下載模擬器... 從已知的模擬器描述檔加載預設引數 是否確定要移除 {0} 模擬器? 檔案目前正被 {1} 個遊戲使用 是否確定移除 {0} 平台? 目前該平台正被 {1} 個遊戲以及 {2} 個模擬器使用 設置指引 按... 排序 排序方式 按... 分組 由少至多 由多至少 不分組 按資料庫分組 按分類分組 按平台分組 檢視類型 檢視 瀏覽面板 過濾面板 圖示 遊戲庫圖示 封面圖片 背景圖片 排序用名稱 資料庫 說明書 名稱 安裝硬碟 帳號名稱 平台 分類 類型 發行日期 發行年份 開發團隊 標籤 發行商 安裝狀態 符合所有篩選條件 勾選時,只有符合所有篩選條件的遊戲才會顯示 取消時,只要符合任意一項篩選條件的遊戲都會顯示 已安裝 已安裝 未安裝 隱藏 我的最愛 啟用 HDR 支援 啟用本選項後,HDR會在遊戲開始前在主顯示器上啟用。 注意,你的主顯示器不支援HDR 最後遊玩 分類 描述 安裝目錄 封面圖片 連結 圖片,ROM或ISO路徑 類型 類型 公司 公司 開發團隊 開發團隊 發行商 發行商 分類 分類 標籤 標籤 特色 特色 年齡分級 年齡分級 地區 區域 來源 來源 近期活動 數據庫錯誤 無法開啟資料庫數據 未開啟數據庫 無法連接資料庫數據。檔案"{0}"正在被其他程式使用或位於無法連接的位置 創建診斷報告失敗 自動上傳診斷報告失敗 診斷訊息成功送出 診斷報告已成功創建並回報。 請在回報內容中附上以下ID: 從 {0} 匯入遊戲失敗。 無法從{0}匯入模擬器遊戲。 無法透過指定的模擬器描述檔搜尋遊戲。描述檔未包含任何副檔名或平台。 Playnite無法啟動。請關閉所有正在執行的程式並重試 無法套用佈景主題 "{0}", 色彩描述檔 "{1}" {2} 無法開啟連結,網址格式不符。 無法開啟應用程式 初始化網頁檢視內容失敗。Playnite無法繼續啟動程序。 更多資訊請參考 https://playnite.link/cefstartup 無法匯入模擬器,因為定義檔已損壞或丟失。 無法執行選單動作 編輯遊戲詳細資訊 圖片網址 新增連結 新增ROM 儲存變更 套用已編輯的遊戲資訊欄位 新增動作 刪除動作 移除啟動動作 新增遊戲 掃描資料夾... 找到已安裝 瀏覽... 打開Playnite 個人檔案設定 遊戲名不能為空白 遊戲指令追蹤資料夾不可為空白 搜尋元資料時遊戲名稱不能為空白 無效的遊戲資料 請使用包含 http:// 或 https:// 的有效網址格式 選擇網址 下載元資料失敗: {0} 下載錯誤 清除過濾條件 私人帳號 公開帳號 API 密鑰 啟動錯誤 佈景主題錯誤 全部清除 安裝中 移除中 啟動中 執行中 無效的網址 不做任何事 最小化 還原視窗 僅在從介面啟動時還原視窗 關閉 變更 進階 從未 完成狀態 完成狀態 使用者評分 專家評分 社群評分 遊戲腳本 應用程式腳本 腳本 外部插件 元資料來源 擴充功能 擴充功能ID 重新載入腳本 互動式 SDK PowerShell 已成功加載所有腳本 找不到指定搜尋/過濾器條件的遊戲 找不到任何項目 回到桌面模式 離開Playnite 資料庫 全部更新 建立者: 版本: 已更新 模組: 資料庫 統計 全部 通知 大小 普通 較大 最大 預設 選擇 全選 全部取消選取 首項 隨機 使用者選擇 加載更多 透明 摺疊 展開 全部折疊 全部展開 其他 佈景主題 模擬器引數 預設引數 自訂引數 其他模擬器引數 覆寫模擬器引數 啟動動作 選擇要匯入的內容 選擇要匯入的遊戲 搜尋元資料 有可用的更新 最後更新至今的變更 下載並安裝更新 檢查更新 更新錯誤 無法檢查新版本 找不到新版本,您已是最新版本 無法下載並安裝更新 目前有背景程式正在執行。是否要取消並進行更新? 目前有背景程式正在執行,是否要取消並關閉Playnite? 目前有背景程式正在執行,更換模式會取消該程式執行,是否要取消並更換模式? Playnite有一個可用的更新 重新載入佈景主題清單 套用選擇的佈景主題 閱讀檔案更動 當來源檔案更改時自動套用佈景主題 腳本執行階段 啟動遊戲前執行的腳本 離開遊戲後執行的腳本 啟動遊戲後執行的腳本 在程式開始時執行 在程式結束時執行 遊戲啟動時腳本 遊戲啟動後腳本 遊戲停止後腳本 執行全域腳本 全域 已過濾 目前 新的 測試腳本 只顯示選取的項目 儲存為預設值 新增至我的最愛 從「我的最愛」中移除 隱藏此遊戲 從隱藏列表中移除 啟用 HDR 支援 停用 HDR 支援 編輯... 計算佔用儲存容量 計算佔用儲存容量 (所有遊戲) 計算佔用儲存容量(只針對缺少的資料) 儲存空間佔用 設定分類... 設定完成狀態 移除 啟動 安裝 遊戲選項 詳細資訊 移除 打開安裝位置 建立桌面捷徑 開啟說明書 更多 由資料庫外掛管理 遊戲的啟動過程將由相對應的資料庫外掛管理 在指定頁面找不到 '{0}' 的相關訊息 提示: 您可以透過"編輯"選單編輯遊戲並執行更進階的元資料下載程序 某些程式正在進行時無法使用 描述文本支持HTML語法 遊戲時間按秒紀錄 儲存空間佔用以 bytes 表示。 發布日期必須以"年-月-日"格式設定。月和日的值可以省略。 輸入0到100的值或者留白以表示無分數 Playnite的開發得到了這些贊助者以及Ko-fi用戶們的支持: 代碼,本地化以及其他貢獻者(無特定順序): 取消遊戲監控? 安裝監控正在執行,是否要取消並將遊戲復原到之前的狀態? 遊戲運行監控正在執行,是否要取消並將遊戲復原至之前的狀態? 遊玩時數 最後遊玩 {0}日 {1}小時 {2}分 {0}小時 {1}分鐘 {0} 分鐘 {0} 秒 尚未遊玩 正在開啟桌面模式... 正在開啟全螢幕模式... 讀取遊戲資料庫中... 計算佔用儲存容量中… 正在計算{0}…的佔用儲存容量 無法安裝腳本 成功安裝腳本 安裝腳本 脚本錯誤 無法執行附加元件 開啟元資料目錄夾 計算 自動以ROM計算佔用儲存容量,如果沒有ROM則以設定的遊戲安裝資料夾計算佔用儲存容量 {0} 未安裝客戶端程式 {0} 客戶端程式即將啟動,請登入並關閉此訊息。 正在等候用戶登入,完成後請關閉此訊息... 找不到遊戲安裝資料夾 無效的遊戲指令設定 解決帳號同步問題 疑難排解 重新命名物件 新增物件 輸入名稱 輸入新的名稱 少於1小時 1到10小時 10到100小時 100到500小時 500到1000小時 1000+ 必須重啟Playnite來完成安裝。是否立即重啟? 擴充功能未被正確封裝 佈景主題未被正確封裝 擴充功能 "{0}" 載入失敗 無法載入"{0}"擴充功能,目前版本的Playnite不支援此功能。 佈景主題 "{0}" 載入失敗 無法載入"{0}"佈景主題,目前版本的Playnite不支援此功能。 載入擴充功能失敗 佈景主題載入失敗 佈景主題/附加元件使用了未支援的API版本 安裝成功 安裝附加元件? 通用 安裝"{0}"附加元件失敗 安裝附加元件失敗。 {0} 是否安裝新的擴充功能? {0} 由 {1} 版本 {2} 是否升級擴充功能 "{0}" ? 目前版本: {1} 新版本: {2} 安裝佈景主題失敗。 {0} 是否安裝新的佈景主題? {0} 由 {1} 版本 {2} 是否升級佈景主題 "{0}" ? 目前版本: {1} 新版本: {2} 即將離開Playnite並透過預設瀏覽器前往以下的網頁。是否繼續? {0} 您選擇的圖片過大,可能導致效能無法最佳化。使用過大的圖片可能會造成介面嚴重卡頓並增加記憶體使用率。 建議最大尺寸: 圖標: {0} px 封面: {1} px 背景: {2} px 效能警告 不再顯示 擴充功能{0}的檔案不相容 不相容的副檔名 已選擇的圖片過大,可能會影響效能。 是否移除選定的佈景主題? 移除將會於下次程式啟動後進行 無法移除內建佈景主題 這個佈景主題不支援目前版本的Playnite 是否移除選定的擴充功能? 移除將會於下次程式啟動後進行 無法移除內建擴充功能 此擴充功能不支援目前版本的Playnite 安裝目錄 資料目錄 產生診斷報告中... 上傳診斷報告中... 匯入檔案... 這是什麼? 您確定要這麼做嗎? 總遊玩時間 平均遊玩時間 最高遊玩時間 總計安裝大小 總覽 側邊欄 在側邊欄顯示 重設設定 所有程式設定將恢復為預設值,不包括: - 資料庫位置 - 匯入例外清單 - 擴充功能設定,包含資料庫整合功能 需要重新啟動程式以套用設定。要重置設定嗎? 開發人員工具 外部擴充功能 輸入完整資料夾路徑 成就 論壇 最新消息 商店頁面 首次安裝尚未完成,Playnite將會以桌面模式重新啟動來完成安裝步驟。 最近玩過 我的最愛 最常遊玩 全部 已套用過濾條件 已套用額外過濾器 搜尋結果: 已存在相同名稱的物件 將選擇限於目前過濾結果 選擇另一個 附加元件... 已安裝 擴充功能設定 瀏覽 更新 更新 {0} 對已安裝的擴充功能及佈景主題的管理, 以及設定選項, 已被移到新增加的"附加元件"選單中。 可以在此處設定所有目前已安裝的資料庫整合附加元件。 如果要安裝或移除額外的整合功能,請使用主選單當中的"附加元件"選項。 桌面模式佈景主題 全螢幕佈景主題 搜尋中... 附加元件與此版本Playnite不相容 無法下載附加元件安裝包 無法下載附加元件安裝清單文件 需要重新啟動程式以套用更改。 本附加元件已被排入安裝排程 安裝 移除 已安裝 未發現新的附加元件更新檔 更新附加元件 沒有可用的更新日誌 已排程安裝 下載失敗 授權被拒 正在下載 {0}... 正在檢查附加元件更新… 有一個以上的附加元件更新可用。 選擇更新項目 擴充功能開發用服務 {0}授權協議 接受 拒絕 包含資料庫整合執行動作 選擇動作 追蹤模式 追蹤路徑 初始追蹤延遲 追蹤頻率 連結 檔案 模擬器 腳本 預設 處理程序 資料夾 原始程序 紀錄追蹤訊息 以下更改將覆寫所有已選遊戲的資料! 統一 只顯示項目 僅開始與結束 捲動靈敏度 滑順捲動 動畫速度 移除項目? 你是否確定要移除此項目? 在頂欄顯示按鈕 一般檢視設定 群組設定 排序設定 過濾器預設 外掛程式項目位置 區塊間隔寬度 將主選單按鈕移至側邊欄 瀏覽面板 隨機選取遊戲 從目前結果隨機選擇遊戲 從目前檢視中隨機選取遊戲 儲存群組及排序設定 在全螢幕模式中顯示為快速過濾器 過去 7 天 過去 31 天 過去 365 天 超過365天以前 設定 儲存預設 啟動遊戲後最小化 遊戲開始後將Playnite最小化。 如將此功能關閉可能會造成遊戲無法在啟動時取得輸入焦點而導致問題發生! 字型大小 字體小 遊戲控制器支援 若停用此選項,則Playnite不會接受任何遊戲控制器輸入的訊號。 如果你有使用其他工具程式來將手把訊號轉譯為滑鼠/鍵盤訊號,而造成在Playnite中有重複輸入的現象,請停用此選項。 在主選單中顯示項目: 反轉X/A主要視圖按鈕綁定 交換按鈕綁定與開始遊戲並且在主要視圖當中顯示遊戲詳細資料頁面 交換確認/取消按鈕綁定 交換A/B按鈕與確認/取消的綁定 只有主控制器 啟動時,只接收主控制器的指令。 導覽按鈕焦點定位於Playnite 介面音量 背景音量 處於背景時靜音 初始化音頻介面失敗 輸出API 這是用於音源輸出的API。如果你遇到音源問題請更換。 一般 視覺 聲音 版面配置 選單 輸入 正在開始{0}... 正在執行{0}... 大寫字母 空白鍵 影像渲染縮放 其他 平衡 畫質 高效能: 最高畫質,速度慢,記憶體用量高。 平衡: 中等畫質,速度快,記憶體用量低。 其他: 中高畫質,速度中等,記憶體用量中。 選擇檔案... 選擇資料夾... 啟動腳本 請注意,擴充功能及佈景主題兩者都會大幅度影響Playnite的運作效能,穩定性以及安全性。 如果你在安裝主題或擴充功能後遇到問題,請嘗試先禁用/移除它們,以確定它們是否是問題的根源。 啟動時選擇 啟動時選擇 內建配置檔 內建配置檔 自訂檔案 自訂設定檔 由內建腳本控制 模擬器格式 平台格式 地區格式 啟動模擬器之前執行 啟動模擬器之後執行 關閉模擬器之後執行 找不到可執行的模擬器檔案 找不到模擬器格式檔 找不到模擬器啟動膠本 分割成不同遊戲 合併成相同遊戲 設定平台 設定地區 掃描資料夾 掃描設定 從校驗掃描中排除模式 符合特定模式的檔案不會被掃描校驗,而會使用檔案名稱配對。更多訊息請參考模擬器幫助頁面 用模擬器掃描 儲存新設定時必須輸入名稱 未配置模擬器或模擬器設定檔 未指定要掃描的資料夾或資料夾不存在 未正確配置掃描設定 包括大量掃描以及自動掃描 掃描模擬器資料夾失敗 掃描模擬器遊戲資料夾失敗 隱藏已匯入 匯入檔案: 自動掃描設定 儲存為自動掃描設定 將儲存設定以用於未來的資料庫更新。可透過"設定模擬器"選單來管理已儲存的設定。 使用相對路徑匯入 可能的話匯入遊戲檔案時使用與Playnite安裝資料夾或者模擬器安裝資料夾相關聯的路徑。 掃描子資料夾 掃描壓縮檔裡的檔案 合併相關檔案 合併相關遊戲檔案,例如個別的遊戲光碟等,到同一個遊戲資料中。 新增掃描器 新增已存檔的掃描器 開始掃描 新增模擬器的掃描設定以掃描特定資料夾。匯入遊戲之前請確保模擬器已正確設定(可透過 資料庫 -> 設定模擬器 選單確認) 已將新加入的遊戲調整為預設狀態 已調整首次遊玩的遊戲狀態 無法啟動PowerShell腳本執行環境。如果你是Windows7的使用者,請嘗試(重新)安裝PowerShell5.1。 已存在指定名稱的過濾器預設,是否使用新設定更新預設? 將從自動填入的遊戲排序名稱的開頭刪除以下的字詞: 使用這個功能以忽略用來排序的名稱開頭的字詞。預設是"The","An","A"。 幫沒有排序名稱的遊戲填入排序名稱 排序 填入排序名稱… 偵測到您的系統正在執行Nahimic服務。這個服務已被認為會造成Playnite(以及其他應用程式)在渲染上出現嚴重錯誤。 如果你遇到圖形破損或其他渲染問題,建議你停用或者完全移除該Nahimic服務。 更多訊息請查看 https://playnite.link/nahimicsucks Playnite目前正以高級權限(系統管理員身分)執行中。我們不建議你使用此方式執行,因為這將會授與所有透過Playnite執行的遊戲/應用程式以及已安裝的擴充功能過高等級的權限! 查看此處以了解詳情 https://playnite.link/adminfaq 如果Playnite正以系統管理員身分執行的話則跳出警告 計算遊戲占用容量時取得在硬碟上的實際容量 如果啟用,掃描速度會較慢且會取得該檔案在硬碟中實際占用的空間 如果停用,掃描速度會較快且會使用檔案本身的大小 以下的附加元件已經被回報含有潛在問題,有可能造成穩定性/效能降低或資訊安全疑慮。我們強烈建議你移除這些附加元件: {0} 不要掃描線上檔案 儲存在雲端且無法直接在本地電腦存取的檔案不會被掃描匯入。 僅支援: Google Drive, DropBox, OneDrive 使用簡易方式掃描,不下載檔案內容 使用較低準確度的方法,不需要下載檔案內容到本地裝置即可匯入檔案。 套用到全部 覆載安裝狀態 開啟時,Playnite會忽略在匯入此遊戲時被外掛程式設定的安裝狀態(包含安裝資料夾) 這個選項對於某些使用特定遊戲匯入方法的插件可能不會正常運作,除非該外掛也有考慮到這個覆載功能。 僅手動 每天一次 每週一次 每次啟動時 確認程式更新狀態 確認附加元件更新狀態 更新資料庫 掃描模擬器資料夾 包含已隱藏遊戲 編輯欄位 全選 / 取消全選 開啟 啟動 指定 開始輸入文字以搜尋遊戲…按下[F1]取得協助 開頭加上 # 會帶入可用的指令清單。 開頭加上 / 會帶入可用的搜尋提供者/外掛清單。 輸入搜尋關鍵字並且在最後加入空白鍵會馬上轉換到該搜尋。 TAB: 轉換動作 ENTER: 啟動選中的動作 SHIFT-ENTER: 開啟項目列表 包含未安裝的遊戲 包含已隱藏遊戲 包含未安裝的遊戲 不包含未安裝的遊戲 已包含已隱藏的遊戲 未包含已隱藏的遊戲 遊玩或安裝 檢視詳細資料 遊戲選單 編輯遊戲 開啟搜尋 搜尋欄位 搜尋鍵 主要遊戲指令 次要遊戲指令 按下CTRL-F 時開啟全域搜尋而非單一搜尋欄位 每次搜尋後記錄遊戲過濾器設定 搜尋提供來源 預設關鍵字 自訂關鍵字 系統級快捷鍵 Playnite搜尋 擴充功能設定 排除 不包含掃描資料夾相關的檔案 不包含與掃描資料夾相關的資料夾 將檔案加入排除清單 將資料夾加入排除清單 排除設定只能新增在已保存的掃描器設定檔當中 排除設定已新增於"{0}" 掃描器當中。 覆載平台 打開此選項時掃描器會把這個平台資訊指定到所有遊戲,並覆載所有已經偵測到的平台資訊。 在預設搜尋中包含指令內容 當這個選項被停用時,未使用#前綴的話,預設搜尋將不會包含指令內容。 在遊戲名稱過濾器中使用模糊比對 啟用時,名稱過濾器會使用等同於全域搜尋的方式來比對遊戲名稱 你可以透過添加首字元 ! 來強制對個別關鍵字執行精確比對 在遊戲結果中顯示的欄位: 隱藏狀態 已取消資料備份。 資料備份失敗。 資料備份錯誤 資料備份中… 正在從備份資料中還原檔案… 從備份檔案還原資料失敗。 設定 遊戲資料庫 遊戲資料庫媒體 已安裝的擴充功能 擴充功能資料 已安裝的主題 選擇要從特定備份檔案還原的資料 Playnite會自動重新啟動並開始還原資料 選擇要包含在備份檔案中的項目。預設包含應用程式設定以及遊戲資料庫。 Playnite將會重新啟動並開始備份資料 自動備份檔案 自動備份頻度 備份資料夾 輪替式備份 包含額外資料: 如果要開啟自動備份,必須設定備份資料夾。 僅顯示小型更新通知 啟用時,只有目前已安裝的主要版本出現可用更新時會發出更新通知。 出現新的主要版本時將不會發出更新通知。 為最近一周使用相對日期 如果日期在一周內,則在"今天","昨天"等格式上使用相對日期。 指定的日期格式將被用在所有其他日期上。 網頁圖片搜索 縮圖搜尋字串 封面圖片搜尋字串 背景圖片搜尋字串 正在獲取插件資訊。 沒有可用的元資料來源 遊玩指令設定 使用掃描器設定 啟動時選擇個人檔案 啟動時選擇模擬器 自動 保持開啟 始終關閉 輔助功能 (螢幕閱讀器) 應用程式選單 遊戲目錄 程式資料夾 使用者資料夾 偵測到資料庫檔案毀損,Playnite即將自動關閉。 你可以在Playnite的GitHub頁面新增回報並請求修復你的毀損檔案。 是否要儲存所做的更改? 可攜式安裝 未偵測到搖桿 ================================================ FILE: source/Playnite/Localization.cs ================================================ using Playnite.Common; using Playnite.SDK; using Playnite.Settings; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Markup; namespace Playnite { public class PlayniteLanguage { public int TranslatedPercentage { get; set; } = -1; public string LocaleString { get; set; } public string Id { get; set; } public string DisplayString { get => ToString(); } public override string ToString() { if (TranslatedPercentage == -1 || TranslatedPercentage == 100) { return LocaleString; } else { return $"{LocaleString} ({Id}, {TranslatedPercentage}%)"; } } } public static class Localization { private static ILogger logger = LogManager.GetLogger(); public const string SourceLanguageId = "english"; public static List AvailableLanguages { get { return GetLanguagesFromFolder(PlaynitePaths.LocalizationsPath); } } public static string CurrentLanguage { get; private set; } = SourceLanguageId; public static CultureInfo ApplicationLanguageCultureInfo { get; private set; } = CultureInfo.CurrentCulture; public static bool IsRightToLeft { get { return ApplicationLanguageCultureInfo.TextInfo.IsRightToLeft; } } public static List GetLanguagesFromFolder(string path) { var coverage = Serialization.FromJsonFile>(PlaynitePaths.LocalizationsStatusPath); var langs = new List() { new PlayniteLanguage() { Id = SourceLanguageId, LocaleString = "English" } }; if (!Directory.Exists(path)) { return langs; } foreach (var file in Directory.GetFiles(path, "*.xaml")) { if (!Regex.IsMatch(file, "[a-zA-Z]+_[a-zA-Z]+")) { continue; } var fileCode = Path.GetFileNameWithoutExtension(file); if (!coverage.TryGetValue(fileCode.Replace('_', '-'), out var lngCov)) { coverage.TryGetValue(fileCode.Split('_')[0], out lngCov); } var langPath = Path.Combine(path, file); var localeString = ""; try { foreach (var line in File.ReadLines(langPath, Encoding.UTF8)) { var match = Regex.Match(line, @"LanguageName""\>(.+)\<"); if (match.Success) { localeString = match.Groups[1].Value; break; } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to parse localization file {file}"); continue; } langs.Add(new PlayniteLanguage() { Id = Path.GetFileNameWithoutExtension(langPath), LocaleString = localeString, TranslatedPercentage = lngCov }); } return langs.OrderBy(a => a.LocaleString).ToList(); } public static void SetLanguage(string language) { var dictionaries = Application.Current.Resources.MergedDictionaries; if (CurrentLanguage != SourceLanguageId) { var currentLang = dictionaries.FirstOrDefault(a => a["LanguageName"] != null && a.Source == null); if (currentLang != null) { dictionaries.Remove(currentLang); } } var langFile = Path.Combine(PlaynitePaths.LocalizationsPath, language + ".xaml"); if (File.Exists(langFile) && language != SourceLanguageId) { ResourceDictionary res = null; try { res = Xaml.FromFile(langFile); res.Source = new Uri(langFile, UriKind.Absolute); // Unstranslated strings are imported as empty entries by Crowdin. // We need to remove them to make sure that origina English text will be displayed instead. foreach (var key in res.Keys) { if (res[key] is string locString && locString.IsNullOrEmpty()) { res.Remove(key); } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to parse localization file {langFile}"); return; } dictionaries.Add(res); ApplicationLanguageCultureInfo = new CultureInfo(language.Replace("_", "-"), false); } else { ApplicationLanguageCultureInfo = new CultureInfo("en-US", false); // english is the default language } CurrentLanguage = language; } public static void LoadAddonLocalization(string addonDir) { var dictionaries = Application.Current.Resources.MergedDictionaries; void loadString(string xamlPath) { ResourceDictionary res = null; try { res = Xaml.FromFile(xamlPath); res.Source = new Uri(xamlPath, UriKind.Absolute); foreach (var key in res.Keys) { if (res[key] is string locString) { if (locString.IsNullOrEmpty()) { res.Remove(key); } } else { res.Remove(key); } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to parse localization file {xamlPath}"); return; } dictionaries.Add(res); } var localDir = Path.Combine(addonDir, PlaynitePaths.LocalizationsDirName); if (!Directory.Exists(localDir)) { return; } var enXaml = Path.Combine(localDir, "en_US.xaml"); if (!File.Exists(enXaml)) { return; } loadString(enXaml); if (CurrentLanguage != "english" && CurrentLanguage != "en_US") { var langXaml = Path.Combine(localDir, $"{CurrentLanguage}.xaml"); if (File.Exists(langXaml)) { loadString(langXaml); } } } } } ================================================ FILE: source/Playnite/Manifests/AddonManifestBase.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public enum AddonType { GameLibrary, MetadataProvider, Generic, ThemeDesktop, ThemeFullscreen } public class AddonManifestBase : ObservableObject { public class AddonUserAgreement { public DateTime Updated { get; set; } public string AgreementUrl { get; set; } } public class AddonScreenshot { public string Thumbnail { get; set; } public string Image { get; set; } } public string IconUrl { get; set; } public List Screenshots { get; set; } public AddonType Type { get; set; } public string InstallerManifestUrl { get; set; } public string ShortDescription { get; set; } public string Description { get; set; } public string Name { get; set; } public string AddonId { get; set; } public string Author { get; set; } public Dictionary Links { get; set; } public List Tags { get; set; } public AddonUserAgreement UserAgreement { get; set; } public string SourceUrl { get; set; } } public class AddonInstallerPackage { public Version Version { get; set; } public string PackageUrl { get; set; } public Version RequiredApiVersion { get; set; } public DateTime ReleaseDate { get; set; } public List Changelog { get; set; } } public class AddonInstallerManifestBase { public string AddonId { get; set; } public List Packages { get; set; } } } ================================================ FILE: source/Playnite/Manifests/AddonManifests.cs ================================================ using Newtonsoft.Json; using Playnite.Common; using Playnite.Common.Web; using Playnite.Plugins; using Playnite.SDK; using Playnite.ViewModels; using Playnite.Windows; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using YamlDotNet.Serialization; namespace Playnite { public class AddonInstallerManifest : AddonInstallerManifestBase { public AddonType AddonType { get; set; } public AddonInstallerPackage GetLatestCompatiblePackage() { if (!Packages.HasItems()) { return null; } var apiVersion = GetApiVersion(AddonType); return GetCompatiblePackages(apiVersion).FirstOrDefault(); } public List GetCompatiblePackages() { return GetCompatiblePackages(GetApiVersion(AddonType)); } public List GetCompatiblePackages(Version apiVersion) { if (!Packages.HasItems()) { return null; } return Packages. Where(a => a.RequiredApiVersion != null && a.RequiredApiVersion.Major == apiVersion.Major && a.RequiredApiVersion <= apiVersion). OrderByDescending(a => a.Version).ToList(); } private static Version GetApiVersion(AddonType type) { switch (type) { case AddonType.GameLibrary: case AddonType.MetadataProvider: case AddonType.Generic: return SdkVersions.SDKVersion; case AddonType.ThemeDesktop: return ThemeManager.DesktopApiVersion; case AddonType.ThemeFullscreen: return ThemeManager.FullscreenApiVersion; } return new Version(999, 0); } } public class AddonManifest : AddonManifestBase { private static ILogger logger = LogManager.GetLogger(); private AddonInstallerManifest installerManifest; [YamlIgnore] [JsonIgnore] public AddonInstallerManifest InstallerManifest { get { if (installerManifest == null) { DownloadInstallerManifest(); } return installerManifest; } } [YamlIgnore] [JsonIgnore] public AddonInstallerPackage LatestPackage { get { var manifest = InstallerManifest; if (!manifest.Packages.HasItems()) { return null; } return manifest.Packages.OrderByDescending(a => a.Version).FirstOrDefault(); } } [YamlIgnore] [JsonIgnore] public bool IsQueuedForInstall { get { var fileName = Path.GetFileName(GetTargetDownloadPath()); return ExtensionInstaller.GetQueuedItems().Any(a => Path.GetFileName(a.Path) == fileName); } } [YamlIgnore] [JsonIgnore] public bool IsInstalled { get { if (IsTheme) { if (Type == AddonType.ThemeDesktop) { return ThemeManager.GetAvailableThemes(ApplicationMode.Desktop).Any(a => a.Id == AddonId); } else { return ThemeManager.GetAvailableThemes(ApplicationMode.Fullscreen).Any(a => a.Id == AddonId); } } else { return ExtensionFactory.GetInstalledManifests().Any(a => a.Id == AddonId); } } } [YamlIgnore] [JsonIgnore] public bool IsTheme => Type == AddonType.ThemeDesktop || Type == AddonType.ThemeFullscreen; [YamlIgnore] [JsonIgnore] public bool IsExtension => Type == AddonType.Generic || Type == AddonType.GameLibrary || Type == AddonType.MetadataProvider; public string GetTargetDownloadPath() { return Path.Combine(PlaynitePaths.TempPath, Paths.GetSafePathName(AddonId) + GetAddonPackageExtension(Type)); } public static string GetAddonPackageExtension(AddonType type) { switch (type) { case AddonType.GameLibrary: case AddonType.MetadataProvider: case AddonType.Generic: return PlaynitePaths.PackedExtensionFileExtention; case AddonType.ThemeDesktop: case AddonType.ThemeFullscreen: return PlaynitePaths.PackedThemeFileExtention; default: throw new Exception($"Uknown addon type {type}"); } } public bool? CheckAddonLicense() { try { if (UserAgreement != null) { var acceptState = ExtensionInstaller.GetAddonLicenseAgreed(AddonId); if (acceptState == null || acceptState < UserAgreement.Updated) { var license = HttpDownloader.DownloadString(UserAgreement.AgreementUrl); var licenseAgree = new LicenseAgreementViewModel( new LicenseAgreementWindowFactory(), license, Name); if (licenseAgree.OpenView() == true) { ExtensionInstaller.AgreeAddonLicense(AddonId); return true; } else { ExtensionInstaller.RemoveAddonLicenseAgreement(AddonId); return false; } } else { return true; } } else { return true; } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to process addon license."); return null; } } public override string ToString() { return Name; } public void DownloadInstallerManifest() { DownloadInstallerManifest(new CancellationToken()); } public void DownloadInstallerManifest(CancellationToken cancelToken) { try { if (InstallerManifestUrl.IsNullOrEmpty()) { throw new Exception("No addon manifest installer url."); } if (InstallerManifestUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { installerManifest = PlayniteApplication.Current.ServicesClient.GetAddonInstaller(AddonId); } else if (File.Exists(InstallerManifestUrl)) { installerManifest = Serialization.FromYamlFile(InstallerManifestUrl); } else { throw new Exception($"Uknown installer manifest url format {InstallerManifestUrl}."); } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to get addon installer manifest data."); installerManifest = new AddonInstallerManifest(); } installerManifest.AddonType = Type; } } } ================================================ FILE: source/Playnite/Manifests/ExtensionManifest.cs ================================================ using Playnite.Common; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using YamlDotNet.Serialization; namespace Playnite { public enum ExtensionType { GenericPlugin, GameLibrary, Script, MetadataProvider } public class BaseExtensionManifest { public string Id { get; set; } public string Name { get; set; } public string Author { get; set; } public string Version { get; set; } public List Links { get; set; } [YamlIgnore] public string DirectoryPath { get; set; } [YamlIgnore] public string DirectoryName { get; set; } [YamlIgnore] public string DescriptionPath { get; set; } public void VerifyManifest() { if (!System.Version.TryParse(Version, out var extver)) { throw new Exception("Extension version string must be a real version!"); } } } public class ExtensionManifest : BaseExtensionManifest { [YamlIgnore] public bool IsExternalDev { get; set; } //[YamlIgnore] //public bool IsCompatible { get; } = false; public string Module { get; set; } public string Icon { get; set; } public ExtensionType Type { get; set; } public ExtensionManifest() { } public static ExtensionManifest FromFile(string descriptorPath) { var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build(); var description = deserializer.Deserialize(File.ReadAllText(descriptorPath)); description.DescriptionPath = descriptorPath; description.DirectoryPath = Path.GetDirectoryName(descriptorPath); description.DirectoryName = Path.GetFileName(description.DirectoryPath); return description; } } } ================================================ FILE: source/Playnite/Manifests/ThemeManifest.cs ================================================ using Playnite.Common; using Playnite.SDK; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using YamlDotNet.Serialization; namespace Playnite { public class ThemeManifest : BaseExtensionManifest { public string ThemeApiVersion { get; set; } public ApplicationMode Mode { get; set; } [YamlIgnore] public bool IsBuiltInTheme { get; } [YamlIgnore] public bool IsCustomTheme => !IsBuiltInTheme; [YamlIgnore] public bool IsCompatible { get; } = false; public ThemeManifest() { } public ThemeManifest(string manifestPath) { var thm = Serialization.FromYaml(File.ReadAllText(manifestPath)); thm.CopyProperties(this, false); DescriptionPath = manifestPath; DirectoryPath = Path.GetDirectoryName(manifestPath); DirectoryName = Path.GetFileName(DirectoryPath); if (Mode == ApplicationMode.Desktop) { IsBuiltInTheme = BuiltinExtensions.BuiltinThemeIds.Contains(thm.Id); } else { IsBuiltInTheme = BuiltinExtensions.BuiltinThemeIds.Contains(thm.Id); } var apiVesion = Mode == ApplicationMode.Desktop ? ThemeManager.DesktopApiVersion : ThemeManager.FullscreenApiVersion; if (!ThemeApiVersion.IsNullOrEmpty()) { var themeVersion = new Version(ThemeApiVersion); if (themeVersion.Major == apiVesion.Major && themeVersion <= apiVesion) { IsCompatible = true; } } } public override string ToString() { return Name; } } } ================================================ FILE: source/Playnite/MenuHelpers.cs ================================================ using Playnite.Common; using Playnite.Converters; using Playnite.Extensions.Markup; using Playnite.SDK; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media; using System.Windows.Media.Imaging; namespace Playnite { public class MenuHelpers { public static object GetIcon(string iconName, double imageHeight = 16, double imageWidth = 16) { if (iconName.IsNullOrEmpty()) { return null; } var resource = ResourceProvider.GetResource(iconName); if (resource != null) { if (resource is string stringIcon) { return Images.GetImageFromFile(ThemeFile.GetFilePath(stringIcon), BitmapScalingMode.Fant, imageHeight, imageWidth); } else if (resource is BitmapImage bitmap) { var image = new System.Windows.Controls.Image() { Source = bitmap }; RenderOptions.SetBitmapScalingMode(image, RenderOptions.GetBitmapScalingMode(bitmap)); return image; } else if (resource is TextBlock textIcon) { var text = new TextBlock { Text = textIcon.Text, FontFamily = textIcon.FontFamily, FontStyle = textIcon.FontStyle }; if (textIcon.ReadLocalValue(TextBlock.ForegroundProperty) != DependencyProperty.UnsetValue) { text.Foreground = textIcon.Foreground; } return text; } } else if (System.IO.File.Exists(iconName)) { return BitmapExtensions.BitmapFromFile(iconName)?.ToImage(); } else { var themeFile = ThemeFile.GetFilePath(iconName); if (themeFile != null) { return Images.GetImageFromFile(themeFile, BitmapScalingMode.Fant, imageHeight, imageWidth); } } return null; } public static MenuItem GenerateMenuParents(Dictionary existingItems, string menuSection, ItemCollection root, int startIndex = -1) { var sections = menuSection.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(a => a.Trim()).ToArray(); var current = ""; MenuItem prev = null; for (int i = 0; i < sections.Length; i++) { var sec = sections[i]; current += sec; if (existingItems.TryGetValue(current, out var menuItem)) { } else { menuItem = new MenuItem { Header = sec }; existingItems.Add(current, menuItem); if (i == 0) { if (startIndex >= 0) { root.Insert(startIndex, menuItem); } else { root.Add(menuItem); } } if (prev != null) { prev.Items.Add(menuItem); } } prev = menuItem; if (i == sections.Length - 1) { return menuItem; } } return null; } public static void SetEnumBinding( MenuItem target, string bindingPath, object bindingSource, object bindingEnum) { BindingOperations.SetBinding(target, MenuItem.IsCheckedProperty, new Binding { Source = bindingSource, Path = new PropertyPath(bindingPath), Converter = new EnumToBooleanConverter(), ConverterParameter = bindingEnum }); } public static void PopulateEnumOptions( ItemCollection parent, string bindingPath, object bindingSource, bool sorted = false, List ignoreValues = null) where T : Enum { var values = Enum.GetValues(typeof(T)).Cast(); if (sorted) { values = values.OrderBy(a => a.GetDescription()); } foreach (T value in values) { if (ignoreValues?.Contains(value) == true) { continue; } var headerText = value.GetDescription(); if (value is Dock dock) { headerText = DockToStringConverter.GetString(dock); } var item = new MenuItem { Header = headerText, IsCheckable = true }; SetEnumBinding(item, bindingPath, bindingSource, value); parent.Add(item); } } } } ================================================ FILE: source/Playnite/MenuItems.cs ================================================ using Playnite.Commands; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using Playnite.ViewModels; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class MenuItems { private static ILogger logger = LogManager.GetLogger(); private static object startIcon; private static object removeIcon; private static object linksIcon; private static object favoriteIcon; private static object unFavoriteIcon; private static object hideIcon; private static object unHideIcon; private static object browseIcon; private static object shortcutIcon; private static object installIcon; private static object editIcon; private static object manualIcon; private static bool iconsCached = false; private static void CacheIcons() { if (!iconsCached) { PlayniteApplication.CurrentNative.Dispatcher.Invoke(() => { startIcon = MenuHelpers.GetIcon("PlayIcon"); removeIcon = MenuHelpers.GetIcon("RemoveGameIcon"); linksIcon = MenuHelpers.GetIcon("LinksIcon"); favoriteIcon = MenuHelpers.GetIcon("AddFavoritesIcon"); unFavoriteIcon = MenuHelpers.GetIcon("RemoveFavoritesIcon"); hideIcon = MenuHelpers.GetIcon("HideIcon"); unHideIcon = MenuHelpers.GetIcon("UnHideIcon"); browseIcon = MenuHelpers.GetIcon("OpenFolderIcon"); shortcutIcon = MenuHelpers.GetIcon("DesktopShortcutIcon"); installIcon = MenuHelpers.GetIcon("InstallIcon"); editIcon = MenuHelpers.GetIcon("EditGameIcon"); manualIcon = MenuHelpers.GetIcon("ManualIcon"); iconsCached = true; }); } } public static List GetGlobalPluginCommands(MainViewModelBase model) { var items = new List(); var args = new GetMainMenuItemsArgs() { IsGlobalSearchRequest = true }; foreach (var plugin in model.Extensions.Plugins.Values) { try { var plugItems = plugin.Plugin.GetSearchGlobalCommands()?.ToList(); if (plugItems.HasItems()) { items.AddRange(plugItems); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get menu items from plugin {plugin.Description.Name}"); } } return items; } public static List GetSearchExtensionsMainMenuItem(MainViewModelBase model) { var items = new List(); var args = new GetMainMenuItemsArgs() { IsGlobalSearchRequest = true }; foreach (var plugin in model.Extensions.Plugins.Values) { try { var plugItems = plugin.Plugin.GetMainMenuItems(args); foreach (var item in plugItems ?? new List()) { if (item.Description == "-") { continue; } item.MenuSection = item.MenuSection?.TrimStart('@'); var description = item.MenuSection.IsNullOrEmpty() ? item.Description : $"{item.MenuSection.Replace("|", " > ")} > {item.Description}"; items.Add(new SearchItem(description, LOC.Activate, () => item.Action(new MainMenuItemActionArgs { SourceItem = item }), item.Icon)); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get menu items from plugin {plugin.Description.Name}"); } } foreach (var script in model.Extensions.Scripts) { if (script.SupportedMenus.Contains(Scripting.SupportedMenuMethods.MainMenu)) { try { var plugItems = script.GetMainMenuItems(args); foreach (var item in plugItems ?? new List()) { if (item.Description == "-") { continue; } item.MenuSection = item.MenuSection?.TrimStart('@'); var newItem = MainMenuItem.FromScriptMainMenuItem(item); newItem.Action = (a) => { script.InvokeFunction(item.FunctionName, new List { new ScriptMainMenuItemActionArgs() }); }; var description = item.MenuSection.IsNullOrEmpty() ? item.Description : $"{item.MenuSection.Replace("|", " > ")} > {item.Description}"; items.Add(new SearchItem(description, LOC.Activate, () => { script.InvokeFunction(item.FunctionName, new List { new ScriptMainMenuItemActionArgs { SourceItem = item } }); }, item.Icon)); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get menu items from script {script.Name}"); } } } return items; } public static List GetSearchGameMenuItems(Game game, MainViewModelBase model) { CacheIcons(); var items = new List(); // Play/Install if (game.IsInstalled) { items.Add(new SearchItem(LOC.PlayGame, LOC.Activate, () => model.StartGame(game, true), startIcon)); } else if (!game.IsCustomGame) { items.Add(new SearchItem(LOC.InstallGame, LOC.Activate, () => model.InstallGame(game), installIcon)); } // Custom Actions foreach (var task in game.GameActions?.Where(a => !a.IsPlayAction) ?? Enumerable.Empty()) { items.Add(new SearchItem(task.Name, LOC.Activate, () => model.App.GamesEditor.ActivateAction(game, task))); } // Links if (game.Links.HasItems()) { var links = new List(); foreach (var link in game.Links.Where(a => a != null)) { links.Add(new SearchItem(link.Name, LOC.Activate, () => { try { GlobalCommands.NavigateUrl(game.ExpandVariables(link.Url)); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to open url."); } }) { Description = link.Url }); } items.Add(new SearchItem(LOC.LinksLabel, new ContextSwitchSearchItemAction(LOC.Activate, new GenericListSearchContext(links, LOC.LinksLabel)), linksIcon)); } // Open Game Location if (game.IsInstalled) { items.Add(new SearchItem(LOC.OpenGameLocation, LOC.Activate, () => model.App.GamesEditor.OpenGameLocation(game), browseIcon)); } // Create Desktop Shortcut items.Add(new SearchItem(LOC.CreateDesktopShortcut, LOC.Activate, () => model.App.GamesEditor.CreateDesktopShortcut(game), shortcutIcon)); // Manual if (!game.Manual.IsNullOrEmpty()) { items.Add(new SearchItem(LOC.OpenGameManual, LOC.Activate, () => model.App.GamesEditor.OpenManual(game), manualIcon)); } // Toggle Favorites items.Add(new SearchItem( game.Favorite ? LOC.RemoveFavoriteGame : LOC.FavoriteGame, LOC.Activate, () => model.App.GamesEditor.ToggleFavoriteGame(game)) { Icon = game.Favorite ? unFavoriteIcon : favoriteIcon }); // Toggle Hide items.Add(new SearchItem( game.Hidden ? LOC.UnHideGame : LOC.HideGame, LOC.Activate, () => model.App.GamesEditor.ToggleHideGame(game)) { Icon = game.Hidden ? unHideIcon : hideIcon }); // Edit items.Add(new SearchItem(LOC.EditGame, LOC.Activate, () => model.EditGame(game), editIcon)); // Set Category items.Add(new SearchItem(LOC.SetGameCategory, LOC.Activate, () => model.AssignCategories(game))); // Set Completion Status var complStats = new List(); foreach (var status in model.Database.CompletionStatuses) { complStats.Add(new SearchItem(status.Name, LOC.Assign, () => model.App.GamesEditor.SetCompletionStatus(game, status))); } if (complStats.HasItems()) { items.Add(new SearchItem(LOC.SetCompletionStatus, new ContextSwitchSearchItemAction(LOC.Activate, new GenericListSearchContext(complStats, LOC.CompletionStatus)))); } // Extensions items var args = new GetGameMenuItemsArgs() { Games = new List(1) { game }, IsGlobalSearchRequest = true }; foreach (var plugin in model.Extensions.Plugins.Values) { try { var plugItems = plugin.Plugin.GetGameMenuItems(args); foreach (var item in plugItems ?? new List()) { if (item.Description == "-") { continue; } var description = item.MenuSection.IsNullOrEmpty() ? item.Description : $"{item.MenuSection.Replace("|", " > ")} > {item.Description}"; items.Add(new SearchItem(description, LOC.Activate, () => item.Action(new GameMenuItemActionArgs { Games = args.Games, SourceItem = item }), item.Icon)); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get menu items from plugin {plugin.Description.Name}"); } } foreach (var script in model.Extensions.Scripts) { if (script.SupportedMenus.Contains(Scripting.SupportedMenuMethods.GameMenu)) { try { var plugItems = script.GetGameMenuItems(args); foreach (var item in plugItems ?? new List()) { if (item.Description == "-") { continue; } var description = item.MenuSection.IsNullOrEmpty() ? item.Description : $"{item.MenuSection.Replace("|", " > ")} > {item.Description}"; items.Add(new SearchItem(description, LOC.Activate, () => { script.InvokeFunction(item.FunctionName, new List { new ScriptGameMenuItemActionArgs { Games = args.Games, SourceItem = item } }); }, item.Icon)); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get menu items from script {script.Name}"); } } } // Remove items.Add(new SearchItem(LOC.RemoveGame, LOC.Activate, () => model.App.GamesEditor.RemoveGame(game), removeIcon)); // Uninstall if (!game.IsCustomGame && game.IsInstalled) { items.Add(new SearchItem(LOC.UninstallGame, LOC.Activate, () => model.App.GamesEditor.UnInstallGame(game))); } return items; } } } ================================================ FILE: source/Playnite/Metadata/MetadataDownloader.cs ================================================ using Playnite.Database; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Playnite.SDK.Models; using Playnite.SDK; using Playnite.SDK.Plugins; using System.Collections.Concurrent; namespace Playnite.Metadata { public class MetadataDownloader : IDisposable { private static ILogger logger = LogManager.GetLogger(); private IGameDatabaseMain database; private readonly List metadataDownloaders; private Dictionary libraryDownloaders = new Dictionary(); public MetadataDownloader(IGameDatabaseMain database, List metadataDownloaders, List libraryPlugins) { this.database = database; this.metadataDownloaders = metadataDownloaders; foreach (var plugin in libraryPlugins) { LibraryMetadataProvider downloader = null; try { downloader = plugin.GetMetadataDownloader(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get metadata downloader from {plugin.Name} library plugin."); } libraryDownloaders.Add(plugin.Id, downloader); } } public MetadataDownloader(IGameDatabaseMain database, List metadataDownloaders, Dictionary libraryDownloaders) { this.database = database; this.metadataDownloaders = metadataDownloaders; this.libraryDownloaders = libraryDownloaders; } public void Dispose() { foreach (var downloader in libraryDownloaders.Values) { // Null check because downloader might be from library without official metadata provider downloader?.Dispose(); } } private LibraryMetadataProvider GetLibraryMetadataDownloader(Guid pluginId) { if (libraryDownloaders.ContainsKey(pluginId)) { return libraryDownloaders[pluginId]; } return null; } private GameMetadata ProcessStoreDownload(Game game) { var downloader = GetLibraryMetadataDownloader(game.PluginId); try { return downloader?.GetMetadata(game); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to download metadat from library plugin downloader {game.PluginId}."); return null; } } internal bool FieldHasValidData(MetadataField field, GameMetadata metadata) { if (metadata == null) { return false; } switch (field) { case MetadataField.Name: return !metadata.Name.IsNullOrWhiteSpace(); case MetadataField.Genres: return metadata.Genres.HasItems(); case MetadataField.ReleaseDate: return metadata.ReleaseDate != null; case MetadataField.Developers: return metadata.Developers.HasItems(); case MetadataField.Publishers: return metadata.Publishers.HasItems(); case MetadataField.Tags: return metadata.Tags.HasItems(); case MetadataField.Description: return !metadata.Description.IsNullOrWhiteSpace(); case MetadataField.Links: return metadata.Links.HasItems(); case MetadataField.CriticScore: return metadata.CriticScore != null; case MetadataField.CommunityScore: return metadata.CommunityScore != null; case MetadataField.Icon: return metadata.Icon != null; case MetadataField.CoverImage: return metadata.CoverImage != null; case MetadataField.BackgroundImage: return metadata.BackgroundImage != null; case MetadataField.Features: return metadata.Features.HasItems(); case MetadataField.AgeRating: return metadata.AgeRatings.HasItems(); case MetadataField.Series: return metadata.Series.HasItems(); case MetadataField.Region: return metadata.Regions.HasItems(); case MetadataField.Platform: return metadata.Platforms.HasItems(); case MetadataField.InstallSize: return metadata.InstallSize != null; default: throw new NotSupportedException($"Uknown metadata field {field}"); } } internal GameMetadata ProcessField( Game game, MetadataFieldSettings fieldSettings, MetadataField gameField, Dictionary existingStoreData, Dictionary existingPluginData, CancellationToken cancelToken) { if (fieldSettings.Sources.Any() == false) { return null; } var getFieldArgs = new GetMetadataFieldArgs { CancelToken = cancelToken }; foreach (var source in fieldSettings.Sources) { if (cancelToken.IsCancellationRequested) { return null; } try { // Skip Store source for manually added games. if (source == Guid.Empty && game.PluginId == Guid.Empty) { continue; } // Check if metadata from this source are already downloaded. if (existingStoreData.ContainsKey(source)) { if (FieldHasValidData(gameField, existingStoreData[source])) { return existingStoreData[source]; } else { continue; } } // Check if downloader supports this game field. if (source != Guid.Empty) { var downloader = metadataDownloaders.FirstOrDefault(a => a.Id == source); if (downloader == null) { continue; } else if (downloader.SupportedFields?.Contains(gameField) != true) { continue; } } // Download metadata. GameMetadata metadata = null; if (source == Guid.Empty) { metadata = ProcessStoreDownload(game); existingStoreData.Add(source, metadata); } else { var downloader = metadataDownloaders.FirstOrDefault(a => a.Id == source); if (downloader == null) { continue; } OnDemandMetadataProvider provider = null; if (existingPluginData.ContainsKey(source)) { provider = existingPluginData[source]; } else { provider = downloader.GetMetadataProvider(new MetadataRequestOptions(game, true)); existingPluginData.Add(source, provider); } if (provider == null) { continue; } if (!provider.AvailableFields.Contains(gameField)) { continue; } metadata = new GameMetadata(); switch (gameField) { case MetadataField.Name: metadata.Name = provider.GetName(getFieldArgs); break; case MetadataField.Genres: metadata.Genres = provider.GetGenres(getFieldArgs)?.Where(a => a != null).ToHashSet(); break; case MetadataField.ReleaseDate: metadata.ReleaseDate = provider.GetReleaseDate(getFieldArgs); break; case MetadataField.Developers: metadata.Developers = provider.GetDevelopers(getFieldArgs)?.Where(a => a != null).ToHashSet(); break; case MetadataField.Publishers: metadata.Publishers = provider.GetPublishers(getFieldArgs)?.Where(a => a != null).ToHashSet(); break; case MetadataField.Tags: metadata.Tags = provider.GetTags(getFieldArgs)?.Where(a => a != null).ToHashSet(); break; case MetadataField.Description: metadata.Description = provider.GetDescription(getFieldArgs); break; case MetadataField.Links: metadata.Links = provider.GetLinks(getFieldArgs)?.Where(a => a != null).ToList(); break; case MetadataField.CriticScore: metadata.CriticScore = provider.GetCriticScore(getFieldArgs); break; case MetadataField.CommunityScore: metadata.CommunityScore = provider.GetCommunityScore(getFieldArgs); break; case MetadataField.Icon: metadata.Icon = provider.GetIcon(getFieldArgs); break; case MetadataField.CoverImage: metadata.CoverImage = provider.GetCoverImage(getFieldArgs); break; case MetadataField.BackgroundImage: metadata.BackgroundImage = provider.GetBackgroundImage(getFieldArgs); break; case MetadataField.Features: metadata.Features = provider.GetFeatures(getFieldArgs)?.Where(a => a != null).ToHashSet(); break; case MetadataField.AgeRating: metadata.AgeRatings = provider.GetAgeRatings(getFieldArgs)?.Where(a => a != null).ToHashSet(); break; case MetadataField.Region: metadata.Regions = provider.GetRegions(getFieldArgs)?.Where(a => a != null).ToHashSet(); break; case MetadataField.Series: metadata.Series = provider.GetSeries(getFieldArgs)?.Where(a => a != null).ToHashSet(); break; case MetadataField.Platform: metadata.Platforms = provider.GetPlatforms(getFieldArgs)?.Where(a => a != null).ToHashSet(); break; case MetadataField.InstallSize: metadata.InstallSize = provider.GetInstallSize(getFieldArgs); break; default: throw new NotImplementedException(); } } if (metadata != null && FieldHasValidData(gameField, metadata)) { return metadata; } else { continue; } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to process metadata download: {gameField}, {source}"); } } return null; } public Task DownloadMetadataAsync( List games, MetadataDownloaderSettings settings, PlayniteSettings playniteSettings, Action progressCallback, CancellationToken cancelToken) { return Task.Run(() => { if (games == null || games.Count == 0) { return; } for (int i = 0; i < games.Count; i++) { Game game = null; var existingStoreData = new Dictionary(); var existingPluginData = new Dictionary(); try { if (cancelToken.IsCancellationRequested == true) { return; } GameMetadata gameData = null; // We need to get new instance from DB in case game got edited or deleted. // We don't want to block game editing while metadata is downloading for other games. game = database.Games[games[i].Id]?.GetClone(); if (game == null) { logger.Warn($"Game {game.Id} no longer in DB, skipping metadata download."); progressCallback?.Invoke(null, i, games.Count); continue; } var dataModified = false; game.PropertyChanged += (_, __) => dataModified = true; if (game != null) { progressCallback?.Invoke(game, i, games.Count); } logger.Debug($"Downloading metadata for {game.Name}, {game.GameId}, {game.PluginId}"); // Name if (settings.Name.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && string.IsNullOrEmpty(game.Name))) { gameData = ProcessField(game, settings.Name, MetadataField.Name, existingStoreData, existingPluginData, cancelToken); if (!string.IsNullOrEmpty(gameData?.Name)) { game.Name = StringExtensions.RemoveTrademarks(gameData.Name); } } } // Genre if (settings.Genre.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.GenreIds.HasItems())) { gameData = ProcessField(game, settings.Genre, MetadataField.Genres, existingStoreData, existingPluginData, cancelToken); if (gameData?.Genres.HasItems() == true) { game.GenreIds = database.Genres.Add(gameData.Genres).Select(a => a.Id).ToList(); } } } // Release Date if (settings.ReleaseDate.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && game.ReleaseDate == null)) { gameData = ProcessField(game, settings.ReleaseDate, MetadataField.ReleaseDate, existingStoreData, existingPluginData, cancelToken); game.ReleaseDate = gameData?.ReleaseDate ?? game.ReleaseDate; } } // Developer if (settings.Developer.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.DeveloperIds.HasItems())) { gameData = ProcessField(game, settings.Developer, MetadataField.Developers, existingStoreData, existingPluginData, cancelToken); if (gameData?.Developers.HasItems() == true) { game.DeveloperIds = database.Companies.Add(gameData.Developers).Select(a => a.Id).ToList(); } } } // Publisher if (settings.Publisher.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.PublisherIds.HasItems())) { gameData = ProcessField(game, settings.Publisher, MetadataField.Publishers, existingStoreData, existingPluginData, cancelToken); if (gameData?.Publishers.HasItems() == true) { game.PublisherIds = database.Companies.Add(gameData.Publishers).Select(a => a.Id).ToList(); } } } // Tags if (settings.Tag.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.TagIds.HasItems())) { gameData = ProcessField(game, settings.Tag, MetadataField.Tags, existingStoreData, existingPluginData, cancelToken); if (gameData?.Tags.HasItems() == true) { game.TagIds = database.Tags.Add(gameData.Tags).Select(a => a.Id).ToList(); } } } // Features if (settings.Feature.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.FeatureIds.HasItems())) { gameData = ProcessField(game, settings.Feature, MetadataField.Features, existingStoreData, existingPluginData, cancelToken); if (gameData?.Features.HasItems() == true) { game.FeatureIds = database.Features.Add(gameData.Features).Select(a => a.Id).ToList(); } } } // Description if (settings.Description.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && string.IsNullOrEmpty(game.Description))) { gameData = ProcessField(game, settings.Description, MetadataField.Description, existingStoreData, existingPluginData, cancelToken); game.Description = string.IsNullOrEmpty(gameData?.Description) == true ? game.Description : gameData.Description; } } // Links if (settings.Links.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.Links.HasItems())) { gameData = ProcessField(game, settings.Links, MetadataField.Links, existingStoreData, existingPluginData, cancelToken); if (gameData?.Links.HasItems() == true) { game.Links = gameData.Links.ToObservable(); } } } // Age rating if (settings.AgeRating.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.AgeRatingIds.HasItems())) { gameData = ProcessField(game, settings.AgeRating, MetadataField.AgeRating, existingStoreData, existingPluginData, cancelToken); if (gameData?.AgeRatings.HasItems() == true) { game.AgeRatingIds = database.AgeRatings.Add(gameData.AgeRatings).Select(a => a.Id).ToList(); } } } // Region if (settings.Region.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.RegionIds.HasItems())) { gameData = ProcessField(game, settings.Region, MetadataField.Region, existingStoreData, existingPluginData, cancelToken); if (gameData?.Regions.HasItems() == true) { game.RegionIds = database.Regions.Add(gameData.Regions).Select(a => a.Id).ToList(); } } } // Series if (settings.Series.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.SeriesIds.HasItems())) { gameData = ProcessField(game, settings.Series, MetadataField.Series, existingStoreData, existingPluginData, cancelToken); if (gameData?.Series.HasItems() == true) { game.SeriesIds = database.Series.Add(gameData.Series).Select(a => a.Id).ToList(); } } } // Platform if (settings.Platform.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && !game.PlatformIds.HasItems())) { gameData = ProcessField(game, settings.Platform, MetadataField.Platform, existingStoreData, existingPluginData, cancelToken); if (gameData?.Platforms.HasItems() == true) { game.PlatformIds = database.Platforms.Add(gameData.Platforms).Select(a => a.Id).ToList(); } } } // Critic Score if (settings.CriticScore.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && game.CriticScore == null)) { gameData = ProcessField(game, settings.CriticScore, MetadataField.CriticScore, existingStoreData, existingPluginData, cancelToken); game.CriticScore = gameData?.CriticScore == null ? game.CriticScore : gameData.CriticScore; } } // Community Score if (settings.CommunityScore.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && game.CommunityScore == null)) { gameData = ProcessField(game, settings.CommunityScore, MetadataField.CommunityScore, existingStoreData, existingPluginData, cancelToken); game.CommunityScore = gameData?.CommunityScore == null ? game.CommunityScore : gameData.CommunityScore; } } // BackgroundImage if (settings.BackgroundImage.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && string.IsNullOrEmpty(game.BackgroundImage))) { gameData = ProcessField(game, settings.BackgroundImage, MetadataField.BackgroundImage, existingStoreData, existingPluginData, cancelToken); if (gameData?.BackgroundImage != null) { if (playniteSettings.DownloadBackgroundsImmediately && gameData.BackgroundImage.HasImageData) { game.BackgroundImage = database.AddFile(gameData.BackgroundImage, game.Id, true, cancelToken); } else if (!playniteSettings.DownloadBackgroundsImmediately && !gameData.BackgroundImage.Path.IsNullOrEmpty()) { game.BackgroundImage = gameData.BackgroundImage.Path; } else if (gameData.BackgroundImage.HasImageData) { game.BackgroundImage = database.AddFile(gameData.BackgroundImage, game.Id, true, cancelToken); } } } } // Cover if (settings.CoverImage.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && string.IsNullOrEmpty(game.CoverImage))) { gameData = ProcessField(game, settings.CoverImage, MetadataField.CoverImage, existingStoreData, existingPluginData, cancelToken); if (gameData?.CoverImage != null) { game.CoverImage = database.AddFile(gameData.CoverImage, game.Id, true, cancelToken); } } } // Icon if (settings.Icon.Import) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && string.IsNullOrEmpty(game.Icon))) { gameData = ProcessField(game, settings.Icon, MetadataField.Icon, existingStoreData, existingPluginData, cancelToken); if (gameData?.Icon != null) { game.Icon = database.AddFile(gameData.Icon, game.Id, true, cancelToken); } } } // InstallSize // Installed games with assigned value are skipped since their scanned size // using local files will be more accurate than the ones provided via metadata var isInstalledAndHasValue = game.IsInstalled && game.InstallSize != null; if (settings.InstallSize.Import && !isInstalledAndHasValue) { if (!settings.SkipExistingValues || (settings.SkipExistingValues && game.InstallSize == null)) { gameData = ProcessField(game, settings.InstallSize, MetadataField.InstallSize, existingStoreData, existingPluginData, cancelToken); if (gameData?.InstallSize != null) { game.InstallSize = gameData.InstallSize; } } } // Just to be sure check if somebody didn't remove game while downloading data if (database.Games.FirstOrDefault(a => a.Id == games[i].Id) != null && dataModified) { game.Modified = DateTime.Now; database.Games.Update(game); } else { logger.Warn($"Game {game.Id} no longer in DB, skipping metadata update in DB."); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to download metadata for game {game?.Name}, {game?.Id}"); } finally { foreach (var plugin in existingPluginData.Values) { // This will be null in case a plugin provider says it can provide metadata for a field, // but then actually doesn't return any metadata provider implementation. plugin?.Dispose(); } } } }); } } } ================================================ FILE: source/Playnite/Metadata/MetadataDownloaderSettings.cs ================================================ using Newtonsoft.Json; using Playnite.SDK; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Metadata { public enum MetadataGamesSource { AllFromDB, Selected, Filtered } public class MetadataFieldSettings : ObservableObject { private bool import = true; public bool Import { get => import; set { import = value; OnPropertyChanged(); } } private List sources = new List(); public List Sources { get => sources; set { sources = value; OnPropertyChanged(); } } public MetadataFieldSettings() { } public MetadataFieldSettings(bool import, List sources) { Import = import; if (sources.HasItems()) { Sources = sources.ToList(); } } } public class MetadataDownloaderSettings : ObservableObject { private MetadataGamesSource gamesSource = MetadataGamesSource.AllFromDB; [JsonIgnore] public MetadataGamesSource GamesSource { get { return gamesSource; } set { gamesSource = value; OnPropertyChanged(); } } private bool skipExistingValues = true; [JsonIgnore] public bool SkipExistingValues { get { return skipExistingValues; } set { skipExistingValues = value; OnPropertyChanged(); } } private MetadataFieldSettings name = new MetadataFieldSettings(); public MetadataFieldSettings Name { get => name; set { name = value; OnPropertyChanged(); } } private MetadataFieldSettings genre = new MetadataFieldSettings(); public MetadataFieldSettings Genre { get => genre; set { genre = value; OnPropertyChanged(); } } private MetadataFieldSettings releaseDate = new MetadataFieldSettings(); public MetadataFieldSettings ReleaseDate { get => releaseDate; set { releaseDate = value; OnPropertyChanged(); } } private MetadataFieldSettings developer = new MetadataFieldSettings(); public MetadataFieldSettings Developer { get => developer; set { developer = value; OnPropertyChanged(); } } private MetadataFieldSettings publisher = new MetadataFieldSettings(); public MetadataFieldSettings Publisher { get => publisher; set { publisher = value; OnPropertyChanged(); } } private MetadataFieldSettings tag = new MetadataFieldSettings(); public MetadataFieldSettings Tag { get => tag; set { tag = value; OnPropertyChanged(); } } private MetadataFieldSettings feature = new MetadataFieldSettings(); public MetadataFieldSettings Feature { get => feature; set { feature = value; OnPropertyChanged(); } } private MetadataFieldSettings description = new MetadataFieldSettings(); public MetadataFieldSettings Description { get => description; set { description = value; OnPropertyChanged(); } } private MetadataFieldSettings coverImage = new MetadataFieldSettings(); public MetadataFieldSettings CoverImage { get => coverImage; set { coverImage = value; OnPropertyChanged(); } } private MetadataFieldSettings backgroundImage = new MetadataFieldSettings(); public MetadataFieldSettings BackgroundImage { get => backgroundImage; set { backgroundImage = value; OnPropertyChanged(); } } private MetadataFieldSettings icon = new MetadataFieldSettings(); public MetadataFieldSettings Icon { get => icon; set { icon = value; OnPropertyChanged(); } } private MetadataFieldSettings links = new MetadataFieldSettings(); public MetadataFieldSettings Links { get => links; set { links = value; OnPropertyChanged(); } } private MetadataFieldSettings criticScore = new MetadataFieldSettings(); public MetadataFieldSettings CriticScore { get => criticScore; set { criticScore = value; OnPropertyChanged(); } } private MetadataFieldSettings communityScore = new MetadataFieldSettings(); public MetadataFieldSettings CommunityScore { get => communityScore; set { communityScore = value; OnPropertyChanged(); } } private MetadataFieldSettings ageRating = new MetadataFieldSettings(); public MetadataFieldSettings AgeRating { get => ageRating; set { ageRating = value; OnPropertyChanged(); } } private MetadataFieldSettings series = new MetadataFieldSettings(); public MetadataFieldSettings Series { get => series; set { series = value; OnPropertyChanged(); } } private MetadataFieldSettings region = new MetadataFieldSettings(); public MetadataFieldSettings Region { get => region; set { region = value; OnPropertyChanged(); } } private MetadataFieldSettings platform = new MetadataFieldSettings(); public MetadataFieldSettings Platform { get => platform; set { platform = value; OnPropertyChanged(); } } private MetadataFieldSettings installSize = new MetadataFieldSettings(); public MetadataFieldSettings InstallSize { get => installSize; set { installSize = value; OnPropertyChanged(); } } public MetadataDownloaderSettings() { } public static MetadataDownloaderSettings GetDefaultSettings() { var igdbPluginId = BuiltinExtensions.GetIdFromExtension(BuiltinExtension.IgdbMetadata); var settings = new MetadataDownloaderSettings(); settings.ConfigureFields(new List { igdbPluginId }, true); settings.Description.Sources = new List { Guid.Empty, igdbPluginId }; settings.Icon.Sources = new List { Guid.Empty, igdbPluginId }; settings.BackgroundImage.Sources = new List { Guid.Empty, igdbPluginId }; settings.CoverImage.Sources = new List { igdbPluginId, Guid.Empty }; settings.Name.Import = false; return settings; } public void ConfigureFields(List sources, bool import) { Name = new MetadataFieldSettings(import, sources); Genre = new MetadataFieldSettings(import, sources); Description = new MetadataFieldSettings(import, sources); Developer = new MetadataFieldSettings(import, sources); Publisher = new MetadataFieldSettings(import, sources); Tag = new MetadataFieldSettings(import, sources); Links = new MetadataFieldSettings(import, sources); CoverImage = new MetadataFieldSettings(import, sources); BackgroundImage = new MetadataFieldSettings(import, sources); Icon = new MetadataFieldSettings(import, sources); ReleaseDate = new MetadataFieldSettings(import, sources); CommunityScore = new MetadataFieldSettings(import, sources); CriticScore = new MetadataFieldSettings(import, sources); Feature = new MetadataFieldSettings(import, sources); AgeRating = new MetadataFieldSettings(import, sources); Platform = new MetadataFieldSettings(import, sources); Series = new MetadataFieldSettings(import, sources); Region = new MetadataFieldSettings(import, sources); InstallSize = new MetadataFieldSettings(import, sources); } } } ================================================ FILE: source/Playnite/Metadata/MetadataFileExtensions.cs ================================================ using Playnite.Common; using Playnite.Common.Web; using Playnite.SDK.Models; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite { public static class MetadataFileExtensions { /// /// Returns local file path of file. If link, downloads file to Playnite temp. If raw content, saves file to Playnite temp. /// /// /// /// public static string GetLocalFile(this MetadataFile file, CancellationToken cancelToken) { if (!file.HasImageData) { return null; } if (file.HasContent) { var resultPath = Path.Combine(PlaynitePaths.TempPath, Guid.NewGuid() + Path.GetExtension(file.FileName)); FileSystem.PrepareSaveFile(resultPath); File.WriteAllBytes(resultPath, file.Content); return resultPath; } else { if (file.Path.IsHttpUrl()) { var extension = Path.GetExtension(new Uri(file.Path).AbsolutePath); var resultPath = Path.Combine(PlaynitePaths.TempPath, Guid.NewGuid() + extension); FileSystem.PrepareSaveFile(resultPath); HttpDownloader.DownloadFile(file.Path, resultPath, cancelToken); if (cancelToken.IsCancellationRequested) { if (File.Exists(resultPath)) { File.Delete(resultPath); } return null; } else { return resultPath; } } else { return file.Path; } } } } } ================================================ FILE: source/Playnite/Native/Fileapi.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public class Fileapi { public const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000; } } ================================================ FILE: source/Playnite/Native/Gdi32.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public class Gdi32 { private const string dllName = "Gdi32.dll"; [DllImport(dllName, SetLastError = true)] public static extern bool DeleteObject(IntPtr hObject); } } ================================================ FILE: source/Playnite/Native/Kernel32.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public class Kernel32 { private const string dllName = "Kernel32.dll"; [DllImport(dllName, SetLastError = true, CharSet = CharSet.Auto)] public extern static uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags); [DllImport(dllName, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool CloseHandle(IntPtr hObject); [DllImport(dllName, CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr CreateFile( [MarshalAs(UnmanagedType.LPTStr)] string filename, [MarshalAs(UnmanagedType.U4)] uint access, [MarshalAs(UnmanagedType.U4)] FileShare share, IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes, IntPtr templateFile); [DllImport(dllName, SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); [DllImport(dllName, SetLastError = true)] public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); [DllImport(dllName, SetLastError = true)] public static extern IntPtr FindResource(IntPtr hModule, string lpName, string lpType); [DllImport(dllName, SetLastError = true)] public static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo); [DllImport(dllName, SetLastError = true)] public static extern bool FreeLibrary(IntPtr hModule); [DllImport(dllName, SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool EnumResourceNames(IntPtr hModule, IntPtr lpszType, ENUMRESNAMEPROC lpEnumFunc, IntPtr lParam); [DllImport(dllName, SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType); [DllImport(dllName, SetLastError = true)] public static extern IntPtr LockResource(IntPtr hResData); [DllImport(dllName, SetLastError = true)] public static extern IntPtr GetCurrentProcess(); [DllImport(dllName, SetLastError = true, CharSet = CharSet.Unicode)] public static extern int QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax); [DllImport(dllName, SetLastError = true, CharSet = CharSet.Auto)] public static extern bool CreateProcess( string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); [DllImport(dllName, SetLastError = true)] public static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, int processId); [DllImport(dllName, CharSet = CharSet.Auto)] public static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] uint dwFlags, [Out] StringBuilder lpExeName, [In, Out] ref uint lpdwSize); [DllImport(dllName, SetLastError = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.U4)] public static extern uint GetFileAttributesW(string lpFileName); [DllImport(dllName, SetLastError = true, CharSet = CharSet.Unicode)] public static extern uint GetCompressedFileSizeW( [In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName, [Out, MarshalAs(UnmanagedType.U4)] out uint lpFileSizeHigh); [DllImport(dllName, SetLastError = true, PreserveSig = true, CharSet = CharSet.Unicode)] public static extern int GetDiskFreeSpaceW( [In, MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName, out uint lpSectorsPerCluster, out uint lpBytesPerSector, out uint lpNumberOfFreeClusters, out uint lpTotalNumberOfClusters); } } ================================================ FILE: source/Playnite/Native/Ntdll.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { [Flags] public enum ProcessAccessFlags : uint { All = 0x001F0FFF, Terminate = 0x00000001, CreateThread = 0x00000002, VirtualMemoryOperation = 0x00000008, VirtualMemoryRead = 0x00000010, VirtualMemoryWrite = 0x00000020, DuplicateHandle = 0x00000040, CreateProcess = 0x000000080, SetQuota = 0x00000100, SetInformation = 0x00000200, QueryInformation = 0x00000400, QueryLimitedInformation = 0x00001000, Synchronize = 0x00100000 } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct PROCESS_BASIC_INFORMATION { public IntPtr ExitStatus; public IntPtr PebBaseAddress; public IntPtr AffinityMask; public IntPtr BasePriority; public UIntPtr UniqueProcessId; public IntPtr InheritedFromUniqueProcessId; public int Size { get { return (int)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); } } } public enum PROCESSINFOCLASS : int { ProcessBasicInformation = 0, // 0, q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX ProcessIoCounters, // q: IO_COUNTERS ProcessVmCounters, // q: VM_COUNTERS, VM_COUNTERS_EX ProcessTimes, // q: KERNEL_USER_TIMES ProcessBasePriority, // s: KPRIORITY ProcessRaisePriority, // s: ULONG ProcessDebugPort, // q: HANDLE ProcessExceptionPort, // s: HANDLE ProcessAccessToken, // s: PROCESS_ACCESS_TOKEN ProcessLdtInformation, // 10 ProcessLdtSize, ProcessDefaultHardErrorMode, // qs: ULONG ProcessIoPortHandlers, // (kernel-mode only) ProcessPooledUsageAndLimits, // q: POOLED_USAGE_AND_LIMITS ProcessWorkingSetWatch, // q: PROCESS_WS_WATCH_INFORMATION[]; s: void ProcessUserModeIOPL, ProcessEnableAlignmentFaultFixup, // s: BOOLEAN ProcessPriorityClass, // qs: PROCESS_PRIORITY_CLASS ProcessWx86Information, ProcessHandleCount, // 20, q: ULONG, PROCESS_HANDLE_INFORMATION ProcessAffinityMask, // s: KAFFINITY ProcessPriorityBoost, // qs: ULONG ProcessDeviceMap, // qs: PROCESS_DEVICEMAP_INFORMATION, PROCESS_DEVICEMAP_INFORMATION_EX ProcessSessionInformation, // q: PROCESS_SESSION_INFORMATION ProcessForegroundInformation, // s: PROCESS_FOREGROUND_BACKGROUND ProcessWow64Information, // q: ULONG_PTR ProcessImageFileName, // q: UNICODE_STRING ProcessLUIDDeviceMapsEnabled, // q: ULONG ProcessBreakOnTermination, // qs: ULONG ProcessDebugObjectHandle, // 30, q: HANDLE ProcessDebugFlags, // qs: ULONG ProcessHandleTracing, // q: PROCESS_HANDLE_TRACING_QUERY; s: size 0 disables, otherwise enables ProcessIoPriority, // qs: ULONG ProcessExecuteFlags, // qs: ULONG ProcessResourceManagement, ProcessCookie, // q: ULONG ProcessImageInformation, // q: SECTION_IMAGE_INFORMATION ProcessCycleTime, // q: PROCESS_CYCLE_TIME_INFORMATION ProcessPagePriority, // q: ULONG ProcessInstrumentationCallback, // 40 ProcessThreadStackAllocation, // s: PROCESS_STACK_ALLOCATION_INFORMATION, PROCESS_STACK_ALLOCATION_INFORMATION_EX ProcessWorkingSetWatchEx, // q: PROCESS_WS_WATCH_INFORMATION_EX[] ProcessImageFileNameWin32, // q: UNICODE_STRING ProcessImageFileMapping, // q: HANDLE (input) ProcessAffinityUpdateMode, // qs: PROCESS_AFFINITY_UPDATE_MODE ProcessMemoryAllocationMode, // qs: PROCESS_MEMORY_ALLOCATION_MODE ProcessGroupInformation, // q: USHORT[] ProcessTokenVirtualizationEnabled, // s: ULONG ProcessConsoleHostProcess, // q: ULONG_PTR ProcessWindowInformation, // 50, q: PROCESS_WINDOW_INFORMATION ProcessHandleInformation, // q: PROCESS_HANDLE_SNAPSHOT_INFORMATION // since WIN8 ProcessMitigationPolicy, // s: PROCESS_MITIGATION_POLICY_INFORMATION ProcessDynamicFunctionTableInformation, ProcessHandleCheckingMode, ProcessKeepAliveCount, // q: PROCESS_KEEPALIVE_COUNT_INFORMATION ProcessRevokeFileHandles, // s: PROCESS_REVOKE_FILE_HANDLES_INFORMATION MaxProcessInfoClass }; public class Ntdll { private const string dllName = "Ntdll.dll"; [DllImport(dllName, SetLastError = true)] public static extern int NtQueryInformationProcess(IntPtr hProcess, PROCESSINFOCLASS pic, ref PROCESS_BASIC_INFORMATION pbi, int cb, out int pSize); } } ================================================ FILE: source/Playnite/Native/Powrprof.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public class Powrprof { private const string dllName = "Powrprof.dll"; [DllImport(dllName, CharSet = CharSet.Auto, ExactSpelling = true)] public static extern bool SetSuspendState(bool hiberate, bool forceCritical, bool disableWakeEvent); } } ================================================ FILE: source/Playnite/Native/Processthreadsapi.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct STARTUPINFO { public Int32 cb; public IntPtr lpReserved; public IntPtr lpDesktop; public IntPtr lpTitle; public Int32 dwX; public Int32 dwY; public Int32 dwXSize; public Int32 dwYSize; public Int32 dwXCountChars; public Int32 dwYCountChars; public Int32 dwFillAttribute; public Int32 dwFlags; public Int16 wShowWindow; public Int16 cbReserved2; public IntPtr lpReserved2; public IntPtr hStdInput; public IntPtr hStdOutput; public IntPtr hStdError; } [StructLayout(LayoutKind.Sequential)] public struct PROCESS_INFORMATION { public IntPtr hProcess; public IntPtr hThread; public int dwProcessId; public int dwThreadId; } } ================================================ FILE: source/Playnite/Native/Psapi.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public class Psapi { private const string dllName = "Psapi.dll"; [DllImport(dllName, SetLastError = true, CharSet = CharSet.Unicode)] public static extern int GetMappedFileName(IntPtr hProcess, IntPtr lpv, StringBuilder lpFilename, int nSize); } } ================================================ FILE: source/Playnite/Native/Shell32.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public class Shell32 { private const string dllName = "Shell32.dll"; [DllImport(dllName)] public extern static int ExtractIconEx(string libName, int iconIndex, IntPtr[] largeIcon, IntPtr[] smallIcon, uint nIcons); } } ================================================ FILE: source/Playnite/Native/Shlwapi.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { [Flags] public enum MatchPatternFlags : uint { Normal = 0x00000000, // PMSF_NORMAL Multiple = 0x00000001, // PMSF_MULTIPLE DontStripSpaces = 0x00010000 // PMSF_DONT_STRIP_SPACES } public class Shlwapi { private const string dllName = "Shlwapi.dll"; [DllImport(dllName, BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)] public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved); [DllImport(dllName, SetLastError = false)] public static extern int PathMatchSpecExW([MarshalAs(UnmanagedType.LPWStr)] string file, [MarshalAs(UnmanagedType.LPWStr)] string spec, MatchPatternFlags flags); } } ================================================ FILE: source/Playnite/Native/User32.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Security; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public class User32 { private const string dllName = "User32.dll"; [DllImport(dllName)] public static extern int GetDisplayConfigBufferSizes(QUERY_DEVICE_CONFIG_FLAGS flags, out uint numPathArrayElements, out uint numModeInfoArrayElements); [DllImport(dllName)] public static extern int QueryDisplayConfig( QUERY_DEVICE_CONFIG_FLAGS flags, ref uint numPathArrayElements, [Out] DISPLAYCONFIG_PATH_INFO[] PathInfoArray, ref uint numModeInfoArrayElements, [Out] DISPLAYCONFIG_MODE_INFO[] ModeInfoArray, IntPtr currentTopologyId); [DllImport(dllName)] public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName); [DllImport(dllName)] public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO requestPacket); [DllImport(dllName)] public static extern int DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_DEVICE_INFO_HEADER setPacket); [DllImport(dllName)] public static extern IntPtr GetForegroundWindow(); [DllImport(dllName)] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); [DllImport(dllName)] public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach); [DllImport(dllName)] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport(dllName)] public static extern int ShowCursor(bool bShow); [DllImport(dllName, EntryPoint = "SetWindowPos", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool _SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SWP uFlags); public static void SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SWP uFlags) { if (!_SetWindowPos(hWnd, hWndInsertAfter, x, y, cx, cy, uFlags)) { throw new Win32Exception(); } } [DllImport("user32", CallingConvention = CallingConvention.Winapi)] public static extern IntPtr DefWindowProc([In] IntPtr hwnd, [In] int msg, [In] IntPtr wParam, [In] IntPtr lParam); [DllImport(dllName, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam); [DllImport(dllName, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam); [DllImport(dllName, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam); [DllImport(dllName, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, ref IntPtr lParam); [DllImport(dllName, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, IntPtr lParam); [DllImport(dllName, CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, uint wParam, IntPtr lParam); [DllImport("Wintrust.dll", PreserveSig = true, SetLastError = false)] public static extern uint WinVerifyTrust(IntPtr hWnd, IntPtr pgActionID, IntPtr pWinTrustData); [DllImport(dllName, CharSet = CharSet.Auto)] public static extern IntPtr FindWindow(string strClassName, string strWindowName); [DllImport(dllName)] public static extern IntPtr MonitorFromWindow(IntPtr hwnd, MonitorOptions dwFlags); [DllImport(dllName, EntryPoint = "GetMonitorInfo", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool _GetMonitorInfo(IntPtr hMonitor, [In, Out] MONITORINFO lpmi); [DllImport(dllName, EntryPoint = "DestroyIcon", SetLastError = true)] public static extern int DestroyIcon(IntPtr hIcon); public static MONITORINFO GetMonitorInfo(IntPtr hMonitor) { var mi = new MONITORINFO(); if (!_GetMonitorInfo(hMonitor, mi)) { throw new Win32Exception(); } return mi; } [DllImport(dllName, SetLastError = true, CharSet = CharSet.Auto)] public static extern int GetWindowLong(IntPtr hWnd, int nIndex); [DllImport(dllName, CharSet = CharSet.Auto)] public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport(dllName, SetLastError = true, CharSet = CharSet.Auto)] public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); [DllImport(dllName, SetLastError = true, CharSet = CharSet.Auto)] public static extern bool UnregisterHotKey(IntPtr hWnd, int id); [DllImport(dllName, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool GetCursorPos(out POINT lpPoint); [DllImport(dllName, SetLastError = true)] public static extern bool LockWorkStation(); [Flags] public enum ExitWindowsFlags { EWX_LOGOFF = 0x00000000, EWX_SHUTDOWN = 0x00000001, EWX_REBOOT = 0x00000002, EWX_FORCE = 0x00000004, EWX_POWEROFF = 0x00000008, EWX_FORCEIFHUNG = 0x00000010, EWX_QUICKRESOLVE = 0x00000020, EWX_RESTARTAPPS = 0x00000040, EWX_HYBRID_SHUTDOWN = 0x00400000, EWX_BOOTOPTIONS = 0x01000000, EWX_ARSO = 0x04000000 // Undocumented https://stackoverflow.com/a/72069512/1107424 } [DllImport(dllName, SetLastError = true)] public static extern bool ExitWindowsEx(ExitWindowsFlags uFlags, uint dwReason); [StructLayout(LayoutKind.Sequential)] internal struct TOKEN_PRIVILEGES { internal int PrivilegeCount; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] internal int[] Privileges; } [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid); [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurity] private static extern int OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int AdjustTokenPrivileges(IntPtr TokenHandle, int DisableAllPrivileges, IntPtr NewState, int BufferLength, IntPtr PreviousState, ref int ReturnLength); private const int SE_PRIVILEGE_ENABLED = 0x00000002; private const int TOKEN_ADJUST_PRIVILEGES = 0X00000020; private const int TOKEN_QUERY = 0X00000008; private const int TOKEN_ALL_ACCESS = 0X001f01ff; private const int PROCESS_QUERY_INFORMATION = 0X00000400; public static bool EnablePrivilege(string lpszPrivilege, bool bEnablePrivilege) { bool retval = false; int ltkpOld = 0; IntPtr hToken = IntPtr.Zero; TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES(); tkp.Privileges = new int[3]; TOKEN_PRIVILEGES tkpOld = new TOKEN_PRIVILEGES(); tkpOld.Privileges = new int[3]; LUID tLUID = new LUID(); tkp.PrivilegeCount = 1; if (bEnablePrivilege) tkp.Privileges[2] = SE_PRIVILEGE_ENABLED; else tkp.Privileges[2] = 0; if (LookupPrivilegeValue(null, lpszPrivilege, ref tLUID)) { var proc = Process.GetCurrentProcess(); if (proc.Handle != IntPtr.Zero) { if (OpenProcessToken(proc.Handle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref hToken) != 0) { tkp.PrivilegeCount = 1; tkp.Privileges[2] = SE_PRIVILEGE_ENABLED; tkp.Privileges[1] = tLUID.HighPart; tkp.Privileges[0] = tLUID.LowPart; const int bufLength = 256; IntPtr tu = Marshal.AllocHGlobal(bufLength); Marshal.StructureToPtr(tkp, tu, true); if (AdjustTokenPrivileges(hToken, 0, tu, bufLength, IntPtr.Zero, ref ltkpOld) != 0) { // successful AdjustTokenPrivileges doesn't mean privilege could be changed if (Marshal.GetLastWin32Error() == 0) { retval = true; // Token changed } } TOKEN_PRIVILEGES tokp = (TOKEN_PRIVILEGES)Marshal.PtrToStructure(tu, typeof(TOKEN_PRIVILEGES)); Marshal.FreeHGlobal(tu); } } } if (hToken != IntPtr.Zero) { Kernel32.CloseHandle(hToken); } return retval; } } } ================================================ FILE: source/Playnite/Native/WinError.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public class WinError { public const int ERROR_SUCCESS = 0; } } ================================================ FILE: source/Playnite/Native/Winbase.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { [UnmanagedFunctionPointer(CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Unicode)] public delegate bool ENUMRESNAMEPROC(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam); [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { public int nLength; public IntPtr lpSecurityDescriptor; public int bInheritHandle; } } ================================================ FILE: source/Playnite/Native/Windef.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public static class Windef { internal static int LOWORD(int i) { return (short)(i & 0xFFFF); } } [StructLayout(LayoutKind.Sequential)] public struct POINTL { public int x; public int y; } [StructLayout(LayoutKind.Sequential)] public struct SIZE { public int cx; public int cy; } [Serializable] [StructLayout(LayoutKind.Sequential)] public struct POINT { private int _x; private int _y; public POINT(int x, int y) { _x = x; _y = y; } public int X { get { return _x; } set { _x = value; } } public int Y { get { return _y; } set { _y = value; } } public override bool Equals(object obj) { if (obj is POINT) { var point = (POINT)obj; return point._x == _x && point._y == _y; } return base.Equals(obj); } public override int GetHashCode() { return _x.GetHashCode() ^ _y.GetHashCode(); } public static bool operator ==(POINT a, POINT b) { return a._x == b._x && a._y == b._y; } public static bool operator !=(POINT a, POINT b) { return !(a == b); } } [StructLayout(LayoutKind.Sequential, Pack = 0)] public struct RECT { private int _left; private int _top; private int _right; private int _bottom; public static readonly RECT Empty = new RECT(); public RECT(int left, int top, int right, int bottom) { this._left = left; this._top = top; this._right = right; this._bottom = bottom; } public RECT(RECT rcSrc) { _left = rcSrc.Left; _top = rcSrc.Top; _right = rcSrc.Right; _bottom = rcSrc.Bottom; } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void Offset(int dx, int dy) { _left += dx; _top += dy; _right += dx; _bottom += dy; } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public int Left { get { return _left; } set { _left = value; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public int Right { get { return _right; } set { _right = value; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public int Top { get { return _top; } set { _top = value; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public int Bottom { get { return _bottom; } set { _bottom = value; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public int Width { get { return _right - _left; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public int Height { get { return _bottom - _top; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public POINT Position { get { return new POINT { X = _left, Y = _top }; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public SIZE Size { get { return new SIZE { cx = Width, cy = Height }; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static RECT Union(RECT rect1, RECT rect2) { return new RECT { Left = Math.Min(rect1.Left, rect2.Left), Top = Math.Min(rect1.Top, rect2.Top), Right = Math.Max(rect1.Right, rect2.Right), Bottom = Math.Max(rect1.Bottom, rect2.Bottom), }; } public override bool Equals(object obj) { try { var rc = (RECT)obj; return rc._bottom == _bottom && rc._left == _left && rc._right == _right && rc._top == _top; } catch (InvalidCastException) { return false; } } public bool IsEmpty { get { // BUGBUG : On Bidi OS (hebrew arabic) left > right return Left >= Right || Top >= Bottom; } } public override string ToString() { if (this == Empty) return "RECT {Empty}"; return "RECT { left : " + Left + " / top : " + Top + " / right : " + Right + " / bottom : " + Bottom + " }"; } public override int GetHashCode() { return (_left << 16 | Windef.LOWORD(_right)) ^ (_top << 16 | Windef.LOWORD(_bottom)); } public static bool operator ==(RECT rect1, RECT rect2) { return (rect1.Left == rect2.Left && rect1.Top == rect2.Top && rect1.Right == rect2.Right && rect1.Bottom == rect2.Bottom); } public static bool operator !=(RECT rect1, RECT rect2) { return !(rect1 == rect2); } } } ================================================ FILE: source/Playnite/Native/Wingdi.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_PATH_INFO { public DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo; public DISPLAYCONFIG_PATH_TARGET_INFO targetInfo; public uint flags; } [StructLayout(LayoutKind.Explicit)] public struct DISPLAYCONFIG_MODE_INFO_UNION { [FieldOffset(0)] public DISPLAYCONFIG_TARGET_MODE targetMode; [FieldOffset(0)] public DISPLAYCONFIG_SOURCE_MODE sourceMode; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_MODE_INFO { public DISPLAYCONFIG_MODE_INFO_TYPE infoType; public uint id; public LUID adapterId; public DISPLAYCONFIG_MODE_INFO_UNION modeInfo; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_PATH_SOURCE_INFO { public LUID adapterId; public uint id; public uint modeInfoIdx; public uint statusFlags; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_PATH_TARGET_INFO { public LUID adapterId; public uint id; public uint modeInfoIdx; private DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; private DISPLAYCONFIG_ROTATION rotation; private DISPLAYCONFIG_SCALING scaling; private DISPLAYCONFIG_RATIONAL refreshRate; private DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; public bool targetAvailable; public uint statusFlags; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_TARGET_MODE { public DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_SOURCE_MODE { public uint width; public uint height; public DISPLAYCONFIG_PIXELFORMAT pixelFormat; public POINTL position; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS { public uint value; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_RATIONAL { public uint Numerator; public uint Denominator; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO { public ulong pixelRate; public DISPLAYCONFIG_RATIONAL hSyncFreq; public DISPLAYCONFIG_RATIONAL vSyncFreq; public DISPLAYCONFIG_2DREGION activeSize; public DISPLAYCONFIG_2DREGION totalSize; public uint videoStandard; public DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_2DREGION { public uint cx; public uint cy; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_DEVICE_INFO_HEADER { public DISPLAYCONFIG_DEVICE_INFO_TYPE type; public uint size; public LUID adapterId; public uint id; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct DISPLAYCONFIG_TARGET_DEVICE_NAME { public DISPLAYCONFIG_DEVICE_INFO_HEADER header; public DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS flags; public DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology; public ushort edidManufactureId; public ushort edidProductCodeId; public uint connectorInstance; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] public string monitorFriendlyDeviceName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string monitorDevicePath; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO { public DISPLAYCONFIG_DEVICE_INFO_HEADER header; public uint value; public DISPLAYCONFIG_COLOR_ENCODING colorEncoding; public uint bitsPerColorChannel; public bool advancedColorSupported => (value & 0x1) == 0x1; public bool advancedColorEnabled => (value & 0x2) == 0x2; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE { public DISPLAYCONFIG_DEVICE_INFO_HEADER header; public uint value; public bool enableAdvancedColor { get => (value & 0x1) == 1; set { uint mask = 0x1; if (value) { this.value |= mask; } else { this.value &= ~mask; } } } } public enum DISPLAYCONFIG_MODE_INFO_TYPE : uint { DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1, DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2, DISPLAYCONFIG_MODE_INFO_TYPE_FORCE_UINT32 = 0xFFFFFFFF } public enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY : uint { DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER = 0xFFFFFFFF, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HD15 = 0, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SVIDEO = 1, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPOSITE_VIDEO = 2, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_COMPONENT_VIDEO = 3, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DVI = 4, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI = 5, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_LVDS = 6, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_D_JPN = 8, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDI = 9, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL = 10, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED = 11, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EXTERNAL = 12, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED = 13, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_SDTVDONGLE = 14, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_MIRACAST = 15, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL = 0x80000000, DISPLAYCONFIG_OUTPUT_TECHNOLOGY_FORCE_UINT32 = 0xFFFFFFFF } public enum DISPLAYCONFIG_SCANLINE_ORDERING : uint { DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED = 0, DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE = 1, DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED = 2, DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_UPPERFIELDFIRST = DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED, DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_LOWERFIELDFIRST = 3, DISPLAYCONFIG_SCANLINE_ORDERING_FORCE_UINT32 = 0xFFFFFFFF } public enum DISPLAYCONFIG_ROTATION : uint { DISPLAYCONFIG_ROTATION_IDENTITY = 1, DISPLAYCONFIG_ROTATION_ROTATE90 = 2, DISPLAYCONFIG_ROTATION_ROTATE180 = 3, DISPLAYCONFIG_ROTATION_ROTATE270 = 4, DISPLAYCONFIG_ROTATION_FORCE_UINT32 = 0xFFFFFFFF } public enum DISPLAYCONFIG_SCALING : uint { DISPLAYCONFIG_SCALING_IDENTITY = 1, DISPLAYCONFIG_SCALING_CENTERED = 2, DISPLAYCONFIG_SCALING_STRETCHED = 3, DISPLAYCONFIG_SCALING_ASPECTRATIOCENTEREDMAX = 4, DISPLAYCONFIG_SCALING_CUSTOM = 5, DISPLAYCONFIG_SCALING_PREFERRED = 128, DISPLAYCONFIG_SCALING_FORCE_UINT32 = 0xFFFFFFFF } public enum DISPLAYCONFIG_PIXELFORMAT : uint { DISPLAYCONFIG_PIXELFORMAT_8BPP = 1, DISPLAYCONFIG_PIXELFORMAT_16BPP = 2, DISPLAYCONFIG_PIXELFORMAT_24BPP = 3, DISPLAYCONFIG_PIXELFORMAT_32BPP = 4, DISPLAYCONFIG_PIXELFORMAT_NONGDI = 5, DISPLAYCONFIG_PIXELFORMAT_FORCE_UINT32 = 0xffffffff } public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : uint { DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE = 3, DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME = 4, DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_PERSISTENCE = 5, DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_BASE_TYPE = 6, DISPLAYCONFIG_DEVICE_INFO_GET_SUPPORT_VIRTUAL_RESOLUTION = 7, DISPLAYCONFIG_DEVICE_INFO_SET_SUPPORT_VIRTUAL_RESOLUTION = 8, DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO = 9, DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE = 10, DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL = 11, DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_SPECIALIZATION = 12, DISPLAYCONFIG_DEVICE_INFO_SET_MONITOR_SPECIALIZATION = 13, DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF } public enum DISPLAYCONFIG_COLOR_ENCODING : uint { DISPLAYCONFIG_COLOR_ENCODING_RGB = 0, DISPLAYCONFIG_COLOR_ENCODING_YCBCR444 = 1, DISPLAYCONFIG_COLOR_ENCODING_YCBCR422 = 2, DISPLAYCONFIG_COLOR_ENCODING_YCBCR420 = 3, DISPLAYCONFIG_COLOR_ENCODING_INTENSITY = 4, DISPLAYCONFIG_COLOR_ENCODING_FORCE_UINT32 = 0xFFFFFFFF } } ================================================ FILE: source/Playnite/Native/Winnt.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public static class Winnt { public const uint FILE_READ_EA = 0x0008; public const uint FILE_ATTRIBUTE_READONLY = 0x00000001; public const uint FILE_ATTRIBUTE_HIDDEN = 0x00000002; public const uint FILE_ATTRIBUTE_SYSTEM = 0x00000004; public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010; public const uint FILE_ATTRIBUTE_ARCHIVE = 0x00000020; public const uint FILE_ATTRIBUTE_DEVICE = 0x00000040; public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080; public const uint FILE_ATTRIBUTE_TEMPORARY = 0x00000100; public const uint FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200; public const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400; public const uint FILE_ATTRIBUTE_COMPRESSED = 0x00000800; public const uint FILE_ATTRIBUTE_OFFLINE = 0x00001000; public const uint FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000; public const uint FILE_ATTRIBUTE_ENCRYPTED = 0x00004000; public const uint FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000; public const uint FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000; } [StructLayout(LayoutKind.Sequential)] public struct LUID { public int LowPart; public int HighPart; } } ================================================ FILE: source/Playnite/Native/Wintrust.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public class Wintrust { internal sealed class UnmanagedPointer : IDisposable { private IntPtr m_ptr; private WINTRUST_DATA.AllocMethod m_meth; internal UnmanagedPointer(IntPtr ptr, WINTRUST_DATA.AllocMethod method) { m_meth = method; m_ptr = ptr; } ~UnmanagedPointer() { Dispose(false); } private void Dispose(bool disposing) { if (m_ptr != IntPtr.Zero) { if (m_meth == WINTRUST_DATA.AllocMethod.HGlobal) { Marshal.FreeHGlobal(m_ptr); } else if (m_meth == WINTRUST_DATA.AllocMethod.CoTaskMem) { Marshal.FreeCoTaskMem(m_ptr); } m_ptr = IntPtr.Zero; } if (disposing) { GC.SuppressFinalize(this); } } public void Dispose() { Dispose(true); } public static implicit operator IntPtr(UnmanagedPointer ptr) { return ptr.m_ptr; } } } internal struct WINTRUST_FILE_INFO : IDisposable { public uint cbStruct; [MarshalAs(UnmanagedType.LPTStr)] public string pcwszFilePath; public IntPtr hFile; public IntPtr pgKnownSubject; public WINTRUST_FILE_INFO(string fileName, Guid subject) { cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)); pcwszFilePath = fileName; if (subject != Guid.Empty) { pgKnownSubject = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))); Marshal.StructureToPtr(subject, pgKnownSubject, true); } else { pgKnownSubject = IntPtr.Zero; } hFile = IntPtr.Zero; } public void Dispose() { Dispose(true); } private void Dispose(bool disposing) { if (pgKnownSubject != IntPtr.Zero) { Marshal.DestroyStructure(pgKnownSubject, typeof(Guid)); Marshal.FreeHGlobal(pgKnownSubject); } } } [StructLayout(LayoutKind.Sequential)] internal struct WINTRUST_DATA : IDisposable { public enum AllocMethod { HGlobal, CoTaskMem }; public enum UnionChoice { File = 1, Catalog, Blob, Signer, Cert }; public enum UiChoice { All = 1, NoUI, NoBad, NoGood }; public enum RevocationCheckFlags { None = 0, WholeChain }; public enum StateAction { Ignore = 0, Verify, Close, AutoCache, AutoCacheFlush }; public enum TrustProviderFlags { UseIE4Trust = 1, NoIE4Chain = 2, NoPolicyUsage = 4, RevocationCheckNone = 16, RevocationCheckEndCert = 32, RevocationCheckChain = 64, RecovationCheckChainExcludeRoot = 128, Safer = 256, HashOnly = 512, UseDefaultOSVerCheck = 1024, LifetimeSigning = 2048 }; public enum UIContext { Execute = 0, Install }; public WINTRUST_DATA(WINTRUST_FILE_INFO fileInfo) { cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_DATA)); pInfoStruct = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_FILE_INFO))); Marshal.StructureToPtr(fileInfo, pInfoStruct, false); dwUnionChoice = UnionChoice.File; pPolicyCallbackData = IntPtr.Zero; pSIPCallbackData = IntPtr.Zero; dwUIChoice = UiChoice.NoUI; fdwRevocationChecks = RevocationCheckFlags.None; dwStateAction = StateAction.Ignore; hWVTStateData = IntPtr.Zero; pwszURLReference = IntPtr.Zero; dwProvFlags = TrustProviderFlags.Safer; dwUIContext = UIContext.Execute; } public uint cbStruct; public IntPtr pPolicyCallbackData; public IntPtr pSIPCallbackData; public UiChoice dwUIChoice; public RevocationCheckFlags fdwRevocationChecks; public UnionChoice dwUnionChoice; public IntPtr pInfoStruct; public StateAction dwStateAction; public IntPtr hWVTStateData; private IntPtr pwszURLReference; public TrustProviderFlags dwProvFlags; public UIContext dwUIContext; public void Dispose() { Dispose(true); } private void Dispose(bool disposing) { if (dwUnionChoice == UnionChoice.File) { WINTRUST_FILE_INFO info = new WINTRUST_FILE_INFO(); Marshal.PtrToStructure(pInfoStruct, info); info.Dispose(); Marshal.DestroyStructure(pInfoStruct, typeof(WINTRUST_FILE_INFO)); } Marshal.FreeHGlobal(pInfoStruct); } } } ================================================ FILE: source/Playnite/Native/winuser.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Playnite.Native { public static class Winuser { public const int GWL_STYLE = -16; public const int WS_SYSMENU = 0x80000; public const int WM_HOTKEY = 0x0312; public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); public static readonly IntPtr HWND_TOP = new IntPtr(0); public static readonly IntPtr HWND_BOTTOM = new IntPtr(1); public const uint WM_QUERYENDSESSION = 0x11; public const uint WM_ENDSESSION = 0x16; public const uint ENDSESSION_CLOSEAPP = 0x1; // http://msdn.microsoft.com/en-us/library/windows/desktop/ms646244(v=vs.85).aspx public const uint WM_MOUSEACTIVATE = 0x0021; public const uint WM_MOUSEMOVE = 0x0200; public const uint WM_LBUTTONDOWN = 0x0201; public const uint WM_LBUTTONUP = 0x0202; public const uint WM_LBUTTONDBLCLK = 0x0203; public const uint WM_RBUTTONDOWN = 0x0204; public const uint WM_RBUTTONUP = 0x0205; public const uint WM_RBUTTONDBLCLK = 0x0206; public const uint WM_MBUTTONDOWN = 0x0207; public const uint WM_MBUTTONUP = 0x0208; public const uint WM_MBUTTONDBLCLK = 0x0209; public const uint MK_CONTROL = 0x0008; // The CTRL key is down. public const uint MK_LBUTTON = 0x0001; // The left mouse button is down. public const uint MK_MBUTTON = 0x0010; // The middle mouse button is down. public const uint MK_RBUTTON = 0x0002; // The right mouse button is down. public const uint MK_SHIFT = 0x0004; // The SHIFT key is down. public const uint MOD_NONE = 0x0000; //(none) public const uint MOD_ALT = 0x0001; //ALT public const uint MOD_CONTROL = 0x0002; //CTRL public const uint MOD_SHIFT = 0x0004; //SHIFT public const uint MOD_WIN = 0x0008; //WINDOWS public const uint WM_KEYDOWN = 0x0100; public const uint WM_KEYUP = 0x0101; public const uint WM_SYSKEYDOWN = 0x0104; public const uint WM_SYSKEYUP = 0x0105; // http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx public const uint VK_LBUTTON = 0x01; // Left mouse button public const uint VK_RBUTTON = 0x02; // Right mouse button public const uint VK_CANCEL = 0x03; // Control-break processing public const uint VK_MBUTTON = 0x04; // Middle mouse button (three-button mouse) public const uint VK_XBUTTON1 = 0x05; // X1 mouse button public const uint VK_XBUTTON2 = 0x06; // X2 mouse button public const uint VK_0x07 = 0x07; // Undefined public const uint VK_BACK = 0x08; // BACKSPACE key public const uint VK_TAB = 0x09; // TAB key public const uint VK_0x0A = 0x0A; // Reserved public const uint VK_0x0B = 0x0B; // Reserved public const uint VK_CLEAR = 0x0C; // CLEAR key public const uint VK_RETURN = 0x0D; // ENTER key public const uint VK_0x0E = 0x0E; // Undefined public const uint VK_0x0F = 0x0F; // Undefined public const uint VK_SHIFT = 0x10; // SHIFT key public const uint VK_CONTROL = 0x11; // CTRL key public const uint VK_MENU = 0x12; // ALT key public const uint VK_PAUSE = 0x13; // PAUSE key public const uint VK_CAPITAL = 0x14; // CAPS LOCK key public const uint VK_KANA = 0x15; // IME Kana mode public const uint VK_HANGUEL = 0x15; // IME Hanguel mode (maintained for compatibility; use VK_HANGUL) public const uint VK_HANGUL = 0x15; // IME Hangul mode public const uint VK_0x16 = 0x16; // Undefined public const uint VK_JUNJA = 0x17; // IME Junja mode public const uint VK_FINAL = 0x18; // IME final mode public const uint VK_HANJA = 0x19; // IME Hanja mode public const uint VK_KANJI = 0x19; // IME Kanji mode public const uint VK_0x1A = 0x1A; // Undefined public const uint VK_ESCAPE = 0x1B; // ESC key public const uint VK_CONVERT = 0x1C; // IME convert public const uint VK_NONCONVERT = 0x1D; // IME nonconvert public const uint VK_ACCEPT = 0x1E; // IME accept public const uint VK_MODECHANGE = 0x1F; // IME mode change request public const uint VK_SPACE = 0x20; // SPACEBAR public const uint VK_PRIOR = 0x21; // PAGE UP key public const uint VK_NEXT = 0x22; // PAGE DOWN key public const uint VK_END = 0x23; // END key public const uint VK_HOME = 0x24; // HOME key public const uint VK_LEFT = 0x25; // LEFT ARROW key public const uint VK_UP = 0x26; // UP ARROW key public const uint VK_RIGHT = 0x27; // RIGHT ARROW key public const uint VK_DOWN = 0x28; // DOWN ARROW key public const uint VK_SELECT = 0x29; // SELECT key public const uint VK_PRINT = 0x2A; // PRINT key public const uint VK_EXECUTE = 0x2B; // EXECUTE key public const uint VK_SNAPSHOT = 0x2C; // PRINT SCREEN key public const uint VK_INSERT = 0x2D; // INS key public const uint VK_DELETE = 0x2E; // DEL key public const uint VK_HELP = 0x2F; // HELP key public const uint VK_0x30 = 0x30; // 0 key public const uint VK_0x31 = 0x31; // 1 key public const uint VK_0x32 = 0x32; // 2 key public const uint VK_0x33 = 0x33; // 3 key public const uint VK_0x34 = 0x34; // 4 key public const uint VK_0x35 = 0x35; // 5 key public const uint VK_0x36 = 0x36; // 6 key public const uint VK_0x37 = 0x37; // 7 key public const uint VK_0x38 = 0x38; // 8 key public const uint VK_0x39 = 0x39; // 9 key public const uint VK_0x3A = 0x3A; // Undefined public const uint VK_0x3B = 0x3B; // Undefined public const uint VK_0x3C = 0x3C; // Undefined public const uint VK_0x3D = 0x3D; // Undefined public const uint VK_0x3E = 0x3E; // Undefined public const uint VK_0x3F = 0x3F; // Undefined public const uint VK_0x40 = 0x40; // Undefined public const uint VK_0x41 = 0x41; // A key public const uint VK_0x42 = 0x42; // B key public const uint VK_0x43 = 0x43; // C key public const uint VK_0x44 = 0x44; // D key public const uint VK_0x45 = 0x45; // E key public const uint VK_0x46 = 0x46; // F key public const uint VK_0x47 = 0x47; // G key public const uint VK_0x48 = 0x48; // H key public const uint VK_0x49 = 0x49; // I key public const uint VK_0x4A = 0x4A; // J key public const uint VK_0x4B = 0x4B; // K key public const uint VK_0x4C = 0x4C; // L key public const uint VK_0x4D = 0x4D; // M key public const uint VK_0x4E = 0x4E; // N key public const uint VK_0x4F = 0x4F; // O key public const uint VK_0x50 = 0x50; // P key public const uint VK_0x51 = 0x51; // Q key public const uint VK_0x52 = 0x52; // R key public const uint VK_0x53 = 0x53; // S key public const uint VK_0x54 = 0x54; // T key public const uint VK_0x55 = 0x55; // U key public const uint VK_0x56 = 0x56; // V key public const uint VK_0x57 = 0x57; // W key public const uint VK_0x58 = 0x58; // X key public const uint VK_0x59 = 0x59; // Y key public const uint VK_0x5A = 0x5A; // Z key public const uint VK_LWIN = 0x5B; // Left Windows key (Natural keyboard) public const uint VK_RWIN = 0x5C; // Right Windows key (Natural keyboard) public const uint VK_APPS = 0x5D; // Applications key (Natural keyboard) public const uint VK_0x5E = 0x5E; // Reserved public const uint VK_SLEEP = 0x5F; // Computer Sleep key public const uint VK_NUMPAD0 = 0x60; // Numeric keypad 0 key public const uint VK_NUMPAD1 = 0x61; // Numeric keypad 1 key public const uint VK_NUMPAD2 = 0x62; // Numeric keypad 2 key public const uint VK_NUMPAD3 = 0x63; // Numeric keypad 3 key public const uint VK_NUMPAD4 = 0x64; // Numeric keypad 4 key public const uint VK_NUMPAD5 = 0x65; // Numeric keypad 5 key public const uint VK_NUMPAD6 = 0x66; // Numeric keypad 6 key public const uint VK_NUMPAD7 = 0x67; // Numeric keypad 7 key public const uint VK_NUMPAD8 = 0x68; // Numeric keypad 8 key public const uint VK_NUMPAD9 = 0x69; // Numeric keypad 9 key public const uint VK_MULTIPLY = 0x6A; // Multiply key public const uint VK_ADD = 0x6B; // Add key public const uint VK_SEPARATOR = 0x6C; // Separator key public const uint VK_SUBTRACT = 0x6D; // Subtract key public const uint VK_DECIMAL = 0x6E; // Decimal key public const uint VK_DIVIDE = 0x6F; // Divide key public const uint VK_F1 = 0x70; // F1 key public const uint VK_F2 = 0x71; // F2 key public const uint VK_F3 = 0x72; // F3 key public const uint VK_F4 = 0x73; // F4 key public const uint VK_F5 = 0x74; // F5 key public const uint VK_F6 = 0x75; // F6 key public const uint VK_F7 = 0x76; // F7 key public const uint VK_F8 = 0x77; // F8 key public const uint VK_F9 = 0x78; // F9 key public const uint VK_F10 = 0x79; // F10 key public const uint VK_F11 = 0x7A; // F11 key public const uint VK_F12 = 0x7B; // F12 key public const uint VK_F13 = 0x7C; // F13 key public const uint VK_F14 = 0x7D; // F14 key public const uint VK_F15 = 0x7E; // F15 key public const uint VK_F16 = 0x7F; // F16 key public const uint VK_F17 = 0x80; // F17 key public const uint VK_F18 = 0x81; // F18 key public const uint VK_F19 = 0x82; // F19 key public const uint VK_F20 = 0x83; // F20 key public const uint VK_F21 = 0x84; // F21 key public const uint VK_F22 = 0x85; // F22 key public const uint VK_F23 = 0x86; // F23 key public const uint VK_F24 = 0x87; // F24 key public const uint VK_0x88 = 0x88; // Unassigned public const uint VK_0x89 = 0x89; // Unassigned public const uint VK_0x8A = 0x8A; // Unassigned public const uint VK_0x8B = 0x8B; // Unassigned public const uint VK_0x8C = 0x8C; // Unassigned public const uint VK_0x8D = 0x8D; // Unassigned public const uint VK_0x8E = 0x8E; // Unassigned public const uint VK_0x8F = 0x8F; // Unassigned public const uint VK_NUMLOCK = 0x90; // NUM LOCK key public const uint VK_SCROLL = 0x91; // SCROLL LOCK key public const uint VK_0x92 = 0x92; // OEM specific public const uint VK_0x93 = 0x93; // OEM specific public const uint VK_0x94 = 0x94; // OEM specific public const uint VK_0x95 = 0x95; // OEM specific public const uint VK_0x96 = 0x96; // OEM specific public const uint VK_0x97 = 0x97; // Unassigned public const uint VK_0x98 = 0x98; // Unassigned public const uint VK_0x99 = 0x99; // Unassigned public const uint VK_0x9A = 0x9A; // Unassigned public const uint VK_0x9B = 0x9B; // Unassigned public const uint VK_0x9C = 0x9C; // Unassigned public const uint VK_0x9D = 0x9D; // Unassigned public const uint VK_0x9E = 0x9E; // Unassigned public const uint VK_0x9F = 0x9F; // Unassigned public const uint VK_LSHIFT = 0xA0; // Left SHIFT key public const uint VK_RSHIFT = 0xA1; // Right SHIFT key public const uint VK_LCONTROL = 0xA2; // Left CONTROL key public const uint VK_RCONTROL = 0xA3; // Right CONTROL key public const uint VK_LMENU = 0xA4; // Left MENU key public const uint VK_RMENU = 0xA5; // Right MENU key public const uint VK_BROWSER_BACK = 0xA6; // Browser Back key public const uint VK_BROWSER_FORWARD = 0xA7; // Browser Forward key public const uint VK_BROWSER_REFRESH = 0xA8; // Browser Refresh key public const uint VK_BROWSER_STOP = 0xA9; // Browser Stop key public const uint VK_BROWSER_SEARCH = 0xAA; // Browser Search key public const uint VK_BROWSER_FAVORITES = 0xAB; // Browser Favorites key public const uint VK_BROWSER_HOME = 0xAC; // Browser Start and Home key public const uint VK_VOLUME_MUTE = 0xAD; // Volume Mute key public const uint VK_VOLUME_DOWN = 0xAE; // Volume Down key public const uint VK_VOLUME_UP = 0xAF; // Volume Up key public const uint VK_MEDIA_NEXT_TRACK = 0xB0; // Next Track key public const uint VK_MEDIA_PREV_TRACK = 0xB1; // Previous Track key public const uint VK_MEDIA_STOP = 0xB2; // Stop Media key public const uint VK_MEDIA_PLAY_PAUSE = 0xB3; // Play/Pause Media key public const uint VK_LAUNCH_MAIL = 0xB4; // Start Mail key public const uint VK_LAUNCH_MEDIA_SELECT = 0xB5; // Select Media key public const uint VK_LAUNCH_APP1 = 0xB6; // Start Application 1 key public const uint VK_LAUNCH_APP2 = 0xB7; // Start Application 2 key public const uint VK_0xB8 = 0xB8; // Reserved public const uint VK_0xB9 = 0xB9; // Reserved public const uint VK_OEM_1 = 0xBA; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key public const uint VK_OEM_PLUS = 0xBB; // For any country/region, the '+' key public const uint VK_OEM_COMMA = 0xBC; // For any country/region, the ',' key public const uint VK_OEM_MINUS = 0xBD; // For any country/region, the '-' key public const uint VK_OEM_PERIOD = 0xBE; // For any country/region, the '.' key public const uint VK_OEM_2 = 0xBF; // Used for miscellaneous characters; it can vary by keyboard. public const uint VK_OEM_3 = 0xC0; // Used for miscellaneous characters; it can vary by keyboard. public const uint VK_0xC1 = 0xC1; // Reserved public const uint VK_0xC2 = 0xC2; // Reserved public const uint VK_0xC3 = 0xC3; // Reserved public const uint VK_0xC4 = 0xC4; // Reserved public const uint VK_0xC5 = 0xC5; // Reserved public const uint VK_0xC6 = 0xC6; // Reserved public const uint VK_0xC7 = 0xC7; // Reserved public const uint VK_0xC8 = 0xC8; // Reserved public const uint VK_0xC9 = 0xC9; // Reserved public const uint VK_0xCA = 0xCA; // Reserved public const uint VK_0xCB = 0xCB; // Reserved public const uint VK_0xCC = 0xCC; // Reserved public const uint VK_0xCD = 0xCD; // Reserved public const uint VK_0xCE = 0xCE; // Reserved public const uint VK_0xCF = 0xCF; // Reserved public const uint VK_0xD0 = 0xD0; // Reserved public const uint VK_0xD1 = 0xD1; // Reserved public const uint VK_0xD2 = 0xD2; // Reserved public const uint VK_0xD3 = 0xD3; // Reserved public const uint VK_0xD4 = 0xD4; // Reserved public const uint VK_0xD5 = 0xD5; // Reserved public const uint VK_0xD6 = 0xD6; // Reserved public const uint VK_0xD7 = 0xD7; // Reserved public const uint VK_0xD8 = 0xD8; // Unassigned public const uint VK_0xD9 = 0xD9; // Unassigned public const uint VK_0xDA = 0xDA; // Unassigned public const uint VK_OEM_4 = 0xDB; // Used for miscellaneous characters; it can vary by keyboard. public const uint VK_OEM_5 = 0xDC; // Used for miscellaneous characters; it can vary by keyboard. public const uint VK_OEM_6 = 0xDD; // Used for miscellaneous characters; it can vary by keyboard. public const uint VK_OEM_7 = 0xDE; // Used for miscellaneous characters; it can vary by keyboard. public const uint VK_OEM_8 = 0xDF; // Used for miscellaneous characters; it can vary by keyboard. public const uint VK_0xE0 = 0xE0; // Reserved public const uint VK_0xE1 = 0xE1; // OEM specific public const uint VK_OEM_102 = 0xE2; // Either the angle bracket key or the backslash key on the RT 102-key keyboard public const uint VK_0xE3 = 0xE3; // OEM specific public const uint VK_0xE4 = 0xE4; // OEM specific public const uint VK_PROCESSKEY = 0xE5; // IME PROCESS key public const uint VK_0xE6 = 0xE6; // OEM specific public const uint VK_PACKET = 0xE7; // Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT,SendInput, WM_KEYDOWN, and WM_KEYUP public const uint VK_0xE8 = 0xE8; // Unassigned public const uint VK_0xE9 = 0xE9; // OEM specific public const uint VK_0xEA = 0xEA; // OEM specific public const uint VK_0xEB = 0xEB; // OEM specific public const uint VK_0xEC = 0xEC; // OEM specific public const uint VK_0xED = 0xED; // OEM specific public const uint VK_0xEE = 0xEE; // OEM specific public const uint VK_0xEF = 0xEF; // OEM specific public const uint VK_0xF0 = 0xF0; // OEM specific public const uint VK_0xF1 = 0xF1; // OEM specific public const uint VK_0xF2 = 0xF2; // OEM specific public const uint VK_0xF3 = 0xF3; // OEM specific public const uint VK_0xF4 = 0xF4; // OEM specific public const uint VK_0xF5 = 0xF5; // OEM specific public const uint VK_ATTN = 0xF6; // Attn key public const uint VK_CRSEL = 0xF7; // CrSel key public const uint VK_EXSEL = 0xF8; // ExSel key public const uint VK_EREOF = 0xF9; // Erase EOF key public const uint VK_PLAY = 0xFA; // Play key public const uint VK_ZOOM = 0xFB; // Zoom key public const uint VK_NONAME = 0xFC; // Reserved public const uint VK_PA1 = 0xFD; // PA1 key public const uint VK_OEM_CLEAR = 0xFE; // Clear key } public enum QUERY_DEVICE_CONFIG_FLAGS : uint { QDC_ALL_PATHS = 0x00000001, QDC_ONLY_ACTIVE_PATHS = 0x00000002, QDC_DATABASE_CURRENT = 0x00000004 } [Flags] public enum SWP { ASYNCWINDOWPOS = 0x4000, DEFERERASE = 0x2000, DRAWFRAME = 0x0020, FRAMECHANGED = 0x0020, HIDEWINDOW = 0x0080, NOACTIVATE = 0x0010, NOCOPYBITS = 0x0100, NOMOVE = 0x0002, NOOWNERZORDER = 0x0200, NOREDRAW = 0x0008, NOREPOSITION = 0x0200, NOSENDCHANGING = 0x0400, NOSIZE = 0x0001, NOZORDER = 0x0004, SHOWWINDOW = 0x0040, TOPMOST = NOACTIVATE | NOOWNERZORDER | NOSIZE | NOMOVE | NOREDRAW | NOSENDCHANGING } public enum MonitorOptions : uint { MONITOR_DEFAULTTONULL = 0x00000000, MONITOR_DEFAULTTOPRIMARY = 0x00000001, MONITOR_DEFAULTTONEAREST = 0x00000002 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public class MONITORINFO { public int cbSize = Marshal.SizeOf(typeof(MONITORINFO)); public RECT rcMonitor = new RECT(); public RECT rcWork = new RECT(); public int dwFlags = 0; } [StructLayout(LayoutKind.Sequential)] public struct MINMAXINFO { public POINT ptReserved; public POINT ptMaxSize; public POINT ptMaxPosition; public POINT ptMinTrackSize; public POINT ptMaxTrackSize; } } ================================================ FILE: source/Playnite/ObservablePowerStatus.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; using System.Windows.Forms; using Playnite.Common; using System.Windows; namespace Playnite { public enum BatteryChargeLevel { Critical, Low, Medium, High } public class ObservablePowerStatus : ObservableObject, IDisposable { private SynchronizationContext context; private CancellationTokenSource watcherToken; private Task currentTask; public int PercentCharge { get => (int)(SystemInformation.PowerStatus.BatteryLifePercent * 100); } public BatteryChargeLevel Charge { get { var charge = PercentCharge; if (charge > 85) { return BatteryChargeLevel.High; } else if (charge > 40) { return BatteryChargeLevel.Medium; } else if (charge > 10) { return BatteryChargeLevel.Low; } else { return BatteryChargeLevel.Critical; } } } public bool IsCharging { get => SystemInformation.PowerStatus.BatteryChargeStatus.HasFlag(BatteryChargeStatus.Charging); } public bool IsBatteryAvailable { get => !SystemInformation.PowerStatus.BatteryChargeStatus.HasFlag(BatteryChargeStatus.NoSystemBattery); } public ObservablePowerStatus() { context = SynchronizationContext.Current; if (!DesignerTools.IsInDesignMode && IsBatteryAvailable) { StartWatcher(); } } public async void StartWatcher() { watcherToken?.Cancel(); if (currentTask != null) { await currentTask; } watcherToken = new CancellationTokenSource(); currentTask = Task.Run(async () => { while (true) { if (watcherToken.IsCancellationRequested) { return; } context.Post((a) => OnPropertyChanged(nameof(PercentCharge)), null); context.Post((a) => OnPropertyChanged(nameof(Charge)), null); context.Post((a) => OnPropertyChanged(nameof(IsCharging)), null); await Task.Delay(10000); } }, watcherToken.Token); } public async void StopWatcher() { watcherToken?.Cancel(); if (currentTask != null) { await currentTask; } } public void Dispose() { StopWatcher(); } } } ================================================ FILE: source/Playnite/ObservableTime.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; using Playnite.Common; using System.Windows; namespace Playnite { public class ObservableTime : ObservableObject { private SynchronizationContext context; private CancellationTokenSource watcherToken; private Task currentTask; public string Time { get => DateTime.Now.ToString(Common.Constants.TimeUiFormat); } public ObservableTime() { context = SynchronizationContext.Current; if (!DesignerTools.IsInDesignMode) { StartWatcher(); } } public async void StartWatcher() { watcherToken?.Cancel(); if (currentTask != null) { await currentTask; } watcherToken = new CancellationTokenSource(); currentTask = Task.Run(async () => { while (true) { if (watcherToken.IsCancellationRequested) { return; } context.Post((a) => OnPropertyChanged(nameof(Time)), null); await Task.Delay(10000); } }, watcherToken.Token); } public async void StopWatcher() { watcherToken?.Cancel(); if (currentTask != null) { await currentTask; } } public void Dispose() { StopWatcher(); } } } ================================================ FILE: source/Playnite/PipeServer.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.IO.Pipes; using System.Linq; using System.ServiceModel; using System.ServiceModel.Description; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite { public delegate void CommandExecutedEventHandler(object sender, CommandExecutedEventArgs args); public class CommandExecutedEventArgs : EventArgs { public CmdlineCommand Command { get; set; } public string Args { get; set; } public CommandExecutedEventArgs() { } public CommandExecutedEventArgs(CmdlineCommand command, string args) { Command = command; Args = args; } } [ServiceContract] public interface IPipeService { [OperationContract(IsOneWay = true)] void InvokeCommand(CmdlineCommand command, string args); } [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)] public class PipeService : IPipeService { private readonly SynchronizationContext syncContext; public event CommandExecutedEventHandler CommandExecuted; public PipeService() { syncContext = SynchronizationContext.Current; } public void InvokeCommand(CmdlineCommand command, string args) { // We don't want to block this call because it causes issues if some sync operation that shuts down server is also called. // For example, mode switch or instance shutdown calls are stopping server, // which results in serviceHost.Close() timeout, since server would be still waiting for InvokeCommand to finish. Task.Run(async () => { await Task.Delay(100); syncContext.Post(_ => CommandExecuted?.Invoke(this, new CommandExecutedEventArgs(command, args)), null); }); } } public class PipeServer { private string endpoint; private ServiceHost serviceHost; public PipeServer(string endpoint) { this.endpoint = endpoint; } public void StartServer(IPipeService service) { serviceHost = new ServiceHost(service, new Uri[] { new Uri(endpoint) }); serviceHost.AddServiceEndpoint(typeof(IPipeService), new NetNamedPipeBinding(), "PlayniteService"); serviceHost.Open(); } public void StopServer() { serviceHost.Close(); } } public class PipeClient : ClientBase { public PipeClient(string endpoint) : base(new ServiceEndpoint(ContractDescription.GetContract(typeof(IPipeService)), new NetNamedPipeBinding(), new EndpointAddress(endpoint.TrimEnd('/') + @"/PlayniteService"))) { } public void InvokeCommand(CmdlineCommand command, string args) { Channel.InvokeCommand(command, args); } } } ================================================ FILE: source/Playnite/Playnite.csproj ================================================  {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Debug AnyCPU {AD271E73-8A13-4C4E-BFDC-3076646B59E3} Library Properties Playnite Playnite v4.6.2 512 true bin\x86\Debug\ DEBUG;TRACE full x86 7.3 prompt MinimumRecommendedRules.ruleset true bin\x86\Release\ TRACE true true x86 7.3 prompt MinimumRecommendedRules.ruleset pdbonly true true true bin\x64\Debug\ DEBUG;TRACE true full x64 7.3 prompt MinimumRecommendedRules.ruleset true bin\x64\Release\ TRACE true true true pdbonly x64 7.3 prompt MinimumRecommendedRules.ruleset ..\packages\AngleSharp.0.9.9\lib\net45\AngleSharp.dll True ..\packages\CefSharp.Common.144.0.120\lib\net462\CefSharp.dll ..\packages\CefSharp.Common.144.0.120\lib\net462\CefSharp.Core.dll ..\packages\CefSharp.OffScreen.144.0.120\lib\net462\CefSharp.OffScreen.dll ..\packages\CefSharp.Wpf.144.0.120\lib\net462\CefSharp.Wpf.dll ..\packages\CommandLineParser.2.8.0\lib\net461\CommandLine.dll ..\packages\Crc32.NET.1.2.0\lib\net20\Crc32.NET.dll ..\..\references\DiscordRPC.dll ..\packages\Flurl.2.7.1\lib\net40\Flurl.dll ..\..\references\HtmlRenderer.dll ..\..\references\HtmlRenderer.WPF.dll ..\packages\LiteDB.4.1.4\lib\net40\LiteDB.dll ..\packages\Magick.NET-Q8-x86.14.10.2\lib\netstandard20\Magick.NET-Q8-x86.dll ..\packages\Magick.NET.Core.14.10.2\lib\netstandard20\Magick.NET.Core.dll ..\packages\Magick.NET.SystemWindowsMedia.8.0.15\lib\net462\Magick.NET.SystemWindowsMedia.dll ..\packages\Markdig.0.18.0\lib\net40\Markdig.dll ..\packages\DynamicLanguageRuntime.1.2.1\lib\net45\Microsoft.Dynamic.dll ..\packages\CommonServiceLocator.1.3\lib\portable-net4+sl5+netcore45+wpa81+wp8\Microsoft.Practices.ServiceLocation.dll ..\packages\DynamicLanguageRuntime.1.2.1\lib\net45\Microsoft.Scripting.dll ..\packages\DynamicLanguageRuntime.1.2.1\lib\net45\Microsoft.Scripting.Metadata.dll ..\packages\DynamicLanguageRuntime.1.2.1\lib\net45\Microsoft.VisualStudio.CodeCoverage.Shim.dll ..\packages\WindowsAPICodePack.1.1.0\lib\Microsoft.WindowsAPICodePack.dll ..\packages\WindowsAPICodePack.1.1.0\lib\Microsoft.WindowsAPICodePack.ExtendedLinguisticServices.dll ..\packages\WindowsAPICodePack.1.1.0\lib\Microsoft.WindowsAPICodePack.Sensors.dll ..\packages\WindowsAPICodePack.1.1.0\lib\Microsoft.WindowsAPICodePack.Shell.dll ..\packages\WindowsAPICodePack.1.1.0\lib\Microsoft.WindowsAPICodePack.ShellExtensions.dll ..\packages\Microsoft.Xaml.Behaviors.Wpf.1.1.39\lib\net45\Microsoft.Xaml.Behaviors.dll ..\packages\Nett.0.10.1\lib\Net40\Nett.dll ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll ..\packages\NLog.4.7.6\lib\net45\NLog.dll ..\packages\PhotoSauce.MagicScaler.0.11.2\lib\net461\PhotoSauce.MagicScaler.dll ..\packages\Polly.5.1.0\lib\net45\Polly.dll ..\packages\Prism.Core.6.3.0\lib\net45\Prism.dll ..\packages\Prism.Wpf.6.3.0\lib\net45\Prism.Wpf.dll ..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll True False ..\..\references\SharpCompress.dll ..\..\references\SQLNado.dll ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll ..\packages\System.IO.Abstractions.2.1.0.227\lib\net40\System.IO.Abstractions.dll ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll ..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll ..\packages\Prism.Wpf.6.3.0\lib\net45\System.Windows.Interactivity.dll ..\..\references\Windows.winmd ..\packages\YamlDotNet.5.4.0\lib\net45\YamlDotNet.dll FadeImage.xaml WebViewWindow.xaml PreserveNewest Designer PreserveNewest PreserveNewest PreserveNewest {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B} 1 0 0 tlbimp False True {19bc9097-5705-4352-90e2-99f0c63230d0} Playnite.SDK MSBuild:Compile Designer MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer PreserveNewest MSBuild:Compile Designer %(Filename)%(Extension) PreserveNewest Dependencies\sqlite3.x86.dll PreserveNewest sqlite3.x86.dll Dependencies\SDL2.dll PreserveNewest SDL2.dll Dependencies\SDL3.dll PreserveNewest SDL3.dll Dependencies\SDL2_mixer.dll PreserveNewest SDL2_mixer.dll PreserveNewest PreserveNewest PreserveNewest This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. ================================================ FILE: source/Playnite/PlayniteEnvironment.cs ================================================ using Playnite.Settings; using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.Linq; using System.Security.Principal; using System.Text; using System.Threading.Tasks; namespace Playnite { public enum ReleaseChannel { Stable, Beta, Patreon } public static class PlayniteEnvironment { public static ReleaseChannel ReleaseChannel { get { switch (PlayniteSettings.GetAppConfigValue("UpdateBranch")) { case "stable": return ReleaseChannel.Stable; case "patreon": return ReleaseChannel.Patreon; case "beta": return ReleaseChannel.Beta; default: return ReleaseChannel.Stable; } } } public static string DocsRootUrl => PlayniteSettings.GetAppConfigValue("DocsRootUrl"); public static string AppBranch => PlayniteSettings.GetAppConfigValue("AppBranch"); public static bool ThrowAllErrors => PlayniteSettings.GetAppConfigBoolValue("ThrowAllErrors") && Debugger.IsAttached; public static bool InOfflineMode => PlayniteSettings.GetAppConfigBoolValue("OfflineMode"); public static bool IsDebuggerAttached => Debugger.IsAttached; public static bool IsDebugBuild { get { #if DEBUG return true; #else return false; #endif } } public static bool IsElevated { get { using (var identity = WindowsIdentity.GetCurrent()) { var principal = new WindowsPrincipal(identity); return principal.IsInRole(WindowsBuiltInRole.Administrator); } } } } } ================================================ FILE: source/Playnite/PlayniteProcess.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class PlayniteProcess { public static long WorkingSetMemory { get => Process.GetCurrentProcess().WorkingSet64; } public static string Path { get => Process.GetCurrentProcess().MainModule.FileName; } public static string Cmdline { get => Process.GetCurrentProcess().GetCommandLine(); } } } ================================================ FILE: source/Playnite/PlayniteUriHandler.cs ================================================ using Flurl; using Playnite.SDK; using Playnite.SDK.Events; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; namespace Playnite { public class UriCommands { public const string StartGame = "start"; public const string CreateDiag = "creatediag"; public const string ShowGame = "showgame"; public const string InstallAddon = "installaddon"; public const string Search = "search"; public const string Restore = "restore"; } public class PlayniteUriHandler : IUriHandlerAPI { private static readonly ILogger logger = LogManager.GetLogger(); private static readonly char[] uriSplitter = new char[] { '/' }; internal readonly Dictionary> Handlers = new Dictionary>(); public void RegisterSource(string source, Action handler) { if (source.IsNullOrEmpty()) { throw new ArgumentNullException("Source cannot be null"); } if (source.Equals("playnite", StringComparison.OrdinalIgnoreCase)) { throw new Exception($"\"playnite\" source is reserved source."); } if (Handlers.Keys.FirstOrDefault(a => a.Equals(source, StringComparison.OrdinalIgnoreCase)) != null) { throw new Exception($"Handler {source} is already registered."); } Handlers.Add(source, handler); } public void RemoveSource(string source) { var handler = Handlers.FirstOrDefault(a => a.Key.Equals(source, StringComparison.OrdinalIgnoreCase)); if (handler.Value != null) { Handlers.Remove(handler.Key); } } public static (string source, string[] arguments) ParseUri(string uri) { var split = uri.Split(uriSplitter, StringSplitOptions.RemoveEmptyEntries); if (split.Length < 2) { throw new Exception("playnite:// uri missing parameters."); } var source = split[1]; var arguments = new string[0]; if (split.Length > 2) { arguments = split.Skip(2).Select(a => HttpUtility.UrlDecode(a)).ToArray(); } return (source, arguments); } internal void ProcessUri(string uri) { logger.Info($"Processing Playnite URI {uri}"); try { var (source, arguments) = ParseUri(uri); var handler = Handlers.FirstOrDefault(a => a.Key.Equals(source, StringComparison.OrdinalIgnoreCase)); if (handler.Value == null) { logger.Error($"URI handler {source} is not registered."); return; } handler.Value.Invoke(new PlayniteUriEventArgs { Arguments = arguments }); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "URI handler failed."); } } } } ================================================ FILE: source/Playnite/Plugins/ExtensionFactory.cs ================================================ using Playnite.API; using Playnite.Database; using Playnite.Controllers; using Playnite.Scripting; using Playnite.SDK; using Playnite.SDK.Plugins; using Playnite.Settings; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using Playnite.Common; using Playnite.SDK.Models; using Playnite.SDK.Events; namespace Playnite.Plugins { public class ExtensionsStatusBinder { public class Status : ObservableObject { private bool isInstalled; public bool IsInstalled { get => isInstalled; set => SetValue(ref isInstalled, value); } } public Status this[string pluginId] { get { var plugin = PlayniteApplication.Current.Extensions?.Plugins.FirstOrDefault(a => a.Value.Description.Id == pluginId).Value; return new Status { IsInstalled = plugin != null }; } set { throw new NotSupportedException(); } } } public class LoadedPlugin { public Plugin Plugin { get; } public ExtensionManifest Description { get; } public string PluginIcon { get; } public LoadedPlugin(Plugin plugin, ExtensionManifest description) { Plugin = plugin; Description = description; if (!string.IsNullOrEmpty(description.Icon)) { PluginIcon = Path.Combine(Path.GetDirectoryName(description.DescriptionPath), description.Icon); } } } public enum AddonLoadError { None, Uknown, SDKVersion } public class ExtensionFactory : ObservableObject, IDisposable { private static ILogger logger = LogManager.GetLogger(); private readonly IGameDatabase database; private readonly GameControllerFactory controllers; private readonly Func apiGenerator; public List<(ExtensionManifest manifest, AddonLoadError error)> FailedExtensions { get; } = new List<(ExtensionManifest manifest, AddonLoadError error)>(); public Dictionary Plugins { get; private set; } = new Dictionary(); public List LibraryPlugins { get => Plugins.Where(a => a.Value.Description.Type == ExtensionType.GameLibrary).Select(a => (LibraryPlugin)a.Value.Plugin).ToList(); } public List MetadataPlugins { get => Plugins.Where(a => a.Value.Description.Type == ExtensionType.MetadataProvider).Select(a => (MetadataPlugin)a.Value.Plugin).ToList(); } public List GenericPlugins { get => Plugins.Where(a => a.Value.Description.Type == ExtensionType.GenericPlugin).Select(a => (GenericPlugin)a.Value.Plugin).ToList(); } public List Scripts { get; private set; } = new List(); public ExtensionFactory(IGameDatabase database, GameControllerFactory controllers, Func apiGenerator) { this.database = database; this.controllers = controllers; this.apiGenerator = apiGenerator; controllers.Installed += Controllers_Installed; controllers.InstallationCancelled += Controllers_InstallationCancelled; controllers.Starting += Controllers_Starting; controllers.Started += Controllers_Started; controllers.Stopped += Controllers_Stopped; controllers.Uninstalled += Controllers_Uninstalled; controllers.StartupCancelled += Controllers_StartupCancelled; } public void Dispose() { DisposePlugins(); DisposeScripts(); controllers.Installed -= Controllers_Installed; controllers.InstallationCancelled -= Controllers_InstallationCancelled; controllers.Starting -= Controllers_Starting; controllers.Started -= Controllers_Started; controllers.Stopped -= Controllers_Stopped; controllers.Uninstalled -= Controllers_Uninstalled; controllers.StartupCancelled -= Controllers_StartupCancelled; } private void DisposeScripts() { if (Scripts?.Any() == true) { foreach (var script in Scripts) { try { script.Dispose(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to dispose script {script.Name}"); } } } Scripts = new List(); } private void DisposePlugins() { if (Plugins?.Any() == true) { foreach (var provider in Plugins.Keys) { try { Plugins[provider].Plugin.Dispose(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to dispose plugin {provider}"); } } } Plugins = new Dictionary(); } public static void CreatePluginFolders() { FileSystem.CreateDirectory(PlaynitePaths.ExtensionsDataPath); FileSystem.CreateDirectory(PlaynitePaths.ExtensionsProgramPath); FileSystem.CreateDirectory(PlaynitePaths.ExtensionsUserDataPath); } private static IEnumerable GetManifestsFromPath(string path) { if (Directory.Exists(path)) { var man = GetManifestFromFile(Path.Combine(path, PlaynitePaths.ExtensionManifestFileName)); if (man != null) { yield return man; } } else if (File.Exists(path)) { foreach (var dirPath in File.ReadAllLines(path).Where(a => !a.IsNullOrWhiteSpace() && !a.StartsWith("#"))) { ExtensionManifest man = null; try { if (Directory.Exists(dirPath)) { man = GetManifestFromFile(Path.Combine(dirPath.Trim(), PlaynitePaths.ExtensionManifestFileName)); } } catch (Exception e) { logger.Error(e, "Failed to read extension dev file."); } if (man != null) { yield return man; } } } } private static ExtensionManifest GetManifestFromFile(string file) { if (File.Exists(file)) { try { return ExtensionManifest.FromFile(file); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to parse plugin description: {file}"); return null; } } return null; } internal static IEnumerable DeduplicateExtList(List list) { return list.GroupBy(a => a.Id).Select(g => g.OrderByDescending(x => x.Version).First()); } public static List GetInstalledManifests(List externalPaths = null) { var externals = new List(); var user = new List(); var install = new List(); if (externalPaths.HasItems()) { foreach (var ext in externalPaths) { foreach (var man in GetManifestsFromPath(ext)) { externals.Add(man); man.IsExternalDev = true; } } } if (Directory.Exists(PlaynitePaths.ExtensionsUserDataPath)) { var enumerator = new SafeFileEnumerator(PlaynitePaths.ExtensionsUserDataPath, PlaynitePaths.ExtensionManifestFileName, SearchOption.AllDirectories); foreach (var desc in enumerator) { var man = GetManifestFromFile(desc.FullName); if (man?.Id.IsNullOrEmpty() == false) { if (externals.Any(a => a.Id == man.Id)) { continue; } else { user.Add(man); } } } } if (Directory.Exists(PlaynitePaths.ExtensionsProgramPath)) { var enumerator = new SafeFileEnumerator(PlaynitePaths.ExtensionsProgramPath, PlaynitePaths.ExtensionManifestFileName, SearchOption.AllDirectories); foreach (var desc in enumerator) { var man = GetManifestFromFile(desc.FullName); if (man?.Id.IsNullOrEmpty() == false) { if (externals.Any(a => a.Id == man.Id) || user.Any(a => a.Id == man.Id)) { continue; } else { install.Add(man); } } } } var result = new List(); result.AddRange(DeduplicateExtList(externals).Cast()); result.AddRange(DeduplicateExtList(user).Cast()); result.AddRange(DeduplicateExtList(install).Cast()); return result; } private bool VerifyAssemblyReferences(Assembly asm, ExtensionManifest manifest) { var references = asm.GetReferencedAssemblies(); if (references.Any(a => a.Name == "Playnite" || a.Name == "Playnite.Common") && !BuiltinExtensions.BuiltinExtensionIds.Contains(manifest.Id)) { logger.Error($"Unsupported Playnite assemblies are referenced by {manifest.Name} plugin."); return false; } var sdkReference = references.FirstOrDefault(a => a.Name == "Playnite.SDK"); if (sdkReference == null) { logger.Error($"Assembly doesn't reference Playnite SDK."); return false; } if (sdkReference.Version.Major != SDK.SdkVersions.SDKVersion.Major || sdkReference.Version > SDK.SdkVersions.SDKVersion) { logger.Error($"Plugin doesn't support current version of Playnite SDK, supports {sdkReference.Version}"); return false; } return true; } public bool LoadScripts(List ignoreList, bool builtInOnly, List externals) { var allSuccess = true; DisposeScripts(); var manifests = GetInstalledManifests(externals).Where(a => a.Type == ExtensionType.Script && !ignoreList.Contains(a.Id)).ToList(); foreach (var desc in manifests) { if (desc.Id.IsNullOrWhiteSpace()) { logger.Error($"Extension {desc.Name}, doesn't have ID."); continue; } if (desc.Module.IsNullOrWhiteSpace()) { logger.Error($"Extension {desc.Name}, doesn't have module specified."); continue; } if (builtInOnly && !BuiltinExtensions.BuiltinExtensionIds.Contains(desc.Id)) { logger.Warn($"Skipping load of {desc.Name}, builtInOnly is enabled."); continue; } PlayniteScript script = null; var scriptPath = Path.Combine(Path.GetDirectoryName(desc.DescriptionPath), desc.Module); if (!File.Exists(scriptPath)) { logger.Error($"Cannot load script extension, {scriptPath} not found."); FailedExtensions.Add((desc, AddonLoadError.Uknown)); continue; } try { script = PlayniteScript.FromFile(scriptPath, $"{desc.DirectoryName}#PS"); if (script == null) { FailedExtensions.Add((desc, AddonLoadError.Uknown)); continue; } Localization.LoadAddonLocalization(desc.DirectoryPath); script.SetVariable("PlayniteApi", apiGenerator(desc)); script.SetVariable("CurrentExtensionInstallPath", desc.DirectoryPath); if (!desc.Id.IsNullOrEmpty()) { var extDir = Path.Combine(PlaynitePaths.ExtensionsDataPath, Paths.GetSafePathName(desc.Id)); FileSystem.CreateDirectory(extDir); script.SetVariable("CurrentExtensionDataPath", extDir); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { allSuccess = false; logger.Error(e, $"Failed to load script file {scriptPath}"); FailedExtensions.Add((desc, AddonLoadError.Uknown)); continue; } Scripts.Add(script); logger.Info($"Loaded script extension: {scriptPath}, version {desc.Version}"); } return allSuccess; } public void LoadPlugins(List ignoreList, bool builtInOnly, List externals) { if (Plugins.HasItems()) { throw new Exception("Plugin can be loaded only once!"); } var manifests = GetInstalledManifests(externals).Where(a => a.Type != ExtensionType.Script && ignoreList?.Contains(a.Id) != true).ToList(); foreach (var desc in manifests) { if (desc.Id.IsNullOrEmpty()) { logger.Error($"Extension {desc.Name}, doesn't have ID."); continue; } if (desc.Module.IsNullOrWhiteSpace()) { logger.Error($"Extension {desc.Name}, doesn't have module specified."); continue; } if (builtInOnly && !BuiltinExtensions.BuiltinExtensionIds.Contains(desc.Id)) { logger.Warn($"Skipping load of {desc.Name}, builtInOnly is enabled."); continue; } try { Localization.LoadAddonLocalization(desc.DirectoryPath); var plugins = LoadPlugins(desc, apiGenerator); foreach (var plugin in plugins) { if (plugin.Id == default) { logger.Error($"Plugin {plugin.GetType()} doesn't have plugin ID specified."); continue; } if (Plugins.ContainsKey(plugin.Id)) { logger.Warn($"Plugin {plugin.Id} is already loaded."); continue; } Plugins.Add(plugin.Id, new LoadedPlugin(plugin, desc)); logger.Info($"Loaded plugin: {desc.Name}, version {desc.Version}"); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e.InnerException, $"Failed to load plugin: {desc.Name}"); if (e.InnerException == null) { logger.Error(e, string.Empty); } if (e is ReflectionTypeLoadException reflectionTypeLoadException) { foreach (var loaderException in reflectionTypeLoadException.LoaderExceptions) { logger.Error(loaderException, string.Empty); } } FailedExtensions.Add((desc, AddonLoadError.Uknown)); } } } private IEnumerable LoadPlugins(ExtensionManifest descriptor, Func apiGenerator) { var asmPath = Path.Combine(Path.GetDirectoryName(descriptor.DescriptionPath), descriptor.Module); var asmName = AssemblyName.GetAssemblyName(asmPath); var assembly = Assembly.Load(asmName); if (VerifyAssemblyReferences(assembly, descriptor)) { foreach (Type type in assembly.GetTypes()) { if (type.IsInterface || type.IsAbstract) { continue; } else { if (typeof(GenericPlugin).IsAssignableFrom(type) || typeof(LibraryPlugin).IsAssignableFrom(type) || typeof(MetadataPlugin).IsAssignableFrom(type)) { var ignore = Attribute.IsDefined(type, typeof(IgnorePluginAttribute)); var load = Attribute.IsDefined(type, typeof(LoadPluginAttribute)); if ((ignore && load) || !ignore) { yield return (Plugin)Activator.CreateInstance(type, new object[] { apiGenerator(descriptor) }); } } } } } else { logger.Error($"Plugin dependencices are not compatible: {descriptor.Name}"); FailedExtensions.Add((descriptor, AddonLoadError.SDKVersion)); // TODO: Unload assembly once Playnite switches to .NET Core } } public bool InvokeExtension(ExtensionFunction function, out Exception error) { try { logger.Debug($"Invoking extension function {function}"); function.Invoke(); error = null; return true; } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to execute extension function."); error = e; return false; } } private void Controllers_Uninstalled(object sender, GameUninstalledEventArgs args) { if (args.Source?.Game == null) { logger.Error("No game controller information found!"); return; } var callbackArgs = new SDK.Events.OnGameUninstalledEventArgs { Game = args.Source.Game }; foreach (var script in Scripts) { try { script.OnGameUninstalled(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameUninstalled method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnGameUninstalled(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameUninstalled method from {plugin.Description.Name} plugin."); } } } private void Controllers_Stopped(object sender, GameStoppedEventArgs args) { if (args.Source?.Game?.Id == null) { logger.Error("No game controller information found!"); return; } } public void InvokeOnGameStopped(Game game, ulong ellapsedTime, bool manuallyStopped) { var callbackArgs = new SDK.Events.OnGameStoppedEventArgs { Game = database.Games[game.Id], ElapsedSeconds = ellapsedTime, ManuallyStopped = manuallyStopped }; foreach (var script in Scripts) { try { script.OnGameStopped(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameStopped method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnGameStopped(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameStopped method from {plugin.Description.Name} plugin."); } } } private void Controllers_StartupCancelled(object sender, OnGameStartupCancelledEventArgs args) { foreach (var script in Scripts) { try { script.OnGameStartupCancelled(args); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameStartupCancelled method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnGameStartupCancelled(args); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameStartupCancelled method from {plugin.Description.Name} plugin."); } } } private void Controllers_Starting(object sender, OnGameStartingEventArgs args) { foreach (var script in Scripts) { try { script.OnGameStarting(args); if (args.CancelStartup) { return; } } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameStarting method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnGameStarting(args); if (args.CancelStartup) { return; } } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameStarting method from {plugin.Description.Name} plugin."); } } } private void Controllers_Started(object sender, GameStartedEventArgs args) { if (args.Source?.Game?.Id == null) { logger.Error("No game controller information found!"); return; } var callbackArgs = new OnGameStartedEventArgs { Game = database.Games[args.Source.Game.Id], SourceAction = (args.Source as GenericPlayController)?.StartingArgs?.SourceAction?.GetClone(), SelectedRomFile = (args.Source as GenericPlayController)?.StartingArgs.SelectedRomFile, StartedProcessId = args.StartedProcessId }; foreach (var script in Scripts) { try { script.OnGameStarted(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameStarted method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnGameStarted(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameStarted method from {plugin.Description.Name} plugin."); } } } private void Controllers_Installed(object sender, GameInstalledEventArgs args) { if (args.Source?.Game?.Id == null) { logger.Error("No game controller information found!"); return; } var callbackArgs = new SDK.Events.OnGameInstalledEventArgs { Game = database.Games[args.Source.Game.Id] }; foreach (var script in Scripts) { try { script.OnGameInstalled(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameInstalled method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnGameInstalled(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameInstalled method from {plugin.Description.Name} plugin."); } } } private void Controllers_InstallationCancelled(object sender, GameInstallationCancelledEventArgs args) { if (args.Source?.Game?.Id == null) { logger.Error("No game controller information found!"); return; } var callbackArgs = new SDK.Events.OnGameInstallationCancelledEventArgs { Game = database.Games[args.Source.Game.Id] }; foreach (var script in Scripts) { try { script.OnGameInstallationCancelled(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameInstallationCancelled method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnGameInstallationCancelled(callbackArgs); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameInstallationCancelled method from {plugin.Description.Name} plugin."); } } } public void InvokeOnGameSelected(List oldValue, List newValue) { var args = new SDK.Events.OnGameSelectedEventArgs(oldValue, newValue); foreach (var script in Scripts) { try { script.OnGameSelected(args); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameSelected method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnGameSelected(args); } catch (Exception e) { logger.Error(e, $"Failed to execute OnGameSelected method from {plugin.Description.Name} plugin."); } } } public void NotifiyOnApplicationStarted() { foreach (var script in Scripts) { try { script.OnApplicationStarted(); } catch (Exception e) { logger.Error(e, $"Failed to execute OnApplicationStarted method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnApplicationStarted(new SDK.Events.OnApplicationStartedEventArgs()); } catch (Exception e) { logger.Error(e, $"Failed to execute OnApplicationStarted method from {plugin.Description.Name} plugin."); } } } public void NotifiyOnApplicationStopped() { foreach (var script in Scripts) { try { script.OnApplicationStopped(); } catch (Exception e) { logger.Error(e, $"Failed to execute OnApplicationStopped method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnApplicationStopped(new SDK.Events.OnApplicationStoppedEventArgs()); } catch (Exception e) { logger.Error(e, $"Failed to execute OnApplicationStopped method from {plugin.Description.Name} plugin."); } } } public void NotifiyOnLibraryUpdated() { foreach (var script in Scripts) { try { script.OnLibraryUpdated(); } catch (Exception e) { logger.Error(e, $"Failed to execute OnLibraryUpdated method from {script.Name} script."); } } foreach (var plugin in Plugins.Values) { try { plugin.Plugin.OnLibraryUpdated(new SDK.Events.OnLibraryUpdatedEventArgs()); } catch (Exception e) { logger.Error(e, $"Failed to execute OnLibraryUpdated method from {plugin.Description.Name} plugin."); } } } public LibraryPlugin GetLibraryPlugin(Guid pluginId) { if (pluginId == Guid.Empty) { return null; } return LibraryPlugins.FirstOrDefault(a => a.Id == pluginId); } public List CustomElementList = new List(); public void AddCustomElementSupport(Plugin source, AddCustomElementSupportArgs args) { if (CustomElementList.Any(a => a.Source == source)) { return; } var elemSupport = args.GetClone(); elemSupport.Source = source; CustomElementList.Add(elemSupport); } public List SettingsSupportList = new List(); public void AddSettingsSupport(Plugin source, AddSettingsSupportArgs args) { if (SettingsSupportList.Any(a => a.Source == source)) { return; } var elemSupport = args.GetClone(); elemSupport.Source = source; SettingsSupportList.Add(elemSupport); } public List ConvertersSupportList = new List(); public void AddConvertersSupport(Plugin source, AddConvertersSupportArgs args) { if (ConvertersSupportList.Any(a => a.Source == source)) { return; } ConvertersSupportList.Add(new PluginConvertersSupport { Source = source, Converters = args.Converters, SourceName = args.SourceName }); } public List GetTopPanelPluginItems() { var res = new List(); foreach (var plugin in Plugins.Values) { try { var items = plugin.Plugin.GetTopPanelItems().ToList(); if (items.HasItems()) { res.AddRange(items); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get top panel itesm from {plugin.Description.Id}"); } } return res; } } public class PluginUiElementSupport : AddCustomElementSupportArgs { public Plugin Source { get; set; } } public class PluginSettingsSupport : AddSettingsSupportArgs { public Plugin Source { get; set; } } public class PluginConvertersSupport : AddConvertersSupportArgs { public Plugin Source { get; set; } } } ================================================ FILE: source/Playnite/Plugins/ExtensionInstaller.cs ================================================ using Playnite.API; using Playnite.Common; using Playnite.SDK; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Plugins { public enum ExtInstallType { Install, Uninstall } public class ExtensionInstallQueueItem { public ExtInstallType InstallType { get; set; } public string Path { get; set; } public ExtensionInstallQueueItem() { } public ExtensionInstallQueueItem(string path, ExtInstallType type) { Path = path; InstallType = type; } public override string ToString() { return Path; } } public class ExtensionInstallResult { public bool Updated { get; set; } public Exception InstallError { get; set; } public BaseExtensionManifest InstalledManifest { get; set; } public string PackagePath { get; set; } public ExtensionInstallResult(bool updated, BaseExtensionManifest installedManifest, string packagePath) { Updated = updated; InstalledManifest = installedManifest; PackagePath = packagePath; } public ExtensionInstallResult(Exception installError, string packagePath) { InstallError = installError; PackagePath = packagePath; } } public class ExtensionInstaller { private static readonly ILogger logger = LogManager.GetLogger(); private static List currentQueue = new List(); private static Dictionary agreedLicenses; static ExtensionInstaller() { if (File.Exists(PlaynitePaths.AddonLicenseAgreementsFilePath)) { agreedLicenses = Serialization.FromJsonFile>(PlaynitePaths.AddonLicenseAgreementsFilePath); } else { agreedLicenses = new Dictionary(); } } public static void AgreeAddonLicense(string addonId) { agreedLicenses[addonId] = DateTime.Today; File.WriteAllText(PlaynitePaths.AddonLicenseAgreementsFilePath, Serialization.ToJson(agreedLicenses, true)); } public static void RemoveAddonLicenseAgreement(string addonId) { if (agreedLicenses.ContainsKey(addonId)) { agreedLicenses.Remove(addonId); File.WriteAllText(PlaynitePaths.AddonLicenseAgreementsFilePath, Serialization.ToJson(agreedLicenses, true)); } } public static DateTime? GetAddonLicenseAgreed(string addonId) { if (agreedLicenses.ContainsKey(addonId)) { return agreedLicenses[addonId]; } else { return null; } } public static List GetQueuedItems() { if (!File.Exists(PlaynitePaths.ExtensionQueueFilePath)) { return new List(); } return Serialization.FromJsonFile>(PlaynitePaths.ExtensionQueueFilePath); } public static List InstallExtensionQueue() { var installedExts = new List(); foreach (var queueItem in GetQueuedItems()) { if (queueItem.InstallType == ExtInstallType.Install) { if (queueItem.Path.EndsWith(PlaynitePaths.PackedThemeFileExtention, StringComparison.OrdinalIgnoreCase)) { try { installedExts.Add(InstallPackedTheme(queueItem.Path)); logger.Info($"Installed theme {queueItem}"); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { installedExts.Add(new ExtensionInstallResult(e, queueItem.Path)); logger.Error(e, $"Failed to install theme {queueItem}"); } } else if (queueItem.Path.EndsWith(PlaynitePaths.PackedExtensionFileExtention, StringComparison.OrdinalIgnoreCase)) { try { installedExts.Add(InstallPackedExtension(queueItem.Path)); logger.Info($"Installed extension {queueItem}"); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { installedExts.Add(new ExtensionInstallResult(e, queueItem.Path)); logger.Error(e, $"Failed to install extension {queueItem}"); } } else { logger.Warn($"Uknown extension file format {queueItem}"); } } else if (queueItem.InstallType == ExtInstallType.Uninstall) { if (Directory.Exists(queueItem.Path)) { try { Directory.Delete(queueItem.Path, true); logger.Info($"Uninstalled theme or extension {queueItem}"); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to uninstall extension {queueItem}"); } } else { logger.Warn($"Can't uninstall extension, directory doesn't exists anymore {queueItem}"); } } } File.Delete(PlaynitePaths.ExtensionQueueFilePath); currentQueue = new List(); return installedExts; } public static ExtensionInstallResult InstallPackedFile(string packagePath, string nanifestFileName, string rootDir, Func newMan) where T : BaseExtensionManifest { logger.Info($"Installing extenstion/theme {packagePath}"); var manifest = GetPackedManifest(packagePath, nanifestFileName); if (manifest == null) { throw new FileNotFoundException("Extenstion/theme manifest not found."); } var entries = Archive.GetArchiveFiles(packagePath); if (entries.Any(a => a.EndsWith(".sln", StringComparison.OrdinalIgnoreCase))) { // Check for themes that are not packaged via Toolbox. throw new Exception("Package content invalid."); } if (manifest is ThemeManifest themeMan) { rootDir = Path.Combine(rootDir, themeMan.Mode.ToString()); } var wasUpdated = false; var installDir = Path.Combine(rootDir, Paths.GetSafePathName(manifest.Id)); if (Directory.Exists(installDir)) { wasUpdated = true; logger.Debug($"Replacing existing extenstion/theme installation: {installDir}."); } FileSystem.CreateDirectory(installDir, true); ZipFile.ExtractToDirectory(packagePath, installDir); if (Paths.AreEqual(PlaynitePaths.TempPath, Path.GetDirectoryName(packagePath))) { File.Delete(packagePath); } return new ExtensionInstallResult(wasUpdated, newMan(Path.Combine(installDir, nanifestFileName)), packagePath); } public static ExtensionInstallResult InstallPackedExtension(string path) { return InstallPackedFile( path, PlaynitePaths.ExtensionManifestFileName, PlaynitePaths.ExtensionsUserDataPath, (a) => ExtensionManifest.FromFile(a)); } public static ExtensionInstallResult InstallPackedTheme(string path) { return InstallPackedFile( path, PlaynitePaths.ThemeManifestFileName, PlaynitePaths.ThemesUserDataPath, (a) => new ThemeManifest(a)); } private static T GetPackedManifest(string packagePath, string nanifestFileName) where T : class { using (var zip = ZipFile.OpenRead(packagePath)) { var manifest = zip.GetEntry(nanifestFileName); if (manifest == null) { return null; } using (var logStream = manifest.Open()) { using (TextReader tr = new StreamReader(logStream)) { return Serialization.FromYaml(tr.ReadToEnd()); } } } } private static BaseExtensionManifest GetManifestFromDir(string extDir) { if (!Directory.Exists(extDir)) { return null; } var extMan = Path.Combine(extDir, PlaynitePaths.ExtensionManifestFileName); if (File.Exists(extMan)) { return GetExtensionManifest(extMan); } var themeMan = Path.Combine(extDir, PlaynitePaths.ThemeManifestFileName); if (File.Exists(themeMan)) { return GetThemeManifest(themeMan); } return null; } private static T GetManifestFile(string manifestPath) where T : class { return Serialization.FromYaml(File.ReadAllText(manifestPath)); } public static ThemeManifest GetPackedThemeManifest(string packagePath) { return GetPackedManifest(packagePath, PlaynitePaths.ThemeManifestFileName); } public static ThemeManifest GetThemeManifest(string manifestPath) { return GetManifestFile(manifestPath); } public static ExtensionManifest GetPackedExtensionManifest(string packagePath) { return GetPackedManifest(packagePath, PlaynitePaths.ExtensionManifestFileName); } public static ExtensionManifest GetExtensionManifest(string manifestPath) { return GetManifestFile(manifestPath); } public static void QueuePackageInstall(string packagePath) { QueueExtensionOperation(packagePath, ExtInstallType.Install); } public static void QueueExtensionUninstall(string extensionDirectory) { QueueExtensionOperation(extensionDirectory, ExtInstallType.Uninstall); } private static void QueueExtensionOperation(string extensionPath, ExtInstallType installationType) { if (currentQueue.FirstOrDefault(a => a.Path == extensionPath) == null) { currentQueue.Add(new ExtensionInstallQueueItem(extensionPath, installationType)); } FileSystem.WriteStringToFile(PlaynitePaths.ExtensionQueueFilePath, Serialization.ToJson(currentQueue)); } public static void VerifyExtensionPackage(string packagePath) { using (var zip = ZipFile.OpenRead(packagePath)) { var manifestEntry = zip.GetEntry(PlaynitePaths.ExtensionManifestFileName); if (manifestEntry == null) { logger.Error("Extension package is invalid, no manifest found."); throw new LocalizedException(LOC.GeneralExtensionPackageError); } using (var logStream = manifestEntry.Open()) { using (TextReader tr = new StreamReader(logStream)) { var manifest = Serialization.FromYaml(tr.ReadToEnd()); if (manifest.Id.IsNullOrEmpty()) { logger.Error("Extension package is invalid, no extension ID found."); throw new LocalizedException(LOC.GeneralExtensionPackageError); } if (!Version.TryParse(manifest.Version, out var _)) { logger.Error($"Extension package is invalid, version is not in correct format {manifest.Version}."); throw new LocalizedException(LOC.GeneralExtensionPackageError); } } } if (zip.Entries.Any(a => a.Name == "Playnite.dll" || a.Name == "Playnite.Common.dll" || a.Name == "Playnite.SDK.dll")) { logger.Error($"Extension package is invalid, includes not allowed Playnite dependencies."); throw new LocalizedException(LOC.GeneralExtensionPackageError); } } } public static void VerifyThemePackage(string packagePath) { using (var zip = ZipFile.OpenRead(packagePath)) { var manifestEntry = zip.GetEntry(PlaynitePaths.ThemeManifestFileName); if (manifestEntry == null) { logger.Error("Theme package is invalid, no manifest found."); throw new LocalizedException(LOC.GeneralThemePackageError); } using (var logStream = manifestEntry.Open()) { using (TextReader tr = new StreamReader(logStream)) { var manifest = Serialization.FromYaml(tr.ReadToEnd()); if (manifest.Id.IsNullOrEmpty()) { logger.Error("Theme package is invalid, no extension ID found."); throw new LocalizedException(LOC.GeneralThemePackageError); } if (!Version.TryParse(manifest.Version, out var _)) { logger.Error($"Theme package is invalid, version is not in correct format {manifest.Version}."); throw new LocalizedException(LOC.GeneralThemePackageError); } } } if (zip.Entries.Any(a => a.Name == PlaynitePaths.ThemeSlnFileName || a.Name == PlaynitePaths.ThemeProjFileName || a.Name == PlaynitePaths.AppXamlFileName)) { logger.Error($"Theme package is invalid, includes not allowed theme project files."); throw new LocalizedException(LOC.GeneralThemePackageError); } if (zip.Entries.Any(a => a.Name == "Playnite.dll" || a.Name == "Playnite.Common.dll" || a.Name == "Playnite.SDK.dll")) { logger.Error($"Theme package is invalid, includes not allowed Playnite dependencies."); throw new LocalizedException(LOC.GeneralThemePackageError); } } } } } ================================================ FILE: source/Playnite/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows.Markup; [assembly: System.Windows.ThemeInfo(System.Windows.ResourceDictionaryLocation.None, System.Windows.ResourceDictionaryLocation.SourceAssembly)] // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Playnite")] [assembly: AssemblyDescription("Playnite Support Library")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Josef Nemec")] [assembly: AssemblyProduct("Playnite")] [assembly: AssemblyCopyright("Copyright © Josef Nemec 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("ad271e73-8a13-4c4e-bfdc-3076646b59e3")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // 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("10.51.0.*")] [assembly: InternalsVisibleTo("Playnite.Tests")] [assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "Playnite")] [assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "Playnite.Controls")] [assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "Playnite.Commands")] [assembly: XmlnsDefinition("http://schemas.microsoft.com/winfx/2006/xaml/presentation", "Playnite.Behaviors")] ================================================ FILE: source/Playnite/SDL2.cs ================================================ #region License /* SDL2# - C# Wrapper for SDL2 * * Copyright (c) 2013-2021 Ethan Lee. * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in a * product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * * Ethan "flibitijibibo" Lee * */ #endregion #region Using Statements using System; using System.Diagnostics; #if NET6_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif using System.Runtime.InteropServices; using System.Text; #endregion namespace SDL2 { public static class SDL { #region SDL2# Variables private const string nativeLibName = "SDL2"; #endregion #region Marshaling #if NET6_0_OR_GREATER internal static T PtrToStructure<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(IntPtr ptr) { return Marshal.PtrToStructure(ptr); } internal static T GetDelegateForFunctionPointer(IntPtr ptr) where T : Delegate { return Marshal.GetDelegateForFunctionPointer(ptr); } #else internal static T PtrToStructure(IntPtr ptr) { return (T) Marshal.PtrToStructure(ptr, typeof(T)); } internal static Delegate GetDelegateForFunctionPointer(IntPtr ptr) { return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)); } #endif internal static int SizeOf() { #if NETSTANDARD2_0_OR_GREATER || NET6_0_OR_GREATER return Marshal.SizeOf(); #else return Marshal.SizeOf(typeof(T)); #endif } #endregion #region UTF8 Marshaling /* Used for stack allocated string marshaling. */ internal static int Utf8Size(string str) { if (str == null) { return 0; } return (str.Length * 4) + 1; } internal static unsafe byte* Utf8Encode(string str, byte* buffer, int bufferSize) { if (str == null) { return (byte*) 0; } fixed (char* strPtr = str) { Encoding.UTF8.GetBytes(strPtr, str.Length + 1, buffer, bufferSize); } return buffer; } /* Used for heap allocated string marshaling. * Returned byte* must be free'd with FreeHGlobal. */ internal static unsafe byte* Utf8EncodeHeap(string str) { if (str == null) { return (byte*) 0; } int bufferSize = Utf8Size(str); byte* buffer = (byte*) Marshal.AllocHGlobal(bufferSize); fixed (char* strPtr = str) { Encoding.UTF8.GetBytes(strPtr, str.Length + 1, buffer, bufferSize); } return buffer; } /* This is public because SDL_DropEvent needs it! */ public static unsafe string UTF8_ToManaged(IntPtr s, bool freePtr = false) { if (s == IntPtr.Zero) { return null; } /* We get to do strlen ourselves! */ byte* ptr = (byte*) s; while (*ptr != 0) { ptr++; } /* TODO: This #ifdef is only here because the equivalent * .NET 2.0 constructor appears to be less efficient? * Here's the pretty version, maybe steal this instead: * string result = new string( (sbyte*) s, // Also, why sbyte??? 0, (int) (ptr - (byte*) s), System.Text.Encoding.UTF8 ); * See the CoreCLR source for more info. * -flibit */ #if NETSTANDARD2_0 /* Modern C# lets you just send the byte*, nice! */ string result = System.Text.Encoding.UTF8.GetString( (byte*) s, (int) (ptr - (byte*) s) ); #else /* Old C# requires an extra memcpy, bleh! */ int len = (int) (ptr - (byte*) s); if (len == 0) { return string.Empty; } char* chars = stackalloc char[len]; int strLen = System.Text.Encoding.UTF8.GetChars((byte*) s, len, chars, len); string result = new string(chars, 0, strLen); #endif /* Some SDL functions will malloc, we have to free! */ if (freePtr) { SDL_free(s); } return result; } #endregion #region SDL_stdinc.h public static uint SDL_FOURCC(byte A, byte B, byte C, byte D) { return (uint) (A | (B << 8) | (C << 16) | (D << 24)); } public enum SDL_bool { SDL_FALSE = 0, SDL_TRUE = 1 } /* malloc/free are used by the marshaler! -flibit */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr SDL_malloc(IntPtr size); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] internal static extern void SDL_free(IntPtr memblock); /* Buffer.BlockCopy is not available in every runtime yet. Also, * using memcpy directly can be a compatibility issue in other * strange ways. So, we expose this to get around all that. * -flibit */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_memcpy(IntPtr dst, IntPtr src, IntPtr len); #endregion #region SDL_rwops.h public const int RW_SEEK_SET = 0; public const int RW_SEEK_CUR = 1; public const int RW_SEEK_END = 2; public const UInt32 SDL_RWOPS_UNKNOWN = 0; /* Unknown stream type */ public const UInt32 SDL_RWOPS_WINFILE = 1; /* Win32 file */ public const UInt32 SDL_RWOPS_STDFILE = 2; /* Stdio file */ public const UInt32 SDL_RWOPS_JNIFILE = 3; /* Android asset */ public const UInt32 SDL_RWOPS_MEMORY = 4; /* Memory stream */ public const UInt32 SDL_RWOPS_MEMORY_RO = 5; /* Read-Only memory stream */ [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate long SDLRWopsSizeCallback(IntPtr context); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate long SDLRWopsSeekCallback( IntPtr context, long offset, int whence ); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr SDLRWopsReadCallback( IntPtr context, IntPtr ptr, IntPtr size, IntPtr maxnum ); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr SDLRWopsWriteCallback( IntPtr context, IntPtr ptr, IntPtr size, IntPtr num ); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int SDLRWopsCloseCallback( IntPtr context ); [StructLayout(LayoutKind.Sequential)] public struct SDL_RWops { public IntPtr size; public IntPtr seek; public IntPtr read; public IntPtr write; public IntPtr close; public UInt32 type; /* NOTE: This isn't the full structure since * the native SDL_RWops contains a hidden union full of * internal information and platform-specific stuff depending * on what conditions the native library was built with */ } /* IntPtr refers to an SDL_RWops* */ [DllImport(nativeLibName, EntryPoint = "SDL_RWFromFile", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe IntPtr INTERNAL_SDL_RWFromFile( byte* file, byte* mode ); public static unsafe IntPtr SDL_RWFromFile( string file, string mode ) { byte* utf8File = Utf8EncodeHeap(file); byte* utf8Mode = Utf8EncodeHeap(mode); IntPtr rwOps = INTERNAL_SDL_RWFromFile( utf8File, utf8Mode ); Marshal.FreeHGlobal((IntPtr) utf8Mode); Marshal.FreeHGlobal((IntPtr) utf8File); return rwOps; } /* IntPtr refers to an SDL_RWops* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_AllocRW(); /* area refers to an SDL_RWops* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FreeRW(IntPtr area); /* fp refers to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_RWFromFP(IntPtr fp, SDL_bool autoclose); /* mem refers to a void*, IntPtr to an SDL_RWops* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_RWFromMem(IntPtr mem, int size); /* mem refers to a const void*, IntPtr to an SDL_RWops* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_RWFromConstMem(IntPtr mem, int size); /* context refers to an SDL_RWops*. * Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern long SDL_RWsize(IntPtr context); /* context refers to an SDL_RWops*. * Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern long SDL_RWseek( IntPtr context, long offset, int whence ); /* context refers to an SDL_RWops*. * Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern long SDL_RWtell(IntPtr context); /* context refers to an SDL_RWops*, ptr refers to a void*. * Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern long SDL_RWread( IntPtr context, IntPtr ptr, IntPtr size, IntPtr maxnum ); /* context refers to an SDL_RWops*, ptr refers to a const void*. * Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern long SDL_RWwrite( IntPtr context, IntPtr ptr, IntPtr size, IntPtr maxnum ); /* Read endian functions */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern byte SDL_ReadU8(IntPtr src); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt16 SDL_ReadLE16(IntPtr src); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt16 SDL_ReadBE16(IntPtr src); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_ReadLE32(IntPtr src); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_ReadBE32(IntPtr src); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt64 SDL_ReadLE64(IntPtr src); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt64 SDL_ReadBE64(IntPtr src); /* Write endian functions */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_WriteU8(IntPtr dst, byte value); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_WriteLE16(IntPtr dst, UInt16 value); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_WriteBE16(IntPtr dst, UInt16 value); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_WriteLE32(IntPtr dst, UInt32 value); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_WriteBE32(IntPtr dst, UInt32 value); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_WriteLE64(IntPtr dst, UInt64 value); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_WriteBE64(IntPtr dst, UInt64 value); /* context refers to an SDL_RWops* * Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern long SDL_RWclose(IntPtr context); /* datasize refers to a size_t* * IntPtr refers to a void* * Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_LoadFile", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe IntPtr INTERNAL_SDL_LoadFile(byte* file, out IntPtr datasize); public static unsafe IntPtr SDL_LoadFile(string file, out IntPtr datasize) { byte* utf8File = Utf8EncodeHeap(file); IntPtr result = INTERNAL_SDL_LoadFile(utf8File, out datasize); Marshal.FreeHGlobal((IntPtr) utf8File); return result; } #endregion #region SDL_main.h [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetMainReady(); /* This is used as a function pointer to a C main() function */ public delegate int SDL_main_func(int argc, IntPtr argv); /* Use this function with UWP to call your C# Main() function! */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_WinRTRunApp( SDL_main_func mainFunction, IntPtr reserved ); /* Use this function with GDK/GDKX to call your C# Main() function! * Only available in SDL 2.24.0 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GDKRunApp( SDL_main_func mainFunction, IntPtr reserved ); /* Use this function with iOS to call your C# Main() function! * Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_UIKitRunApp( int argc, IntPtr argv, SDL_main_func mainFunction ); #endregion #region SDL.h public const uint SDL_INIT_TIMER = 0x00000001; public const uint SDL_INIT_AUDIO = 0x00000010; public const uint SDL_INIT_VIDEO = 0x00000020; public const uint SDL_INIT_JOYSTICK = 0x00000200; public const uint SDL_INIT_HAPTIC = 0x00001000; public const uint SDL_INIT_GAMECONTROLLER = 0x00002000; public const uint SDL_INIT_EVENTS = 0x00004000; public const uint SDL_INIT_SENSOR = 0x00008000; public const uint SDL_INIT_NOPARACHUTE = 0x00100000; public const uint SDL_INIT_EVERYTHING = ( SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_Init(uint flags); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_InitSubSystem(uint flags); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_Quit(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_QuitSubSystem(uint flags); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_WasInit(uint flags); #endregion #region SDL_platform.h [DllImport(nativeLibName, EntryPoint = "SDL_GetPlatform", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetPlatform(); public static string SDL_GetPlatform() { return UTF8_ToManaged(INTERNAL_SDL_GetPlatform()); } #endregion #region SDL_hints.h public const string SDL_HINT_FRAMEBUFFER_ACCELERATION = "SDL_FRAMEBUFFER_ACCELERATION"; public const string SDL_HINT_RENDER_DRIVER = "SDL_RENDER_DRIVER"; public const string SDL_HINT_RENDER_OPENGL_SHADERS = "SDL_RENDER_OPENGL_SHADERS"; public const string SDL_HINT_RENDER_DIRECT3D_THREADSAFE = "SDL_RENDER_DIRECT3D_THREADSAFE"; public const string SDL_HINT_RENDER_VSYNC = "SDL_RENDER_VSYNC"; public const string SDL_HINT_VIDEO_X11_XVIDMODE = "SDL_VIDEO_X11_XVIDMODE"; public const string SDL_HINT_VIDEO_X11_XINERAMA = "SDL_VIDEO_X11_XINERAMA"; public const string SDL_HINT_VIDEO_X11_XRANDR = "SDL_VIDEO_X11_XRANDR"; public const string SDL_HINT_GRAB_KEYBOARD = "SDL_GRAB_KEYBOARD"; public const string SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS = "SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS"; public const string SDL_HINT_IDLE_TIMER_DISABLED = "SDL_IOS_IDLE_TIMER_DISABLED"; public const string SDL_HINT_ORIENTATIONS = "SDL_IOS_ORIENTATIONS"; public const string SDL_HINT_XINPUT_ENABLED = "SDL_XINPUT_ENABLED"; public const string SDL_HINT_GAMECONTROLLERCONFIG = "SDL_GAMECONTROLLERCONFIG"; public const string SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS = "SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS"; public const string SDL_HINT_ALLOW_TOPMOST = "SDL_ALLOW_TOPMOST"; public const string SDL_HINT_TIMER_RESOLUTION = "SDL_TIMER_RESOLUTION"; public const string SDL_HINT_RENDER_SCALE_QUALITY = "SDL_RENDER_SCALE_QUALITY"; /* Only available in SDL 2.0.1 or higher. */ public const string SDL_HINT_VIDEO_HIGHDPI_DISABLED = "SDL_VIDEO_HIGHDPI_DISABLED"; /* Only available in SDL 2.0.2 or higher. */ public const string SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK = "SDL_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK"; public const string SDL_HINT_VIDEO_WIN_D3DCOMPILER = "SDL_VIDEO_WIN_D3DCOMPILER"; public const string SDL_HINT_MOUSE_RELATIVE_MODE_WARP = "SDL_MOUSE_RELATIVE_MODE_WARP"; public const string SDL_HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT = "SDL_VIDEO_WINDOW_SHARE_PIXEL_FORMAT"; public const string SDL_HINT_VIDEO_ALLOW_SCREENSAVER = "SDL_VIDEO_ALLOW_SCREENSAVER"; public const string SDL_HINT_ACCELEROMETER_AS_JOYSTICK = "SDL_ACCELEROMETER_AS_JOYSTICK"; public const string SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES = "SDL_VIDEO_MAC_FULLSCREEN_SPACES"; /* Only available in SDL 2.0.3 or higher. */ public const string SDL_HINT_WINRT_PRIVACY_POLICY_URL = "SDL_WINRT_PRIVACY_POLICY_URL"; public const string SDL_HINT_WINRT_PRIVACY_POLICY_LABEL = "SDL_WINRT_PRIVACY_POLICY_LABEL"; public const string SDL_HINT_WINRT_HANDLE_BACK_BUTTON = "SDL_WINRT_HANDLE_BACK_BUTTON"; /* Only available in SDL 2.0.4 or higher. */ public const string SDL_HINT_NO_SIGNAL_HANDLERS = "SDL_NO_SIGNAL_HANDLERS"; public const string SDL_HINT_IME_INTERNAL_EDITING = "SDL_IME_INTERNAL_EDITING"; public const string SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH = "SDL_ANDROID_SEPARATE_MOUSE_AND_TOUCH"; public const string SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT = "SDL_EMSCRIPTEN_KEYBOARD_ELEMENT"; public const string SDL_HINT_THREAD_STACK_SIZE = "SDL_THREAD_STACK_SIZE"; public const string SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN = "SDL_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN"; public const string SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP = "SDL_WINDOWS_ENABLE_MESSAGELOOP"; public const string SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 = "SDL_WINDOWS_NO_CLOSE_ON_ALT_F4"; public const string SDL_HINT_XINPUT_USE_OLD_JOYSTICK_MAPPING = "SDL_XINPUT_USE_OLD_JOYSTICK_MAPPING"; public const string SDL_HINT_MAC_BACKGROUND_APP = "SDL_MAC_BACKGROUND_APP"; public const string SDL_HINT_VIDEO_X11_NET_WM_PING = "SDL_VIDEO_X11_NET_WM_PING"; public const string SDL_HINT_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION = "SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION"; public const string SDL_HINT_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION = "SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION"; /* Only available in 2.0.5 or higher. */ public const string SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH = "SDL_MOUSE_FOCUS_CLICKTHROUGH"; public const string SDL_HINT_BMP_SAVE_LEGACY_FORMAT = "SDL_BMP_SAVE_LEGACY_FORMAT"; public const string SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING = "SDL_WINDOWS_DISABLE_THREAD_NAMING"; public const string SDL_HINT_APPLE_TV_REMOTE_ALLOW_ROTATION = "SDL_APPLE_TV_REMOTE_ALLOW_ROTATION"; /* Only available in 2.0.6 or higher. */ public const string SDL_HINT_AUDIO_RESAMPLING_MODE = "SDL_AUDIO_RESAMPLING_MODE"; public const string SDL_HINT_RENDER_LOGICAL_SIZE_MODE = "SDL_RENDER_LOGICAL_SIZE_MODE"; public const string SDL_HINT_MOUSE_NORMAL_SPEED_SCALE = "SDL_MOUSE_NORMAL_SPEED_SCALE"; public const string SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE = "SDL_MOUSE_RELATIVE_SPEED_SCALE"; public const string SDL_HINT_TOUCH_MOUSE_EVENTS = "SDL_TOUCH_MOUSE_EVENTS"; public const string SDL_HINT_WINDOWS_INTRESOURCE_ICON = "SDL_WINDOWS_INTRESOURCE_ICON"; public const string SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL = "SDL_WINDOWS_INTRESOURCE_ICON_SMALL"; /* Only available in 2.0.8 or higher. */ public const string SDL_HINT_IOS_HIDE_HOME_INDICATOR = "SDL_IOS_HIDE_HOME_INDICATOR"; public const string SDL_HINT_TV_REMOTE_AS_JOYSTICK = "SDL_TV_REMOTE_AS_JOYSTICK"; public const string SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR = "SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR"; /* Only available in 2.0.9 or higher. */ public const string SDL_HINT_MOUSE_DOUBLE_CLICK_TIME = "SDL_MOUSE_DOUBLE_CLICK_TIME"; public const string SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS = "SDL_MOUSE_DOUBLE_CLICK_RADIUS"; public const string SDL_HINT_JOYSTICK_HIDAPI = "SDL_JOYSTICK_HIDAPI"; public const string SDL_HINT_JOYSTICK_HIDAPI_PS4 = "SDL_JOYSTICK_HIDAPI_PS4"; public const string SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE = "SDL_JOYSTICK_HIDAPI_PS4_RUMBLE"; public const string SDL_HINT_JOYSTICK_HIDAPI_STEAM = "SDL_JOYSTICK_HIDAPI_STEAM"; public const string SDL_HINT_JOYSTICK_HIDAPI_SWITCH = "SDL_JOYSTICK_HIDAPI_SWITCH"; public const string SDL_HINT_JOYSTICK_HIDAPI_XBOX = "SDL_JOYSTICK_HIDAPI_XBOX"; public const string SDL_HINT_ENABLE_STEAM_CONTROLLERS = "SDL_ENABLE_STEAM_CONTROLLERS"; public const string SDL_HINT_ANDROID_TRAP_BACK_BUTTON = "SDL_ANDROID_TRAP_BACK_BUTTON"; /* Only available in 2.0.10 or higher. */ public const string SDL_HINT_MOUSE_TOUCH_EVENTS = "SDL_MOUSE_TOUCH_EVENTS"; public const string SDL_HINT_GAMECONTROLLERCONFIG_FILE = "SDL_GAMECONTROLLERCONFIG_FILE"; public const string SDL_HINT_ANDROID_BLOCK_ON_PAUSE = "SDL_ANDROID_BLOCK_ON_PAUSE"; public const string SDL_HINT_RENDER_BATCHING = "SDL_RENDER_BATCHING"; public const string SDL_HINT_EVENT_LOGGING = "SDL_EVENT_LOGGING"; public const string SDL_HINT_WAVE_RIFF_CHUNK_SIZE = "SDL_WAVE_RIFF_CHUNK_SIZE"; public const string SDL_HINT_WAVE_TRUNCATION = "SDL_WAVE_TRUNCATION"; public const string SDL_HINT_WAVE_FACT_CHUNK = "SDL_WAVE_FACT_CHUNK"; /* Only available in 2.0.11 or higher. */ public const string SDL_HINT_VIDO_X11_WINDOW_VISUALID = "SDL_VIDEO_X11_WINDOW_VISUALID"; public const string SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS = "SDL_GAMECONTROLLER_USE_BUTTON_LABELS"; public const string SDL_HINT_VIDEO_EXTERNAL_CONTEXT = "SDL_VIDEO_EXTERNAL_CONTEXT"; public const string SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE = "SDL_JOYSTICK_HIDAPI_GAMECUBE"; public const string SDL_HINT_DISPLAY_USABLE_BOUNDS = "SDL_DISPLAY_USABLE_BOUNDS"; public const string SDL_HINT_VIDEO_X11_FORCE_EGL = "SDL_VIDEO_X11_FORCE_EGL"; public const string SDL_HINT_GAMECONTROLLERTYPE = "SDL_GAMECONTROLLERTYPE"; /* Only available in 2.0.14 or higher. */ public const string SDL_HINT_JOYSTICK_HIDAPI_CORRELATE_XINPUT = "SDL_JOYSTICK_HIDAPI_CORRELATE_XINPUT"; /* NOTE: This was removed in 2.0.16. */ public const string SDL_HINT_JOYSTICK_RAWINPUT = "SDL_JOYSTICK_RAWINPUT"; public const string SDL_HINT_AUDIO_DEVICE_APP_NAME = "SDL_AUDIO_DEVICE_APP_NAME"; public const string SDL_HINT_AUDIO_DEVICE_STREAM_NAME = "SDL_AUDIO_DEVICE_STREAM_NAME"; public const string SDL_HINT_PREFERRED_LOCALES = "SDL_PREFERRED_LOCALES"; public const string SDL_HINT_THREAD_PRIORITY_POLICY = "SDL_THREAD_PRIORITY_POLICY"; public const string SDL_HINT_EMSCRIPTEN_ASYNCIFY = "SDL_EMSCRIPTEN_ASYNCIFY"; public const string SDL_HINT_LINUX_JOYSTICK_DEADZONES = "SDL_LINUX_JOYSTICK_DEADZONES"; public const string SDL_HINT_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO = "SDL_ANDROID_BLOCK_ON_PAUSE_PAUSEAUDIO"; public const string SDL_HINT_JOYSTICK_HIDAPI_PS5 = "SDL_JOYSTICK_HIDAPI_PS5"; public const string SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL = "SDL_THREAD_FORCE_REALTIME_TIME_CRITICAL"; public const string SDL_HINT_JOYSTICK_THREAD = "SDL_JOYSTICK_THREAD"; public const string SDL_HINT_AUTO_UPDATE_JOYSTICKS = "SDL_AUTO_UPDATE_JOYSTICKS"; public const string SDL_HINT_AUTO_UPDATE_SENSORS = "SDL_AUTO_UPDATE_SENSORS"; public const string SDL_HINT_MOUSE_RELATIVE_SCALING = "SDL_MOUSE_RELATIVE_SCALING"; public const string SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE = "SDL_JOYSTICK_HIDAPI_PS5_RUMBLE"; /* Only available in 2.0.16 or higher. */ public const string SDL_HINT_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS = "SDL_WINDOWS_FORCE_MUTEX_CRITICAL_SECTIONS"; public const string SDL_HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL = "SDL_WINDOWS_FORCE_SEMAPHORE_KERNEL"; public const string SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED = "SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED"; public const string SDL_HINT_WINDOWS_USE_D3D9EX = "SDL_WINDOWS_USE_D3D9EX"; public const string SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS = "SDL_JOYSTICK_HIDAPI_JOY_CONS"; public const string SDL_HINT_JOYSTICK_HIDAPI_STADIA = "SDL_JOYSTICK_HIDAPI_STADIA"; public const string SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED = "SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED"; public const string SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED = "SDL_ALLOW_ALT_TAB_WHILE_GRABBED"; public const string SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER = "SDL_KMSDRM_REQUIRE_DRM_MASTER"; public const string SDL_HINT_AUDIO_DEVICE_STREAM_ROLE = "SDL_AUDIO_DEVICE_STREAM_ROLE"; public const string SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT = "SDL_X11_FORCE_OVERRIDE_REDIRECT"; public const string SDL_HINT_JOYSTICK_HIDAPI_LUNA = "SDL_JOYSTICK_HIDAPI_LUNA"; public const string SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT = "SDL_JOYSTICK_RAWINPUT_CORRELATE_XINPUT"; public const string SDL_HINT_AUDIO_INCLUDE_MONITORS = "SDL_AUDIO_INCLUDE_MONITORS"; public const string SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR = "SDL_VIDEO_WAYLAND_ALLOW_LIBDECOR"; /* Only available in 2.0.18 or higher. */ public const string SDL_HINT_VIDEO_EGL_ALLOW_TRANSPARENCY = "SDL_VIDEO_EGL_ALLOW_TRANSPARENCY"; public const string SDL_HINT_APP_NAME = "SDL_APP_NAME"; public const string SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME = "SDL_SCREENSAVER_INHIBIT_ACTIVITY_NAME"; public const string SDL_HINT_IME_SHOW_UI = "SDL_IME_SHOW_UI"; public const string SDL_HINT_WINDOW_NO_ACTIVATION_WHEN_SHOWN = "SDL_WINDOW_NO_ACTIVATION_WHEN_SHOWN"; public const string SDL_HINT_POLL_SENTINEL = "SDL_POLL_SENTINEL"; public const string SDL_HINT_JOYSTICK_DEVICE = "SDL_JOYSTICK_DEVICE"; public const string SDL_HINT_LINUX_JOYSTICK_CLASSIC = "SDL_LINUX_JOYSTICK_CLASSIC"; /* Only available in 2.0.20 or higher. */ public const string SDL_HINT_RENDER_LINE_METHOD = "SDL_RENDER_LINE_METHOD"; /* Only available in 2.0.22 or higher. */ public const string SDL_HINT_FORCE_RAISEWINDOW = "SDL_HINT_FORCE_RAISEWINDOW"; public const string SDL_HINT_IME_SUPPORT_EXTENDED_TEXT = "SDL_IME_SUPPORT_EXTENDED_TEXT"; public const string SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE = "SDL_JOYSTICK_GAMECUBE_RUMBLE_BRAKE"; public const string SDL_HINT_JOYSTICK_ROG_CHAKRAM = "SDL_JOYSTICK_ROG_CHAKRAM"; public const string SDL_HINT_MOUSE_RELATIVE_MODE_CENTER = "SDL_MOUSE_RELATIVE_MODE_CENTER"; public const string SDL_HINT_MOUSE_AUTO_CAPTURE = "SDL_MOUSE_AUTO_CAPTURE"; public const string SDL_HINT_VITA_TOUCH_MOUSE_DEVICE = "SDL_HINT_VITA_TOUCH_MOUSE_DEVICE"; public const string SDL_HINT_VIDEO_WAYLAND_PREFER_LIBDECOR = "SDL_VIDEO_WAYLAND_PREFER_LIBDECOR"; public const string SDL_HINT_VIDEO_FOREIGN_WINDOW_OPENGL = "SDL_VIDEO_FOREIGN_WINDOW_OPENGL"; public const string SDL_HINT_VIDEO_FOREIGN_WINDOW_VULKAN = "SDL_VIDEO_FOREIGN_WINDOW_VULKAN"; public const string SDL_HINT_X11_WINDOW_TYPE = "SDL_X11_WINDOW_TYPE"; public const string SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE = "SDL_QUIT_ON_LAST_WINDOW_CLOSE"; public enum SDL_HintPriority { SDL_HINT_DEFAULT, SDL_HINT_NORMAL, SDL_HINT_OVERRIDE } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_ClearHints(); [DllImport(nativeLibName, EntryPoint = "SDL_GetHint", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe IntPtr INTERNAL_SDL_GetHint(byte* name); public static unsafe string SDL_GetHint(string name) { int utf8NameBufSize = Utf8Size(name); byte* utf8Name = stackalloc byte[utf8NameBufSize]; return UTF8_ToManaged( INTERNAL_SDL_GetHint( Utf8Encode(name, utf8Name, utf8NameBufSize) ) ); } [DllImport(nativeLibName, EntryPoint = "SDL_SetHint", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe SDL_bool INTERNAL_SDL_SetHint( byte* name, byte* value ); public static unsafe SDL_bool SDL_SetHint(string name, string value) { int utf8NameBufSize = Utf8Size(name); byte* utf8Name = stackalloc byte[utf8NameBufSize]; int utf8ValueBufSize = Utf8Size(value); byte* utf8Value = stackalloc byte[utf8ValueBufSize]; return INTERNAL_SDL_SetHint( Utf8Encode(name, utf8Name, utf8NameBufSize), Utf8Encode(value, utf8Value, utf8ValueBufSize) ); } [DllImport(nativeLibName, EntryPoint = "SDL_SetHintWithPriority", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe SDL_bool INTERNAL_SDL_SetHintWithPriority( byte* name, byte* value, SDL_HintPriority priority ); public static unsafe SDL_bool SDL_SetHintWithPriority( string name, string value, SDL_HintPriority priority ) { int utf8NameBufSize = Utf8Size(name); byte* utf8Name = stackalloc byte[utf8NameBufSize]; int utf8ValueBufSize = Utf8Size(value); byte* utf8Value = stackalloc byte[utf8ValueBufSize]; return INTERNAL_SDL_SetHintWithPriority( Utf8Encode(name, utf8Name, utf8NameBufSize), Utf8Encode(value, utf8Value, utf8ValueBufSize), priority ); } /* Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_GetHintBoolean", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe SDL_bool INTERNAL_SDL_GetHintBoolean( byte* name, SDL_bool default_value ); public static unsafe SDL_bool SDL_GetHintBoolean( string name, SDL_bool default_value ) { int utf8NameBufSize = Utf8Size(name); byte* utf8Name = stackalloc byte[utf8NameBufSize]; return INTERNAL_SDL_GetHintBoolean( Utf8Encode(name, utf8Name, utf8NameBufSize), default_value ); } #endregion #region SDL_error.h [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_ClearError(); [DllImport(nativeLibName, EntryPoint = "SDL_GetError", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetError(); public static string SDL_GetError() { return UTF8_ToManaged(INTERNAL_SDL_GetError()); } /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_SetError", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_SetError(byte* fmtAndArglist); public static unsafe void SDL_SetError(string fmtAndArglist) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_SetError( Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } /* IntPtr refers to a char*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetErrorMsg(IntPtr errstr, int maxlength); #endregion #region SDL_log.h public enum SDL_LogCategory { SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_CATEGORY_ERROR, SDL_LOG_CATEGORY_ASSERT, SDL_LOG_CATEGORY_SYSTEM, SDL_LOG_CATEGORY_AUDIO, SDL_LOG_CATEGORY_VIDEO, SDL_LOG_CATEGORY_RENDER, SDL_LOG_CATEGORY_INPUT, SDL_LOG_CATEGORY_TEST, /* Reserved for future SDL library use */ SDL_LOG_CATEGORY_RESERVED1, SDL_LOG_CATEGORY_RESERVED2, SDL_LOG_CATEGORY_RESERVED3, SDL_LOG_CATEGORY_RESERVED4, SDL_LOG_CATEGORY_RESERVED5, SDL_LOG_CATEGORY_RESERVED6, SDL_LOG_CATEGORY_RESERVED7, SDL_LOG_CATEGORY_RESERVED8, SDL_LOG_CATEGORY_RESERVED9, SDL_LOG_CATEGORY_RESERVED10, /* Beyond this point is reserved for application use, e.g. enum { MYAPP_CATEGORY_AWESOME1 = SDL_LOG_CATEGORY_CUSTOM, MYAPP_CATEGORY_AWESOME2, MYAPP_CATEGORY_AWESOME3, ... }; */ SDL_LOG_CATEGORY_CUSTOM } public enum SDL_LogPriority { SDL_LOG_PRIORITY_VERBOSE = 1, SDL_LOG_PRIORITY_DEBUG, SDL_LOG_PRIORITY_INFO, SDL_LOG_PRIORITY_WARN, SDL_LOG_PRIORITY_ERROR, SDL_LOG_PRIORITY_CRITICAL, SDL_NUM_LOG_PRIORITIES } /* userdata refers to a void*, message to a const char* */ [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void SDL_LogOutputFunction( IntPtr userdata, int category, SDL_LogPriority priority, IntPtr message ); /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_Log", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_Log(byte* fmtAndArglist); public static unsafe void SDL_Log(string fmtAndArglist) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_Log( Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_LogVerbose", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_LogVerbose( int category, byte* fmtAndArglist ); public static unsafe void SDL_LogVerbose( int category, string fmtAndArglist ) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_LogVerbose( category, Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_LogDebug", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_LogDebug( int category, byte* fmtAndArglist ); public static unsafe void SDL_LogDebug( int category, string fmtAndArglist ) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_LogDebug( category, Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_LogInfo", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_LogInfo( int category, byte* fmtAndArglist ); public static unsafe void SDL_LogInfo( int category, string fmtAndArglist ) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_LogInfo( category, Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_LogWarn", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_LogWarn( int category, byte* fmtAndArglist ); public static unsafe void SDL_LogWarn( int category, string fmtAndArglist ) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_LogWarn( category, Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_LogError", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_LogError( int category, byte* fmtAndArglist ); public static unsafe void SDL_LogError( int category, string fmtAndArglist ) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_LogError( category, Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_LogCritical", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_LogCritical( int category, byte* fmtAndArglist ); public static unsafe void SDL_LogCritical( int category, string fmtAndArglist ) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_LogCritical( category, Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_LogMessage", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_LogMessage( int category, SDL_LogPriority priority, byte* fmtAndArglist ); public static unsafe void SDL_LogMessage( int category, SDL_LogPriority priority, string fmtAndArglist ) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_LogMessage( category, priority, Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } /* Use string.Format for arglists */ [DllImport(nativeLibName, EntryPoint = "SDL_LogMessageV", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_LogMessageV( int category, SDL_LogPriority priority, byte* fmtAndArglist ); public static unsafe void SDL_LogMessageV( int category, SDL_LogPriority priority, string fmtAndArglist ) { int utf8FmtAndArglistBufSize = Utf8Size(fmtAndArglist); byte* utf8FmtAndArglist = stackalloc byte[utf8FmtAndArglistBufSize]; INTERNAL_SDL_LogMessageV( category, priority, Utf8Encode(fmtAndArglist, utf8FmtAndArglist, utf8FmtAndArglistBufSize) ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_LogPriority SDL_LogGetPriority( int category ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_LogSetPriority( int category, SDL_LogPriority priority ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_LogSetAllPriority( SDL_LogPriority priority ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_LogResetPriorities(); /* userdata refers to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] private static extern void SDL_LogGetOutputFunction( out IntPtr callback, out IntPtr userdata ); public static void SDL_LogGetOutputFunction( out SDL_LogOutputFunction callback, out IntPtr userdata ) { IntPtr result = IntPtr.Zero; SDL_LogGetOutputFunction( out result, out userdata ); if (result != IntPtr.Zero) { callback = (SDL_LogOutputFunction) GetDelegateForFunctionPointer( result ); } else { callback = null; } } /* userdata refers to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_LogSetOutputFunction( SDL_LogOutputFunction callback, IntPtr userdata ); #endregion #region SDL_messagebox.h [Flags] public enum SDL_MessageBoxFlags : uint { SDL_MESSAGEBOX_ERROR = 0x00000010, SDL_MESSAGEBOX_WARNING = 0x00000020, SDL_MESSAGEBOX_INFORMATION = 0x00000040 } [Flags] public enum SDL_MessageBoxButtonFlags : uint { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT = 0x00000001, SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT = 0x00000002 } [StructLayout(LayoutKind.Sequential)] private struct INTERNAL_SDL_MessageBoxButtonData { public SDL_MessageBoxButtonFlags flags; public int buttonid; public IntPtr text; /* The UTF-8 button text */ } [StructLayout(LayoutKind.Sequential)] public struct SDL_MessageBoxButtonData { public SDL_MessageBoxButtonFlags flags; public int buttonid; public string text; /* The UTF-8 button text */ } [StructLayout(LayoutKind.Sequential)] public struct SDL_MessageBoxColor { public byte r, g, b; } public enum SDL_MessageBoxColorType { SDL_MESSAGEBOX_COLOR_BACKGROUND, SDL_MESSAGEBOX_COLOR_TEXT, SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, SDL_MESSAGEBOX_COLOR_MAX } [StructLayout(LayoutKind.Sequential)] public struct SDL_MessageBoxColorScheme { [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst = (int)SDL_MessageBoxColorType.SDL_MESSAGEBOX_COLOR_MAX)] public SDL_MessageBoxColor[] colors; } [StructLayout(LayoutKind.Sequential)] private struct INTERNAL_SDL_MessageBoxData { public SDL_MessageBoxFlags flags; public IntPtr window; /* Parent window, can be NULL */ public IntPtr title; /* UTF-8 title */ public IntPtr message; /* UTF-8 message text */ public int numbuttons; public IntPtr buttons; public IntPtr colorScheme; /* Can be NULL to use system settings */ } [StructLayout(LayoutKind.Sequential)] public struct SDL_MessageBoxData { public SDL_MessageBoxFlags flags; public IntPtr window; /* Parent window, can be NULL */ public string title; /* UTF-8 title */ public string message; /* UTF-8 message text */ public int numbuttons; public SDL_MessageBoxButtonData[] buttons; public SDL_MessageBoxColorScheme? colorScheme; /* Can be NULL to use system settings */ } [DllImport(nativeLibName, EntryPoint = "SDL_ShowMessageBox", CallingConvention = CallingConvention.Cdecl)] private static extern int INTERNAL_SDL_ShowMessageBox([In()] ref INTERNAL_SDL_MessageBoxData messageboxdata, out int buttonid); /* Ripped from Jameson's LpUtf8StrMarshaler */ private static IntPtr INTERNAL_AllocUTF8(string str) { if (string.IsNullOrEmpty(str)) { return IntPtr.Zero; } byte[] bytes = System.Text.Encoding.UTF8.GetBytes(str + '\0'); IntPtr mem = SDL.SDL_malloc((IntPtr) bytes.Length); Marshal.Copy(bytes, 0, mem, bytes.Length); return mem; } public static unsafe int SDL_ShowMessageBox([In()] ref SDL_MessageBoxData messageboxdata, out int buttonid) { var data = new INTERNAL_SDL_MessageBoxData() { flags = messageboxdata.flags, window = messageboxdata.window, title = INTERNAL_AllocUTF8(messageboxdata.title), message = INTERNAL_AllocUTF8(messageboxdata.message), numbuttons = messageboxdata.numbuttons, }; var buttons = new INTERNAL_SDL_MessageBoxButtonData[messageboxdata.numbuttons]; for (int i = 0; i < messageboxdata.numbuttons; i++) { buttons[i] = new INTERNAL_SDL_MessageBoxButtonData() { flags = messageboxdata.buttons[i].flags, buttonid = messageboxdata.buttons[i].buttonid, text = INTERNAL_AllocUTF8(messageboxdata.buttons[i].text), }; } if (messageboxdata.colorScheme != null) { data.colorScheme = Marshal.AllocHGlobal(SizeOf()); Marshal.StructureToPtr(messageboxdata.colorScheme.Value, data.colorScheme, false); } int result; fixed (INTERNAL_SDL_MessageBoxButtonData* buttonsPtr = &buttons[0]) { data.buttons = (IntPtr)buttonsPtr; result = INTERNAL_SDL_ShowMessageBox(ref data, out buttonid); } Marshal.FreeHGlobal(data.colorScheme); for (int i = 0; i < messageboxdata.numbuttons; i++) { SDL_free(buttons[i].text); } SDL_free(data.message); SDL_free(data.title); return result; } /* window refers to an SDL_Window* */ [DllImport(nativeLibName, EntryPoint = "SDL_ShowSimpleMessageBox", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe int INTERNAL_SDL_ShowSimpleMessageBox( SDL_MessageBoxFlags flags, byte* title, byte* message, IntPtr window ); public static unsafe int SDL_ShowSimpleMessageBox( SDL_MessageBoxFlags flags, string title, string message, IntPtr window ) { int utf8TitleBufSize = Utf8Size(title); byte* utf8Title = stackalloc byte[utf8TitleBufSize]; int utf8MessageBufSize = Utf8Size(message); byte* utf8Message = stackalloc byte[utf8MessageBufSize]; return INTERNAL_SDL_ShowSimpleMessageBox( flags, Utf8Encode(title, utf8Title, utf8TitleBufSize), Utf8Encode(message, utf8Message, utf8MessageBufSize), window ); } #endregion #region SDL_version.h, SDL_revision.h /* Similar to the headers, this is the version we're expecting to be * running with. You will likely want to check this somewhere in your * program! */ public const int SDL_MAJOR_VERSION = 2; public const int SDL_MINOR_VERSION = 0; public const int SDL_PATCHLEVEL = 22; public static readonly int SDL_COMPILEDVERSION = SDL_VERSIONNUM( SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL ); [StructLayout(LayoutKind.Sequential)] public struct SDL_version { public byte major; public byte minor; public byte patch; } public static void SDL_VERSION(out SDL_version x) { x.major = SDL_MAJOR_VERSION; x.minor = SDL_MINOR_VERSION; x.patch = SDL_PATCHLEVEL; } public static int SDL_VERSIONNUM(int X, int Y, int Z) { return (X * 1000) + (Y * 100) + Z; } public static bool SDL_VERSION_ATLEAST(int X, int Y, int Z) { return (SDL_COMPILEDVERSION >= SDL_VERSIONNUM(X, Y, Z)); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GetVersion(out SDL_version ver); [DllImport(nativeLibName, EntryPoint = "SDL_GetRevision", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetRevision(); public static string SDL_GetRevision() { return UTF8_ToManaged(INTERNAL_SDL_GetRevision()); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetRevisionNumber(); #endregion #region SDL_video.h public enum SDL_GLattr { SDL_GL_RED_SIZE, SDL_GL_GREEN_SIZE, SDL_GL_BLUE_SIZE, SDL_GL_ALPHA_SIZE, SDL_GL_BUFFER_SIZE, SDL_GL_DOUBLEBUFFER, SDL_GL_DEPTH_SIZE, SDL_GL_STENCIL_SIZE, SDL_GL_ACCUM_RED_SIZE, SDL_GL_ACCUM_GREEN_SIZE, SDL_GL_ACCUM_BLUE_SIZE, SDL_GL_ACCUM_ALPHA_SIZE, SDL_GL_STEREO, SDL_GL_MULTISAMPLEBUFFERS, SDL_GL_MULTISAMPLESAMPLES, SDL_GL_ACCELERATED_VISUAL, SDL_GL_RETAINED_BACKING, SDL_GL_CONTEXT_MAJOR_VERSION, SDL_GL_CONTEXT_MINOR_VERSION, SDL_GL_CONTEXT_EGL, SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_SHARE_WITH_CURRENT_CONTEXT, SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, SDL_GL_CONTEXT_RELEASE_BEHAVIOR, SDL_GL_CONTEXT_RESET_NOTIFICATION, /* Requires >= 2.0.6 */ SDL_GL_CONTEXT_NO_ERROR, /* Requires >= 2.0.6 */ } [Flags] public enum SDL_GLprofile { SDL_GL_CONTEXT_PROFILE_CORE = 0x0001, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY = 0x0002, SDL_GL_CONTEXT_PROFILE_ES = 0x0004 } [Flags] public enum SDL_GLcontext { SDL_GL_CONTEXT_DEBUG_FLAG = 0x0001, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG = 0x0002, SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG = 0x0004, SDL_GL_CONTEXT_RESET_ISOLATION_FLAG = 0x0008 } public enum SDL_WindowEventID : byte { SDL_WINDOWEVENT_NONE, SDL_WINDOWEVENT_SHOWN, SDL_WINDOWEVENT_HIDDEN, SDL_WINDOWEVENT_EXPOSED, SDL_WINDOWEVENT_MOVED, SDL_WINDOWEVENT_RESIZED, SDL_WINDOWEVENT_SIZE_CHANGED, SDL_WINDOWEVENT_MINIMIZED, SDL_WINDOWEVENT_MAXIMIZED, SDL_WINDOWEVENT_RESTORED, SDL_WINDOWEVENT_ENTER, SDL_WINDOWEVENT_LEAVE, SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_FOCUS_LOST, SDL_WINDOWEVENT_CLOSE, /* Only available in 2.0.5 or higher. */ SDL_WINDOWEVENT_TAKE_FOCUS, SDL_WINDOWEVENT_HIT_TEST, /* Only available in 2.0.18 or higher. */ SDL_WINDOWEVENT_ICCPROF_CHANGED, SDL_WINDOWEVENT_DISPLAY_CHANGED } public enum SDL_DisplayEventID : byte { SDL_DISPLAYEVENT_NONE, SDL_DISPLAYEVENT_ORIENTATION, SDL_DISPLAYEVENT_CONNECTED, /* Requires >= 2.0.14 */ SDL_DISPLAYEVENT_DISCONNECTED /* Requires >= 2.0.14 */ } public enum SDL_DisplayOrientation { SDL_ORIENTATION_UNKNOWN, SDL_ORIENTATION_LANDSCAPE, SDL_ORIENTATION_LANDSCAPE_FLIPPED, SDL_ORIENTATION_PORTRAIT, SDL_ORIENTATION_PORTRAIT_FLIPPED } /* Only available in 2.0.16 or higher. */ public enum SDL_FlashOperation { SDL_FLASH_CANCEL, SDL_FLASH_BRIEFLY, SDL_FLASH_UNTIL_FOCUSED } [Flags] public enum SDL_WindowFlags : uint { SDL_WINDOW_FULLSCREEN = 0x00000001, SDL_WINDOW_OPENGL = 0x00000002, SDL_WINDOW_SHOWN = 0x00000004, SDL_WINDOW_HIDDEN = 0x00000008, SDL_WINDOW_BORDERLESS = 0x00000010, SDL_WINDOW_RESIZABLE = 0x00000020, SDL_WINDOW_MINIMIZED = 0x00000040, SDL_WINDOW_MAXIMIZED = 0x00000080, SDL_WINDOW_MOUSE_GRABBED = 0x00000100, SDL_WINDOW_INPUT_FOCUS = 0x00000200, SDL_WINDOW_MOUSE_FOCUS = 0x00000400, SDL_WINDOW_FULLSCREEN_DESKTOP = (SDL_WINDOW_FULLSCREEN | 0x00001000), SDL_WINDOW_FOREIGN = 0x00000800, SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000, /* Requires >= 2.0.1 */ SDL_WINDOW_MOUSE_CAPTURE = 0x00004000, /* Requires >= 2.0.4 */ SDL_WINDOW_ALWAYS_ON_TOP = 0x00008000, /* Requires >= 2.0.5 */ SDL_WINDOW_SKIP_TASKBAR = 0x00010000, /* Requires >= 2.0.5 */ SDL_WINDOW_UTILITY = 0x00020000, /* Requires >= 2.0.5 */ SDL_WINDOW_TOOLTIP = 0x00040000, /* Requires >= 2.0.5 */ SDL_WINDOW_POPUP_MENU = 0x00080000, /* Requires >= 2.0.5 */ SDL_WINDOW_KEYBOARD_GRABBED = 0x00100000, /* Requires >= 2.0.16 */ SDL_WINDOW_VULKAN = 0x10000000, /* Requires >= 2.0.6 */ SDL_WINDOW_METAL = 0x2000000, /* Requires >= 2.0.14 */ SDL_WINDOW_INPUT_GRABBED = SDL_WINDOW_MOUSE_GRABBED, } /* Only available in 2.0.4 or higher. */ public enum SDL_HitTestResult { SDL_HITTEST_NORMAL, /* Region is normal. No special properties. */ SDL_HITTEST_DRAGGABLE, /* Region can drag entire window. */ SDL_HITTEST_RESIZE_TOPLEFT, SDL_HITTEST_RESIZE_TOP, SDL_HITTEST_RESIZE_TOPRIGHT, SDL_HITTEST_RESIZE_RIGHT, SDL_HITTEST_RESIZE_BOTTOMRIGHT, SDL_HITTEST_RESIZE_BOTTOM, SDL_HITTEST_RESIZE_BOTTOMLEFT, SDL_HITTEST_RESIZE_LEFT } public const int SDL_WINDOWPOS_UNDEFINED_MASK = 0x1FFF0000; public const int SDL_WINDOWPOS_CENTERED_MASK = 0x2FFF0000; public const int SDL_WINDOWPOS_UNDEFINED = 0x1FFF0000; public const int SDL_WINDOWPOS_CENTERED = 0x2FFF0000; public static int SDL_WINDOWPOS_UNDEFINED_DISPLAY(int X) { return (SDL_WINDOWPOS_UNDEFINED_MASK | X); } public static bool SDL_WINDOWPOS_ISUNDEFINED(int X) { return (X & 0xFFFF0000) == SDL_WINDOWPOS_UNDEFINED_MASK; } public static int SDL_WINDOWPOS_CENTERED_DISPLAY(int X) { return (SDL_WINDOWPOS_CENTERED_MASK | X); } public static bool SDL_WINDOWPOS_ISCENTERED(int X) { return (X & 0xFFFF0000) == SDL_WINDOWPOS_CENTERED_MASK; } [StructLayout(LayoutKind.Sequential)] public struct SDL_DisplayMode { public uint format; public int w; public int h; public int refresh_rate; public IntPtr driverdata; // void* } /* win refers to an SDL_Window*, area to a const SDL_Point*, data to a void*. * Only available in 2.0.4 or higher. */ [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate SDL_HitTestResult SDL_HitTest(IntPtr win, IntPtr area, IntPtr data); /* IntPtr refers to an SDL_Window* */ [DllImport(nativeLibName, EntryPoint = "SDL_CreateWindow", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe IntPtr INTERNAL_SDL_CreateWindow( byte* title, int x, int y, int w, int h, SDL_WindowFlags flags ); public static unsafe IntPtr SDL_CreateWindow( string title, int x, int y, int w, int h, SDL_WindowFlags flags ) { int utf8TitleBufSize = Utf8Size(title); byte* utf8Title = stackalloc byte[utf8TitleBufSize]; return INTERNAL_SDL_CreateWindow( Utf8Encode(title, utf8Title, utf8TitleBufSize), x, y, w, h, flags ); } /* window refers to an SDL_Window*, renderer to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_CreateWindowAndRenderer( int width, int height, SDL_WindowFlags window_flags, out IntPtr window, out IntPtr renderer ); /* data refers to some native window type, IntPtr to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateWindowFrom(IntPtr data); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_DestroyWindow(IntPtr window); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_DisableScreenSaver(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_EnableScreenSaver(); /* IntPtr refers to an SDL_DisplayMode. Just use closest. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetClosestDisplayMode( int displayIndex, ref SDL_DisplayMode mode, out SDL_DisplayMode closest ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetCurrentDisplayMode( int displayIndex, out SDL_DisplayMode mode ); [DllImport(nativeLibName, EntryPoint = "SDL_GetCurrentVideoDriver", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetCurrentVideoDriver(); public static string SDL_GetCurrentVideoDriver() { return UTF8_ToManaged(INTERNAL_SDL_GetCurrentVideoDriver()); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetDesktopDisplayMode( int displayIndex, out SDL_DisplayMode mode ); [DllImport(nativeLibName, EntryPoint = "SDL_GetDisplayName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetDisplayName(int index); public static string SDL_GetDisplayName(int index) { return UTF8_ToManaged(INTERNAL_SDL_GetDisplayName(index)); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetDisplayBounds( int displayIndex, out SDL_Rect rect ); /* Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetDisplayDPI( int displayIndex, out float ddpi, out float hdpi, out float vdpi ); /* Only available in 2.0.9 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_DisplayOrientation SDL_GetDisplayOrientation( int displayIndex ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetDisplayMode( int displayIndex, int modeIndex, out SDL_DisplayMode mode ); /* Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetDisplayUsableBounds( int displayIndex, out SDL_Rect rect ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetNumDisplayModes( int displayIndex ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetNumVideoDisplays(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetNumVideoDrivers(); [DllImport(nativeLibName, EntryPoint = "SDL_GetVideoDriver", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetVideoDriver( int index ); public static string SDL_GetVideoDriver(int index) { return UTF8_ToManaged(INTERNAL_SDL_GetVideoDriver(index)); } /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern float SDL_GetWindowBrightness( IntPtr window ); /* window refers to an SDL_Window* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowOpacity( IntPtr window, float opacity ); /* window refers to an SDL_Window* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetWindowOpacity( IntPtr window, out float out_opacity ); /* modal_window and parent_window refer to an SDL_Window*s * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowModalFor( IntPtr modal_window, IntPtr parent_window ); /* window refers to an SDL_Window* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowInputFocus(IntPtr window); /* window refers to an SDL_Window*, IntPtr to a void* */ [DllImport(nativeLibName, EntryPoint = "SDL_GetWindowData", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe IntPtr INTERNAL_SDL_GetWindowData( IntPtr window, byte* name ); public static unsafe IntPtr SDL_GetWindowData( IntPtr window, string name ) { int utf8NameBufSize = Utf8Size(name); byte* utf8Name = stackalloc byte[utf8NameBufSize]; return INTERNAL_SDL_GetWindowData( window, Utf8Encode(name, utf8Name, utf8NameBufSize) ); } /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetWindowDisplayIndex( IntPtr window ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetWindowDisplayMode( IntPtr window, out SDL_DisplayMode mode ); /* IntPtr refers to a void* * window refers to an SDL_Window* * mode refers to a size_t* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetWindowICCProfile( IntPtr window, out IntPtr mode ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_GetWindowFlags(IntPtr window); /* IntPtr refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetWindowFromID(uint id); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetWindowGammaRamp( IntPtr window, [Out()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeConst = 256)] ushort[] red, [Out()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeConst = 256)] ushort[] green, [Out()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeConst = 256)] ushort[] blue ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GetWindowGrab(IntPtr window); /* window refers to an SDL_Window* * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GetWindowKeyboardGrab(IntPtr window); /* window refers to an SDL_Window* * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GetWindowMouseGrab(IntPtr window); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_GetWindowID(IntPtr window); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_GetWindowPixelFormat( IntPtr window ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GetWindowMaximumSize( IntPtr window, out int max_w, out int max_h ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GetWindowMinimumSize( IntPtr window, out int min_w, out int min_h ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GetWindowPosition( IntPtr window, out int x, out int y ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GetWindowSize( IntPtr window, out int w, out int h ); /* IntPtr refers to an SDL_Surface*, window to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetWindowSurface(IntPtr window); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, EntryPoint = "SDL_GetWindowTitle", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetWindowTitle( IntPtr window ); public static string SDL_GetWindowTitle(IntPtr window) { return UTF8_ToManaged( INTERNAL_SDL_GetWindowTitle(window) ); } /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GL_BindTexture( IntPtr texture, out float texw, out float texh ); /* IntPtr and window refer to an SDL_GLContext and SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GL_CreateContext(IntPtr window); /* context refers to an SDL_GLContext */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GL_DeleteContext(IntPtr context); [DllImport(nativeLibName, EntryPoint = "SDL_GL_LoadLibrary", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe int INTERNAL_SDL_GL_LoadLibrary(byte* path); public static unsafe int SDL_GL_LoadLibrary(string path) { byte* utf8Path = Utf8EncodeHeap(path); int result = INTERNAL_SDL_GL_LoadLibrary( utf8Path ); Marshal.FreeHGlobal((IntPtr) utf8Path); return result; } /* IntPtr refers to a function pointer, proc to a const char* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GL_GetProcAddress(IntPtr proc); /* IntPtr refers to a function pointer */ public static unsafe IntPtr SDL_GL_GetProcAddress(string proc) { int utf8ProcBufSize = Utf8Size(proc); byte* utf8Proc = stackalloc byte[utf8ProcBufSize]; return SDL_GL_GetProcAddress( (IntPtr) Utf8Encode(proc, utf8Proc, utf8ProcBufSize) ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GL_UnloadLibrary(); [DllImport(nativeLibName, EntryPoint = "SDL_GL_ExtensionSupported", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe SDL_bool INTERNAL_SDL_GL_ExtensionSupported( byte* extension ); public static unsafe SDL_bool SDL_GL_ExtensionSupported(string extension) { int utf8ExtensionBufSize = Utf8Size(extension); byte* utf8Extension = stackalloc byte[utf8ExtensionBufSize]; return INTERNAL_SDL_GL_ExtensionSupported( Utf8Encode(extension, utf8Extension, utf8ExtensionBufSize) ); } /* Only available in SDL 2.0.2 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GL_ResetAttributes(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GL_GetAttribute( SDL_GLattr attr, out int value ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GL_GetSwapInterval(); /* window and context refer to an SDL_Window* and SDL_GLContext */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GL_MakeCurrent( IntPtr window, IntPtr context ); /* IntPtr refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GL_GetCurrentWindow(); /* IntPtr refers to an SDL_Context */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GL_GetCurrentContext(); /* window refers to an SDL_Window*. * Only available in SDL 2.0.1 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GL_GetDrawableSize( IntPtr window, out int w, out int h ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GL_SetAttribute( SDL_GLattr attr, int value ); public static int SDL_GL_SetAttribute( SDL_GLattr attr, SDL_GLprofile profile ) { return SDL_GL_SetAttribute(attr, (int)profile); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GL_SetSwapInterval(int interval); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GL_SwapWindow(IntPtr window); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GL_UnbindTexture(IntPtr texture); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_HideWindow(IntPtr window); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsScreenSaverEnabled(); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_MaximizeWindow(IntPtr window); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_MinimizeWindow(IntPtr window); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_RaiseWindow(IntPtr window); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_RestoreWindow(IntPtr window); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowBrightness( IntPtr window, float brightness ); /* IntPtr and userdata are void*, window is an SDL_Window* */ [DllImport(nativeLibName, EntryPoint = "SDL_SetWindowData", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe IntPtr INTERNAL_SDL_SetWindowData( IntPtr window, byte* name, IntPtr userdata ); public static unsafe IntPtr SDL_SetWindowData( IntPtr window, string name, IntPtr userdata ) { int utf8NameBufSize = Utf8Size(name); byte* utf8Name = stackalloc byte[utf8NameBufSize]; return INTERNAL_SDL_SetWindowData( window, Utf8Encode(name, utf8Name, utf8NameBufSize), userdata ); } /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowDisplayMode( IntPtr window, ref SDL_DisplayMode mode ); /* window refers to an SDL_Window* */ /* NULL overload - use the window's dimensions and the desktop's format and refresh rate */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowDisplayMode( IntPtr window, IntPtr mode ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowFullscreen( IntPtr window, uint flags ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowGammaRamp( IntPtr window, [In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeConst = 256)] ushort[] red, [In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeConst = 256)] ushort[] green, [In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeConst = 256)] ushort[] blue ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowGrab( IntPtr window, SDL_bool grabbed ); /* window refers to an SDL_Window* * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowKeyboardGrab( IntPtr window, SDL_bool grabbed ); /* window refers to an SDL_Window* * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowMouseGrab( IntPtr window, SDL_bool grabbed ); /* window refers to an SDL_Window*, icon to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowIcon( IntPtr window, IntPtr icon ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowMaximumSize( IntPtr window, int max_w, int max_h ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowMinimumSize( IntPtr window, int min_w, int min_h ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowPosition( IntPtr window, int x, int y ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowSize( IntPtr window, int w, int h ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowBordered( IntPtr window, SDL_bool bordered ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetWindowBordersSize( IntPtr window, out int top, out int left, out int bottom, out int right ); /* window refers to an SDL_Window* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowResizable( IntPtr window, SDL_bool resizable ); /* window refers to an SDL_Window* * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowAlwaysOnTop( IntPtr window, SDL_bool on_top ); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, EntryPoint = "SDL_SetWindowTitle", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe void INTERNAL_SDL_SetWindowTitle( IntPtr window, byte* title ); public static unsafe void SDL_SetWindowTitle( IntPtr window, string title ) { int utf8TitleBufSize = Utf8Size(title); byte* utf8Title = stackalloc byte[utf8TitleBufSize]; INTERNAL_SDL_SetWindowTitle( window, Utf8Encode(title, utf8Title, utf8TitleBufSize) ); } /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_ShowWindow(IntPtr window); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_UpdateWindowSurface(IntPtr window); /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_UpdateWindowSurfaceRects( IntPtr window, [In] SDL_Rect[] rects, int numrects ); [DllImport(nativeLibName, EntryPoint = "SDL_VideoInit", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe int INTERNAL_SDL_VideoInit( byte* driver_name ); public static unsafe int SDL_VideoInit(string driver_name) { int utf8DriverNameBufSize = Utf8Size(driver_name); byte* utf8DriverName = stackalloc byte[utf8DriverNameBufSize]; return INTERNAL_SDL_VideoInit( Utf8Encode(driver_name, utf8DriverName, utf8DriverNameBufSize) ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_VideoQuit(); /* window refers to an SDL_Window*, callback_data to a void* * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowHitTest( IntPtr window, SDL_HitTest callback, IntPtr callback_data ); /* IntPtr refers to an SDL_Window* * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetGrabbedWindow(); /* window refers to an SDL_Window* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowMouseRect( IntPtr window, ref SDL_Rect rect ); /* window refers to an SDL_Window* * rect refers to an SDL_Rect* * This overload allows for IntPtr.Zero (null) to be passed for rect. * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowMouseRect( IntPtr window, IntPtr rect ); /* window refers to an SDL_Window* * IntPtr refers to an SDL_Rect* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetWindowMouseRect( IntPtr window ); /* window refers to an SDL_Window* * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_FlashWindow( IntPtr window, SDL_FlashOperation operation ); #endregion #region SDL_blendmode.h [Flags] public enum SDL_BlendMode { SDL_BLENDMODE_NONE = 0x00000000, SDL_BLENDMODE_BLEND = 0x00000001, SDL_BLENDMODE_ADD = 0x00000002, SDL_BLENDMODE_MOD = 0x00000004, SDL_BLENDMODE_MUL = 0x00000008, /* >= 2.0.11 */ SDL_BLENDMODE_INVALID = 0x7FFFFFFF } public enum SDL_BlendOperation { SDL_BLENDOPERATION_ADD = 0x1, SDL_BLENDOPERATION_SUBTRACT = 0x2, SDL_BLENDOPERATION_REV_SUBTRACT = 0x3, SDL_BLENDOPERATION_MINIMUM = 0x4, SDL_BLENDOPERATION_MAXIMUM = 0x5 } public enum SDL_BlendFactor { SDL_BLENDFACTOR_ZERO = 0x1, SDL_BLENDFACTOR_ONE = 0x2, SDL_BLENDFACTOR_SRC_COLOR = 0x3, SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR = 0x4, SDL_BLENDFACTOR_SRC_ALPHA = 0x5, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA = 0x6, SDL_BLENDFACTOR_DST_COLOR = 0x7, SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR = 0x8, SDL_BLENDFACTOR_DST_ALPHA = 0x9, SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA = 0xA } /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_BlendMode SDL_ComposeCustomBlendMode( SDL_BlendFactor srcColorFactor, SDL_BlendFactor dstColorFactor, SDL_BlendOperation colorOperation, SDL_BlendFactor srcAlphaFactor, SDL_BlendFactor dstAlphaFactor, SDL_BlendOperation alphaOperation ); #endregion #region SDL_vulkan.h /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_Vulkan_LoadLibrary", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe int INTERNAL_SDL_Vulkan_LoadLibrary( byte* path ); public static unsafe int SDL_Vulkan_LoadLibrary(string path) { byte* utf8Path = Utf8EncodeHeap(path); int result = INTERNAL_SDL_Vulkan_LoadLibrary( utf8Path ); Marshal.FreeHGlobal((IntPtr) utf8Path); return result; } /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_Vulkan_GetVkGetInstanceProcAddr(); /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_Vulkan_UnloadLibrary(); /* window refers to an SDL_Window*, pNames to a const char**. * Only available in 2.0.6 or higher. * This overload allows for IntPtr.Zero (null) to be passed for pNames. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_Vulkan_GetInstanceExtensions( IntPtr window, out uint pCount, IntPtr pNames ); /* window refers to an SDL_Window*, pNames to a const char**. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_Vulkan_GetInstanceExtensions( IntPtr window, out uint pCount, IntPtr[] pNames ); /* window refers to an SDL_Window. * instance refers to a VkInstance. * surface refers to a VkSurfaceKHR. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_Vulkan_CreateSurface( IntPtr window, IntPtr instance, out ulong surface ); /* window refers to an SDL_Window*. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_Vulkan_GetDrawableSize( IntPtr window, out int w, out int h ); #endregion #region SDL_metal.h /* Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_Metal_CreateView( IntPtr window ); /* Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_Metal_DestroyView( IntPtr view ); /* view refers to an SDL_MetalView. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_Metal_GetLayer( IntPtr view ); /* window refers to an SDL_Window*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_Metal_GetDrawableSize( IntPtr window, out int w, out int h ); #endregion #region SDL_render.h [Flags] public enum SDL_RendererFlags : uint { SDL_RENDERER_SOFTWARE = 0x00000001, SDL_RENDERER_ACCELERATED = 0x00000002, SDL_RENDERER_PRESENTVSYNC = 0x00000004, SDL_RENDERER_TARGETTEXTURE = 0x00000008 } [Flags] public enum SDL_RendererFlip { SDL_FLIP_NONE = 0x00000000, SDL_FLIP_HORIZONTAL = 0x00000001, SDL_FLIP_VERTICAL = 0x00000002 } public enum SDL_TextureAccess { SDL_TEXTUREACCESS_STATIC, SDL_TEXTUREACCESS_STREAMING, SDL_TEXTUREACCESS_TARGET } [Flags] public enum SDL_TextureModulate { SDL_TEXTUREMODULATE_NONE = 0x00000000, SDL_TEXTUREMODULATE_HORIZONTAL = 0x00000001, SDL_TEXTUREMODULATE_VERTICAL = 0x00000002 } [StructLayout(LayoutKind.Sequential)] public unsafe struct SDL_RendererInfo { public IntPtr name; // const char* public uint flags; public uint num_texture_formats; public fixed uint texture_formats[16]; public int max_texture_width; public int max_texture_height; } /* Only available in 2.0.11 or higher. */ public enum SDL_ScaleMode { SDL_ScaleModeNearest, SDL_ScaleModeLinear, SDL_ScaleModeBest } /* Only available in 2.0.18 or higher. */ [StructLayout(LayoutKind.Sequential)] public struct SDL_Vertex { public SDL_FPoint position; public SDL_Color color; public SDL_FPoint tex_coord; } /* IntPtr refers to an SDL_Renderer*, window to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateRenderer( IntPtr window, int index, SDL_RendererFlags flags ); /* IntPtr refers to an SDL_Renderer*, surface to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateSoftwareRenderer(IntPtr surface); /* IntPtr refers to an SDL_Texture*, renderer to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateTexture( IntPtr renderer, uint format, int access, int w, int h ); /* IntPtr refers to an SDL_Texture* * renderer refers to an SDL_Renderer* * surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateTextureFromSurface( IntPtr renderer, IntPtr surface ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_DestroyRenderer(IntPtr renderer); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_DestroyTexture(IntPtr texture); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetNumRenderDrivers(); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetRenderDrawBlendMode( IntPtr renderer, out SDL_BlendMode blendMode ); /* texture refers to an SDL_Texture* * Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetTextureScaleMode( IntPtr texture, SDL_ScaleMode scaleMode ); /* texture refers to an SDL_Texture* * Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetTextureScaleMode( IntPtr texture, out SDL_ScaleMode scaleMode ); /* texture refers to an SDL_Texture* * userdata refers to a void* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetTextureUserData( IntPtr texture, IntPtr userdata ); /* IntPtr refers to a void*, texture refers to an SDL_Texture* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetTextureUserData(IntPtr texture); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetRenderDrawColor( IntPtr renderer, out byte r, out byte g, out byte b, out byte a ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetRenderDriverInfo( int index, out SDL_RendererInfo info ); /* IntPtr refers to an SDL_Renderer*, window to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetRenderer(IntPtr window); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetRendererInfo( IntPtr renderer, out SDL_RendererInfo info ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetRendererOutputSize( IntPtr renderer, out int w, out int h ); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetTextureAlphaMod( IntPtr texture, out byte alpha ); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetTextureBlendMode( IntPtr texture, out SDL_BlendMode blendMode ); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetTextureColorMod( IntPtr texture, out byte r, out byte g, out byte b ); /* texture refers to an SDL_Texture*, pixels to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_LockTexture( IntPtr texture, ref SDL_Rect rect, out IntPtr pixels, out int pitch ); /* texture refers to an SDL_Texture*, pixels to a void*. * Internally, this function contains logic to use default values when * the rectangle is passed as NULL. * This overload allows for IntPtr.Zero to be passed for rect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_LockTexture( IntPtr texture, IntPtr rect, out IntPtr pixels, out int pitch ); /* texture refers to an SDL_Texture*, surface to an SDL_Surface* * Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_LockTextureToSurface( IntPtr texture, ref SDL_Rect rect, out IntPtr surface ); /* texture refers to an SDL_Texture*, surface to an SDL_Surface* * Internally, this function contains logic to use default values when * the rectangle is passed as NULL. * This overload allows for IntPtr.Zero to be passed for rect. * Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_LockTextureToSurface( IntPtr texture, IntPtr rect, out IntPtr surface ); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_QueryTexture( IntPtr texture, out uint format, out int access, out int w, out int h ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderClear(IntPtr renderer); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopy( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, ref SDL_Rect dstrect ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for srcrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopy( IntPtr renderer, IntPtr texture, IntPtr srcrect, ref SDL_Rect dstrect ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for dstrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopy( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, IntPtr dstrect ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both SDL_Rects. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopy( IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, ref SDL_Rect dstrect, double angle, ref SDL_Point center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for srcrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, IntPtr srcrect, ref SDL_Rect dstrect, double angle, ref SDL_Point center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for dstrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, IntPtr dstrect, double angle, ref SDL_Point center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for center. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, ref SDL_Rect dstrect, double angle, IntPtr center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both * srcrect and dstrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect, double angle, ref SDL_Point center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both * srcrect and center. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, IntPtr srcrect, ref SDL_Rect dstrect, double angle, IntPtr center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both * dstrect and center. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, IntPtr dstrect, double angle, IntPtr center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for all * three parameters. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect, double angle, IntPtr center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawLine( IntPtr renderer, int x1, int y1, int x2, int y2 ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawLines( IntPtr renderer, [In] SDL_Point[] points, int count ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawPoint( IntPtr renderer, int x, int y ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawPoints( IntPtr renderer, [In] SDL_Point[] points, int count ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawRect( IntPtr renderer, ref SDL_Rect rect ); /* renderer refers to an SDL_Renderer*, rect to an SDL_Rect*. * This overload allows for IntPtr.Zero (null) to be passed for rect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawRect( IntPtr renderer, IntPtr rect ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawRects( IntPtr renderer, [In] SDL_Rect[] rects, int count ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderFillRect( IntPtr renderer, ref SDL_Rect rect ); /* renderer refers to an SDL_Renderer*, rect to an SDL_Rect*. * This overload allows for IntPtr.Zero (null) to be passed for rect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderFillRect( IntPtr renderer, IntPtr rect ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderFillRects( IntPtr renderer, [In] SDL_Rect[] rects, int count ); #region Floating Point Render Functions /* This region only available in SDL 2.0.10 or higher. */ /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyF( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, ref SDL_FRect dstrect ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for srcrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyF( IntPtr renderer, IntPtr texture, IntPtr srcrect, ref SDL_FRect dstrect ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for dstrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyF( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, IntPtr dstrect ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both SDL_Rects. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyF( IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, ref SDL_FRect dstrect, double angle, ref SDL_FPoint center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for srcrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyEx( IntPtr renderer, IntPtr texture, IntPtr srcrect, ref SDL_FRect dstrect, double angle, ref SDL_FPoint center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for dstrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyExF( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, IntPtr dstrect, double angle, ref SDL_FPoint center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for center. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyExF( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, ref SDL_FRect dstrect, double angle, IntPtr center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both * srcrect and dstrect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyExF( IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect, double angle, ref SDL_FPoint center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both * srcrect and center. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyExF( IntPtr renderer, IntPtr texture, IntPtr srcrect, ref SDL_FRect dstrect, double angle, IntPtr center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both * dstrect and center. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyExF( IntPtr renderer, IntPtr texture, ref SDL_Rect srcrect, IntPtr dstrect, double angle, IntPtr center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture*. * Internally, this function contains logic to use default values when * source, destination, and/or center are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for all * three parameters. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderCopyExF( IntPtr renderer, IntPtr texture, IntPtr srcrect, IntPtr dstrect, double angle, IntPtr center, SDL_RendererFlip flip ); /* renderer refers to an SDL_Renderer* * texture refers to an SDL_Texture* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderGeometry( IntPtr renderer, IntPtr texture, [In] SDL_Vertex[] vertices, int num_vertices, [In] int[] indices, int num_indices ); /* renderer refers to an SDL_Renderer* * texture refers to an SDL_Texture* * indices refers to a void* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderGeometryRaw( IntPtr renderer, IntPtr texture, [In] float[] xy, int xy_stride, [In] int[] color, int color_stride, [In] float[] uv, int uv_stride, int num_vertices, IntPtr indices, int num_indices, int size_indices ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawPointF( IntPtr renderer, float x, float y ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawPointsF( IntPtr renderer, [In] SDL_FPoint[] points, int count ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawLineF( IntPtr renderer, float x1, float y1, float x2, float y2 ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawLinesF( IntPtr renderer, [In] SDL_FPoint[] points, int count ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawRectF( IntPtr renderer, ref SDL_FRect rect ); /* renderer refers to an SDL_Renderer*, rect to an SDL_Rect*. * This overload allows for IntPtr.Zero (null) to be passed for rect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawRectF( IntPtr renderer, IntPtr rect ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderDrawRectsF( IntPtr renderer, [In] SDL_FRect[] rects, int count ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderFillRectF( IntPtr renderer, ref SDL_FRect rect ); /* renderer refers to an SDL_Renderer*, rect to an SDL_Rect*. * This overload allows for IntPtr.Zero (null) to be passed for rect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderFillRectF( IntPtr renderer, IntPtr rect ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderFillRectsF( IntPtr renderer, [In] SDL_FRect[] rects, int count ); #endregion /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_RenderGetClipRect( IntPtr renderer, out SDL_Rect rect ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_RenderGetLogicalSize( IntPtr renderer, out int w, out int h ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_RenderGetScale( IntPtr renderer, out float scaleX, out float scaleY ); /* renderer refers to an SDL_Renderer* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_RenderWindowToLogical( IntPtr renderer, int windowX, int windowY, out float logicalX, out float logicalY ); /* renderer refers to an SDL_Renderer* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_RenderLogicalToWindow( IntPtr renderer, float logicalX, float logicalY, out int windowX, out int windowY ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderGetViewport( IntPtr renderer, out SDL_Rect rect ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_RenderPresent(IntPtr renderer); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderReadPixels( IntPtr renderer, ref SDL_Rect rect, uint format, IntPtr pixels, int pitch ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderSetClipRect( IntPtr renderer, ref SDL_Rect rect ); /* renderer refers to an SDL_Renderer* * This overload allows for IntPtr.Zero (null) to be passed for rect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderSetClipRect( IntPtr renderer, IntPtr rect ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderSetLogicalSize( IntPtr renderer, int w, int h ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderSetScale( IntPtr renderer, float scaleX, float scaleY ); /* renderer refers to an SDL_Renderer* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderSetIntegerScale( IntPtr renderer, SDL_bool enable ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderSetViewport( IntPtr renderer, ref SDL_Rect rect ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetRenderDrawBlendMode( IntPtr renderer, SDL_BlendMode blendMode ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetRenderDrawColor( IntPtr renderer, byte r, byte g, byte b, byte a ); /* renderer refers to an SDL_Renderer*, texture to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetRenderTarget( IntPtr renderer, IntPtr texture ); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetTextureAlphaMod( IntPtr texture, byte alpha ); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetTextureBlendMode( IntPtr texture, SDL_BlendMode blendMode ); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetTextureColorMod( IntPtr texture, byte r, byte g, byte b ); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_UnlockTexture(IntPtr texture); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_UpdateTexture( IntPtr texture, ref SDL_Rect rect, IntPtr pixels, int pitch ); /* texture refers to an SDL_Texture* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_UpdateTexture( IntPtr texture, IntPtr rect, IntPtr pixels, int pitch ); /* texture refers to an SDL_Texture* * Only available in 2.0.1 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_UpdateYUVTexture( IntPtr texture, ref SDL_Rect rect, IntPtr yPlane, int yPitch, IntPtr uPlane, int uPitch, IntPtr vPlane, int vPitch ); /* texture refers to an SDL_Texture*. * yPlane and uvPlane refer to const Uint*. * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_UpdateNVTexture( IntPtr texture, ref SDL_Rect rect, IntPtr yPlane, int yPitch, IntPtr uvPlane, int uvPitch ); /* renderer refers to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_RenderTargetSupported( IntPtr renderer ); /* IntPtr refers to an SDL_Texture*, renderer to an SDL_Renderer* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetRenderTarget(IntPtr renderer); /* renderer refers to an SDL_Renderer* * Only available in 2.0.8 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_RenderGetMetalLayer( IntPtr renderer ); /* renderer refers to an SDL_Renderer* * Only available in 2.0.8 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_RenderGetMetalCommandEncoder( IntPtr renderer ); /* renderer refers to an SDL_Renderer* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderSetVSync(IntPtr renderer, int vsync); /* renderer refers to an SDL_Renderer* * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_RenderIsClipEnabled(IntPtr renderer); /* renderer refers to an SDL_Renderer* * Only available in 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_RenderFlush(IntPtr renderer); #endregion #region SDL_pixels.h public static uint SDL_DEFINE_PIXELFOURCC(byte A, byte B, byte C, byte D) { return SDL_FOURCC(A, B, C, D); } public static uint SDL_DEFINE_PIXELFORMAT( SDL_PixelType type, uint order, SDL_PackedLayout layout, byte bits, byte bytes ) { return (uint) ( (1 << 28) | (((byte) type) << 24) | (((byte) order) << 20) | (((byte) layout) << 16) | (bits << 8) | (bytes) ); } public static byte SDL_PIXELFLAG(uint X) { return (byte) ((X >> 28) & 0x0F); } public static byte SDL_PIXELTYPE(uint X) { return (byte) ((X >> 24) & 0x0F); } public static byte SDL_PIXELORDER(uint X) { return (byte) ((X >> 20) & 0x0F); } public static byte SDL_PIXELLAYOUT(uint X) { return (byte) ((X >> 16) & 0x0F); } public static byte SDL_BITSPERPIXEL(uint X) { return (byte) ((X >> 8) & 0xFF); } public static byte SDL_BYTESPERPIXEL(uint X) { if (SDL_ISPIXELFORMAT_FOURCC(X)) { if ( (X == SDL_PIXELFORMAT_YUY2) || (X == SDL_PIXELFORMAT_UYVY) || (X == SDL_PIXELFORMAT_YVYU) ) { return 2; } return 1; } return (byte) (X & 0xFF); } public static bool SDL_ISPIXELFORMAT_INDEXED(uint format) { if (SDL_ISPIXELFORMAT_FOURCC(format)) { return false; } SDL_PixelType pType = (SDL_PixelType) SDL_PIXELTYPE(format); return ( pType == SDL_PixelType.SDL_PIXELTYPE_INDEX1 || pType == SDL_PixelType.SDL_PIXELTYPE_INDEX4 || pType == SDL_PixelType.SDL_PIXELTYPE_INDEX8 ); } public static bool SDL_ISPIXELFORMAT_PACKED(uint format) { if (SDL_ISPIXELFORMAT_FOURCC(format)) { return false; } SDL_PixelType pType = (SDL_PixelType) SDL_PIXELTYPE(format); return ( pType == SDL_PixelType.SDL_PIXELTYPE_PACKED8 || pType == SDL_PixelType.SDL_PIXELTYPE_PACKED16 || pType == SDL_PixelType.SDL_PIXELTYPE_PACKED32 ); } public static bool SDL_ISPIXELFORMAT_ARRAY(uint format) { if (SDL_ISPIXELFORMAT_FOURCC(format)) { return false; } SDL_PixelType pType = (SDL_PixelType) SDL_PIXELTYPE(format); return ( pType == SDL_PixelType.SDL_PIXELTYPE_ARRAYU8 || pType == SDL_PixelType.SDL_PIXELTYPE_ARRAYU16 || pType == SDL_PixelType.SDL_PIXELTYPE_ARRAYU32 || pType == SDL_PixelType.SDL_PIXELTYPE_ARRAYF16 || pType == SDL_PixelType.SDL_PIXELTYPE_ARRAYF32 ); } public static bool SDL_ISPIXELFORMAT_ALPHA(uint format) { if (SDL_ISPIXELFORMAT_PACKED(format)) { SDL_PackedOrder pOrder = (SDL_PackedOrder) SDL_PIXELORDER(format); return ( pOrder == SDL_PackedOrder.SDL_PACKEDORDER_ARGB || pOrder == SDL_PackedOrder.SDL_PACKEDORDER_RGBA || pOrder == SDL_PackedOrder.SDL_PACKEDORDER_ABGR || pOrder == SDL_PackedOrder.SDL_PACKEDORDER_BGRA ); } else if (SDL_ISPIXELFORMAT_ARRAY(format)) { SDL_ArrayOrder aOrder = (SDL_ArrayOrder) SDL_PIXELORDER(format); return ( aOrder == SDL_ArrayOrder.SDL_ARRAYORDER_ARGB || aOrder == SDL_ArrayOrder.SDL_ARRAYORDER_RGBA || aOrder == SDL_ArrayOrder.SDL_ARRAYORDER_ABGR || aOrder == SDL_ArrayOrder.SDL_ARRAYORDER_BGRA ); } return false; } public static bool SDL_ISPIXELFORMAT_FOURCC(uint format) { return (format == 0) && (SDL_PIXELFLAG(format) != 1); } public enum SDL_PixelType { SDL_PIXELTYPE_UNKNOWN, SDL_PIXELTYPE_INDEX1, SDL_PIXELTYPE_INDEX4, SDL_PIXELTYPE_INDEX8, SDL_PIXELTYPE_PACKED8, SDL_PIXELTYPE_PACKED16, SDL_PIXELTYPE_PACKED32, SDL_PIXELTYPE_ARRAYU8, SDL_PIXELTYPE_ARRAYU16, SDL_PIXELTYPE_ARRAYU32, SDL_PIXELTYPE_ARRAYF16, SDL_PIXELTYPE_ARRAYF32 } public enum SDL_BitmapOrder { SDL_BITMAPORDER_NONE, SDL_BITMAPORDER_4321, SDL_BITMAPORDER_1234 } public enum SDL_PackedOrder { SDL_PACKEDORDER_NONE, SDL_PACKEDORDER_XRGB, SDL_PACKEDORDER_RGBX, SDL_PACKEDORDER_ARGB, SDL_PACKEDORDER_RGBA, SDL_PACKEDORDER_XBGR, SDL_PACKEDORDER_BGRX, SDL_PACKEDORDER_ABGR, SDL_PACKEDORDER_BGRA } public enum SDL_ArrayOrder { SDL_ARRAYORDER_NONE, SDL_ARRAYORDER_RGB, SDL_ARRAYORDER_RGBA, SDL_ARRAYORDER_ARGB, SDL_ARRAYORDER_BGR, SDL_ARRAYORDER_BGRA, SDL_ARRAYORDER_ABGR } public enum SDL_PackedLayout { SDL_PACKEDLAYOUT_NONE, SDL_PACKEDLAYOUT_332, SDL_PACKEDLAYOUT_4444, SDL_PACKEDLAYOUT_1555, SDL_PACKEDLAYOUT_5551, SDL_PACKEDLAYOUT_565, SDL_PACKEDLAYOUT_8888, SDL_PACKEDLAYOUT_2101010, SDL_PACKEDLAYOUT_1010102 } public static readonly uint SDL_PIXELFORMAT_UNKNOWN = 0; public static readonly uint SDL_PIXELFORMAT_INDEX1LSB = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_INDEX1, (uint) SDL_BitmapOrder.SDL_BITMAPORDER_4321, 0, 1, 0 ); public static readonly uint SDL_PIXELFORMAT_INDEX1MSB = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_INDEX1, (uint) SDL_BitmapOrder.SDL_BITMAPORDER_1234, 0, 1, 0 ); public static readonly uint SDL_PIXELFORMAT_INDEX4LSB = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_INDEX4, (uint) SDL_BitmapOrder.SDL_BITMAPORDER_4321, 0, 4, 0 ); public static readonly uint SDL_PIXELFORMAT_INDEX4MSB = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_INDEX4, (uint) SDL_BitmapOrder.SDL_BITMAPORDER_1234, 0, 4, 0 ); public static readonly uint SDL_PIXELFORMAT_INDEX8 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1 ); public static readonly uint SDL_PIXELFORMAT_RGB332 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED8, (uint) SDL_PackedOrder.SDL_PACKEDORDER_XRGB, SDL_PackedLayout.SDL_PACKEDLAYOUT_332, 8, 1 ); public static readonly uint SDL_PIXELFORMAT_XRGB444 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_XRGB, SDL_PackedLayout.SDL_PACKEDLAYOUT_4444, 12, 2 ); public static readonly uint SDL_PIXELFORMAT_RGB444 = SDL_PIXELFORMAT_XRGB444; public static readonly uint SDL_PIXELFORMAT_XBGR444 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_XBGR, SDL_PackedLayout.SDL_PACKEDLAYOUT_4444, 12, 2 ); public static readonly uint SDL_PIXELFORMAT_BGR444 = SDL_PIXELFORMAT_XBGR444; public static readonly uint SDL_PIXELFORMAT_XRGB1555 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_XRGB, SDL_PackedLayout.SDL_PACKEDLAYOUT_1555, 15, 2 ); public static readonly uint SDL_PIXELFORMAT_RGB555 = SDL_PIXELFORMAT_XRGB1555; public static readonly uint SDL_PIXELFORMAT_XBGR1555 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_INDEX1, (uint) SDL_BitmapOrder.SDL_BITMAPORDER_4321, SDL_PackedLayout.SDL_PACKEDLAYOUT_1555, 15, 2 ); public static readonly uint SDL_PIXELFORMAT_BGR555 = SDL_PIXELFORMAT_XBGR1555; public static readonly uint SDL_PIXELFORMAT_ARGB4444 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_ARGB, SDL_PackedLayout.SDL_PACKEDLAYOUT_4444, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_RGBA4444 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_RGBA, SDL_PackedLayout.SDL_PACKEDLAYOUT_4444, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_ABGR4444 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_ABGR, SDL_PackedLayout.SDL_PACKEDLAYOUT_4444, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_BGRA4444 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_BGRA, SDL_PackedLayout.SDL_PACKEDLAYOUT_4444, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_ARGB1555 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_ARGB, SDL_PackedLayout.SDL_PACKEDLAYOUT_1555, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_RGBA5551 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_RGBA, SDL_PackedLayout.SDL_PACKEDLAYOUT_5551, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_ABGR1555 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_ABGR, SDL_PackedLayout.SDL_PACKEDLAYOUT_1555, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_BGRA5551 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_BGRA, SDL_PackedLayout.SDL_PACKEDLAYOUT_5551, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_RGB565 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_XRGB, SDL_PackedLayout.SDL_PACKEDLAYOUT_565, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_BGR565 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED16, (uint) SDL_PackedOrder.SDL_PACKEDORDER_XBGR, SDL_PackedLayout.SDL_PACKEDLAYOUT_565, 16, 2 ); public static readonly uint SDL_PIXELFORMAT_RGB24 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_ARRAYU8, (uint) SDL_ArrayOrder.SDL_ARRAYORDER_RGB, 0, 24, 3 ); public static readonly uint SDL_PIXELFORMAT_BGR24 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_ARRAYU8, (uint) SDL_ArrayOrder.SDL_ARRAYORDER_BGR, 0, 24, 3 ); public static readonly uint SDL_PIXELFORMAT_XRGB888 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED32, (uint) SDL_PackedOrder.SDL_PACKEDORDER_XRGB, SDL_PackedLayout.SDL_PACKEDLAYOUT_8888, 24, 4 ); public static readonly uint SDL_PIXELFORMAT_RGB888 = SDL_PIXELFORMAT_XRGB888; public static readonly uint SDL_PIXELFORMAT_RGBX8888 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED32, (uint) SDL_PackedOrder.SDL_PACKEDORDER_RGBX, SDL_PackedLayout.SDL_PACKEDLAYOUT_8888, 24, 4 ); public static readonly uint SDL_PIXELFORMAT_XBGR888 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED32, (uint) SDL_PackedOrder.SDL_PACKEDORDER_XBGR, SDL_PackedLayout.SDL_PACKEDLAYOUT_8888, 24, 4 ); public static readonly uint SDL_PIXELFORMAT_BGR888 = SDL_PIXELFORMAT_XBGR888; public static readonly uint SDL_PIXELFORMAT_BGRX8888 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED32, (uint) SDL_PackedOrder.SDL_PACKEDORDER_BGRX, SDL_PackedLayout.SDL_PACKEDLAYOUT_8888, 24, 4 ); public static readonly uint SDL_PIXELFORMAT_ARGB8888 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED32, (uint) SDL_PackedOrder.SDL_PACKEDORDER_ARGB, SDL_PackedLayout.SDL_PACKEDLAYOUT_8888, 32, 4 ); public static readonly uint SDL_PIXELFORMAT_RGBA8888 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED32, (uint) SDL_PackedOrder.SDL_PACKEDORDER_RGBA, SDL_PackedLayout.SDL_PACKEDLAYOUT_8888, 32, 4 ); public static readonly uint SDL_PIXELFORMAT_ABGR8888 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED32, (uint) SDL_PackedOrder.SDL_PACKEDORDER_ABGR, SDL_PackedLayout.SDL_PACKEDLAYOUT_8888, 32, 4 ); public static readonly uint SDL_PIXELFORMAT_BGRA8888 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED32, (uint) SDL_PackedOrder.SDL_PACKEDORDER_BGRA, SDL_PackedLayout.SDL_PACKEDLAYOUT_8888, 32, 4 ); public static readonly uint SDL_PIXELFORMAT_ARGB2101010 = SDL_DEFINE_PIXELFORMAT( SDL_PixelType.SDL_PIXELTYPE_PACKED32, (uint) SDL_PackedOrder.SDL_PACKEDORDER_ARGB, SDL_PackedLayout.SDL_PACKEDLAYOUT_2101010, 32, 4 ); public static readonly uint SDL_PIXELFORMAT_YV12 = SDL_DEFINE_PIXELFOURCC( (byte) 'Y', (byte) 'V', (byte) '1', (byte) '2' ); public static readonly uint SDL_PIXELFORMAT_IYUV = SDL_DEFINE_PIXELFOURCC( (byte) 'I', (byte) 'Y', (byte) 'U', (byte) 'V' ); public static readonly uint SDL_PIXELFORMAT_YUY2 = SDL_DEFINE_PIXELFOURCC( (byte) 'Y', (byte) 'U', (byte) 'Y', (byte) '2' ); public static readonly uint SDL_PIXELFORMAT_UYVY = SDL_DEFINE_PIXELFOURCC( (byte) 'U', (byte) 'Y', (byte) 'V', (byte) 'Y' ); public static readonly uint SDL_PIXELFORMAT_YVYU = SDL_DEFINE_PIXELFOURCC( (byte) 'Y', (byte) 'V', (byte) 'Y', (byte) 'U' ); [StructLayout(LayoutKind.Sequential)] public struct SDL_Color { public byte r; public byte g; public byte b; public byte a; } [StructLayout(LayoutKind.Sequential)] public struct SDL_Palette { public int ncolors; public IntPtr colors; public int version; public int refcount; } [StructLayout(LayoutKind.Sequential)] public struct SDL_PixelFormat { public uint format; public IntPtr palette; // SDL_Palette* public byte BitsPerPixel; public byte BytesPerPixel; public uint Rmask; public uint Gmask; public uint Bmask; public uint Amask; public byte Rloss; public byte Gloss; public byte Bloss; public byte Aloss; public byte Rshift; public byte Gshift; public byte Bshift; public byte Ashift; public int refcount; public IntPtr next; // SDL_PixelFormat* } /* IntPtr refers to an SDL_PixelFormat* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_AllocFormat(uint pixel_format); /* IntPtr refers to an SDL_Palette* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_AllocPalette(int ncolors); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_CalculateGammaRamp( float gamma, [Out()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U2, SizeConst = 256)] ushort[] ramp ); /* format refers to an SDL_PixelFormat* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FreeFormat(IntPtr format); /* palette refers to an SDL_Palette* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FreePalette(IntPtr palette); [DllImport(nativeLibName, EntryPoint = "SDL_GetPixelFormatName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetPixelFormatName( uint format ); public static string SDL_GetPixelFormatName(uint format) { return UTF8_ToManaged( INTERNAL_SDL_GetPixelFormatName(format) ); } /* format refers to an SDL_PixelFormat* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GetRGB( uint pixel, IntPtr format, out byte r, out byte g, out byte b ); /* format refers to an SDL_PixelFormat* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GetRGBA( uint pixel, IntPtr format, out byte r, out byte g, out byte b, out byte a ); /* format refers to an SDL_PixelFormat* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_MapRGB( IntPtr format, byte r, byte g, byte b ); /* format refers to an SDL_PixelFormat* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_MapRGBA( IntPtr format, byte r, byte g, byte b, byte a ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_MasksToPixelFormatEnum( int bpp, uint Rmask, uint Gmask, uint Bmask, uint Amask ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_PixelFormatEnumToMasks( uint format, out int bpp, out uint Rmask, out uint Gmask, out uint Bmask, out uint Amask ); /* palette refers to an SDL_Palette* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetPaletteColors( IntPtr palette, [In] SDL_Color[] colors, int firstcolor, int ncolors ); /* format and palette refer to an SDL_PixelFormat* and SDL_Palette* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetPixelFormatPalette( IntPtr format, IntPtr palette ); #endregion #region SDL_rect.h [StructLayout(LayoutKind.Sequential)] public struct SDL_Point { public int x; public int y; } [StructLayout(LayoutKind.Sequential)] public struct SDL_Rect { public int x; public int y; public int w; public int h; } /* Only available in 2.0.10 or higher. */ [StructLayout(LayoutKind.Sequential)] public struct SDL_FPoint { public float x; public float y; } /* Only available in 2.0.10 or higher. */ [StructLayout(LayoutKind.Sequential)] public struct SDL_FRect { public float x; public float y; public float w; public float h; } /* Only available in 2.0.4 or higher. */ public static SDL_bool SDL_PointInRect(ref SDL_Point p, ref SDL_Rect r) { return ( (p.x >= r.x) && (p.x < (r.x + r.w)) && (p.y >= r.y) && (p.y < (r.y + r.h)) ) ? SDL_bool.SDL_TRUE : SDL_bool.SDL_FALSE; } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_EnclosePoints( [In] SDL_Point[] points, int count, ref SDL_Rect clip, out SDL_Rect result ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasIntersection( ref SDL_Rect A, ref SDL_Rect B ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IntersectRect( ref SDL_Rect A, ref SDL_Rect B, out SDL_Rect result ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IntersectRectAndLine( ref SDL_Rect rect, ref int X1, ref int Y1, ref int X2, ref int Y2 ); public static SDL_bool SDL_RectEmpty(ref SDL_Rect r) { return ((r.w <= 0) || (r.h <= 0)) ? SDL_bool.SDL_TRUE : SDL_bool.SDL_FALSE; } public static SDL_bool SDL_RectEquals( ref SDL_Rect a, ref SDL_Rect b ) { return ( (a.x == b.x) && (a.y == b.y) && (a.w == b.w) && (a.h == b.h) ) ? SDL_bool.SDL_TRUE : SDL_bool.SDL_FALSE; } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_UnionRect( ref SDL_Rect A, ref SDL_Rect B, out SDL_Rect result ); #endregion #region SDL_shape.h public const int SDL_NONSHAPEABLE_WINDOW = -1; public const int SDL_INVALID_SHAPE_ARGUMENT = -2; public const int SDL_WINDOW_LACKS_SHAPE = -3; [DllImport(nativeLibName, EntryPoint = "SDL_CreateShapedWindow", CallingConvention = CallingConvention.Cdecl)] private static unsafe extern IntPtr INTERNAL_SDL_CreateShapedWindow( byte* title, uint x, uint y, uint w, uint h, SDL_WindowFlags flags ); public static unsafe IntPtr SDL_CreateShapedWindow(string title, uint x, uint y, uint w, uint h, SDL_WindowFlags flags) { byte* utf8Title = Utf8EncodeHeap(title); IntPtr result = INTERNAL_SDL_CreateShapedWindow(utf8Title, x, y, w, h, flags); Marshal.FreeHGlobal((IntPtr)utf8Title); return result; } [DllImport(nativeLibName, EntryPoint = "SDL_IsShapedWindow", CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsShapedWindow(IntPtr window); public enum WindowShapeMode { ShapeModeDefault, ShapeModeBinarizeAlpha, ShapeModeReverseBinarizeAlpha, ShapeModeColorKey } public static bool SDL_SHAPEMODEALPHA(WindowShapeMode mode) { switch (mode) { case WindowShapeMode.ShapeModeDefault: case WindowShapeMode.ShapeModeBinarizeAlpha: case WindowShapeMode.ShapeModeReverseBinarizeAlpha: return true; default: return false; } } [StructLayout(LayoutKind.Explicit)] public struct SDL_WindowShapeParams { [FieldOffset(0)] public byte binarizationCutoff; [FieldOffset(0)] public SDL_Color colorKey; } [StructLayout(LayoutKind.Sequential)] public struct SDL_WindowShapeMode { public WindowShapeMode mode; public SDL_WindowShapeParams parameters; } [DllImport(nativeLibName, EntryPoint = "SDL_SetWindowShape", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetWindowShape( IntPtr window, IntPtr shape, ref SDL_WindowShapeMode shape_mode ); [DllImport(nativeLibName, EntryPoint = "SDL_GetShapedWindowMode", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetShapedWindowMode( IntPtr window, out SDL_WindowShapeMode shape_mode ); [DllImport(nativeLibName, EntryPoint = "SDL_GetShapedWindowMode", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetShapedWindowMode( IntPtr window, IntPtr shape_mode ); #endregion #region SDL_surface.h public const uint SDL_SWSURFACE = 0x00000000; public const uint SDL_PREALLOC = 0x00000001; public const uint SDL_RLEACCEL = 0x00000002; public const uint SDL_DONTFREE = 0x00000004; [StructLayout(LayoutKind.Sequential)] public struct SDL_Surface { public uint flags; public IntPtr format; // SDL_PixelFormat* public int w; public int h; public int pitch; public IntPtr pixels; // void* public IntPtr userdata; // void* public int locked; public IntPtr list_blitmap; // void* public SDL_Rect clip_rect; public IntPtr map; // SDL_BlitMap* public int refcount; } /* surface refers to an SDL_Surface* */ public static bool SDL_MUSTLOCK(IntPtr surface) { SDL_Surface sur; sur = PtrToStructure( surface ); return (sur.flags & SDL_RLEACCEL) != 0; } /* src and dst refer to an SDL_Surface* */ [DllImport(nativeLibName, EntryPoint = "SDL_UpperBlit", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_BlitSurface( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* src and dst refer to an SDL_Surface* * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for srcrect. */ [DllImport(nativeLibName, EntryPoint = "SDL_UpperBlit", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_BlitSurface( IntPtr src, IntPtr srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* src and dst refer to an SDL_Surface* * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for dstrect. */ [DllImport(nativeLibName, EntryPoint = "SDL_UpperBlit", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_BlitSurface( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, IntPtr dstrect ); /* src and dst refer to an SDL_Surface* * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both SDL_Rects. */ [DllImport(nativeLibName, EntryPoint = "SDL_UpperBlit", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_BlitSurface( IntPtr src, IntPtr srcrect, IntPtr dst, IntPtr dstrect ); /* src and dst refer to an SDL_Surface* */ [DllImport(nativeLibName, EntryPoint = "SDL_UpperBlitScaled", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_BlitScaled( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* src and dst refer to an SDL_Surface* * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for srcrect. */ [DllImport(nativeLibName, EntryPoint = "SDL_UpperBlitScaled", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_BlitScaled( IntPtr src, IntPtr srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* src and dst refer to an SDL_Surface* * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for dstrect. */ [DllImport(nativeLibName, EntryPoint = "SDL_UpperBlitScaled", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_BlitScaled( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, IntPtr dstrect ); /* src and dst refer to an SDL_Surface* * Internally, this function contains logic to use default values when * source and destination rectangles are passed as NULL. * This overload allows for IntPtr.Zero (null) to be passed for both SDL_Rects. */ [DllImport(nativeLibName, EntryPoint = "SDL_UpperBlitScaled", CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_BlitScaled( IntPtr src, IntPtr srcrect, IntPtr dst, IntPtr dstrect ); /* src and dst are void* pointers */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_ConvertPixels( int width, int height, uint src_format, IntPtr src, int src_pitch, uint dst_format, IntPtr dst, int dst_pitch ); /* src and dst are void* pointers * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_PremultiplyAlpha( int width, int height, uint src_format, IntPtr src, int src_pitch, uint dst_format, IntPtr dst, int dst_pitch ); /* IntPtr refers to an SDL_Surface* * src refers to an SDL_Surface* * fmt refers to an SDL_PixelFormat* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_ConvertSurface( IntPtr src, IntPtr fmt, uint flags ); /* IntPtr refers to an SDL_Surface*, src to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_ConvertSurfaceFormat( IntPtr src, uint pixel_format, uint flags ); /* IntPtr refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateRGBSurface( uint flags, int width, int height, int depth, uint Rmask, uint Gmask, uint Bmask, uint Amask ); /* IntPtr refers to an SDL_Surface*, pixels to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateRGBSurfaceFrom( IntPtr pixels, int width, int height, int depth, int pitch, uint Rmask, uint Gmask, uint Bmask, uint Amask ); /* IntPtr refers to an SDL_Surface* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateRGBSurfaceWithFormat( uint flags, int width, int height, int depth, uint format ); /* IntPtr refers to an SDL_Surface*, pixels to a void* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateRGBSurfaceWithFormatFrom( IntPtr pixels, int width, int height, int depth, int pitch, uint format ); /* dst refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_FillRect( IntPtr dst, ref SDL_Rect rect, uint color ); /* dst refers to an SDL_Surface*. * This overload allows passing NULL to rect. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_FillRect( IntPtr dst, IntPtr rect, uint color ); /* dst refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_FillRects( IntPtr dst, [In] SDL_Rect[] rects, int count, uint color ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FreeSurface(IntPtr surface); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GetClipRect( IntPtr surface, out SDL_Rect rect ); /* surface refers to an SDL_Surface*. * Only available in 2.0.9 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasColorKey(IntPtr surface); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetColorKey( IntPtr surface, out uint key ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetSurfaceAlphaMod( IntPtr surface, out byte alpha ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetSurfaceBlendMode( IntPtr surface, out SDL_BlendMode blendMode ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetSurfaceColorMod( IntPtr surface, out byte r, out byte g, out byte b ); /* These are for SDL_LoadBMP, which is a macro in the SDL headers. */ /* IntPtr refers to an SDL_Surface* */ /* THIS IS AN RWops FUNCTION! */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_LoadBMP_RW( IntPtr src, int freesrc ); public static IntPtr SDL_LoadBMP(string file) { IntPtr rwops = SDL_RWFromFile(file, "rb"); return SDL_LoadBMP_RW(rwops, 1); } /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_LockSurface(IntPtr surface); /* src and dst refer to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_LowerBlit( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* src and dst refer to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_LowerBlitScaled( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* These are for SDL_SaveBMP, which is a macro in the SDL headers. */ /* IntPtr refers to an SDL_Surface* */ /* THIS IS AN RWops FUNCTION! */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SaveBMP_RW( IntPtr surface, IntPtr src, int freesrc ); public static int SDL_SaveBMP(IntPtr surface, string file) { IntPtr rwops = SDL_RWFromFile(file, "wb"); return SDL_SaveBMP_RW(surface, rwops, 1); } /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_SetClipRect( IntPtr surface, ref SDL_Rect rect ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetColorKey( IntPtr surface, int flag, uint key ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetSurfaceAlphaMod( IntPtr surface, byte alpha ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetSurfaceBlendMode( IntPtr surface, SDL_BlendMode blendMode ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetSurfaceColorMod( IntPtr surface, byte r, byte g, byte b ); /* surface refers to an SDL_Surface*, palette to an SDL_Palette* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetSurfacePalette( IntPtr surface, IntPtr palette ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetSurfaceRLE( IntPtr surface, int flag ); /* surface refers to an SDL_Surface*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasSurfaceRLE( IntPtr surface ); /* src and dst refer to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SoftStretch( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* src and dst refer to an SDL_Surface* * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SoftStretchLinear( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* surface refers to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_UnlockSurface(IntPtr surface); /* src and dst refer to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_UpperBlit( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* src and dst refer to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_UpperBlitScaled( IntPtr src, ref SDL_Rect srcrect, IntPtr dst, ref SDL_Rect dstrect ); /* surface and IntPtr refer to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_DuplicateSurface(IntPtr surface); #endregion #region SDL_clipboard.h [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasClipboardText(); [DllImport(nativeLibName, EntryPoint = "SDL_GetClipboardText", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetClipboardText(); public static string SDL_GetClipboardText() { return UTF8_ToManaged(INTERNAL_SDL_GetClipboardText(), true); } [DllImport(nativeLibName, EntryPoint = "SDL_SetClipboardText", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe int INTERNAL_SDL_SetClipboardText( byte* text ); public static unsafe int SDL_SetClipboardText( string text ) { byte* utf8Text = Utf8EncodeHeap(text); int result = INTERNAL_SDL_SetClipboardText( utf8Text ); Marshal.FreeHGlobal((IntPtr) utf8Text); return result; } #endregion #region SDL_events.h /* General keyboard/mouse state definitions. */ public const byte SDL_PRESSED = 1; public const byte SDL_RELEASED = 0; /* Default size is according to SDL2 default. */ public const int SDL_TEXTEDITINGEVENT_TEXT_SIZE = 32; public const int SDL_TEXTINPUTEVENT_TEXT_SIZE = 32; /* The types of events that can be delivered. */ public enum SDL_EventType : uint { SDL_FIRSTEVENT = 0, /* Application events */ SDL_QUIT = 0x100, /* iOS/Android/WinRT app events */ SDL_APP_TERMINATING, SDL_APP_LOWMEMORY, SDL_APP_WILLENTERBACKGROUND, SDL_APP_DIDENTERBACKGROUND, SDL_APP_WILLENTERFOREGROUND, SDL_APP_DIDENTERFOREGROUND, /* Only available in SDL 2.0.14 or higher. */ SDL_LOCALECHANGED, /* Display events */ /* Only available in SDL 2.0.9 or higher. */ SDL_DISPLAYEVENT = 0x150, /* Window events */ SDL_WINDOWEVENT = 0x200, SDL_SYSWMEVENT, /* Keyboard events */ SDL_KEYDOWN = 0x300, SDL_KEYUP, SDL_TEXTEDITING, SDL_TEXTINPUT, SDL_KEYMAPCHANGED, SDL_TEXTEDITING_EXT, /* Mouse events */ SDL_MOUSEMOTION = 0x400, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP, SDL_MOUSEWHEEL, /* Joystick events */ SDL_JOYAXISMOTION = 0x600, SDL_JOYBALLMOTION, SDL_JOYHATMOTION, SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED, /* Game controller events */ SDL_CONTROLLERAXISMOTION = 0x650, SDL_CONTROLLERBUTTONDOWN, SDL_CONTROLLERBUTTONUP, SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED, SDL_CONTROLLERDEVICEREMAPPED, SDL_CONTROLLERTOUCHPADDOWN, /* Requires >= 2.0.14 */ SDL_CONTROLLERTOUCHPADMOTION, /* Requires >= 2.0.14 */ SDL_CONTROLLERTOUCHPADUP, /* Requires >= 2.0.14 */ SDL_CONTROLLERSENSORUPDATE, /* Requires >= 2.0.14 */ /* Touch events */ SDL_FINGERDOWN = 0x700, SDL_FINGERUP, SDL_FINGERMOTION, /* Gesture events */ SDL_DOLLARGESTURE = 0x800, SDL_DOLLARRECORD, SDL_MULTIGESTURE, /* Clipboard events */ SDL_CLIPBOARDUPDATE = 0x900, /* Drag and drop events */ SDL_DROPFILE = 0x1000, /* Only available in 2.0.4 or higher. */ SDL_DROPTEXT, SDL_DROPBEGIN, SDL_DROPCOMPLETE, /* Audio hotplug events */ /* Only available in SDL 2.0.4 or higher. */ SDL_AUDIODEVICEADDED = 0x1100, SDL_AUDIODEVICEREMOVED, /* Sensor events */ /* Only available in SDL 2.0.9 or higher. */ SDL_SENSORUPDATE = 0x1200, /* Render events */ /* Only available in SDL 2.0.2 or higher. */ SDL_RENDER_TARGETS_RESET = 0x2000, /* Only available in SDL 2.0.4 or higher. */ SDL_RENDER_DEVICE_RESET, /* Internal events */ /* Only available in 2.0.18 or higher. */ SDL_POLLSENTINEL = 0x7F00, /* Events SDL_USEREVENT through SDL_LASTEVENT are for * your use, and should be allocated with * SDL_RegisterEvents() */ SDL_USEREVENT = 0x8000, /* The last event, used for bouding arrays. */ SDL_LASTEVENT = 0xFFFF } /* Only available in 2.0.4 or higher. */ public enum SDL_MouseWheelDirection : uint { SDL_MOUSEWHEEL_NORMAL, SDL_MOUSEWHEEL_FLIPPED } /* Fields shared by every event */ [StructLayout(LayoutKind.Sequential)] public struct SDL_GenericEvent { public SDL_EventType type; public UInt32 timestamp; } // Ignore private members used for padding in this struct #pragma warning disable 0169 [StructLayout(LayoutKind.Sequential)] public struct SDL_DisplayEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 display; public SDL_DisplayEventID displayEvent; // event, lolC# private byte padding1; private byte padding2; private byte padding3; public Int32 data1; } #pragma warning restore 0169 // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Window state change event data (event.window.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_WindowEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 windowID; public SDL_WindowEventID windowEvent; // event, lolC# private byte padding1; private byte padding2; private byte padding3; public Int32 data1; public Int32 data2; } #pragma warning restore 0169 // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Keyboard button event structure (event.key.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_KeyboardEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 windowID; public byte state; public byte repeat; /* non-zero if this is a repeat */ private byte padding2; private byte padding3; public SDL_Keysym keysym; } #pragma warning restore 0169 [StructLayout(LayoutKind.Sequential)] public unsafe struct SDL_TextEditingEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 windowID; public fixed byte text[SDL_TEXTEDITINGEVENT_TEXT_SIZE]; public Int32 start; public Int32 length; } [StructLayout(LayoutKind.Sequential)] public unsafe struct SDL_TextEditingExtEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 windowID; public IntPtr text; /* char*, free with SDL_free */ public Int32 start; public Int32 length; } [StructLayout(LayoutKind.Sequential)] public unsafe struct SDL_TextInputEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 windowID; public fixed byte text[SDL_TEXTINPUTEVENT_TEXT_SIZE]; } // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Mouse motion event structure (event.motion.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_MouseMotionEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 windowID; public UInt32 which; public byte state; /* bitmask of buttons */ private byte padding1; private byte padding2; private byte padding3; public Int32 x; public Int32 y; public Int32 xrel; public Int32 yrel; } #pragma warning restore 0169 // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Mouse button event structure (event.button.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_MouseButtonEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 windowID; public UInt32 which; public byte button; /* button id */ public byte state; /* SDL_PRESSED or SDL_RELEASED */ public byte clicks; /* 1 for single-click, 2 for double-click, etc. */ private byte padding1; public Int32 x; public Int32 y; } #pragma warning restore 0169 /* Mouse wheel event structure (event.wheel.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_MouseWheelEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 windowID; public UInt32 which; public Int32 x; /* amount scrolled horizontally */ public Int32 y; /* amount scrolled vertically */ public UInt32 direction; /* Set to one of the SDL_MOUSEWHEEL_* defines */ public float preciseX; /* Requires >= 2.0.18 */ public float preciseY; /* Requires >= 2.0.18 */ } // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Joystick axis motion event structure (event.jaxis.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_JoyAxisEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* SDL_JoystickID */ public byte axis; private byte padding1; private byte padding2; private byte padding3; public Int16 axisValue; /* value, lolC# */ public UInt16 padding4; } #pragma warning restore 0169 // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Joystick trackball motion event structure (event.jball.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_JoyBallEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* SDL_JoystickID */ public byte ball; private byte padding1; private byte padding2; private byte padding3; public Int16 xrel; public Int16 yrel; } #pragma warning restore 0169 // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Joystick hat position change event struct (event.jhat.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_JoyHatEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* SDL_JoystickID */ public byte hat; /* index of the hat */ public byte hatValue; /* value, lolC# */ private byte padding1; private byte padding2; } #pragma warning restore 0169 // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Joystick button event structure (event.jbutton.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_JoyButtonEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* SDL_JoystickID */ public byte button; public byte state; /* SDL_PRESSED or SDL_RELEASED */ private byte padding1; private byte padding2; } #pragma warning restore 0169 /* Joystick device event structure (event.jdevice.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_JoyDeviceEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* SDL_JoystickID */ } // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Game controller axis motion event (event.caxis.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_ControllerAxisEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* SDL_JoystickID */ public byte axis; private byte padding1; private byte padding2; private byte padding3; public Int16 axisValue; /* value, lolC# */ private UInt16 padding4; } #pragma warning restore 0169 // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Game controller button event (event.cbutton.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_ControllerButtonEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* SDL_JoystickID */ public byte button; public byte state; private byte padding1; private byte padding2; } #pragma warning restore 0169 /* Game controller device event (event.cdevice.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_ControllerDeviceEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* joystick id for ADDED, * else instance id */ } /* Game controller touchpad event structure (event.ctouchpad.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_ControllerTouchpadEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* SDL_JoystickID */ public Int32 touchpad; public Int32 finger; public float x; public float y; public float pressure; } /* Game controller sensor event structure (event.csensor.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_ControllerSensorEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; /* SDL_JoystickID */ public Int32 sensor; public float data1; public float data2; public float data3; } // Ignore private members used for padding in this struct #pragma warning disable 0169 /* Audio device event (event.adevice.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_AudioDeviceEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 which; public byte iscapture; private byte padding1; private byte padding2; private byte padding3; } #pragma warning restore 0169 [StructLayout(LayoutKind.Sequential)] public struct SDL_TouchFingerEvent { public SDL_EventType type; public UInt32 timestamp; public Int64 touchId; // SDL_TouchID public Int64 fingerId; // SDL_GestureID public float x; public float y; public float dx; public float dy; public float pressure; public uint windowID; } [StructLayout(LayoutKind.Sequential)] public struct SDL_MultiGestureEvent { public SDL_EventType type; public UInt32 timestamp; public Int64 touchId; // SDL_TouchID public float dTheta; public float dDist; public float x; public float y; public UInt16 numFingers; public UInt16 padding; } [StructLayout(LayoutKind.Sequential)] public struct SDL_DollarGestureEvent { public SDL_EventType type; public UInt32 timestamp; public Int64 touchId; // SDL_TouchID public Int64 gestureId; // SDL_GestureID public UInt32 numFingers; public float error; public float x; public float y; } /* File open request by system (event.drop.*), enabled by * default */ [StructLayout(LayoutKind.Sequential)] public struct SDL_DropEvent { public SDL_EventType type; public UInt32 timestamp; /* char* filename, to be freed. * Access the variable EXACTLY ONCE like this: * string s = SDL.UTF8_ToManaged(evt.drop.file, true); */ public IntPtr file; public UInt32 windowID; } [StructLayout(LayoutKind.Sequential)] public unsafe struct SDL_SensorEvent { public SDL_EventType type; public UInt32 timestamp; public Int32 which; public fixed float data[6]; } /* The "quit requested" event */ [StructLayout(LayoutKind.Sequential)] public struct SDL_QuitEvent { public SDL_EventType type; public UInt32 timestamp; } /* A user defined event (event.user.*) */ [StructLayout(LayoutKind.Sequential)] public struct SDL_UserEvent { public SDL_EventType type; public UInt32 timestamp; public UInt32 windowID; public Int32 code; public IntPtr data1; /* user-defined */ public IntPtr data2; /* user-defined */ } /* A video driver dependent event (event.syswm.*), disabled */ [StructLayout(LayoutKind.Sequential)] public struct SDL_SysWMEvent { public SDL_EventType type; public UInt32 timestamp; public IntPtr msg; /* SDL_SysWMmsg*, system-dependent*/ } /* General event structure */ // C# doesn't do unions, so we do this ugly thing. */ [StructLayout(LayoutKind.Explicit)] public unsafe struct SDL_Event { [FieldOffset(0)] public SDL_EventType type; [FieldOffset(0)] public SDL_EventType typeFSharp; [FieldOffset(0)] public SDL_DisplayEvent display; [FieldOffset(0)] public SDL_WindowEvent window; [FieldOffset(0)] public SDL_KeyboardEvent key; [FieldOffset(0)] public SDL_TextEditingEvent edit; [FieldOffset(0)] public SDL_TextEditingExtEvent editExt; [FieldOffset(0)] public SDL_TextInputEvent text; [FieldOffset(0)] public SDL_MouseMotionEvent motion; [FieldOffset(0)] public SDL_MouseButtonEvent button; [FieldOffset(0)] public SDL_MouseWheelEvent wheel; [FieldOffset(0)] public SDL_JoyAxisEvent jaxis; [FieldOffset(0)] public SDL_JoyBallEvent jball; [FieldOffset(0)] public SDL_JoyHatEvent jhat; [FieldOffset(0)] public SDL_JoyButtonEvent jbutton; [FieldOffset(0)] public SDL_JoyDeviceEvent jdevice; [FieldOffset(0)] public SDL_ControllerAxisEvent caxis; [FieldOffset(0)] public SDL_ControllerButtonEvent cbutton; [FieldOffset(0)] public SDL_ControllerDeviceEvent cdevice; [FieldOffset(0)] public SDL_ControllerTouchpadEvent ctouchpad; [FieldOffset(0)] public SDL_ControllerSensorEvent csensor; [FieldOffset(0)] public SDL_AudioDeviceEvent adevice; [FieldOffset(0)] public SDL_SensorEvent sensor; [FieldOffset(0)] public SDL_QuitEvent quit; [FieldOffset(0)] public SDL_UserEvent user; [FieldOffset(0)] public SDL_SysWMEvent syswm; [FieldOffset(0)] public SDL_TouchFingerEvent tfinger; [FieldOffset(0)] public SDL_MultiGestureEvent mgesture; [FieldOffset(0)] public SDL_DollarGestureEvent dgesture; [FieldOffset(0)] public SDL_DropEvent drop; [FieldOffset(0)] private fixed byte padding[56]; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int SDL_EventFilter( IntPtr userdata, // void* IntPtr sdlevent // SDL_Event* event, lolC# ); /* Pump the event loop, getting events from the input devices*/ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_PumpEvents(); public enum SDL_eventaction { SDL_ADDEVENT, SDL_PEEKEVENT, SDL_GETEVENT } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_PeepEvents( [Out] SDL_Event[] events, int numevents, SDL_eventaction action, SDL_EventType minType, SDL_EventType maxType ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern unsafe int SDL_PeepEvents( SDL_Event* events, int numevents, SDL_eventaction action, SDL_EventType minType, SDL_EventType maxType ); /* Checks to see if certain events are in the event queue */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasEvent(SDL_EventType type); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasEvents( SDL_EventType minType, SDL_EventType maxType ); /* Clears events from the event queue */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FlushEvent(SDL_EventType type); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FlushEvents( SDL_EventType min, SDL_EventType max ); /* Polls for currently pending events */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_PollEvent(out SDL_Event _event); /* Waits indefinitely for the next event */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_WaitEvent(out SDL_Event _event); /* Waits until the specified timeout (in ms) for the next event */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_WaitEventTimeout(out SDL_Event _event, int timeout); /* Add an event to the event queue */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_PushEvent(ref SDL_Event _event); /* userdata refers to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetEventFilter( SDL_EventFilter filter, IntPtr userdata ); /* userdata refers to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] private static extern SDL_bool SDL_GetEventFilter( out IntPtr filter, out IntPtr userdata ); public static SDL_bool SDL_GetEventFilter( out SDL_EventFilter filter, out IntPtr userdata ) { IntPtr result = IntPtr.Zero; SDL_bool retval = SDL_GetEventFilter(out result, out userdata); if (result != IntPtr.Zero) { filter = (SDL_EventFilter) GetDelegateForFunctionPointer( result ); } else { filter = null; } return retval; } /* userdata refers to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_AddEventWatch( SDL_EventFilter filter, IntPtr userdata ); /* userdata refers to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_DelEventWatch( SDL_EventFilter filter, IntPtr userdata ); /* userdata refers to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FilterEvents( SDL_EventFilter filter, IntPtr userdata ); /* These are for SDL_EventState() */ public const int SDL_QUERY = -1; public const int SDL_IGNORE = 0; public const int SDL_DISABLE = 0; public const int SDL_ENABLE = 1; /* This function allows you to enable/disable certain events */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern byte SDL_EventState(SDL_EventType type, int state); /* Get the state of an event */ public static byte SDL_GetEventState(SDL_EventType type) { return SDL_EventState(type, SDL_QUERY); } /* Allocate a set of user-defined events */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_RegisterEvents(int numevents); #endregion #region SDL_scancode.h /* Scancodes based off USB keyboard page (0x07) */ public enum SDL_Scancode { SDL_SCANCODE_UNKNOWN = 0, SDL_SCANCODE_A = 4, SDL_SCANCODE_B = 5, SDL_SCANCODE_C = 6, SDL_SCANCODE_D = 7, SDL_SCANCODE_E = 8, SDL_SCANCODE_F = 9, SDL_SCANCODE_G = 10, SDL_SCANCODE_H = 11, SDL_SCANCODE_I = 12, SDL_SCANCODE_J = 13, SDL_SCANCODE_K = 14, SDL_SCANCODE_L = 15, SDL_SCANCODE_M = 16, SDL_SCANCODE_N = 17, SDL_SCANCODE_O = 18, SDL_SCANCODE_P = 19, SDL_SCANCODE_Q = 20, SDL_SCANCODE_R = 21, SDL_SCANCODE_S = 22, SDL_SCANCODE_T = 23, SDL_SCANCODE_U = 24, SDL_SCANCODE_V = 25, SDL_SCANCODE_W = 26, SDL_SCANCODE_X = 27, SDL_SCANCODE_Y = 28, SDL_SCANCODE_Z = 29, SDL_SCANCODE_1 = 30, SDL_SCANCODE_2 = 31, SDL_SCANCODE_3 = 32, SDL_SCANCODE_4 = 33, SDL_SCANCODE_5 = 34, SDL_SCANCODE_6 = 35, SDL_SCANCODE_7 = 36, SDL_SCANCODE_8 = 37, SDL_SCANCODE_9 = 38, SDL_SCANCODE_0 = 39, SDL_SCANCODE_RETURN = 40, SDL_SCANCODE_ESCAPE = 41, SDL_SCANCODE_BACKSPACE = 42, SDL_SCANCODE_TAB = 43, SDL_SCANCODE_SPACE = 44, SDL_SCANCODE_MINUS = 45, SDL_SCANCODE_EQUALS = 46, SDL_SCANCODE_LEFTBRACKET = 47, SDL_SCANCODE_RIGHTBRACKET = 48, SDL_SCANCODE_BACKSLASH = 49, SDL_SCANCODE_NONUSHASH = 50, SDL_SCANCODE_SEMICOLON = 51, SDL_SCANCODE_APOSTROPHE = 52, SDL_SCANCODE_GRAVE = 53, SDL_SCANCODE_COMMA = 54, SDL_SCANCODE_PERIOD = 55, SDL_SCANCODE_SLASH = 56, SDL_SCANCODE_CAPSLOCK = 57, SDL_SCANCODE_F1 = 58, SDL_SCANCODE_F2 = 59, SDL_SCANCODE_F3 = 60, SDL_SCANCODE_F4 = 61, SDL_SCANCODE_F5 = 62, SDL_SCANCODE_F6 = 63, SDL_SCANCODE_F7 = 64, SDL_SCANCODE_F8 = 65, SDL_SCANCODE_F9 = 66, SDL_SCANCODE_F10 = 67, SDL_SCANCODE_F11 = 68, SDL_SCANCODE_F12 = 69, SDL_SCANCODE_PRINTSCREEN = 70, SDL_SCANCODE_SCROLLLOCK = 71, SDL_SCANCODE_PAUSE = 72, SDL_SCANCODE_INSERT = 73, SDL_SCANCODE_HOME = 74, SDL_SCANCODE_PAGEUP = 75, SDL_SCANCODE_DELETE = 76, SDL_SCANCODE_END = 77, SDL_SCANCODE_PAGEDOWN = 78, SDL_SCANCODE_RIGHT = 79, SDL_SCANCODE_LEFT = 80, SDL_SCANCODE_DOWN = 81, SDL_SCANCODE_UP = 82, SDL_SCANCODE_NUMLOCKCLEAR = 83, SDL_SCANCODE_KP_DIVIDE = 84, SDL_SCANCODE_KP_MULTIPLY = 85, SDL_SCANCODE_KP_MINUS = 86, SDL_SCANCODE_KP_PLUS = 87, SDL_SCANCODE_KP_ENTER = 88, SDL_SCANCODE_KP_1 = 89, SDL_SCANCODE_KP_2 = 90, SDL_SCANCODE_KP_3 = 91, SDL_SCANCODE_KP_4 = 92, SDL_SCANCODE_KP_5 = 93, SDL_SCANCODE_KP_6 = 94, SDL_SCANCODE_KP_7 = 95, SDL_SCANCODE_KP_8 = 96, SDL_SCANCODE_KP_9 = 97, SDL_SCANCODE_KP_0 = 98, SDL_SCANCODE_KP_PERIOD = 99, SDL_SCANCODE_NONUSBACKSLASH = 100, SDL_SCANCODE_APPLICATION = 101, SDL_SCANCODE_POWER = 102, SDL_SCANCODE_KP_EQUALS = 103, SDL_SCANCODE_F13 = 104, SDL_SCANCODE_F14 = 105, SDL_SCANCODE_F15 = 106, SDL_SCANCODE_F16 = 107, SDL_SCANCODE_F17 = 108, SDL_SCANCODE_F18 = 109, SDL_SCANCODE_F19 = 110, SDL_SCANCODE_F20 = 111, SDL_SCANCODE_F21 = 112, SDL_SCANCODE_F22 = 113, SDL_SCANCODE_F23 = 114, SDL_SCANCODE_F24 = 115, SDL_SCANCODE_EXECUTE = 116, SDL_SCANCODE_HELP = 117, SDL_SCANCODE_MENU = 118, SDL_SCANCODE_SELECT = 119, SDL_SCANCODE_STOP = 120, SDL_SCANCODE_AGAIN = 121, SDL_SCANCODE_UNDO = 122, SDL_SCANCODE_CUT = 123, SDL_SCANCODE_COPY = 124, SDL_SCANCODE_PASTE = 125, SDL_SCANCODE_FIND = 126, SDL_SCANCODE_MUTE = 127, SDL_SCANCODE_VOLUMEUP = 128, SDL_SCANCODE_VOLUMEDOWN = 129, /* not sure whether there's a reason to enable these */ /* SDL_SCANCODE_LOCKINGCAPSLOCK = 130, */ /* SDL_SCANCODE_LOCKINGNUMLOCK = 131, */ /* SDL_SCANCODE_LOCKINGSCROLLLOCK = 132, */ SDL_SCANCODE_KP_COMMA = 133, SDL_SCANCODE_KP_EQUALSAS400 = 134, SDL_SCANCODE_INTERNATIONAL1 = 135, SDL_SCANCODE_INTERNATIONAL2 = 136, SDL_SCANCODE_INTERNATIONAL3 = 137, SDL_SCANCODE_INTERNATIONAL4 = 138, SDL_SCANCODE_INTERNATIONAL5 = 139, SDL_SCANCODE_INTERNATIONAL6 = 140, SDL_SCANCODE_INTERNATIONAL7 = 141, SDL_SCANCODE_INTERNATIONAL8 = 142, SDL_SCANCODE_INTERNATIONAL9 = 143, SDL_SCANCODE_LANG1 = 144, SDL_SCANCODE_LANG2 = 145, SDL_SCANCODE_LANG3 = 146, SDL_SCANCODE_LANG4 = 147, SDL_SCANCODE_LANG5 = 148, SDL_SCANCODE_LANG6 = 149, SDL_SCANCODE_LANG7 = 150, SDL_SCANCODE_LANG8 = 151, SDL_SCANCODE_LANG9 = 152, SDL_SCANCODE_ALTERASE = 153, SDL_SCANCODE_SYSREQ = 154, SDL_SCANCODE_CANCEL = 155, SDL_SCANCODE_CLEAR = 156, SDL_SCANCODE_PRIOR = 157, SDL_SCANCODE_RETURN2 = 158, SDL_SCANCODE_SEPARATOR = 159, SDL_SCANCODE_OUT = 160, SDL_SCANCODE_OPER = 161, SDL_SCANCODE_CLEARAGAIN = 162, SDL_SCANCODE_CRSEL = 163, SDL_SCANCODE_EXSEL = 164, SDL_SCANCODE_KP_00 = 176, SDL_SCANCODE_KP_000 = 177, SDL_SCANCODE_THOUSANDSSEPARATOR = 178, SDL_SCANCODE_DECIMALSEPARATOR = 179, SDL_SCANCODE_CURRENCYUNIT = 180, SDL_SCANCODE_CURRENCYSUBUNIT = 181, SDL_SCANCODE_KP_LEFTPAREN = 182, SDL_SCANCODE_KP_RIGHTPAREN = 183, SDL_SCANCODE_KP_LEFTBRACE = 184, SDL_SCANCODE_KP_RIGHTBRACE = 185, SDL_SCANCODE_KP_TAB = 186, SDL_SCANCODE_KP_BACKSPACE = 187, SDL_SCANCODE_KP_A = 188, SDL_SCANCODE_KP_B = 189, SDL_SCANCODE_KP_C = 190, SDL_SCANCODE_KP_D = 191, SDL_SCANCODE_KP_E = 192, SDL_SCANCODE_KP_F = 193, SDL_SCANCODE_KP_XOR = 194, SDL_SCANCODE_KP_POWER = 195, SDL_SCANCODE_KP_PERCENT = 196, SDL_SCANCODE_KP_LESS = 197, SDL_SCANCODE_KP_GREATER = 198, SDL_SCANCODE_KP_AMPERSAND = 199, SDL_SCANCODE_KP_DBLAMPERSAND = 200, SDL_SCANCODE_KP_VERTICALBAR = 201, SDL_SCANCODE_KP_DBLVERTICALBAR = 202, SDL_SCANCODE_KP_COLON = 203, SDL_SCANCODE_KP_HASH = 204, SDL_SCANCODE_KP_SPACE = 205, SDL_SCANCODE_KP_AT = 206, SDL_SCANCODE_KP_EXCLAM = 207, SDL_SCANCODE_KP_MEMSTORE = 208, SDL_SCANCODE_KP_MEMRECALL = 209, SDL_SCANCODE_KP_MEMCLEAR = 210, SDL_SCANCODE_KP_MEMADD = 211, SDL_SCANCODE_KP_MEMSUBTRACT = 212, SDL_SCANCODE_KP_MEMMULTIPLY = 213, SDL_SCANCODE_KP_MEMDIVIDE = 214, SDL_SCANCODE_KP_PLUSMINUS = 215, SDL_SCANCODE_KP_CLEAR = 216, SDL_SCANCODE_KP_CLEARENTRY = 217, SDL_SCANCODE_KP_BINARY = 218, SDL_SCANCODE_KP_OCTAL = 219, SDL_SCANCODE_KP_DECIMAL = 220, SDL_SCANCODE_KP_HEXADECIMAL = 221, SDL_SCANCODE_LCTRL = 224, SDL_SCANCODE_LSHIFT = 225, SDL_SCANCODE_LALT = 226, SDL_SCANCODE_LGUI = 227, SDL_SCANCODE_RCTRL = 228, SDL_SCANCODE_RSHIFT = 229, SDL_SCANCODE_RALT = 230, SDL_SCANCODE_RGUI = 231, SDL_SCANCODE_MODE = 257, /* These come from the USB consumer page (0x0C) */ SDL_SCANCODE_AUDIONEXT = 258, SDL_SCANCODE_AUDIOPREV = 259, SDL_SCANCODE_AUDIOSTOP = 260, SDL_SCANCODE_AUDIOPLAY = 261, SDL_SCANCODE_AUDIOMUTE = 262, SDL_SCANCODE_MEDIASELECT = 263, SDL_SCANCODE_WWW = 264, SDL_SCANCODE_MAIL = 265, SDL_SCANCODE_CALCULATOR = 266, SDL_SCANCODE_COMPUTER = 267, SDL_SCANCODE_AC_SEARCH = 268, SDL_SCANCODE_AC_HOME = 269, SDL_SCANCODE_AC_BACK = 270, SDL_SCANCODE_AC_FORWARD = 271, SDL_SCANCODE_AC_STOP = 272, SDL_SCANCODE_AC_REFRESH = 273, SDL_SCANCODE_AC_BOOKMARKS = 274, /* These come from other sources, and are mostly mac related */ SDL_SCANCODE_BRIGHTNESSDOWN = 275, SDL_SCANCODE_BRIGHTNESSUP = 276, SDL_SCANCODE_DISPLAYSWITCH = 277, SDL_SCANCODE_KBDILLUMTOGGLE = 278, SDL_SCANCODE_KBDILLUMDOWN = 279, SDL_SCANCODE_KBDILLUMUP = 280, SDL_SCANCODE_EJECT = 281, SDL_SCANCODE_SLEEP = 282, SDL_SCANCODE_APP1 = 283, SDL_SCANCODE_APP2 = 284, /* These come from the USB consumer page (0x0C) */ SDL_SCANCODE_AUDIOREWIND = 285, SDL_SCANCODE_AUDIOFASTFORWARD = 286, /* This is not a key, simply marks the number of scancodes * so that you know how big to make your arrays. */ SDL_NUM_SCANCODES = 512 } #endregion #region SDL_keycode.h public const int SDLK_SCANCODE_MASK = (1 << 30); public static SDL_Keycode SDL_SCANCODE_TO_KEYCODE(SDL_Scancode X) { return (SDL_Keycode)((int)X | SDLK_SCANCODE_MASK); } public enum SDL_Keycode { SDLK_UNKNOWN = 0, SDLK_RETURN = '\r', SDLK_ESCAPE = 27, // '\033' SDLK_BACKSPACE = '\b', SDLK_TAB = '\t', SDLK_SPACE = ' ', SDLK_EXCLAIM = '!', SDLK_QUOTEDBL = '"', SDLK_HASH = '#', SDLK_PERCENT = '%', SDLK_DOLLAR = '$', SDLK_AMPERSAND = '&', SDLK_QUOTE = '\'', SDLK_LEFTPAREN = '(', SDLK_RIGHTPAREN = ')', SDLK_ASTERISK = '*', SDLK_PLUS = '+', SDLK_COMMA = ',', SDLK_MINUS = '-', SDLK_PERIOD = '.', SDLK_SLASH = '/', SDLK_0 = '0', SDLK_1 = '1', SDLK_2 = '2', SDLK_3 = '3', SDLK_4 = '4', SDLK_5 = '5', SDLK_6 = '6', SDLK_7 = '7', SDLK_8 = '8', SDLK_9 = '9', SDLK_COLON = ':', SDLK_SEMICOLON = ';', SDLK_LESS = '<', SDLK_EQUALS = '=', SDLK_GREATER = '>', SDLK_QUESTION = '?', SDLK_AT = '@', /* Skip uppercase letters */ SDLK_LEFTBRACKET = '[', SDLK_BACKSLASH = '\\', SDLK_RIGHTBRACKET = ']', SDLK_CARET = '^', SDLK_UNDERSCORE = '_', SDLK_BACKQUOTE = '`', SDLK_a = 'a', SDLK_b = 'b', SDLK_c = 'c', SDLK_d = 'd', SDLK_e = 'e', SDLK_f = 'f', SDLK_g = 'g', SDLK_h = 'h', SDLK_i = 'i', SDLK_j = 'j', SDLK_k = 'k', SDLK_l = 'l', SDLK_m = 'm', SDLK_n = 'n', SDLK_o = 'o', SDLK_p = 'p', SDLK_q = 'q', SDLK_r = 'r', SDLK_s = 's', SDLK_t = 't', SDLK_u = 'u', SDLK_v = 'v', SDLK_w = 'w', SDLK_x = 'x', SDLK_y = 'y', SDLK_z = 'z', SDLK_CAPSLOCK = (int)SDL_Scancode.SDL_SCANCODE_CAPSLOCK | SDLK_SCANCODE_MASK, SDLK_F1 = (int)SDL_Scancode.SDL_SCANCODE_F1 | SDLK_SCANCODE_MASK, SDLK_F2 = (int)SDL_Scancode.SDL_SCANCODE_F2 | SDLK_SCANCODE_MASK, SDLK_F3 = (int)SDL_Scancode.SDL_SCANCODE_F3 | SDLK_SCANCODE_MASK, SDLK_F4 = (int)SDL_Scancode.SDL_SCANCODE_F4 | SDLK_SCANCODE_MASK, SDLK_F5 = (int)SDL_Scancode.SDL_SCANCODE_F5 | SDLK_SCANCODE_MASK, SDLK_F6 = (int)SDL_Scancode.SDL_SCANCODE_F6 | SDLK_SCANCODE_MASK, SDLK_F7 = (int)SDL_Scancode.SDL_SCANCODE_F7 | SDLK_SCANCODE_MASK, SDLK_F8 = (int)SDL_Scancode.SDL_SCANCODE_F8 | SDLK_SCANCODE_MASK, SDLK_F9 = (int)SDL_Scancode.SDL_SCANCODE_F9 | SDLK_SCANCODE_MASK, SDLK_F10 = (int)SDL_Scancode.SDL_SCANCODE_F10 | SDLK_SCANCODE_MASK, SDLK_F11 = (int)SDL_Scancode.SDL_SCANCODE_F11 | SDLK_SCANCODE_MASK, SDLK_F12 = (int)SDL_Scancode.SDL_SCANCODE_F12 | SDLK_SCANCODE_MASK, SDLK_PRINTSCREEN = (int)SDL_Scancode.SDL_SCANCODE_PRINTSCREEN | SDLK_SCANCODE_MASK, SDLK_SCROLLLOCK = (int)SDL_Scancode.SDL_SCANCODE_SCROLLLOCK | SDLK_SCANCODE_MASK, SDLK_PAUSE = (int)SDL_Scancode.SDL_SCANCODE_PAUSE | SDLK_SCANCODE_MASK, SDLK_INSERT = (int)SDL_Scancode.SDL_SCANCODE_INSERT | SDLK_SCANCODE_MASK, SDLK_HOME = (int)SDL_Scancode.SDL_SCANCODE_HOME | SDLK_SCANCODE_MASK, SDLK_PAGEUP = (int)SDL_Scancode.SDL_SCANCODE_PAGEUP | SDLK_SCANCODE_MASK, SDLK_DELETE = 127, SDLK_END = (int)SDL_Scancode.SDL_SCANCODE_END | SDLK_SCANCODE_MASK, SDLK_PAGEDOWN = (int)SDL_Scancode.SDL_SCANCODE_PAGEDOWN | SDLK_SCANCODE_MASK, SDLK_RIGHT = (int)SDL_Scancode.SDL_SCANCODE_RIGHT | SDLK_SCANCODE_MASK, SDLK_LEFT = (int)SDL_Scancode.SDL_SCANCODE_LEFT | SDLK_SCANCODE_MASK, SDLK_DOWN = (int)SDL_Scancode.SDL_SCANCODE_DOWN | SDLK_SCANCODE_MASK, SDLK_UP = (int)SDL_Scancode.SDL_SCANCODE_UP | SDLK_SCANCODE_MASK, SDLK_NUMLOCKCLEAR = (int)SDL_Scancode.SDL_SCANCODE_NUMLOCKCLEAR | SDLK_SCANCODE_MASK, SDLK_KP_DIVIDE = (int)SDL_Scancode.SDL_SCANCODE_KP_DIVIDE | SDLK_SCANCODE_MASK, SDLK_KP_MULTIPLY = (int)SDL_Scancode.SDL_SCANCODE_KP_MULTIPLY | SDLK_SCANCODE_MASK, SDLK_KP_MINUS = (int)SDL_Scancode.SDL_SCANCODE_KP_MINUS | SDLK_SCANCODE_MASK, SDLK_KP_PLUS = (int)SDL_Scancode.SDL_SCANCODE_KP_PLUS | SDLK_SCANCODE_MASK, SDLK_KP_ENTER = (int)SDL_Scancode.SDL_SCANCODE_KP_ENTER | SDLK_SCANCODE_MASK, SDLK_KP_1 = (int)SDL_Scancode.SDL_SCANCODE_KP_1 | SDLK_SCANCODE_MASK, SDLK_KP_2 = (int)SDL_Scancode.SDL_SCANCODE_KP_2 | SDLK_SCANCODE_MASK, SDLK_KP_3 = (int)SDL_Scancode.SDL_SCANCODE_KP_3 | SDLK_SCANCODE_MASK, SDLK_KP_4 = (int)SDL_Scancode.SDL_SCANCODE_KP_4 | SDLK_SCANCODE_MASK, SDLK_KP_5 = (int)SDL_Scancode.SDL_SCANCODE_KP_5 | SDLK_SCANCODE_MASK, SDLK_KP_6 = (int)SDL_Scancode.SDL_SCANCODE_KP_6 | SDLK_SCANCODE_MASK, SDLK_KP_7 = (int)SDL_Scancode.SDL_SCANCODE_KP_7 | SDLK_SCANCODE_MASK, SDLK_KP_8 = (int)SDL_Scancode.SDL_SCANCODE_KP_8 | SDLK_SCANCODE_MASK, SDLK_KP_9 = (int)SDL_Scancode.SDL_SCANCODE_KP_9 | SDLK_SCANCODE_MASK, SDLK_KP_0 = (int)SDL_Scancode.SDL_SCANCODE_KP_0 | SDLK_SCANCODE_MASK, SDLK_KP_PERIOD = (int)SDL_Scancode.SDL_SCANCODE_KP_PERIOD | SDLK_SCANCODE_MASK, SDLK_APPLICATION = (int)SDL_Scancode.SDL_SCANCODE_APPLICATION | SDLK_SCANCODE_MASK, SDLK_POWER = (int)SDL_Scancode.SDL_SCANCODE_POWER | SDLK_SCANCODE_MASK, SDLK_KP_EQUALS = (int)SDL_Scancode.SDL_SCANCODE_KP_EQUALS | SDLK_SCANCODE_MASK, SDLK_F13 = (int)SDL_Scancode.SDL_SCANCODE_F13 | SDLK_SCANCODE_MASK, SDLK_F14 = (int)SDL_Scancode.SDL_SCANCODE_F14 | SDLK_SCANCODE_MASK, SDLK_F15 = (int)SDL_Scancode.SDL_SCANCODE_F15 | SDLK_SCANCODE_MASK, SDLK_F16 = (int)SDL_Scancode.SDL_SCANCODE_F16 | SDLK_SCANCODE_MASK, SDLK_F17 = (int)SDL_Scancode.SDL_SCANCODE_F17 | SDLK_SCANCODE_MASK, SDLK_F18 = (int)SDL_Scancode.SDL_SCANCODE_F18 | SDLK_SCANCODE_MASK, SDLK_F19 = (int)SDL_Scancode.SDL_SCANCODE_F19 | SDLK_SCANCODE_MASK, SDLK_F20 = (int)SDL_Scancode.SDL_SCANCODE_F20 | SDLK_SCANCODE_MASK, SDLK_F21 = (int)SDL_Scancode.SDL_SCANCODE_F21 | SDLK_SCANCODE_MASK, SDLK_F22 = (int)SDL_Scancode.SDL_SCANCODE_F22 | SDLK_SCANCODE_MASK, SDLK_F23 = (int)SDL_Scancode.SDL_SCANCODE_F23 | SDLK_SCANCODE_MASK, SDLK_F24 = (int)SDL_Scancode.SDL_SCANCODE_F24 | SDLK_SCANCODE_MASK, SDLK_EXECUTE = (int)SDL_Scancode.SDL_SCANCODE_EXECUTE | SDLK_SCANCODE_MASK, SDLK_HELP = (int)SDL_Scancode.SDL_SCANCODE_HELP | SDLK_SCANCODE_MASK, SDLK_MENU = (int)SDL_Scancode.SDL_SCANCODE_MENU | SDLK_SCANCODE_MASK, SDLK_SELECT = (int)SDL_Scancode.SDL_SCANCODE_SELECT | SDLK_SCANCODE_MASK, SDLK_STOP = (int)SDL_Scancode.SDL_SCANCODE_STOP | SDLK_SCANCODE_MASK, SDLK_AGAIN = (int)SDL_Scancode.SDL_SCANCODE_AGAIN | SDLK_SCANCODE_MASK, SDLK_UNDO = (int)SDL_Scancode.SDL_SCANCODE_UNDO | SDLK_SCANCODE_MASK, SDLK_CUT = (int)SDL_Scancode.SDL_SCANCODE_CUT | SDLK_SCANCODE_MASK, SDLK_COPY = (int)SDL_Scancode.SDL_SCANCODE_COPY | SDLK_SCANCODE_MASK, SDLK_PASTE = (int)SDL_Scancode.SDL_SCANCODE_PASTE | SDLK_SCANCODE_MASK, SDLK_FIND = (int)SDL_Scancode.SDL_SCANCODE_FIND | SDLK_SCANCODE_MASK, SDLK_MUTE = (int)SDL_Scancode.SDL_SCANCODE_MUTE | SDLK_SCANCODE_MASK, SDLK_VOLUMEUP = (int)SDL_Scancode.SDL_SCANCODE_VOLUMEUP | SDLK_SCANCODE_MASK, SDLK_VOLUMEDOWN = (int)SDL_Scancode.SDL_SCANCODE_VOLUMEDOWN | SDLK_SCANCODE_MASK, SDLK_KP_COMMA = (int)SDL_Scancode.SDL_SCANCODE_KP_COMMA | SDLK_SCANCODE_MASK, SDLK_KP_EQUALSAS400 = (int)SDL_Scancode.SDL_SCANCODE_KP_EQUALSAS400 | SDLK_SCANCODE_MASK, SDLK_ALTERASE = (int)SDL_Scancode.SDL_SCANCODE_ALTERASE | SDLK_SCANCODE_MASK, SDLK_SYSREQ = (int)SDL_Scancode.SDL_SCANCODE_SYSREQ | SDLK_SCANCODE_MASK, SDLK_CANCEL = (int)SDL_Scancode.SDL_SCANCODE_CANCEL | SDLK_SCANCODE_MASK, SDLK_CLEAR = (int)SDL_Scancode.SDL_SCANCODE_CLEAR | SDLK_SCANCODE_MASK, SDLK_PRIOR = (int)SDL_Scancode.SDL_SCANCODE_PRIOR | SDLK_SCANCODE_MASK, SDLK_RETURN2 = (int)SDL_Scancode.SDL_SCANCODE_RETURN2 | SDLK_SCANCODE_MASK, SDLK_SEPARATOR = (int)SDL_Scancode.SDL_SCANCODE_SEPARATOR | SDLK_SCANCODE_MASK, SDLK_OUT = (int)SDL_Scancode.SDL_SCANCODE_OUT | SDLK_SCANCODE_MASK, SDLK_OPER = (int)SDL_Scancode.SDL_SCANCODE_OPER | SDLK_SCANCODE_MASK, SDLK_CLEARAGAIN = (int)SDL_Scancode.SDL_SCANCODE_CLEARAGAIN | SDLK_SCANCODE_MASK, SDLK_CRSEL = (int)SDL_Scancode.SDL_SCANCODE_CRSEL | SDLK_SCANCODE_MASK, SDLK_EXSEL = (int)SDL_Scancode.SDL_SCANCODE_EXSEL | SDLK_SCANCODE_MASK, SDLK_KP_00 = (int)SDL_Scancode.SDL_SCANCODE_KP_00 | SDLK_SCANCODE_MASK, SDLK_KP_000 = (int)SDL_Scancode.SDL_SCANCODE_KP_000 | SDLK_SCANCODE_MASK, SDLK_THOUSANDSSEPARATOR = (int)SDL_Scancode.SDL_SCANCODE_THOUSANDSSEPARATOR | SDLK_SCANCODE_MASK, SDLK_DECIMALSEPARATOR = (int)SDL_Scancode.SDL_SCANCODE_DECIMALSEPARATOR | SDLK_SCANCODE_MASK, SDLK_CURRENCYUNIT = (int)SDL_Scancode.SDL_SCANCODE_CURRENCYUNIT | SDLK_SCANCODE_MASK, SDLK_CURRENCYSUBUNIT = (int)SDL_Scancode.SDL_SCANCODE_CURRENCYSUBUNIT | SDLK_SCANCODE_MASK, SDLK_KP_LEFTPAREN = (int)SDL_Scancode.SDL_SCANCODE_KP_LEFTPAREN | SDLK_SCANCODE_MASK, SDLK_KP_RIGHTPAREN = (int)SDL_Scancode.SDL_SCANCODE_KP_RIGHTPAREN | SDLK_SCANCODE_MASK, SDLK_KP_LEFTBRACE = (int)SDL_Scancode.SDL_SCANCODE_KP_LEFTBRACE | SDLK_SCANCODE_MASK, SDLK_KP_RIGHTBRACE = (int)SDL_Scancode.SDL_SCANCODE_KP_RIGHTBRACE | SDLK_SCANCODE_MASK, SDLK_KP_TAB = (int)SDL_Scancode.SDL_SCANCODE_KP_TAB | SDLK_SCANCODE_MASK, SDLK_KP_BACKSPACE = (int)SDL_Scancode.SDL_SCANCODE_KP_BACKSPACE | SDLK_SCANCODE_MASK, SDLK_KP_A = (int)SDL_Scancode.SDL_SCANCODE_KP_A | SDLK_SCANCODE_MASK, SDLK_KP_B = (int)SDL_Scancode.SDL_SCANCODE_KP_B | SDLK_SCANCODE_MASK, SDLK_KP_C = (int)SDL_Scancode.SDL_SCANCODE_KP_C | SDLK_SCANCODE_MASK, SDLK_KP_D = (int)SDL_Scancode.SDL_SCANCODE_KP_D | SDLK_SCANCODE_MASK, SDLK_KP_E = (int)SDL_Scancode.SDL_SCANCODE_KP_E | SDLK_SCANCODE_MASK, SDLK_KP_F = (int)SDL_Scancode.SDL_SCANCODE_KP_F | SDLK_SCANCODE_MASK, SDLK_KP_XOR = (int)SDL_Scancode.SDL_SCANCODE_KP_XOR | SDLK_SCANCODE_MASK, SDLK_KP_POWER = (int)SDL_Scancode.SDL_SCANCODE_KP_POWER | SDLK_SCANCODE_MASK, SDLK_KP_PERCENT = (int)SDL_Scancode.SDL_SCANCODE_KP_PERCENT | SDLK_SCANCODE_MASK, SDLK_KP_LESS = (int)SDL_Scancode.SDL_SCANCODE_KP_LESS | SDLK_SCANCODE_MASK, SDLK_KP_GREATER = (int)SDL_Scancode.SDL_SCANCODE_KP_GREATER | SDLK_SCANCODE_MASK, SDLK_KP_AMPERSAND = (int)SDL_Scancode.SDL_SCANCODE_KP_AMPERSAND | SDLK_SCANCODE_MASK, SDLK_KP_DBLAMPERSAND = (int)SDL_Scancode.SDL_SCANCODE_KP_DBLAMPERSAND | SDLK_SCANCODE_MASK, SDLK_KP_VERTICALBAR = (int)SDL_Scancode.SDL_SCANCODE_KP_VERTICALBAR | SDLK_SCANCODE_MASK, SDLK_KP_DBLVERTICALBAR = (int)SDL_Scancode.SDL_SCANCODE_KP_DBLVERTICALBAR | SDLK_SCANCODE_MASK, SDLK_KP_COLON = (int)SDL_Scancode.SDL_SCANCODE_KP_COLON | SDLK_SCANCODE_MASK, SDLK_KP_HASH = (int)SDL_Scancode.SDL_SCANCODE_KP_HASH | SDLK_SCANCODE_MASK, SDLK_KP_SPACE = (int)SDL_Scancode.SDL_SCANCODE_KP_SPACE | SDLK_SCANCODE_MASK, SDLK_KP_AT = (int)SDL_Scancode.SDL_SCANCODE_KP_AT | SDLK_SCANCODE_MASK, SDLK_KP_EXCLAM = (int)SDL_Scancode.SDL_SCANCODE_KP_EXCLAM | SDLK_SCANCODE_MASK, SDLK_KP_MEMSTORE = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMSTORE | SDLK_SCANCODE_MASK, SDLK_KP_MEMRECALL = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMRECALL | SDLK_SCANCODE_MASK, SDLK_KP_MEMCLEAR = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMCLEAR | SDLK_SCANCODE_MASK, SDLK_KP_MEMADD = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMADD | SDLK_SCANCODE_MASK, SDLK_KP_MEMSUBTRACT = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMSUBTRACT | SDLK_SCANCODE_MASK, SDLK_KP_MEMMULTIPLY = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMMULTIPLY | SDLK_SCANCODE_MASK, SDLK_KP_MEMDIVIDE = (int)SDL_Scancode.SDL_SCANCODE_KP_MEMDIVIDE | SDLK_SCANCODE_MASK, SDLK_KP_PLUSMINUS = (int)SDL_Scancode.SDL_SCANCODE_KP_PLUSMINUS | SDLK_SCANCODE_MASK, SDLK_KP_CLEAR = (int)SDL_Scancode.SDL_SCANCODE_KP_CLEAR | SDLK_SCANCODE_MASK, SDLK_KP_CLEARENTRY = (int)SDL_Scancode.SDL_SCANCODE_KP_CLEARENTRY | SDLK_SCANCODE_MASK, SDLK_KP_BINARY = (int)SDL_Scancode.SDL_SCANCODE_KP_BINARY | SDLK_SCANCODE_MASK, SDLK_KP_OCTAL = (int)SDL_Scancode.SDL_SCANCODE_KP_OCTAL | SDLK_SCANCODE_MASK, SDLK_KP_DECIMAL = (int)SDL_Scancode.SDL_SCANCODE_KP_DECIMAL | SDLK_SCANCODE_MASK, SDLK_KP_HEXADECIMAL = (int)SDL_Scancode.SDL_SCANCODE_KP_HEXADECIMAL | SDLK_SCANCODE_MASK, SDLK_LCTRL = (int)SDL_Scancode.SDL_SCANCODE_LCTRL | SDLK_SCANCODE_MASK, SDLK_LSHIFT = (int)SDL_Scancode.SDL_SCANCODE_LSHIFT | SDLK_SCANCODE_MASK, SDLK_LALT = (int)SDL_Scancode.SDL_SCANCODE_LALT | SDLK_SCANCODE_MASK, SDLK_LGUI = (int)SDL_Scancode.SDL_SCANCODE_LGUI | SDLK_SCANCODE_MASK, SDLK_RCTRL = (int)SDL_Scancode.SDL_SCANCODE_RCTRL | SDLK_SCANCODE_MASK, SDLK_RSHIFT = (int)SDL_Scancode.SDL_SCANCODE_RSHIFT | SDLK_SCANCODE_MASK, SDLK_RALT = (int)SDL_Scancode.SDL_SCANCODE_RALT | SDLK_SCANCODE_MASK, SDLK_RGUI = (int)SDL_Scancode.SDL_SCANCODE_RGUI | SDLK_SCANCODE_MASK, SDLK_MODE = (int)SDL_Scancode.SDL_SCANCODE_MODE | SDLK_SCANCODE_MASK, SDLK_AUDIONEXT = (int)SDL_Scancode.SDL_SCANCODE_AUDIONEXT | SDLK_SCANCODE_MASK, SDLK_AUDIOPREV = (int)SDL_Scancode.SDL_SCANCODE_AUDIOPREV | SDLK_SCANCODE_MASK, SDLK_AUDIOSTOP = (int)SDL_Scancode.SDL_SCANCODE_AUDIOSTOP | SDLK_SCANCODE_MASK, SDLK_AUDIOPLAY = (int)SDL_Scancode.SDL_SCANCODE_AUDIOPLAY | SDLK_SCANCODE_MASK, SDLK_AUDIOMUTE = (int)SDL_Scancode.SDL_SCANCODE_AUDIOMUTE | SDLK_SCANCODE_MASK, SDLK_MEDIASELECT = (int)SDL_Scancode.SDL_SCANCODE_MEDIASELECT | SDLK_SCANCODE_MASK, SDLK_WWW = (int)SDL_Scancode.SDL_SCANCODE_WWW | SDLK_SCANCODE_MASK, SDLK_MAIL = (int)SDL_Scancode.SDL_SCANCODE_MAIL | SDLK_SCANCODE_MASK, SDLK_CALCULATOR = (int)SDL_Scancode.SDL_SCANCODE_CALCULATOR | SDLK_SCANCODE_MASK, SDLK_COMPUTER = (int)SDL_Scancode.SDL_SCANCODE_COMPUTER | SDLK_SCANCODE_MASK, SDLK_AC_SEARCH = (int)SDL_Scancode.SDL_SCANCODE_AC_SEARCH | SDLK_SCANCODE_MASK, SDLK_AC_HOME = (int)SDL_Scancode.SDL_SCANCODE_AC_HOME | SDLK_SCANCODE_MASK, SDLK_AC_BACK = (int)SDL_Scancode.SDL_SCANCODE_AC_BACK | SDLK_SCANCODE_MASK, SDLK_AC_FORWARD = (int)SDL_Scancode.SDL_SCANCODE_AC_FORWARD | SDLK_SCANCODE_MASK, SDLK_AC_STOP = (int)SDL_Scancode.SDL_SCANCODE_AC_STOP | SDLK_SCANCODE_MASK, SDLK_AC_REFRESH = (int)SDL_Scancode.SDL_SCANCODE_AC_REFRESH | SDLK_SCANCODE_MASK, SDLK_AC_BOOKMARKS = (int)SDL_Scancode.SDL_SCANCODE_AC_BOOKMARKS | SDLK_SCANCODE_MASK, SDLK_BRIGHTNESSDOWN = (int)SDL_Scancode.SDL_SCANCODE_BRIGHTNESSDOWN | SDLK_SCANCODE_MASK, SDLK_BRIGHTNESSUP = (int)SDL_Scancode.SDL_SCANCODE_BRIGHTNESSUP | SDLK_SCANCODE_MASK, SDLK_DISPLAYSWITCH = (int)SDL_Scancode.SDL_SCANCODE_DISPLAYSWITCH | SDLK_SCANCODE_MASK, SDLK_KBDILLUMTOGGLE = (int)SDL_Scancode.SDL_SCANCODE_KBDILLUMTOGGLE | SDLK_SCANCODE_MASK, SDLK_KBDILLUMDOWN = (int)SDL_Scancode.SDL_SCANCODE_KBDILLUMDOWN | SDLK_SCANCODE_MASK, SDLK_KBDILLUMUP = (int)SDL_Scancode.SDL_SCANCODE_KBDILLUMUP | SDLK_SCANCODE_MASK, SDLK_EJECT = (int)SDL_Scancode.SDL_SCANCODE_EJECT | SDLK_SCANCODE_MASK, SDLK_SLEEP = (int)SDL_Scancode.SDL_SCANCODE_SLEEP | SDLK_SCANCODE_MASK, SDLK_APP1 = (int)SDL_Scancode.SDL_SCANCODE_APP1 | SDLK_SCANCODE_MASK, SDLK_APP2 = (int)SDL_Scancode.SDL_SCANCODE_APP2 | SDLK_SCANCODE_MASK, SDLK_AUDIOREWIND = (int)SDL_Scancode.SDL_SCANCODE_AUDIOREWIND | SDLK_SCANCODE_MASK, SDLK_AUDIOFASTFORWARD = (int)SDL_Scancode.SDL_SCANCODE_AUDIOFASTFORWARD | SDLK_SCANCODE_MASK } /* Key modifiers (bitfield) */ [Flags] public enum SDL_Keymod : ushort { KMOD_NONE = 0x0000, KMOD_LSHIFT = 0x0001, KMOD_RSHIFT = 0x0002, KMOD_LCTRL = 0x0040, KMOD_RCTRL = 0x0080, KMOD_LALT = 0x0100, KMOD_RALT = 0x0200, KMOD_LGUI = 0x0400, KMOD_RGUI = 0x0800, KMOD_NUM = 0x1000, KMOD_CAPS = 0x2000, KMOD_MODE = 0x4000, KMOD_SCROLL = 0x8000, /* These are defines in the SDL headers */ KMOD_CTRL = (KMOD_LCTRL | KMOD_RCTRL), KMOD_SHIFT = (KMOD_LSHIFT | KMOD_RSHIFT), KMOD_ALT = (KMOD_LALT | KMOD_RALT), KMOD_GUI = (KMOD_LGUI | KMOD_RGUI), KMOD_RESERVED = KMOD_SCROLL } #endregion #region SDL_keyboard.h [StructLayout(LayoutKind.Sequential)] public struct SDL_Keysym { public SDL_Scancode scancode; public SDL_Keycode sym; public SDL_Keymod mod; /* UInt16 */ public UInt32 unicode; /* Deprecated */ } /* Get the window which has kbd focus */ /* Return type is an SDL_Window pointer */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetKeyboardFocus(); /* Get a snapshot of the keyboard state. */ /* Return value is a pointer to a UInt8 array */ /* Numkeys returns the size of the array if non-null */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetKeyboardState(out int numkeys); /* Get the current key modifier state for the keyboard. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_Keymod SDL_GetModState(); /* Set the current key modifier state */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetModState(SDL_Keymod modstate); /* Get the key code corresponding to the given scancode * with the current keyboard layout. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_Keycode SDL_GetKeyFromScancode(SDL_Scancode scancode); /* Get the scancode for the given keycode */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_Scancode SDL_GetScancodeFromKey(SDL_Keycode key); /* Wrapper for SDL_GetScancodeName */ [DllImport(nativeLibName, EntryPoint = "SDL_GetScancodeName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetScancodeName(SDL_Scancode scancode); public static string SDL_GetScancodeName(SDL_Scancode scancode) { return UTF8_ToManaged( INTERNAL_SDL_GetScancodeName(scancode) ); } /* Get a scancode from a human-readable name */ [DllImport(nativeLibName, EntryPoint = "SDL_GetScancodeFromName", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe SDL_Scancode INTERNAL_SDL_GetScancodeFromName( byte* name ); public static unsafe SDL_Scancode SDL_GetScancodeFromName(string name) { int utf8NameBufSize = Utf8Size(name); byte* utf8Name = stackalloc byte[utf8NameBufSize]; return INTERNAL_SDL_GetScancodeFromName( Utf8Encode(name, utf8Name, utf8NameBufSize) ); } /* Wrapper for SDL_GetKeyName */ [DllImport(nativeLibName, EntryPoint = "SDL_GetKeyName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetKeyName(SDL_Keycode key); public static string SDL_GetKeyName(SDL_Keycode key) { return UTF8_ToManaged(INTERNAL_SDL_GetKeyName(key)); } /* Get a key code from a human-readable name */ [DllImport(nativeLibName, EntryPoint = "SDL_GetKeyFromName", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe SDL_Keycode INTERNAL_SDL_GetKeyFromName( byte* name ); public static unsafe SDL_Keycode SDL_GetKeyFromName(string name) { int utf8NameBufSize = Utf8Size(name); byte* utf8Name = stackalloc byte[utf8NameBufSize]; return INTERNAL_SDL_GetKeyFromName( Utf8Encode(name, utf8Name, utf8NameBufSize) ); } /* Start accepting Unicode text input events, show keyboard */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_StartTextInput(); /* Check if unicode input events are enabled */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsTextInputActive(); /* Stop receiving any text input events, hide onscreen kbd */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_StopTextInput(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_ClearComposition(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsTextInputShown(); /* Set the rectangle used for text input, hint for IME */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetTextInputRect(ref SDL_Rect rect); /* Does the platform support an on-screen keyboard? */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasScreenKeyboardSupport(); /* Is the on-screen keyboard shown for a given window? */ /* window is an SDL_Window pointer */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsScreenKeyboardShown(IntPtr window); #endregion #region SDL_mouse.c /* Note: SDL_Cursor is a typedef normally. We'll treat it as * an IntPtr, because C# doesn't do typedefs. Yay! */ /* System cursor types */ public enum SDL_SystemCursor { SDL_SYSTEM_CURSOR_ARROW, // Arrow SDL_SYSTEM_CURSOR_IBEAM, // I-beam SDL_SYSTEM_CURSOR_WAIT, // Wait SDL_SYSTEM_CURSOR_CROSSHAIR, // Crosshair SDL_SYSTEM_CURSOR_WAITARROW, // Small wait cursor (or Wait if not available) SDL_SYSTEM_CURSOR_SIZENWSE, // Double arrow pointing northwest and southeast SDL_SYSTEM_CURSOR_SIZENESW, // Double arrow pointing northeast and southwest SDL_SYSTEM_CURSOR_SIZEWE, // Double arrow pointing west and east SDL_SYSTEM_CURSOR_SIZENS, // Double arrow pointing north and south SDL_SYSTEM_CURSOR_SIZEALL, // Four pointed arrow pointing north, south, east, and west SDL_SYSTEM_CURSOR_NO, // Slashed circle or crossbones SDL_SYSTEM_CURSOR_HAND, // Hand SDL_NUM_SYSTEM_CURSORS } /* Get the window which currently has mouse focus */ /* Return value is an SDL_Window pointer */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetMouseFocus(); /* Get the current state of the mouse */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetMouseState(out int x, out int y); /* Get the current state of the mouse */ /* This overload allows for passing NULL to x */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetMouseState(IntPtr x, out int y); /* Get the current state of the mouse */ /* This overload allows for passing NULL to y */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetMouseState(out int x, IntPtr y); /* Get the current state of the mouse */ /* This overload allows for passing NULL to both x and y */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetMouseState(IntPtr x, IntPtr y); /* Get the current state of the mouse, in relation to the desktop. * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetGlobalMouseState(out int x, out int y); /* Get the current state of the mouse, in relation to the desktop. * Only available in 2.0.4 or higher. * This overload allows for passing NULL to x. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetGlobalMouseState(IntPtr x, out int y); /* Get the current state of the mouse, in relation to the desktop. * Only available in 2.0.4 or higher. * This overload allows for passing NULL to y. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetGlobalMouseState(out int x, IntPtr y); /* Get the current state of the mouse, in relation to the desktop. * Only available in 2.0.4 or higher. * This overload allows for passing NULL to both x and y */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetGlobalMouseState(IntPtr x, IntPtr y); /* Get the mouse state with relative coords*/ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetRelativeMouseState(out int x, out int y); /* Set the mouse cursor's position (within a window) */ /* window is an SDL_Window pointer */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_WarpMouseInWindow(IntPtr window, int x, int y); /* Set the mouse cursor's position in global screen space. * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_WarpMouseGlobal(int x, int y); /* Enable/Disable relative mouse mode (grabs mouse, rel coords) */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SetRelativeMouseMode(SDL_bool enabled); /* Capture the mouse, to track input outside an SDL window. * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_CaptureMouse(SDL_bool enabled); /* Query if the relative mouse mode is enabled */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GetRelativeMouseMode(); /* Create a cursor from bitmap data (amd mask) in MSB format. * data and mask are byte arrays, and w must be a multiple of 8. * return value is an SDL_Cursor pointer. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateCursor( IntPtr data, IntPtr mask, int w, int h, int hot_x, int hot_y ); /* Create a cursor from an SDL_Surface. * IntPtr refers to an SDL_Cursor*, surface to an SDL_Surface* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateColorCursor( IntPtr surface, int hot_x, int hot_y ); /* Create a cursor from a system cursor id. * return value is an SDL_Cursor pointer */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_CreateSystemCursor(SDL_SystemCursor id); /* Set the active cursor. * cursor is an SDL_Cursor pointer */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetCursor(IntPtr cursor); /* Return the active cursor * return value is an SDL_Cursor pointer */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetCursor(); /* Frees a cursor created with one of the CreateCursor functions. * cursor in an SDL_Cursor pointer */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FreeCursor(IntPtr cursor); /* Toggle whether or not the cursor is shown */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_ShowCursor(int toggle); public static uint SDL_BUTTON(uint X) { // If only there were a better way of doing this in C# return (uint) (1 << ((int) X - 1)); } public const uint SDL_BUTTON_LEFT = 1; public const uint SDL_BUTTON_MIDDLE = 2; public const uint SDL_BUTTON_RIGHT = 3; public const uint SDL_BUTTON_X1 = 4; public const uint SDL_BUTTON_X2 = 5; public static readonly UInt32 SDL_BUTTON_LMASK = SDL_BUTTON(SDL_BUTTON_LEFT); public static readonly UInt32 SDL_BUTTON_MMASK = SDL_BUTTON(SDL_BUTTON_MIDDLE); public static readonly UInt32 SDL_BUTTON_RMASK = SDL_BUTTON(SDL_BUTTON_RIGHT); public static readonly UInt32 SDL_BUTTON_X1MASK = SDL_BUTTON(SDL_BUTTON_X1); public static readonly UInt32 SDL_BUTTON_X2MASK = SDL_BUTTON(SDL_BUTTON_X2); #endregion #region SDL_touch.h public const uint SDL_TOUCH_MOUSEID = uint.MaxValue; public struct SDL_Finger { public long id; // SDL_FingerID public float x; public float y; public float pressure; } /* Only available in 2.0.10 or higher. */ public enum SDL_TouchDeviceType { SDL_TOUCH_DEVICE_INVALID = -1, SDL_TOUCH_DEVICE_DIRECT, /* touch screen with window-relative coordinates */ SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE, /* trackpad with absolute device coordinates */ SDL_TOUCH_DEVICE_INDIRECT_RELATIVE /* trackpad with screen cursor-relative coordinates */ } /** * \brief Get the number of registered touch devices. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetNumTouchDevices(); /** * \brief Get the touch ID with the given index, or 0 if the index is invalid. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern long SDL_GetTouchDevice(int index); /** * \brief Get the number of active fingers for a given touch device. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetNumTouchFingers(long touchID); /** * \brief Get the finger object of the given touch, with the given index. * Returns pointer to SDL_Finger. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetTouchFinger(long touchID, int index); /* Only available in 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_TouchDeviceType SDL_GetTouchDeviceType(Int64 touchID); /* Only available in 2.0.22 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_GetTouchName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetTouchName(int index); /* Only available in 2.0.22 or higher. */ public static string SDL_GetTouchName(int index) { return UTF8_ToManaged(INTERNAL_SDL_GetTouchName(index)); } #endregion #region SDL_joystick.h public const byte SDL_HAT_CENTERED = 0x00; public const byte SDL_HAT_UP = 0x01; public const byte SDL_HAT_RIGHT = 0x02; public const byte SDL_HAT_DOWN = 0x04; public const byte SDL_HAT_LEFT = 0x08; public const byte SDL_HAT_RIGHTUP = SDL_HAT_RIGHT | SDL_HAT_UP; public const byte SDL_HAT_RIGHTDOWN = SDL_HAT_RIGHT | SDL_HAT_DOWN; public const byte SDL_HAT_LEFTUP = SDL_HAT_LEFT | SDL_HAT_UP; public const byte SDL_HAT_LEFTDOWN = SDL_HAT_LEFT | SDL_HAT_DOWN; public enum SDL_JoystickPowerLevel { SDL_JOYSTICK_POWER_UNKNOWN = -1, SDL_JOYSTICK_POWER_EMPTY, SDL_JOYSTICK_POWER_LOW, SDL_JOYSTICK_POWER_MEDIUM, SDL_JOYSTICK_POWER_FULL, SDL_JOYSTICK_POWER_WIRED, SDL_JOYSTICK_POWER_MAX } public enum SDL_JoystickType { SDL_JOYSTICK_TYPE_UNKNOWN, SDL_JOYSTICK_TYPE_GAMECONTROLLER, SDL_JOYSTICK_TYPE_WHEEL, SDL_JOYSTICK_TYPE_ARCADE_STICK, SDL_JOYSTICK_TYPE_FLIGHT_STICK, SDL_JOYSTICK_TYPE_DANCE_PAD, SDL_JOYSTICK_TYPE_GUITAR, SDL_JOYSTICK_TYPE_DRUM_KIT, SDL_JOYSTICK_TYPE_ARCADE_PAD } /* Only available in 2.0.14 or higher. */ public const float SDL_IPHONE_MAX_GFORCE = 5.0f; /* joystick refers to an SDL_Joystick*. * Only available in 2.0.9 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickRumble( IntPtr joystick, UInt16 low_frequency_rumble, UInt16 high_frequency_rumble, UInt32 duration_ms ); /* joystick refers to an SDL_Joystick*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickRumbleTriggers( IntPtr joystick, UInt16 left_rumble, UInt16 right_rumble, UInt32 duration_ms ); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_JoystickClose(IntPtr joystick); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickEventState(int state); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern short SDL_JoystickGetAxis( IntPtr joystick, int axis ); /* joystick refers to an SDL_Joystick*. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_JoystickGetAxisInitialState( IntPtr joystick, int axis, out short state ); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickGetBall( IntPtr joystick, int ball, out int dx, out int dy ); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern byte SDL_JoystickGetButton( IntPtr joystick, int button ); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern byte SDL_JoystickGetHat( IntPtr joystick, int hat ); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, EntryPoint = "SDL_JoystickName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_JoystickName( IntPtr joystick ); public static string SDL_JoystickName(IntPtr joystick) { return UTF8_ToManaged( INTERNAL_SDL_JoystickName(joystick) ); } [DllImport(nativeLibName, EntryPoint = "SDL_JoystickPath", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_JoystickPath( IntPtr joystick ); public static string SDL_JoystickPath(IntPtr joystick) { return UTF8_ToManaged( INTERNAL_SDL_JoystickPath(joystick) ); } [DllImport(nativeLibName, EntryPoint = "SDL_JoystickNameForIndex", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_JoystickNameForIndex( int device_index ); public static string SDL_JoystickNameForIndex(int device_index) { return UTF8_ToManaged( INTERNAL_SDL_JoystickNameForIndex(device_index) ); } /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickNumAxes(IntPtr joystick); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickNumBalls(IntPtr joystick); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickNumButtons(IntPtr joystick); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickNumHats(IntPtr joystick); /* IntPtr refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_JoystickOpen(int device_index); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_JoystickUpdate(); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_NumJoysticks(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern Guid SDL_JoystickGetDeviceGUID( int device_index ); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern Guid SDL_JoystickGetGUID( IntPtr joystick ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_JoystickGetGUIDString( Guid guid, byte[] pszGUID, int cbGUID ); [DllImport(nativeLibName, EntryPoint = "SDL_JoystickGetGUIDFromString", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe Guid INTERNAL_SDL_JoystickGetGUIDFromString( byte* pchGUID ); public static unsafe Guid SDL_JoystickGetGUIDFromString(string pchGuid) { int utf8PchGuidBufSize = Utf8Size(pchGuid); byte* utf8PchGuid = stackalloc byte[utf8PchGuidBufSize]; return INTERNAL_SDL_JoystickGetGUIDFromString( Utf8Encode(pchGuid, utf8PchGuid, utf8PchGuidBufSize) ); } /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern ushort SDL_JoystickGetDeviceVendor(int device_index); /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern ushort SDL_JoystickGetDeviceProduct(int device_index); /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern ushort SDL_JoystickGetDeviceProductVersion(int device_index); /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_JoystickType SDL_JoystickGetDeviceType(int device_index); /* int refers to an SDL_JoystickID. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickGetDeviceInstanceID(int device_index); /* joystick refers to an SDL_Joystick*. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern ushort SDL_JoystickGetVendor(IntPtr joystick); /* joystick refers to an SDL_Joystick*. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern ushort SDL_JoystickGetProduct(IntPtr joystick); /* joystick refers to an SDL_Joystick*. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern ushort SDL_JoystickGetProductVersion(IntPtr joystick); /* joystick refers to an SDL_Joystick*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_JoystickGetSerial", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_JoystickGetSerial( IntPtr joystick ); public static string SDL_JoystickGetSerial( IntPtr joystick ) { return UTF8_ToManaged( INTERNAL_SDL_JoystickGetSerial(joystick) ); } /* joystick refers to an SDL_Joystick*. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_JoystickType SDL_JoystickGetType(IntPtr joystick); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_JoystickGetAttached(IntPtr joystick); /* int refers to an SDL_JoystickID, joystick to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickInstanceID(IntPtr joystick); /* joystick refers to an SDL_Joystick*. * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel( IntPtr joystick ); /* int refers to an SDL_JoystickID, IntPtr to an SDL_Joystick*. * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_JoystickFromInstanceID(int instance_id); /* Only available in 2.0.7 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_LockJoysticks(); /* Only available in 2.0.7 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_UnlockJoysticks(); /* IntPtr refers to an SDL_Joystick*. * Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_JoystickFromPlayerIndex(int player_index); /* IntPtr refers to an SDL_Joystick*. * Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_JoystickSetPlayerIndex( IntPtr joystick, int player_index ); /* Int32 refers to an SDL_JoystickType. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickAttachVirtual( Int32 type, int naxes, int nbuttons, int nhats ); /* Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickDetachVirtual(int device_index); /* Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_JoystickIsVirtual(int device_index); /* IntPtr refers to an SDL_Joystick*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickSetVirtualAxis( IntPtr joystick, int axis, Int16 value ); /* IntPtr refers to an SDL_Joystick*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickSetVirtualButton( IntPtr joystick, int button, byte value ); /* IntPtr refers to an SDL_Joystick*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickSetVirtualHat( IntPtr joystick, int hat, byte value ); /* IntPtr refers to an SDL_Joystick*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_JoystickHasLED(IntPtr joystick); /* IntPtr refers to an SDL_Joystick*. * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_JoystickHasRumble(IntPtr joystick); /* IntPtr refers to an SDL_Joystick*. * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_JoystickHasRumbleTriggers(IntPtr joystick); /* IntPtr refers to an SDL_Joystick*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickSetLED( IntPtr joystick, byte red, byte green, byte blue ); /* joystick refers to an SDL_Joystick*. * data refers to a const void*. * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickSendEffect( IntPtr joystick, IntPtr data, int size ); #endregion #region SDL_gamecontroller.h public enum SDL_GameControllerBindType { SDL_CONTROLLER_BINDTYPE_NONE, SDL_CONTROLLER_BINDTYPE_BUTTON, SDL_CONTROLLER_BINDTYPE_AXIS, SDL_CONTROLLER_BINDTYPE_HAT } public enum SDL_GameControllerAxis { SDL_CONTROLLER_AXIS_INVALID = -1, SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY, SDL_CONTROLLER_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTY, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_CONTROLLER_AXIS_MAX } public enum SDL_GameControllerButton { SDL_CONTROLLER_BUTTON_INVALID = -1, SDL_CONTROLLER_BUTTON_A, SDL_CONTROLLER_BUTTON_B, SDL_CONTROLLER_BUTTON_X, SDL_CONTROLLER_BUTTON_Y, SDL_CONTROLLER_BUTTON_BACK, SDL_CONTROLLER_BUTTON_GUIDE, SDL_CONTROLLER_BUTTON_START, SDL_CONTROLLER_BUTTON_LEFTSTICK, SDL_CONTROLLER_BUTTON_RIGHTSTICK, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, SDL_CONTROLLER_BUTTON_DPAD_UP, SDL_CONTROLLER_BUTTON_DPAD_DOWN, SDL_CONTROLLER_BUTTON_DPAD_LEFT, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, SDL_CONTROLLER_BUTTON_MISC1, SDL_CONTROLLER_BUTTON_PADDLE1, SDL_CONTROLLER_BUTTON_PADDLE2, SDL_CONTROLLER_BUTTON_PADDLE3, SDL_CONTROLLER_BUTTON_PADDLE4, SDL_CONTROLLER_BUTTON_TOUCHPAD, SDL_CONTROLLER_BUTTON_MAX, } public enum SDL_GameControllerType { SDL_CONTROLLER_TYPE_UNKNOWN = 0, SDL_CONTROLLER_TYPE_XBOX360, SDL_CONTROLLER_TYPE_XBOXONE, SDL_CONTROLLER_TYPE_PS3, SDL_CONTROLLER_TYPE_PS4, SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO, SDL_CONTROLLER_TYPE_VIRTUAL, /* Requires >= 2.0.14 */ SDL_CONTROLLER_TYPE_PS5, /* Requires >= 2.0.14 */ SDL_CONTROLLER_TYPE_AMAZON_LUNA, /* Requires >= 2.0.16 */ SDL_CONTROLLER_TYPE_GOOGLE_STADIA /* Requires >= 2.0.16 */ } // FIXME: I'd rather this somehow be private... [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_GameControllerButtonBind_hat { public int hat; public int hat_mask; } // FIXME: I'd rather this somehow be private... [StructLayout(LayoutKind.Explicit)] public struct INTERNAL_GameControllerButtonBind_union { [FieldOffset(0)] public int button; [FieldOffset(0)] public int axis; [FieldOffset(0)] public INTERNAL_GameControllerButtonBind_hat hat; } [StructLayout(LayoutKind.Sequential)] public struct SDL_GameControllerButtonBind { public SDL_GameControllerBindType bindType; public INTERNAL_GameControllerButtonBind_union value; } /* This exists to deal with C# being stupid about blittable types. */ [StructLayout(LayoutKind.Sequential)] private struct INTERNAL_SDL_GameControllerButtonBind { public int bindType; /* Largest data type in the union is two ints in size */ public int unionVal0; public int unionVal1; } [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerAddMapping", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe int INTERNAL_SDL_GameControllerAddMapping( byte* mappingString ); public static unsafe int SDL_GameControllerAddMapping( string mappingString ) { byte* utf8MappingString = Utf8EncodeHeap(mappingString); int result = INTERNAL_SDL_GameControllerAddMapping( utf8MappingString ); Marshal.FreeHGlobal((IntPtr) utf8MappingString); return result; } /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerNumMappings(); /* Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerMappingForIndex", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerMappingForIndex(int mapping_index); public static string SDL_GameControllerMappingForIndex(int mapping_index) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerMappingForIndex( mapping_index ), true ); } /* THIS IS AN RWops FUNCTION! */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerAddMappingsFromRW", CallingConvention = CallingConvention.Cdecl)] private static extern int INTERNAL_SDL_GameControllerAddMappingsFromRW( IntPtr rw, int freerw ); public static int SDL_GameControllerAddMappingsFromFile(string file) { IntPtr rwops = SDL_RWFromFile(file, "rb"); return INTERNAL_SDL_GameControllerAddMappingsFromRW(rwops, 1); } [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerMappingForGUID", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerMappingForGUID( Guid guid ); public static string SDL_GameControllerMappingForGUID(Guid guid) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerMappingForGUID(guid), true ); } /* gamecontroller refers to an SDL_GameController* */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerMapping", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerMapping( IntPtr gamecontroller ); public static string SDL_GameControllerMapping( IntPtr gamecontroller ) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerMapping( gamecontroller ), true ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsGameController(int joystick_index); [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerNameForIndex", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerNameForIndex( int joystick_index ); public static string SDL_GameControllerNameForIndex( int joystick_index ) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerNameForIndex(joystick_index) ); } /* Only available in 2.0.9 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerMappingForDeviceIndex", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerMappingForDeviceIndex( int joystick_index ); public static string SDL_GameControllerMappingForDeviceIndex( int joystick_index ) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerMappingForDeviceIndex(joystick_index), true ); } /* IntPtr refers to an SDL_GameController* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GameControllerOpen(int joystick_index); /* gamecontroller refers to an SDL_GameController* */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerName( IntPtr gamecontroller ); public static string SDL_GameControllerName( IntPtr gamecontroller ) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerName(gamecontroller) ); } /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern ushort SDL_GameControllerGetVendor( IntPtr gamecontroller ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern ushort SDL_GameControllerGetProduct( IntPtr gamecontroller ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.6 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern ushort SDL_GameControllerGetProductVersion( IntPtr gamecontroller ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerGetSerial", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerGetSerial( IntPtr gamecontroller ); public static string SDL_GameControllerGetSerial( IntPtr gamecontroller ) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerGetSerial(gamecontroller) ); } /* gamecontroller refers to an SDL_GameController* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GameControllerGetAttached( IntPtr gamecontroller ); /* IntPtr refers to an SDL_Joystick* * gamecontroller refers to an SDL_GameController* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GameControllerGetJoystick( IntPtr gamecontroller ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerEventState(int state); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GameControllerUpdate(); [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerGetAxisFromString", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe SDL_GameControllerAxis INTERNAL_SDL_GameControllerGetAxisFromString( byte* pchString ); public static unsafe SDL_GameControllerAxis SDL_GameControllerGetAxisFromString( string pchString ) { int utf8PchStringBufSize = Utf8Size(pchString); byte* utf8PchString = stackalloc byte[utf8PchStringBufSize]; return INTERNAL_SDL_GameControllerGetAxisFromString( Utf8Encode(pchString, utf8PchString, utf8PchStringBufSize) ); } [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerGetStringForAxis", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerGetStringForAxis( SDL_GameControllerAxis axis ); public static string SDL_GameControllerGetStringForAxis( SDL_GameControllerAxis axis ) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerGetStringForAxis( axis ) ); } /* gamecontroller refers to an SDL_GameController* */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerGetBindForAxis", CallingConvention = CallingConvention.Cdecl)] private static extern INTERNAL_SDL_GameControllerButtonBind INTERNAL_SDL_GameControllerGetBindForAxis( IntPtr gamecontroller, SDL_GameControllerAxis axis ); public static SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis( IntPtr gamecontroller, SDL_GameControllerAxis axis ) { // This is guaranteed to never be null INTERNAL_SDL_GameControllerButtonBind dumb = INTERNAL_SDL_GameControllerGetBindForAxis( gamecontroller, axis ); SDL_GameControllerButtonBind result = new SDL_GameControllerButtonBind(); result.bindType = (SDL_GameControllerBindType) dumb.bindType; result.value.hat.hat = dumb.unionVal0; result.value.hat.hat_mask = dumb.unionVal1; return result; } /* gamecontroller refers to an SDL_GameController* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern short SDL_GameControllerGetAxis( IntPtr gamecontroller, SDL_GameControllerAxis axis ); [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerGetButtonFromString", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe SDL_GameControllerButton INTERNAL_SDL_GameControllerGetButtonFromString( byte* pchString ); public static unsafe SDL_GameControllerButton SDL_GameControllerGetButtonFromString( string pchString ) { int utf8PchStringBufSize = Utf8Size(pchString); byte* utf8PchString = stackalloc byte[utf8PchStringBufSize]; return INTERNAL_SDL_GameControllerGetButtonFromString( Utf8Encode(pchString, utf8PchString, utf8PchStringBufSize) ); } [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerGetStringForButton", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerGetStringForButton( SDL_GameControllerButton button ); public static string SDL_GameControllerGetStringForButton( SDL_GameControllerButton button ) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerGetStringForButton(button) ); } /* gamecontroller refers to an SDL_GameController* */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerGetBindForButton", CallingConvention = CallingConvention.Cdecl)] private static extern INTERNAL_SDL_GameControllerButtonBind INTERNAL_SDL_GameControllerGetBindForButton( IntPtr gamecontroller, SDL_GameControllerButton button ); public static SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton( IntPtr gamecontroller, SDL_GameControllerButton button ) { // This is guaranteed to never be null INTERNAL_SDL_GameControllerButtonBind dumb = INTERNAL_SDL_GameControllerGetBindForButton( gamecontroller, button ); SDL_GameControllerButtonBind result = new SDL_GameControllerButtonBind(); result.bindType = (SDL_GameControllerBindType) dumb.bindType; result.value.hat.hat = dumb.unionVal0; result.value.hat.hat_mask = dumb.unionVal1; return result; } /* gamecontroller refers to an SDL_GameController* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern byte SDL_GameControllerGetButton( IntPtr gamecontroller, SDL_GameControllerButton button ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.9 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerRumble( IntPtr gamecontroller, UInt16 low_frequency_rumble, UInt16 high_frequency_rumble, UInt32 duration_ms ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerRumbleTriggers( IntPtr gamecontroller, UInt16 left_rumble, UInt16 right_rumble, UInt32 duration_ms ); /* gamecontroller refers to an SDL_GameController* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GameControllerClose( IntPtr gamecontroller ); /* gamecontroller refers to an SDL_GameController* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerGetAppleSFSymbolsNameForButton", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerGetAppleSFSymbolsNameForButton( IntPtr gamecontroller, SDL_GameControllerButton button ); public static string SDL_GameControllerGetAppleSFSymbolsNameForButton( IntPtr gamecontroller, SDL_GameControllerButton button ) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerGetAppleSFSymbolsNameForButton(gamecontroller, button) ); } /* gamecontroller refers to an SDL_GameController* * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_GameControllerGetAppleSFSymbolsNameForAxis", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GameControllerGetAppleSFSymbolsNameForAxis( IntPtr gamecontroller, SDL_GameControllerAxis axis ); public static string SDL_GameControllerGetAppleSFSymbolsNameForAxis( IntPtr gamecontroller, SDL_GameControllerAxis axis ) { return UTF8_ToManaged( INTERNAL_SDL_GameControllerGetAppleSFSymbolsNameForAxis(gamecontroller, axis) ); } /* int refers to an SDL_JoystickID, IntPtr to an SDL_GameController*. * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GameControllerFromInstanceID(int joyid); /* Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_GameControllerType SDL_GameControllerTypeForIndex( int joystick_index ); /* IntPtr refers to an SDL_GameController*. * Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_GameControllerType SDL_GameControllerGetType( IntPtr gamecontroller ); /* IntPtr refers to an SDL_GameController*. * Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GameControllerFromPlayerIndex( int player_index ); /* IntPtr refers to an SDL_GameController*. * Only available in 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_GameControllerSetPlayerIndex( IntPtr gamecontroller, int player_index ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GameControllerHasLED( IntPtr gamecontroller ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GameControllerHasRumble( IntPtr gamecontroller ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GameControllerHasRumbleTriggers( IntPtr gamecontroller ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerSetLED( IntPtr gamecontroller, byte red, byte green, byte blue ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GameControllerHasAxis( IntPtr gamecontroller, SDL_GameControllerAxis axis ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GameControllerHasButton( IntPtr gamecontroller, SDL_GameControllerButton button ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerGetNumTouchpads( IntPtr gamecontroller ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerGetNumTouchpadFingers( IntPtr gamecontroller, int touchpad ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerGetTouchpadFinger( IntPtr gamecontroller, int touchpad, int finger, out byte state, out float x, out float y, out float pressure ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GameControllerHasSensor( IntPtr gamecontroller, SDL_SensorType type ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerSetSensorEnabled( IntPtr gamecontroller, SDL_SensorType type, SDL_bool enabled ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GameControllerIsSensorEnabled( IntPtr gamecontroller, SDL_SensorType type ); /* gamecontroller refers to an SDL_GameController*. * data refers to a float*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerGetSensorData( IntPtr gamecontroller, SDL_SensorType type, IntPtr data, int num_values ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerGetSensorData( IntPtr gamecontroller, SDL_SensorType type, [In] float[] data, int num_values ); /* gamecontroller refers to an SDL_GameController*. * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern float SDL_GameControllerGetSensorDataRate( IntPtr gamecontroller, SDL_SensorType type ); /* gamecontroller refers to an SDL_GameController*. * data refers to a const void*. * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GameControllerSendEffect( IntPtr gamecontroller, IntPtr data, int size ); #endregion #region SDL_haptic.h /* SDL_HapticEffect type */ public const ushort SDL_HAPTIC_CONSTANT = (1 << 0); public const ushort SDL_HAPTIC_SINE = (1 << 1); public const ushort SDL_HAPTIC_LEFTRIGHT = (1 << 2); public const ushort SDL_HAPTIC_TRIANGLE = (1 << 3); public const ushort SDL_HAPTIC_SAWTOOTHUP = (1 << 4); public const ushort SDL_HAPTIC_SAWTOOTHDOWN = (1 << 5); public const ushort SDL_HAPTIC_SPRING = (1 << 7); public const ushort SDL_HAPTIC_DAMPER = (1 << 8); public const ushort SDL_HAPTIC_INERTIA = (1 << 9); public const ushort SDL_HAPTIC_FRICTION = (1 << 10); public const ushort SDL_HAPTIC_CUSTOM = (1 << 11); public const ushort SDL_HAPTIC_GAIN = (1 << 12); public const ushort SDL_HAPTIC_AUTOCENTER = (1 << 13); public const ushort SDL_HAPTIC_STATUS = (1 << 14); public const ushort SDL_HAPTIC_PAUSE = (1 << 15); /* SDL_HapticDirection type */ public const byte SDL_HAPTIC_POLAR = 0; public const byte SDL_HAPTIC_CARTESIAN = 1; public const byte SDL_HAPTIC_SPHERICAL = 2; public const byte SDL_HAPTIC_STEERING_AXIS = 3; /* Requires >= 2.0.14 */ /* SDL_HapticRunEffect */ public const uint SDL_HAPTIC_INFINITY = 4294967295U; [StructLayout(LayoutKind.Sequential)] public unsafe struct SDL_HapticDirection { public byte type; public fixed int dir[3]; } [StructLayout(LayoutKind.Sequential)] public struct SDL_HapticConstant { // Header public ushort type; public SDL_HapticDirection direction; // Replay public uint length; public ushort delay; // Trigger public ushort button; public ushort interval; // Constant public short level; // Envelope public ushort attack_length; public ushort attack_level; public ushort fade_length; public ushort fade_level; } [StructLayout(LayoutKind.Sequential)] public struct SDL_HapticPeriodic { // Header public ushort type; public SDL_HapticDirection direction; // Replay public uint length; public ushort delay; // Trigger public ushort button; public ushort interval; // Periodic public ushort period; public short magnitude; public short offset; public ushort phase; // Envelope public ushort attack_length; public ushort attack_level; public ushort fade_length; public ushort fade_level; } [StructLayout(LayoutKind.Sequential)] public unsafe struct SDL_HapticCondition { // Header public ushort type; public SDL_HapticDirection direction; // Replay public uint length; public ushort delay; // Trigger public ushort button; public ushort interval; // Condition public fixed ushort right_sat[3]; public fixed ushort left_sat[3]; public fixed short right_coeff[3]; public fixed short left_coeff[3]; public fixed ushort deadband[3]; public fixed short center[3]; } [StructLayout(LayoutKind.Sequential)] public struct SDL_HapticRamp { // Header public ushort type; public SDL_HapticDirection direction; // Replay public uint length; public ushort delay; // Trigger public ushort button; public ushort interval; // Ramp public short start; public short end; // Envelope public ushort attack_length; public ushort attack_level; public ushort fade_length; public ushort fade_level; } [StructLayout(LayoutKind.Sequential)] public struct SDL_HapticLeftRight { // Header public ushort type; // Replay public uint length; // Rumble public ushort large_magnitude; public ushort small_magnitude; } [StructLayout(LayoutKind.Sequential)] public struct SDL_HapticCustom { // Header public ushort type; public SDL_HapticDirection direction; // Replay public uint length; public ushort delay; // Trigger public ushort button; public ushort interval; // Custom public byte channels; public ushort period; public ushort samples; public IntPtr data; // Uint16* // Envelope public ushort attack_length; public ushort attack_level; public ushort fade_length; public ushort fade_level; } [StructLayout(LayoutKind.Explicit)] public struct SDL_HapticEffect { [FieldOffset(0)] public ushort type; [FieldOffset(0)] public SDL_HapticConstant constant; [FieldOffset(0)] public SDL_HapticPeriodic periodic; [FieldOffset(0)] public SDL_HapticCondition condition; [FieldOffset(0)] public SDL_HapticRamp ramp; [FieldOffset(0)] public SDL_HapticLeftRight leftright; [FieldOffset(0)] public SDL_HapticCustom custom; } /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_HapticClose(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_HapticDestroyEffect( IntPtr haptic, int effect ); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticEffectSupported( IntPtr haptic, ref SDL_HapticEffect effect ); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticGetEffectStatus( IntPtr haptic, int effect ); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticIndex(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, EntryPoint = "SDL_HapticName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_HapticName(int device_index); public static string SDL_HapticName(int device_index) { return UTF8_ToManaged(INTERNAL_SDL_HapticName(device_index)); } /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticNewEffect( IntPtr haptic, ref SDL_HapticEffect effect ); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticNumAxes(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticNumEffects(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticNumEffectsPlaying(IntPtr haptic); /* IntPtr refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_HapticOpen(int device_index); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticOpened(int device_index); /* IntPtr refers to an SDL_Haptic*, joystick to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_HapticOpenFromJoystick( IntPtr joystick ); /* IntPtr refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_HapticOpenFromMouse(); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticPause(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_HapticQuery(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticRumbleInit(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticRumblePlay( IntPtr haptic, float strength, uint length ); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticRumbleStop(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticRumbleSupported(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticRunEffect( IntPtr haptic, int effect, uint iterations ); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticSetAutocenter( IntPtr haptic, int autocenter ); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticSetGain( IntPtr haptic, int gain ); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticStopAll(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticStopEffect( IntPtr haptic, int effect ); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticUnpause(IntPtr haptic); /* haptic refers to an SDL_Haptic* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_HapticUpdateEffect( IntPtr haptic, int effect, ref SDL_HapticEffect data ); /* joystick refers to an SDL_Joystick* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_JoystickIsHaptic(IntPtr joystick); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_MouseIsHaptic(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_NumHaptics(); #endregion #region SDL_sensor.h /* This region is only available in 2.0.9 or higher. */ public enum SDL_SensorType { SDL_SENSOR_INVALID = -1, SDL_SENSOR_UNKNOWN, SDL_SENSOR_ACCEL, SDL_SENSOR_GYRO } public const float SDL_STANDARD_GRAVITY = 9.80665f; [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_NumSensors(); [DllImport(nativeLibName, EntryPoint = "SDL_SensorGetDeviceName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_SensorGetDeviceName(int device_index); public static string SDL_SensorGetDeviceName(int device_index) { return UTF8_ToManaged(INTERNAL_SDL_SensorGetDeviceName(device_index)); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_SensorType SDL_SensorGetDeviceType(int device_index); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SensorGetDeviceNonPortableType(int device_index); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern Int32 SDL_SensorGetDeviceInstanceID(int device_index); /* IntPtr refers to an SDL_Sensor* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_SensorOpen(int device_index); /* IntPtr refers to an SDL_Sensor* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_SensorFromInstanceID( Int32 instance_id ); /* sensor refers to an SDL_Sensor* */ [DllImport(nativeLibName, EntryPoint = "SDL_SensorGetName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_SensorGetName(IntPtr sensor); public static string SDL_SensorGetName(IntPtr sensor) { return UTF8_ToManaged(INTERNAL_SDL_SensorGetName(sensor)); } /* sensor refers to an SDL_Sensor* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_SensorType SDL_SensorGetType(IntPtr sensor); /* sensor refers to an SDL_Sensor* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SensorGetNonPortableType(IntPtr sensor); /* sensor refers to an SDL_Sensor* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern Int32 SDL_SensorGetInstanceID(IntPtr sensor); /* sensor refers to an SDL_Sensor* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_SensorGetData( IntPtr sensor, float[] data, int num_values ); /* sensor refers to an SDL_Sensor* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SensorClose(IntPtr sensor); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SensorUpdate(); /* Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_LockSensors(); /* Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_UnlockSensors(); #endregion #region SDL_audio.h public const ushort SDL_AUDIO_MASK_BITSIZE = 0xFF; public const ushort SDL_AUDIO_MASK_DATATYPE = (1 << 8); public const ushort SDL_AUDIO_MASK_ENDIAN = (1 << 12); public const ushort SDL_AUDIO_MASK_SIGNED = (1 << 15); public static ushort SDL_AUDIO_BITSIZE(ushort x) { return (ushort) (x & SDL_AUDIO_MASK_BITSIZE); } public static bool SDL_AUDIO_ISFLOAT(ushort x) { return (x & SDL_AUDIO_MASK_DATATYPE) != 0; } public static bool SDL_AUDIO_ISBIGENDIAN(ushort x) { return (x & SDL_AUDIO_MASK_ENDIAN) != 0; } public static bool SDL_AUDIO_ISSIGNED(ushort x) { return (x & SDL_AUDIO_MASK_SIGNED) != 0; } public static bool SDL_AUDIO_ISINT(ushort x) { return (x & SDL_AUDIO_MASK_DATATYPE) == 0; } public static bool SDL_AUDIO_ISLITTLEENDIAN(ushort x) { return (x & SDL_AUDIO_MASK_ENDIAN) == 0; } public static bool SDL_AUDIO_ISUNSIGNED(ushort x) { return (x & SDL_AUDIO_MASK_SIGNED) == 0; } public const ushort AUDIO_U8 = 0x0008; public const ushort AUDIO_S8 = 0x8008; public const ushort AUDIO_U16LSB = 0x0010; public const ushort AUDIO_S16LSB = 0x8010; public const ushort AUDIO_U16MSB = 0x1010; public const ushort AUDIO_S16MSB = 0x9010; public const ushort AUDIO_U16 = AUDIO_U16LSB; public const ushort AUDIO_S16 = AUDIO_S16LSB; public const ushort AUDIO_S32LSB = 0x8020; public const ushort AUDIO_S32MSB = 0x9020; public const ushort AUDIO_S32 = AUDIO_S32LSB; public const ushort AUDIO_F32LSB = 0x8120; public const ushort AUDIO_F32MSB = 0x9120; public const ushort AUDIO_F32 = AUDIO_F32LSB; public static readonly ushort AUDIO_U16SYS = BitConverter.IsLittleEndian ? AUDIO_U16LSB : AUDIO_U16MSB; public static readonly ushort AUDIO_S16SYS = BitConverter.IsLittleEndian ? AUDIO_S16LSB : AUDIO_S16MSB; public static readonly ushort AUDIO_S32SYS = BitConverter.IsLittleEndian ? AUDIO_S32LSB : AUDIO_S32MSB; public static readonly ushort AUDIO_F32SYS = BitConverter.IsLittleEndian ? AUDIO_F32LSB : AUDIO_F32MSB; public const uint SDL_AUDIO_ALLOW_FREQUENCY_CHANGE = 0x00000001; public const uint SDL_AUDIO_ALLOW_FORMAT_CHANGE = 0x00000002; public const uint SDL_AUDIO_ALLOW_CHANNELS_CHANGE = 0x00000004; public const uint SDL_AUDIO_ALLOW_SAMPLES_CHANGE = 0x00000008; public const uint SDL_AUDIO_ALLOW_ANY_CHANGE = ( SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_FORMAT_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE | SDL_AUDIO_ALLOW_SAMPLES_CHANGE ); public const int SDL_MIX_MAXVOLUME = 128; public enum SDL_AudioStatus { SDL_AUDIO_STOPPED, SDL_AUDIO_PLAYING, SDL_AUDIO_PAUSED } [StructLayout(LayoutKind.Sequential)] public struct SDL_AudioSpec { public int freq; public ushort format; // SDL_AudioFormat public byte channels; public byte silence; public ushort samples; public uint size; public SDL_AudioCallback callback; public IntPtr userdata; // void* } /* userdata refers to a void*, stream to a Uint8 */ [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void SDL_AudioCallback( IntPtr userdata, IntPtr stream, int len ); [DllImport(nativeLibName, EntryPoint = "SDL_AudioInit", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe int INTERNAL_SDL_AudioInit( byte* driver_name ); public static unsafe int SDL_AudioInit(string driver_name) { int utf8DriverNameBufSize = Utf8Size(driver_name); byte* utf8DriverName = stackalloc byte[utf8DriverNameBufSize]; return INTERNAL_SDL_AudioInit( Utf8Encode(driver_name, utf8DriverName, utf8DriverNameBufSize) ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_AudioQuit(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_CloseAudio(); /* dev refers to an SDL_AudioDeviceID */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_CloseAudioDevice(uint dev); /* audio_buf refers to a malloc()'d buffer from SDL_LoadWAV */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FreeWAV(IntPtr audio_buf); [DllImport(nativeLibName, EntryPoint = "SDL_GetAudioDeviceName", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetAudioDeviceName( int index, int iscapture ); public static string SDL_GetAudioDeviceName( int index, int iscapture ) { return UTF8_ToManaged( INTERNAL_SDL_GetAudioDeviceName(index, iscapture) ); } /* dev refers to an SDL_AudioDeviceID */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_AudioStatus SDL_GetAudioDeviceStatus( uint dev ); [DllImport(nativeLibName, EntryPoint = "SDL_GetAudioDriver", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetAudioDriver(int index); public static string SDL_GetAudioDriver(int index) { return UTF8_ToManaged( INTERNAL_SDL_GetAudioDriver(index) ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_AudioStatus SDL_GetAudioStatus(); [DllImport(nativeLibName, EntryPoint = "SDL_GetCurrentAudioDriver", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetCurrentAudioDriver(); public static string SDL_GetCurrentAudioDriver() { return UTF8_ToManaged(INTERNAL_SDL_GetCurrentAudioDriver()); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetNumAudioDevices(int iscapture); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetNumAudioDrivers(); /* audio_buf refers to a malloc()'d buffer, IntPtr to an SDL_AudioSpec* */ /* THIS IS AN RWops FUNCTION! */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_LoadWAV_RW( IntPtr src, int freesrc, out SDL_AudioSpec spec, out IntPtr audio_buf, out uint audio_len ); public static IntPtr SDL_LoadWAV( string file, out SDL_AudioSpec spec, out IntPtr audio_buf, out uint audio_len ) { IntPtr rwops = SDL_RWFromFile(file, "rb"); return SDL_LoadWAV_RW( rwops, 1, out spec, out audio_buf, out audio_len ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_LockAudio(); /* dev refers to an SDL_AudioDeviceID */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_LockAudioDevice(uint dev); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_MixAudio( [Out()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] dst, [In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] src, uint len, int volume ); /* format refers to an SDL_AudioFormat */ /* This overload allows raw pointers to be passed for dst and src. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_MixAudioFormat( IntPtr dst, IntPtr src, ushort format, uint len, int volume ); /* format refers to an SDL_AudioFormat */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_MixAudioFormat( [Out()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 3)] byte[] dst, [In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 3)] byte[] src, ushort format, uint len, int volume ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_OpenAudio( ref SDL_AudioSpec desired, out SDL_AudioSpec obtained ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_OpenAudio( ref SDL_AudioSpec desired, IntPtr obtained ); /* uint refers to an SDL_AudioDeviceID */ /* This overload allows for IntPtr.Zero (null) to be passed for device. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern unsafe uint SDL_OpenAudioDevice( IntPtr device, int iscapture, ref SDL_AudioSpec desired, out SDL_AudioSpec obtained, int allowed_changes ); /* uint refers to an SDL_AudioDeviceID */ [DllImport(nativeLibName, EntryPoint = "SDL_OpenAudioDevice", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe uint INTERNAL_SDL_OpenAudioDevice( byte* device, int iscapture, ref SDL_AudioSpec desired, out SDL_AudioSpec obtained, int allowed_changes ); public static unsafe uint SDL_OpenAudioDevice( string device, int iscapture, ref SDL_AudioSpec desired, out SDL_AudioSpec obtained, int allowed_changes ) { int utf8DeviceBufSize = Utf8Size(device); byte* utf8Device = stackalloc byte[utf8DeviceBufSize]; return INTERNAL_SDL_OpenAudioDevice( Utf8Encode(device, utf8Device, utf8DeviceBufSize), iscapture, ref desired, out obtained, allowed_changes ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_PauseAudio(int pause_on); /* dev refers to an SDL_AudioDeviceID */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_PauseAudioDevice( uint dev, int pause_on ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_UnlockAudio(); /* dev refers to an SDL_AudioDeviceID */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_UnlockAudioDevice(uint dev); /* dev refers to an SDL_AudioDeviceID, data to a void* * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_QueueAudio( uint dev, IntPtr data, UInt32 len ); /* dev refers to an SDL_AudioDeviceID, data to a void* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_DequeueAudio( uint dev, IntPtr data, uint len ); /* dev refers to an SDL_AudioDeviceID * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetQueuedAudioSize(uint dev); /* dev refers to an SDL_AudioDeviceID * Only available in 2.0.4 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_ClearQueuedAudio(uint dev); /* src_format and dst_format refer to SDL_AudioFormats. * IntPtr refers to an SDL_AudioStream*. * Only available in 2.0.7 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_NewAudioStream( ushort src_format, byte src_channels, int src_rate, ushort dst_format, byte dst_channels, int dst_rate ); /* stream refers to an SDL_AudioStream*, buf to a void*. * Only available in 2.0.7 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_AudioStreamPut( IntPtr stream, IntPtr buf, int len ); /* stream refers to an SDL_AudioStream*, buf to a void*. * Only available in 2.0.7 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_AudioStreamGet( IntPtr stream, IntPtr buf, int len ); /* stream refers to an SDL_AudioStream*. * Only available in 2.0.7 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_AudioStreamAvailable(IntPtr stream); /* stream refers to an SDL_AudioStream*. * Only available in 2.0.7 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_AudioStreamClear(IntPtr stream); /* stream refers to an SDL_AudioStream*. * Only available in 2.0.7 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_FreeAudioStream(IntPtr stream); /* Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetAudioDeviceSpec( int index, int iscapture, out SDL_AudioSpec spec ); #endregion #region SDL_timer.h /* System timers rely on different OS mechanisms depending on * which operating system SDL2 is compiled against. */ /* Compare tick values, return true if A has passed B. Introduced in SDL 2.0.1, * but does not require it (it was a macro). */ public static bool SDL_TICKS_PASSED(UInt32 A, UInt32 B) { return ((Int32)(B - A) <= 0); } /* Delays the thread's processing based on the milliseconds parameter */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_Delay(UInt32 ms); /* Returns the milliseconds that have passed since SDL was initialized */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt32 SDL_GetTicks(); /* Returns the milliseconds that have passed since SDL was initialized * Only available in 2.0.18 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt64 SDL_GetTicks64(); /* Get the current value of the high resolution counter */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt64 SDL_GetPerformanceCounter(); /* Get the count per second of the high resolution counter */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern UInt64 SDL_GetPerformanceFrequency(); /* param refers to a void* */ [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate UInt32 SDL_TimerCallback(UInt32 interval, IntPtr param); /* int refers to an SDL_TimerID, param to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_AddTimer( UInt32 interval, SDL_TimerCallback callback, IntPtr param ); /* id refers to an SDL_TimerID */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_RemoveTimer(int id); #endregion #region SDL_system.h /* Windows */ [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr SDL_WindowsMessageHook( IntPtr userdata, IntPtr hWnd, uint message, ulong wParam, long lParam ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SetWindowsMessageHook( SDL_WindowsMessageHook callback, IntPtr userdata ); /* renderer refers to an SDL_Renderer* * IntPtr refers to an IDirect3DDevice9* * Only available in 2.0.1 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_RenderGetD3D9Device(IntPtr renderer); /* renderer refers to an SDL_Renderer* * IntPtr refers to an ID3D11Device* * Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_RenderGetD3D11Device(IntPtr renderer); /* iOS */ [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void SDL_iPhoneAnimationCallback(IntPtr p); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_iPhoneSetAnimationCallback( IntPtr window, /* SDL_Window* */ int interval, SDL_iPhoneAnimationCallback callback, IntPtr callbackParam ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_iPhoneSetEventPump(SDL_bool enabled); /* Android */ public const int SDL_ANDROID_EXTERNAL_STORAGE_READ = 0x01; public const int SDL_ANDROID_EXTERNAL_STORAGE_WRITE = 0x02; /* IntPtr refers to a JNIEnv* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_AndroidGetJNIEnv(); /* IntPtr refers to a jobject */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_AndroidGetActivity(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsAndroidTV(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsChromebook(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsDeXMode(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_AndroidBackButton(); [DllImport(nativeLibName, EntryPoint = "SDL_AndroidGetInternalStoragePath", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_AndroidGetInternalStoragePath(); public static string SDL_AndroidGetInternalStoragePath() { return UTF8_ToManaged( INTERNAL_SDL_AndroidGetInternalStoragePath() ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_AndroidGetExternalStorageState(); [DllImport(nativeLibName, EntryPoint = "SDL_AndroidGetExternalStoragePath", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_AndroidGetExternalStoragePath(); public static string SDL_AndroidGetExternalStoragePath() { return UTF8_ToManaged( INTERNAL_SDL_AndroidGetExternalStoragePath() ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetAndroidSDKVersion(); /* Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_AndroidRequestPermission", CallingConvention = CallingConvention.Cdecl)] private static unsafe extern SDL_bool INTERNAL_SDL_AndroidRequestPermission( byte* permission ); public static unsafe SDL_bool SDL_AndroidRequestPermission( string permission ) { byte* permissionPtr = Utf8EncodeHeap(permission); SDL_bool result = INTERNAL_SDL_AndroidRequestPermission( permissionPtr ); Marshal.FreeHGlobal((IntPtr) permissionPtr); return result; } /* Only available in 2.0.16 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_AndroidShowToast", CallingConvention = CallingConvention.Cdecl)] private static unsafe extern int INTERNAL_SDL_AndroidShowToast( byte* message, int duration, int gravity, int xOffset, int yOffset ); public static unsafe int SDL_AndroidShowToast( string message, int duration, int gravity, int xOffset, int yOffset ) { byte* messagePtr = Utf8EncodeHeap(message); int result = INTERNAL_SDL_AndroidShowToast( messagePtr, duration, gravity, xOffset, yOffset ); Marshal.FreeHGlobal((IntPtr) messagePtr); return result; } /* WinRT */ public enum SDL_WinRT_DeviceFamily { SDL_WINRT_DEVICEFAMILY_UNKNOWN, SDL_WINRT_DEVICEFAMILY_DESKTOP, SDL_WINRT_DEVICEFAMILY_MOBILE, SDL_WINRT_DEVICEFAMILY_XBOX } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_WinRT_DeviceFamily SDL_WinRTGetDeviceFamily(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_IsTablet(); #endregion #region SDL_syswm.h public enum SDL_SYSWM_TYPE { SDL_SYSWM_UNKNOWN, SDL_SYSWM_WINDOWS, SDL_SYSWM_X11, SDL_SYSWM_DIRECTFB, SDL_SYSWM_COCOA, SDL_SYSWM_UIKIT, SDL_SYSWM_WAYLAND, SDL_SYSWM_MIR, SDL_SYSWM_WINRT, SDL_SYSWM_ANDROID, SDL_SYSWM_VIVANTE, SDL_SYSWM_OS2, SDL_SYSWM_HAIKU, SDL_SYSWM_KMSDRM /* requires >= 2.0.16 */ } // FIXME: I wish these weren't public... [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_windows_wminfo { public IntPtr window; // Refers to an HWND public IntPtr hdc; // Refers to an HDC public IntPtr hinstance; // Refers to an HINSTANCE } [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_winrt_wminfo { public IntPtr window; // Refers to an IInspectable* } [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_x11_wminfo { public IntPtr display; // Refers to a Display* public IntPtr window; // Refers to a Window (XID, use ToInt64!) } [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_directfb_wminfo { public IntPtr dfb; // Refers to an IDirectFB* public IntPtr window; // Refers to an IDirectFBWindow* public IntPtr surface; // Refers to an IDirectFBSurface* } [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_cocoa_wminfo { public IntPtr window; // Refers to an NSWindow* } [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_uikit_wminfo { public IntPtr window; // Refers to a UIWindow* public uint framebuffer; public uint colorbuffer; public uint resolveFramebuffer; } [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_wayland_wminfo { public IntPtr display; // Refers to a wl_display* public IntPtr surface; // Refers to a wl_surface* public IntPtr shell_surface; // Refers to a wl_shell_surface* public IntPtr egl_window; // Refers to an egl_window*, requires >= 2.0.16 public IntPtr xdg_surface; // Refers to an xdg_surface*, requires >= 2.0.16 public IntPtr xdg_toplevel; // Referes to an xdg_toplevel*, requires >= 2.0.18 public IntPtr xdg_popup; public IntPtr xdg_positioner; } [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_mir_wminfo { public IntPtr connection; // Refers to a MirConnection* public IntPtr surface; // Refers to a MirSurface* } [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_android_wminfo { public IntPtr window; // Refers to an ANativeWindow public IntPtr surface; // Refers to an EGLSurface } [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_vivante_wminfo { public IntPtr display; // Refers to an EGLNativeDisplayType public IntPtr window; // Refers to an EGLNativeWindowType } /* Only available in 2.0.14 or higher. */ [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_os2_wminfo { public IntPtr hwnd; // Refers to an HWND public IntPtr hwndFrame; // Refers to an HWND } /* Only available in 2.0.16 or higher. */ [StructLayout(LayoutKind.Sequential)] public struct INTERNAL_kmsdrm_wminfo { int dev_index; int drm_fd; IntPtr gbm_dev; // Refers to a gbm_device* } [StructLayout(LayoutKind.Explicit)] public struct INTERNAL_SysWMDriverUnion { [FieldOffset(0)] public INTERNAL_windows_wminfo win; [FieldOffset(0)] public INTERNAL_winrt_wminfo winrt; [FieldOffset(0)] public INTERNAL_x11_wminfo x11; [FieldOffset(0)] public INTERNAL_directfb_wminfo dfb; [FieldOffset(0)] public INTERNAL_cocoa_wminfo cocoa; [FieldOffset(0)] public INTERNAL_uikit_wminfo uikit; [FieldOffset(0)] public INTERNAL_wayland_wminfo wl; [FieldOffset(0)] public INTERNAL_mir_wminfo mir; [FieldOffset(0)] public INTERNAL_android_wminfo android; [FieldOffset(0)] public INTERNAL_os2_wminfo os2; [FieldOffset(0)] public INTERNAL_vivante_wminfo vivante; [FieldOffset(0)] public INTERNAL_kmsdrm_wminfo ksmdrm; // private int dummy; } [StructLayout(LayoutKind.Sequential)] public struct SDL_SysWMinfo { public SDL_version version; public SDL_SYSWM_TYPE subsystem; public INTERNAL_SysWMDriverUnion info; } /* window refers to an SDL_Window* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_GetWindowWMInfo( IntPtr window, ref SDL_SysWMinfo info ); #endregion #region SDL_filesystem.h /* Only available in 2.0.1 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_GetBasePath", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_SDL_GetBasePath(); public static string SDL_GetBasePath() { return UTF8_ToManaged(INTERNAL_SDL_GetBasePath(), true); } /* Only available in 2.0.1 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_GetPrefPath", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe IntPtr INTERNAL_SDL_GetPrefPath( byte* org, byte* app ); public static unsafe string SDL_GetPrefPath(string org, string app) { int utf8OrgBufSize = Utf8Size(org); byte* utf8Org = stackalloc byte[utf8OrgBufSize]; int utf8AppBufSize = Utf8Size(app); byte* utf8App = stackalloc byte[utf8AppBufSize]; return UTF8_ToManaged( INTERNAL_SDL_GetPrefPath( Utf8Encode(org, utf8Org, utf8OrgBufSize), Utf8Encode(app, utf8App, utf8AppBufSize) ), true ); } #endregion #region SDL_power.h public enum SDL_PowerState { SDL_POWERSTATE_UNKNOWN = 0, SDL_POWERSTATE_ON_BATTERY, SDL_POWERSTATE_NO_BATTERY, SDL_POWERSTATE_CHARGING, SDL_POWERSTATE_CHARGED } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_PowerState SDL_GetPowerInfo( out int secs, out int pct ); #endregion #region SDL_cpuinfo.h [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetCPUCount(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetCPUCacheLineSize(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasRDTSC(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasAltiVec(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasMMX(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_Has3DNow(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasSSE(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasSSE2(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasSSE3(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasSSE41(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasSSE42(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasAVX(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasAVX2(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasAVX512F(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasNEON(); /* Only available in 2.0.1 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int SDL_GetSystemRAM(); /* Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern uint SDL_SIMDGetAlignment(); /* Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_SIMDAlloc(uint len); /* Only available in SDL 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_SIMDRealloc(IntPtr ptr, uint len); /* Only available in SDL 2.0.10 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void SDL_SIMDFree(IntPtr ptr); /* Only available in SDL 2.0.11 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern SDL_bool SDL_HasARMSIMD(); #endregion #region SDL_locale.h [StructLayout(LayoutKind.Sequential)] public struct SDL_Locale { public IntPtr language; /* char* */ public IntPtr country; /* char* */ } /* IntPtr refers to an SDL_Locale*. * Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr SDL_GetPreferredLocales(); #endregion #region SDL_misc.h /* Only available in 2.0.14 or higher. */ [DllImport(nativeLibName, EntryPoint = "SDL_OpenURL", CallingConvention = CallingConvention.Cdecl)] private static unsafe extern int INTERNAL_SDL_OpenURL(byte* url); public static unsafe int SDL_OpenURL(string url) { byte* urlPtr = Utf8EncodeHeap(url); int result = INTERNAL_SDL_OpenURL(urlPtr); Marshal.FreeHGlobal((IntPtr) urlPtr); return result; } #endregion } } ================================================ FILE: source/Playnite/SDL2_mixer.cs ================================================ #region License /* SDL2# - C# Wrapper for SDL2 * * Copyright (c) 2013-2021 Ethan Lee. * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in a * product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * * Ethan "flibitijibibo" Lee * */ #endregion #region Using Statements using System; using System.Runtime.InteropServices; #endregion namespace SDL2 { public static class SDL_mixer { #region SDL2# Variables /* Used by DllImport to load the native library. */ private const string nativeLibName = "SDL2_mixer"; #endregion #region SDL_mixer.h /* Similar to the headers, this is the version we're expecting to be * running with. You will likely want to check this somewhere in your * program! */ public const int SDL_MIXER_MAJOR_VERSION = 2; public const int SDL_MIXER_MINOR_VERSION = 0; public const int SDL_MIXER_PATCHLEVEL = 5; /* In C, you can redefine this value before including SDL_mixer.h. * We're not going to allow this in SDL2#, since the value of this * variable is persistent and not dependent on preprocessor ordering. */ public const int MIX_CHANNELS = 8; public static readonly int MIX_DEFAULT_FREQUENCY = 44100; public static readonly ushort MIX_DEFAULT_FORMAT = BitConverter.IsLittleEndian ? SDL.AUDIO_S16LSB : SDL.AUDIO_S16MSB; public static readonly int MIX_DEFAULT_CHANNELS = 2; public static readonly byte MIX_MAX_VOLUME = 128; [Flags] public enum MIX_InitFlags { MIX_INIT_FLAC = 0x00000001, MIX_INIT_MOD = 0x00000002, MIX_INIT_MP3 = 0x00000008, MIX_INIT_OGG = 0x00000010, MIX_INIT_MID = 0x00000020, MIX_INIT_OPUS = 0x00000040 } public struct MIX_Chunk { public int allocated; public IntPtr abuf; /* Uint8* */ public uint alen; public byte volume; } public enum Mix_Fading { MIX_NO_FADING, MIX_FADING_OUT, MIX_FADING_IN } public enum Mix_MusicType { MUS_NONE, MUS_CMD, MUS_WAV, MUS_MOD, MUS_MID, MUS_OGG, MUS_MP3, MUS_MP3_MAD_UNUSED, MUS_FLAC, MUS_MODPLUG_UNUSED, MUS_OPUS } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void MixFuncDelegate( IntPtr udata, // void* IntPtr stream, // Uint8* int len ); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void Mix_EffectFunc_t( int chan, IntPtr stream, // void* int len, IntPtr udata // void* ); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void Mix_EffectDone_t( int chan, IntPtr udata // void* ); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void MusicFinishedDelegate(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ChannelFinishedDelegate(int channel); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int SoundFontDelegate( IntPtr a, // const char* IntPtr b // void* ); public static void SDL_MIXER_VERSION(out SDL.SDL_version X) { X.major = SDL_MIXER_MAJOR_VERSION; X.minor = SDL_MIXER_MINOR_VERSION; X.patch = SDL_MIXER_PATCHLEVEL; } [DllImport(nativeLibName, EntryPoint = "MIX_Linked_Version", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_MIX_Linked_Version(); public static SDL.SDL_version MIX_Linked_Version() { SDL.SDL_version result; IntPtr result_ptr = INTERNAL_MIX_Linked_Version(); result = SDL.PtrToStructure( result_ptr ); return result; } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_Init(MIX_InitFlags flags); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_Quit(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_OpenAudio( int frequency, ushort format, int channels, int chunksize ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_OpenAudioDevice( int frequency, ushort format, int channels, int chunksize, string device, uint allowed_changes ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_AllocateChannels(int numchans); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_QuerySpec( out int frequency, out ushort format, out int channels ); /* src refers to an SDL_RWops*, IntPtr to a Mix_Chunk* */ /* THIS IS A PUBLIC RWops FUNCTION! */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr Mix_LoadWAV_RW( IntPtr src, int freesrc ); /* IntPtr refers to a Mix_Chunk* */ /* This is an RWops macro in the C header. */ public static IntPtr Mix_LoadWAV(string file) { IntPtr rwops = SDL.SDL_RWFromFile(file, "rb"); return Mix_LoadWAV_RW(rwops, 1); } /* IntPtr refers to a Mix_Music* */ [DllImport(nativeLibName, EntryPoint = "Mix_LoadMUS", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe IntPtr INTERNAL_Mix_LoadMUS( byte* file ); public static unsafe IntPtr Mix_LoadMUS(string file) { byte* utf8File = SDL.Utf8EncodeHeap(file); IntPtr handle = INTERNAL_Mix_LoadMUS( utf8File ); Marshal.FreeHGlobal((IntPtr) utf8File); return handle; } /* IntPtr refers to a Mix_Chunk* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr Mix_QuickLoad_WAV( [In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)] byte[] mem ); /* IntPtr refers to a Mix_Chunk* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr Mix_QuickLoad_RAW( [In()] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 1)] byte[] mem, uint len ); /* chunk refers to a Mix_Chunk* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_FreeChunk(IntPtr chunk); /* music refers to a Mix_Music* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_FreeMusic(IntPtr music); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GetNumChunkDecoders(); [DllImport(nativeLibName, EntryPoint = "Mix_GetChunkDecoder", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_Mix_GetChunkDecoder(int index); public static string Mix_GetChunkDecoder(int index) { return SDL.UTF8_ToManaged( INTERNAL_Mix_GetChunkDecoder(index) ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GetNumMusicDecoders(); [DllImport(nativeLibName, EntryPoint = "Mix_GetMusicDecoder", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_Mix_GetMusicDecoder(int index); public static string Mix_GetMusicDecoder(int index) { return SDL.UTF8_ToManaged( INTERNAL_Mix_GetMusicDecoder(index) ); } /* music refers to a Mix_Music* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern Mix_MusicType Mix_GetMusicType(IntPtr music); /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, EntryPoint = "Mix_GetMusicTitle", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr INTERNAL_Mix_GetMusicTitle(IntPtr music); public static string Mix_GetMusicTitle(IntPtr music) { return SDL.UTF8_ToManaged( INTERNAL_Mix_GetMusicTitle(music) ); } /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, EntryPoint = "Mix_GetMusicTitleTag", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr INTERNAL_Mix_GetMusicTitleTag(IntPtr music); public static string Mix_GetMusicTitleTag(IntPtr music) { return SDL.UTF8_ToManaged( INTERNAL_Mix_GetMusicTitleTag(music) ); } /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, EntryPoint = "Mix_GetMusicArtistTag", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr INTERNAL_Mix_GetMusicArtistTag(IntPtr music); public static string Mix_GetMusicArtistTag(IntPtr music) { return SDL.UTF8_ToManaged( INTERNAL_Mix_GetMusicArtistTag(music) ); } /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, EntryPoint = "Mix_GetMusicAlbumTag", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr INTERNAL_Mix_GetMusicAlbumTag(IntPtr music); public static string Mix_GetMusicAlbumTag(IntPtr music) { return SDL.UTF8_ToManaged( INTERNAL_Mix_GetMusicAlbumTag(music) ); } /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, EntryPoint = "Mix_GetMusicCopyrightTag", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr INTERNAL_Mix_GetMusicCopyrightTag(IntPtr music); public static string Mix_GetMusicCopyrightTag(IntPtr music) { return SDL.UTF8_ToManaged( INTERNAL_Mix_GetMusicCopyrightTag(music) ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_SetPostMix( MixFuncDelegate mix_func, IntPtr arg // void* ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_HookMusic( MixFuncDelegate mix_func, IntPtr arg // void* ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_HookMusicFinished( MusicFinishedDelegate music_finished ); /* IntPtr refers to a void* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr Mix_GetMusicHookData(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_ChannelFinished( ChannelFinishedDelegate channel_finished ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_RegisterEffect( int chan, Mix_EffectFunc_t f, Mix_EffectDone_t d, IntPtr arg // void* ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_UnregisterEffect( int channel, Mix_EffectFunc_t f ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_UnregisterAllEffects(int channel); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_SetPanning( int channel, byte left, byte right ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_SetPosition( int channel, short angle, byte distance ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_SetDistance(int channel, byte distance); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_SetReverseStereo(int channel, int flip); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_ReserveChannels(int num); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GroupChannel(int which, int tag); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GroupChannels(int from, int to, int tag); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GroupAvailable(int tag); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GroupCount(int tag); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GroupOldest(int tag); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GroupNewer(int tag); /* chunk refers to a Mix_Chunk* */ public static int Mix_PlayChannel( int channel, IntPtr chunk, int loops ) { return Mix_PlayChannelTimed(channel, chunk, loops, -1); } /* chunk refers to a Mix_Chunk* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_PlayChannelTimed( int channel, IntPtr chunk, int loops, int ticks ); /* music refers to a Mix_Music* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_PlayMusic(IntPtr music, int loops); /* music refers to a Mix_Music* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_FadeInMusic( IntPtr music, int loops, int ms ); /* music refers to a Mix_Music* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_FadeInMusicPos( IntPtr music, int loops, int ms, double position ); /* chunk refers to a Mix_Chunk* */ public static int Mix_FadeInChannel( int channel, IntPtr chunk, int loops, int ms ) { return Mix_FadeInChannelTimed(channel, chunk, loops, ms, -1); } /* chunk refers to a Mix_Chunk* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_FadeInChannelTimed( int channel, IntPtr chunk, int loops, int ms, int ticks ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_Volume(int channel, int volume); /* chunk refers to a Mix_Chunk* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_VolumeChunk( IntPtr chunk, int volume ); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_VolumeMusic(int volume); /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GetVolumeMusicStream(IntPtr music); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_HaltChannel(int channel); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_HaltGroup(int tag); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_HaltMusic(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_ExpireChannel(int channel, int ticks); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_FadeOutChannel(int which, int ms); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_FadeOutGroup(int tag, int ms); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_FadeOutMusic(int ms); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern Mix_Fading Mix_FadingMusic(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern Mix_Fading Mix_FadingChannel(int which); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_Pause(int channel); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_Resume(int channel); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_Paused(int channel); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_PauseMusic(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_ResumeMusic(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_RewindMusic(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_PausedMusic(); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_SetMusicPosition(double position); /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern double Mix_GetMusicPosition(IntPtr music); /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern double Mix_MusicDuration(IntPtr music); /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern double Mix_GetMusicLoopStartTime(IntPtr music); /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern double Mix_GetMusicLoopEndTime(IntPtr music); /* music refers to a Mix_Music* * Only available in 2.0.5 or higher. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern double Mix_GetMusicLoopLengthTime(IntPtr music); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_Playing(int channel); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_PlayingMusic(); [DllImport(nativeLibName, EntryPoint = "Mix_SetMusicCMD", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe int INTERNAL_Mix_SetMusicCMD( byte* command ); public static unsafe int Mix_SetMusicCMD(string command) { byte* utf8Cmd = SDL.Utf8EncodeHeap(command); int result = INTERNAL_Mix_SetMusicCMD( utf8Cmd ); Marshal.FreeHGlobal((IntPtr) utf8Cmd); return result; } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_SetSynchroValue(int value); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_GetSynchroValue(); [DllImport(nativeLibName, EntryPoint = "Mix_SetSoundFonts", CallingConvention = CallingConvention.Cdecl)] private static extern unsafe int INTERNAL_Mix_SetSoundFonts( byte* paths ); public static unsafe int Mix_SetSoundFonts(string paths) { byte* utf8Paths = SDL.Utf8EncodeHeap(paths); int result = INTERNAL_Mix_SetSoundFonts( utf8Paths ); Marshal.FreeHGlobal((IntPtr) utf8Paths); return result; } [DllImport(nativeLibName, EntryPoint = "Mix_GetSoundFonts", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr INTERNAL_Mix_GetSoundFonts(); public static string Mix_GetSoundFonts() { return SDL.UTF8_ToManaged( INTERNAL_Mix_GetSoundFonts() ); } [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_EachSoundFont( SoundFontDelegate function, IntPtr data // void* ); /* Only available in 2.0.5 or later. */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern int Mix_SetTimidityCfg( [In()] [MarshalAs(UnmanagedType.LPStr)] string path ); /* Only available in 2.0.5 or later. */ [DllImport(nativeLibName, EntryPoint = "Mix_GetTimidityCfg", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr INTERNAL_Mix_GetTimidityCfg(); public static string Mix_GetTimidityCfg() { return SDL.UTF8_ToManaged( INTERNAL_Mix_GetTimidityCfg() ); } /* IntPtr refers to a Mix_Chunk* */ [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr Mix_GetChunk(int channel); [DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)] public static extern void Mix_CloseAudio(); public static string Mix_GetError() { return SDL.SDL_GetError(); } public static void Mix_SetError(string fmtAndArglist) { SDL.SDL_SetError(fmtAndArglist); } public static void Mix_ClearError() { SDL.SDL_ClearError(); } #endregion } } ================================================ FILE: source/Playnite/Safe Mode.bat ================================================ start Playnite.DesktopApp.exe --safestartup ================================================ FILE: source/Playnite/Scripting/PlayniteScript.cs ================================================ using Playnite.Scripting.PowerShell; using Playnite.SDK; using Playnite.SDK.Events; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Scripting { public enum SupportedMenuMethods { GameMenu, MainMenu } public class ScriptFunctionExport : ExtensionFunction { public string FunctionName { get; set; } public PlayniteScript Script { get; set; } public ScriptFunctionExport(string name, string functionName, PlayniteScript script) : base (name) { Name = name; FunctionName = functionName; Script = script; } public override string ToString() { return Name; } public override void Invoke() { Script.InvokeExportedFunction(this); } } public abstract class PlayniteScript: IDisposable { private static ILogger logger = LogManager.GetLogger(); public List SupportedEvents { get; internal set; } public List SupportedMenus { get; internal set; } public string Path { get; private set; } private string name; public string Name { get => name ?? System.IO.Path.GetFileName(Path); } public PlayniteScript(string path, string name = null) { Path = path; this.name = name; } public virtual List GetGameMenuItems(GetGameMenuItemsArgs args) { return new List(); } public virtual List GetMainMenuItems(GetMainMenuItemsArgs args) { return new List(); } public static PlayniteScript FromFile(string path, string name) { var extension = System.IO.Path.GetExtension(path).ToLower(); if (extension == ".psm1" || extension == ".psd1") { if (PowerShellRuntime.IsInstalled) { return new PowerShellScript(path, name); } else { logger.Warn("Cannot load PowerShell script, PowerShell 3+ not installed."); return null; } } else { throw new Exception("Cannot load script file, uknown format."); } } public override string ToString() { return System.IO.Path.GetFileName(Path); } public virtual void Dispose() { } public abstract object InvokeFunction(string functionName); public abstract object InvokeFunction(string functionName, List arguments); public abstract void InvokeExportedFunction(ScriptFunctionExport function); public abstract void SetVariable(string name, object value); public abstract void OnApplicationStarted(); public abstract void OnApplicationStopped(); public abstract void OnLibraryUpdated(); public abstract void OnGameStarting(OnGameStartingEventArgs args); public abstract void OnGameStarted(OnGameStartedEventArgs args); public abstract void OnGameStopped(OnGameStoppedEventArgs args); public abstract void OnGameInstalled(OnGameInstalledEventArgs args); public abstract void OnGameInstallationCancelled(OnGameInstallationCancelledEventArgs args); public abstract void OnGameUninstalled(OnGameUninstalledEventArgs args); public abstract void OnGameSelected(OnGameSelectedEventArgs args); public abstract void OnGameStartupCancelled(OnGameStartupCancelledEventArgs args); } } ================================================ FILE: source/Playnite/Scripting/PowerShell/PowerShell.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.Collections.ObjectModel; using Playnite.API; using Microsoft.Win32; using System.IO; using Playnite.SDK.Exceptions; using Microsoft.PowerShell; using Playnite.SDK; using System.Diagnostics; using Playnite.Native; using System.Windows; namespace Playnite.Scripting.PowerShell { public interface IPowerShellRuntime : IDisposable { object Execute(string script, string workDir = null, Dictionary variables = null); object ExecuteFile(string path, string workDir = null); object ExecuteFile(string path, string workDir = null, Dictionary variables = null); void SetVariable(string name, object value); object GetVariable(string name); CommandInfo GetFunction(string name); void ImportModule(string path); object InvokeFunction(string name, List arguments); } public class DummyPowerShellRuntime : IPowerShellRuntime { public DummyPowerShellRuntime() { } public object Execute(string script, string workDir = null, Dictionary variables = null) { return null; } public object ExecuteFile(string path, string workDir = null) { return null; } public object ExecuteFile(string path, string workDir = null, Dictionary variables = null) { return null; } public void SetVariable(string name, object value) { } public object GetVariable(string name) { return null; } public CommandInfo GetFunction(string name) { throw new NotImplementedException(); } public void ImportModule(string path) { throw new NotImplementedException(); } public object InvokeFunction(string name, List arguments) { return null; } public void Dispose() { } } public class PowerShellRuntime : IPowerShellRuntime { private static PowerShellRuntime interactiveRuntime; private static Process interactiveProcess; private System.Management.Automation.PowerShell powershell; private Runspace runspace; private PSModuleInfo module; private InitialSessionState initialSessionState; public bool IsDisposed { get; internal set; } public static bool IsInstalled { get { return Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\3", "Install", null)?.ToString() == "1"; } } public PowerShellRuntime(string runspaceName) { if (!IsInstalled) { throw new Exception("PowerShell 5.1 is not installed."); } initialSessionState = InitialSessionState.CreateDefault(); initialSessionState.ExecutionPolicy = ExecutionPolicy.Bypass; initialSessionState.ThreadOptions = PSThreadOptions.UseCurrentThread; powershell = System.Management.Automation.PowerShell.Create(initialSessionState); runspace = powershell.Runspace; runspace.Name = runspaceName; SetVariable("ErrorActionPreference", "Stop"); SetVariable("__logger", LogManager.GetLogger(runspaceName)); } public void Dispose() { IsDisposed = true; runspace.Close(); runspace.Dispose(); } public static void StartInteractiveSession(Dictionary variables = null) { if (interactiveRuntime != null) { throw new Exception("Interactive session is already running"); } interactiveRuntime = new PowerShellRuntime("PSInteractive"); variables?.ForEach(a => interactiveRuntime.SetVariable(a.Key, a.Value)); interactiveProcess = new Process(); interactiveProcess.StartInfo.FileName = @"c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"; // This is really sad solution, but there's currently no way how to initialize these variables automatically, because: // - Enter-PSHostProcess is blocking so we can't pass any command after it // - We can't redirect stdin because then user wouldn't be able to interact witht the console // - Messages like WM_PASTE don't work on PowerShell console // - Passing CTLR-V and ENTER is not possible, because the only reliable method works globaly and can't be sent directly to window handle interactiveProcess.StartInfo.Arguments = $"-NoExit -Command \"" + $"Write-Host \"`n" + $"`tConnected to Playnite process.`n" + $"`tUse CTLR-V and ENTER to paste commands to initialize basic SDK variables.`n" + $"`tMore information at:`n" + $"`thttps://playnite.link/docs/master/tutorials/extensions/scriptingDebugging.html`n" + $"\" -ForegroundColor Green;" + $"Enter-PSHostProcess -Id {Process.GetCurrentProcess().Id};"; interactiveProcess.EnableRaisingEvents = true; interactiveProcess.Exited += (_, __) => { interactiveProcess.Dispose(); interactiveProcess = null; interactiveRuntime.Dispose(); interactiveRuntime = null; }; interactiveProcess.Start(); Clipboard.SetText(@"$PlayniteRunspace = Get-Runspace -Name 'PSInteractive' $PlayniteApi = $PlayniteRunspace.SessionStateProxy.GetVariable('PlayniteApi') "); } public void ImportModule(string path) { powershell.Runspace.SessionStateProxy.Path.SetLocation(Path.GetDirectoryName(path)); module = powershell .AddCommand("Import-Module") .AddParameter("PassThru") .AddArgument(path) .Invoke().FirstOrDefault(); powershell.Streams.ClearStreams(); powershell.Commands.Clear(); } public object Execute(string script, string workDir = null, Dictionary variables = null) { if (!workDir.IsNullOrEmpty() && Directory.Exists(workDir)) { runspace.SessionStateProxy.Path.PushCurrentLocation("main"); runspace.SessionStateProxy.Path.SetLocation(WildcardPattern.Escape(workDir)); } try { using (var pipe = runspace.CreatePipeline(script)) { if (variables != null) { foreach (var key in variables.Keys) { runspace.SessionStateProxy.SetVariable(key, variables[key]); } } Collection result = null; try { result = pipe.Invoke(); } catch (RuntimeException e) { throw new ScriptRuntimeException(e.Message, e.ErrorRecord.ScriptStackTrace); } if (result == null) { return null; } else { if (result.Count == 1) { return result[0]?.BaseObject; } else { return result.Select(a => a?.BaseObject).ToList(); } } } } finally { if (!workDir.IsNullOrEmpty() && Directory.Exists(workDir) && runspace.RunspaceStateInfo.State == RunspaceState.Opened) { runspace.SessionStateProxy.Path.PopLocation("main"); } } } public object ExecuteFile(string path, string workDir = null) { return ExecuteFile(path, workDir); } public object ExecuteFile(string path, string workDir = null, Dictionary variables = null) { var cmd = "& '{0}' $__FileArg".Format(Path.GetFullPath(path)); return Execute(cmd, workDir, variables != null ? new Dictionary { { "__FileArg", variables } } : null); } public void SetVariable(string name, object value) { runspace.SessionStateProxy.SetVariable(name, value); } public object GetVariable(string name) { return runspace.SessionStateProxy.GetVariable(name); } public CommandInfo GetFunction(string name) { if (module == null) { return null; } CommandInfo command; return module.ExportedCommands.TryGetValue(name, out command) ? command : null; } public object InvokeFunction(string name, List arguments) { try { var command = GetFunction(name); powershell.AddCommand(command); foreach (var argument in arguments) { powershell.AddArgument(argument); } var result = powershell.Invoke(); if (result.Count == 1) { return result[0].BaseObject; } else { return result.Select(a => a?.BaseObject).ToList(); } } finally { powershell.Streams.ClearStreams(); powershell.Commands.Clear(); } } } } ================================================ FILE: source/Playnite/Scripting/PowerShell/PowerShellScript.cs ================================================ using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Events; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Management.Automation; using Playnite.Common; using Playnite.SDK.Plugins; namespace Playnite.Scripting.PowerShell { public class PowerShellScript : PlayniteScript { private static ILogger logger = LogManager.GetLogger(); public PowerShellRuntime Runtime { get; } public PowerShellScript(string path, string name) : base(path, name) { Runtime = new PowerShellRuntime(Name); Runtime.ImportModule(path); SupportedEvents = GetSupportedEvents(); SupportedMenus = GetSupportedMenus(); } public override void Dispose() { base.Dispose(); Runtime.Dispose(); } public override List GetGameMenuItems(GetGameMenuItemsArgs args) { if (SupportedMenus.Contains(SupportedMenuMethods.GameMenu)) { var res = InvokeFunction(nameof(Plugin.GetGameMenuItems), new List { args }); if (res is ScriptGameMenuItem item) { return new List { item }; } else if (res is List items) { return items.Cast().ToList(); } else { return base.GetGameMenuItems(args); } } else { return base.GetGameMenuItems(args); } } public override List GetMainMenuItems(GetMainMenuItemsArgs args) { if (SupportedMenus.Contains(SupportedMenuMethods.MainMenu)) { var res = InvokeFunction(nameof(Plugin.GetMainMenuItems), new List { args }); if (res is ScriptMainMenuItem item) { return new List { item }; } else if (res is List items) { return items.Cast().ToList(); } else { return base.GetMainMenuItems(args); } } else { return base.GetMainMenuItems(args); } } internal List GetSupportedMenus() { var menus = new List(); if (Runtime.GetFunction(nameof(Plugin.GetMainMenuItems)) != null) { menus.Add(SupportedMenuMethods.MainMenu); } if (Runtime.GetFunction(nameof(Plugin.GetGameMenuItems)) != null) { menus.Add(SupportedMenuMethods.GameMenu); } return menus; } internal List GetSupportedEvents() { var events = new List(); foreach (ApplicationEvent ev in Enum.GetValues(typeof(ApplicationEvent))) { if (Runtime.GetFunction(ev.ToString()) != null) { events.Add(ev); } } return events; } public override void InvokeExportedFunction(ScriptFunctionExport function) { Runtime.Execute(function.FunctionName); } public override void SetVariable(string name, object value) { Runtime.SetVariable(name, value); } public override void OnApplicationStarted() { if (SupportedEvents.Contains(ApplicationEvent.OnApplicationStarted)) { InvokeFunction(ApplicationEvent.OnApplicationStarted.ToString()); } } public override void OnApplicationStopped() { if (SupportedEvents.Contains(ApplicationEvent.OnApplicationStopped)) { InvokeFunction(ApplicationEvent.OnApplicationStopped.ToString()); } } public override void OnLibraryUpdated() { if (SupportedEvents.Contains(ApplicationEvent.OnLibraryUpdated)) { InvokeFunction(ApplicationEvent.OnLibraryUpdated.ToString()); } } public override void OnGameStarting(OnGameStartingEventArgs args) { if (SupportedEvents.Contains(ApplicationEvent.OnGameStarting)) { InvokeFunction(ApplicationEvent.OnGameStarting.ToString(), new List { args }); } } public override void OnGameStarted(OnGameStartedEventArgs args) { if (SupportedEvents.Contains(ApplicationEvent.OnGameStarted)) { InvokeFunction(ApplicationEvent.OnGameStarted.ToString(), new List { args }); } } public override void OnGameStopped(OnGameStoppedEventArgs args) { if (SupportedEvents.Contains(ApplicationEvent.OnGameStopped)) { InvokeFunction(ApplicationEvent.OnGameStopped.ToString(), new List { args }); } } public override void OnGameStartupCancelled(OnGameStartupCancelledEventArgs args) { if (SupportedEvents.Contains(ApplicationEvent.OnGameStartupCancelled)) { InvokeFunction(ApplicationEvent.OnGameStartupCancelled.ToString(), new List { args }); } } public override void OnGameInstalled(OnGameInstalledEventArgs args) { if (SupportedEvents.Contains(ApplicationEvent.OnGameInstalled)) { InvokeFunction(ApplicationEvent.OnGameInstalled.ToString(), new List { args }); } } public override void OnGameInstallationCancelled(OnGameInstallationCancelledEventArgs args) { if (SupportedEvents.Contains(ApplicationEvent.OnGameInstallationCancelled)) { InvokeFunction(ApplicationEvent.OnGameInstallationCancelled.ToString(), new List { args }); } } public override void OnGameUninstalled(OnGameUninstalledEventArgs args) { if (SupportedEvents.Contains(ApplicationEvent.OnGameUninstalled)) { InvokeFunction(ApplicationEvent.OnGameUninstalled.ToString(), new List { args }); } } public override void OnGameSelected(OnGameSelectedEventArgs args) { if (SupportedEvents.Contains(ApplicationEvent.OnGameSelected)) { InvokeFunction(ApplicationEvent.OnGameSelected.ToString(), new List { args }); } } public override object InvokeFunction(string functionName) { return Runtime.InvokeFunction(functionName, new List()); } public override object InvokeFunction(string functionName, List arguments) { return Runtime.InvokeFunction(functionName, arguments); } } } ================================================ FILE: source/Playnite/SdkHelpers.cs ================================================ using Playnite.Common; using Playnite.Database; using Playnite.Extensions.Markup; using Playnite.SDK; using System; using System.Collections.Generic; using System.Drawing.Imaging; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; namespace Playnite { public class SdkHelpers { private static readonly ILogger logger = LogManager.GetLogger(); public static object ResolveUiItemIcon(object icon, SynchronizationContext syncContext = null) { if (syncContext == null) { syncContext = SynchronizationContext.Current; } if (icon == null) { return null; } try { if (icon is string stringIcon) { var resource = ResourceProvider.GetResource(stringIcon); if (resource != null) { if (resource is BitmapImage bitmap) { var image = new System.Windows.Controls.Image() { Source = bitmap }; RenderOptions.SetBitmapScalingMode(image, RenderOptions.GetBitmapScalingMode(bitmap)); return image; } else if (resource is TextBlock textIcon) { var text = new TextBlock { Text = textIcon.Text, FontFamily = textIcon.FontFamily, FontStyle = textIcon.FontStyle }; if (textIcon.ReadLocalValue(TextBlock.ForegroundProperty) != DependencyProperty.UnsetValue) { text.Foreground = textIcon.Foreground; } return text; } } else if (stringIcon.IsHttpUrl()) { try { var cachedFile = HttpFileCache.GetWebFile(stringIcon); if (string.IsNullOrEmpty(cachedFile)) { return null; } var image = BitmapExtensions.BitmapFromFile(cachedFile); Image imageObj = null; if (image != null) { syncContext.Send(_ => { imageObj = image.ToImage(); RenderOptions.SetBitmapScalingMode(imageObj, BitmapScalingMode.Fant); }, null); } return imageObj; } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, $"Failed to create bitmap from {stringIcon} file."); return null; } } else if (System.IO.File.Exists(stringIcon)) { var image = BitmapExtensions.BitmapFromFile(stringIcon); Image imageObj = null; if (image != null) { syncContext.Send(_ => { imageObj = image.ToImage(); RenderOptions.SetBitmapScalingMode(imageObj, BitmapScalingMode.Fant); }, null); } return imageObj; } else { var themeFile = ThemeFile.GetFilePath(stringIcon); if (themeFile != null) { return Images.GetImageFromFile(themeFile, BitmapScalingMode.Fant, double.NaN, double.NaN); } var dbFile = GameDatabase.Instance.GetFileAsImage(stringIcon); if (dbFile != null) { return dbFile.ToImage(); } } } else { return icon; } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to get icon object."); } return null; } } } ================================================ FILE: source/Playnite/SelectableItem.cs ================================================ using Playnite; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows.Data; namespace System { public class NamedObject : ObservableObject { public string Name { get; } public TItem Value { get; } public NamedObject(TItem value) { Value = value; Name = value.ToString(); } public NamedObject(TItem value, string name) { Value = value; Name = name; } public override string ToString() { return Name; } public override bool Equals(object obj) { return Equals(obj as NamedObject); } public bool Equals(NamedObject p) { if (p is null) { return false; } return Value.Equals(p.Value); } public override int GetHashCode() { return Value.GetHashCode(); } public static bool operator ==(NamedObject l, NamedObject r) { if ((l is null && !(r is null)) || (!(l is null) && r is null)) { return false; } if (l is null && r is null) { return true; } return l.Value.Equals(r.Value); } public static bool operator !=(NamedObject l, NamedObject r) { return !(l == r); } } // TODO: refactor this and merge it with NamedObject and SelectableItem public class SelectableNamedObject : NamedObject { private bool selected = false; public bool Selected { get => selected; set => SetValue(ref selected, value); } public SelectableNamedObject(TItem value) : base(value) { } public SelectableNamedObject(TItem value, string name) : base(value, name) { } public SelectableNamedObject(TItem value, string name, bool selected) : base(value, name) { Selected = selected; } } // TODO: refactor this and merge it with NamedObject and SelectableNamedObject public class SelectableItem : ObservableObject { private bool? selected = false; public bool? Selected { get => selected; set { selected = value; OnPropertyChanged(); } } private TItem item; public TItem Item { get => item; set { item = value; OnPropertyChanged(); } } public SelectableItem(TItem item) { this.item = item; } public override string ToString() { return Item?.ToString() ?? base.ToString(); } } public class SelectableObjectList : ObservableObject, ICollection>, INotifyCollectionChanged { internal readonly List> Items; public int Count => Items.Count; public bool IsReadOnly => true; public string AsString => ToString(); public event EventHandler SelectionChanged; public event NotifyCollectionChangedEventHandler CollectionChanged; public SelectableObjectList( IEnumerable collection, IEnumerable selected = null) { if (collection.HasItems()) { Items = new List>(collection.Select(a => { var newItem = new SelectableItem(a); newItem.Selected = selected?.Contains(a) == true; newItem.PropertyChanged += NewItem_PropertyChanged; return newItem; })); } else { Items = new List>(); } } internal virtual void OnSelectionChanged() { OnPropertyChanged(nameof(AsString)); SelectionChanged?.Invoke(this, EventArgs.Empty); } internal void OnCollectionChanged() { CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } internal void NewItem_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(SelectableItem.Selected)) { if (!SuppressNotifications) { OnSelectionChanged(); } } } public void SetSelection(IEnumerable toSelect) { SuppressNotifications = true; if (toSelect?.Any() == true) { foreach (var item in Items) { item.Selected = toSelect?.Contains(item.Item) == true; } } else { Items.ForEach(a => a.Selected = false); } SuppressNotifications = false; OnSelectionChanged(); } public List GetSelectedItems() { return Items.Where(a => a.Selected == true).Select(a => a.Item).ToList(); } public void Add(TItem item, bool selected = false) { if (Items.Any(a => a.Item.Equals(item))) { return; } var newItem = new SelectableItem(item) { Selected = selected }; newItem.PropertyChanged += NewItem_PropertyChanged; Items.Add(newItem); OnCollectionChanged(); if (selected) { OnSelectionChanged(); } } public virtual void SetItems(IEnumerable items, IEnumerable selected = null) { SuppressNotifications = true; var oldSelection = GetSelectedItems(); foreach (var item in Items) { item.PropertyChanged -= NewItem_PropertyChanged; } Items.Clear(); if (items.HasItems()) { foreach (var item in items) { var newItem = new SelectableItem(item) { Selected = selected?.Contains(item) == true }; newItem.PropertyChanged += NewItem_PropertyChanged; Items.Add(newItem); } } SuppressNotifications = false; OnCollectionChanged(); if (!oldSelection.IsListEqual(GetSelectedItems())) { OnSelectionChanged(); } } public void Add(SelectableItem item) { throw new NotImplementedException(); } public bool Remove(SelectableItem item) { throw new NotImplementedException(); } public void Clear() { throw new NotImplementedException(); } public bool Contains(SelectableItem item) { throw new NotImplementedException(); } public void CopyTo(SelectableItem[] array, int arrayIndex) { Items.CopyTo(array, arrayIndex); } public IEnumerator> GetEnumerator() { return Items.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return Items.GetEnumerator(); } public override string ToString() { return string.Join(", ", this.Where(a => a.Selected == true).Select(a => a.Item.ToString())); } } public class SelectableStringList : SelectableObjectList { private readonly bool includeNoneItem; public SelectableStringList( IEnumerable collection, IEnumerable selected = null, bool includeNoneItem = false) : base(collection, selected) { this.includeNoneItem = includeNoneItem; if (includeNoneItem) { var newItem = new SelectableItem(Playnite.FilterSettings.MissingFieldString); newItem.PropertyChanged += NewItem_PropertyChanged; Items.Insert(0, newItem); } } public override void SetItems(IEnumerable items, IEnumerable selected = null) { SuppressNotifications = true; var oldSelection = GetSelectedItems(); foreach (var item in Items) { item.PropertyChanged -= NewItem_PropertyChanged; } Items.Clear(); if (includeNoneItem) { var newItem = new SelectableItem(Playnite.FilterSettings.MissingFieldString); newItem.PropertyChanged += NewItem_PropertyChanged; Items.Add(newItem); } foreach (var item in items) { var newItem = new SelectableItem(item) { Selected = selected?.Contains(item) == true }; newItem.PropertyChanged += NewItem_PropertyChanged; Items.Add(newItem); } SuppressNotifications = false; OnCollectionChanged(); if (!oldSelection.IsListEqual(GetSelectedItems())) { OnSelectionChanged(); } } } public abstract class SelectableIdItemList : ObservableObject { public event EventHandler SelectionChanged; public abstract string AsString { get; } public abstract List GetSelectedIds(); public abstract void SetSelection(IEnumerable toSelect); internal virtual void OnSelectionChanged() { OnPropertyChanged(nameof(AsString)); SelectionChanged?.Invoke(this, EventArgs.Empty); } } public class SelectableIdItemList : SelectableIdItemList, ICollection> { private readonly Func idSelector; internal readonly List> Items; public event NotifyCollectionChangedEventHandler CollectionChanged; public override string AsString { get => ToString(); } public SelectableIdItemList( IEnumerable collection, Func idSelector, IEnumerable selected = null, IEnumerable undetermined = null) { this.idSelector = idSelector; Items = new List>(collection.Select(a => { var newItem = new SelectableItem(a); if (selected?.Contains(idSelector(a)) == true) { newItem.Selected = true; } else if (undetermined?.Contains(idSelector(a)) == true) { newItem.Selected = null; } else { newItem.Selected = false; } newItem.PropertyChanged += NewItem_PropertyChanged; return newItem; })); } internal void NewItem_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(SelectableItem.Selected)) { if (!SuppressNotifications) { OnSelectionChanged(); } } } public override void SetSelection(IEnumerable toSelect) { SuppressNotifications = true; if (toSelect?.Any() == true) { foreach (var item in Items) { item.Selected = toSelect?.Contains(idSelector(item.Item)) == true; } } else { Items.ForEach(a => a.Selected = false); } SuppressNotifications = false; OnSelectionChanged(); } public override List GetSelectedIds() { return Items.Where(a => a.Selected == true).Select(a => idSelector(a.Item)).ToList(); } public int Count => Items.Count; public bool IsReadOnly => true; public void Add(SelectableItem item) { throw new NotImplementedException(); } public void Clear() { foreach (var item in Items) { item.PropertyChanged -= NewItem_PropertyChanged; } Items.Clear(); OnSelectionChanged(); OnCollectionChanged(); } public bool Contains(SelectableItem item) { throw new NotImplementedException(); } public void CopyTo(SelectableItem[] array, int arrayIndex) { Items.CopyTo(array, arrayIndex); } public bool Remove(SelectableItem item) { throw new NotImplementedException(); } public IEnumerator> GetEnumerator() { return Items.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return Items.GetEnumerator(); } internal void OnCollectionChanged() { CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } public override string ToString() { return string.Join(", ", this.Where(a => a.Selected == true).Select(a => a.Item.ToString())); } } /// /// TODO: Reworkd this whole thing with CollectionView, this should be just data holder... /// public class SelectableDbItemList : SelectableIdItemList, INotifyCollectionChanged { private readonly bool includeNoneItem; private bool showSelectedOnly = false; public bool ShowSelectedOnly { get { return showSelectedOnly; } set { showSelectedOnly = value; OnPropertyChanged(); CollectionView?.Refresh(); } } private string searchText = string.Empty; public string SearchText { get { return searchText; } set { searchText = value; OnPropertyChanged(); CollectionView?.Refresh(); } } private ListCollectionView collectionView; public ListCollectionView CollectionView { get => collectionView; private set { collectionView = value; OnPropertyChanged(); } } public SelectableDbItemList( IEnumerable collection, IEnumerable selected = null, IEnumerable undetermined = null, bool includeNoneItem = false) : base(collection.OrderBy(a => a.Name), (a) => a.Id, selected, undetermined) { this.includeNoneItem = includeNoneItem; if (includeNoneItem) { var newItem = new SelectableItem(new DatabaseObject() { Id = Guid.Empty, Name = ResourceProvider.GetString("LOCNone") }) { Selected = selected?.Contains(Guid.Empty) == true }; newItem.PropertyChanged += NewItem_PropertyChanged; Items.Insert(0, newItem); } CollectionView = (ListCollectionView)CollectionViewSource.GetDefaultView(Items); CollectionView.Filter = CollectionViewFilter; CollectionView.SortDescriptions.Add(new SortDescription("Item.Name", ListSortDirection.Ascending)); } public void Add(DatabaseObject item, bool selected = false) { var existing = Items.FirstOrDefault(a => a.Item.Id == item.Id); if (existing != null) { if (selected) { existing.Selected = true; } } var newItem = new SelectableItem(item) { Selected = selected }; newItem.PropertyChanged += NewItem_PropertyChanged; Items.Add(newItem); OnCollectionChanged(); if (selected) { OnSelectionChanged(); } CollectionView?.Refresh(); } public void SetItems(IEnumerable items, IEnumerable selected = null) { SuppressNotifications = true; var oldSelection = GetSelectedIds(); foreach (var item in Items) { item.PropertyChanged -= NewItem_PropertyChanged; } Items.Clear(); if (includeNoneItem) { var noneItem = new SelectableItem(new DatabaseObject() { Id = Guid.Empty, Name = ResourceProvider.GetString("LOCNone") }) { Selected = selected?.Contains(Guid.Empty) == true }; noneItem.PropertyChanged += NewItem_PropertyChanged; Items.Add(noneItem); } foreach (var item in items.OrderBy(a => a.Name)) { var newItem = new SelectableItem(item) { Selected = selected?.Contains(item.Id) == true }; newItem.PropertyChanged += NewItem_PropertyChanged; Items.Add(newItem); } SuppressNotifications = false; OnCollectionChanged(); if (!oldSelection.IsListEqual(GetSelectedIds())) { OnSelectionChanged(); } CollectionView?.Refresh(); } public bool Remove(DatabaseObject item) { var listItem = Items.FirstOrDefault(a => a.Item.Id == item.Id); if (listItem == null) { return false; } else { listItem.PropertyChanged -= NewItem_PropertyChanged; Items.Remove(listItem); if (listItem.Selected == true) { OnSelectionChanged(); } OnCollectionChanged(); CollectionView?.Refresh(); return true; } } public bool ContainsId(Guid id) { return Items.Any(a => a.Item.Id == id); } public bool ContainsIds(IEnumerable ids) { return Items.Select(a => a.Item.Id).Contains(ids); } private bool CollectionViewFilter(object item) { var entry = (SelectableItem)item; return (ShowSelectedOnly ? entry.Selected == true : true) && entry.Item.Name.Contains(SearchText, StringComparison.OrdinalIgnoreCase); } public override string ToString() { return string.Join(", ", this.Where(a => a.Selected == true).Select(a => a.Item.Name)); } } public class SelectableLibraryPluginList : SelectableIdItemList { public SelectableLibraryPluginList(IEnumerable collection, IEnumerable selected = null) : base(collection, (a) => a.Id, selected) { } public override string ToString() { return string.Join(", ", this.Where(a => a.Selected == true).Select(a => a.Item.Name)); } } } ================================================ FILE: source/Playnite/Services/BaseServicesClient.cs ================================================ using Playnite.Common; using Playnite.SDK; using Playnite.SDK.Data; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Serialization = Playnite.SDK.Data.Serialization; namespace Playnite.Services { public class BaseServicesClient { private static ILogger logger = LogManager.GetLogger(); public readonly string Endpoint; public HttpClient HttpClient = new HttpClient() { Timeout = new TimeSpan(0, 0, 60) }; public BaseServicesClient(string endpoint, Version playniteVersion) { Endpoint = endpoint.TrimEnd('/'); HttpClient.DefaultRequestHeaders.Add("Playnite-Version", playniteVersion.ToString(4)); } public T ExecuteGetRequest(string subUrl) { var url = Uri.EscapeUriString(Endpoint + subUrl); var strResult = HttpClient.GetStringAsync(url).GetAwaiter().GetResult(); var result = Serialization.FromJson>(strResult); if (!string.IsNullOrEmpty(result.Error)) { logger.Error("Service request error by proxy: " + result.Error); throw new Exception(result.Error); } return result.Data; } public T ExecutePostRequest(string subUrl, string jsonContent) { var url = Uri.EscapeUriString(Endpoint + subUrl); var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); var response = HttpClient.PostAsync(url, content).GetAwaiter().GetResult(); var strResult = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); var result = Serialization.FromJson>(strResult); if (!string.IsNullOrEmpty(result.Error)) { logger.Error("Service request error by proxy: " + result.Error); throw new Exception(result.Error); } return result.Data; } } } ================================================ FILE: source/Playnite/Services/GenericResponse.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Playnite.Services { public class GenericResponse { public string Error { get; set; } public object Data { get; set; } public GenericResponse() { } public GenericResponse(object data, string error) { Data = data; Error = error; } } public class ServicesResponse { public string Error { get; set; } public T Data { get; set; } public ServicesResponse(T data, string error) { Data = data; Error = error; } } } ================================================ FILE: source/Playnite/Services/ServicesClient.cs ================================================ using Microsoft.Win32; using Newtonsoft.Json; using Playnite.Common; using Playnite.SDK; using System; using System.Collections.Generic; using System.Configuration; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace Playnite.Services { public class ServicesClient : BaseServicesClient { public class RecommendedAddons { public Dictionary Libraries { get; set; } public Dictionary Generic { get; set; } } private static ILogger logger = LogManager.GetLogger(); public ServicesClient() : this(ConfigurationManager.AppSettings["ServicesUrl"]) { } public ServicesClient(string endpoint) : base(endpoint, Updater.CurrentVersion) { } public List GetPatrons() { return ExecuteGetRequest>("/patreon/patrons"); } public Guid UploadDiagPackage(string diagPath) { using (var fs = new FileStream(diagPath, FileMode.Open)) { using (var content = new StreamContent(fs)) { var response = HttpClient.PostAsync(Endpoint + "/playnite/diag", content).GetAwaiter().GetResult(); var strResult = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); var result = JsonConvert.DeserializeObject>(strResult); if (!string.IsNullOrEmpty(result.Error)) { logger.Error("Service request error by proxy: " + result.Error); throw new Exception(result.Error); } return result.Data; } } } public List GetAllAddons(AddonType type, string searchTerm) { return ExecuteGetRequest>($"/addons?type={type}&searchTerm={searchTerm}".UrlEncode()); } public AddonManifest GetAddon(string addonId) { return ExecuteGetRequest>($"/addons?addonId={addonId}".UrlEncode()).FirstOrDefault(); } public AddonInstallerManifest GetAddonInstaller(string addonId) { return ExecuteGetRequest($"/addons/installer?addonId={addonId}".UrlEncode()); } public string[] GetAddonBlacklist() { return ExecuteGetRequest("/addons/blacklist") ?? new string[0]; } public RecommendedAddons GetDefaultExtensions() { var stringData = ExecuteGetRequest("/addons/defaultextensions"); if (stringData.IsNullOrEmpty()) { return new RecommendedAddons(); } else { return Serialization.FromJson(stringData); } } } } ================================================ FILE: source/Playnite/Settings/AutoClientShutdownSettings.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class AutoClientShutdownSettings : ObservableObject { private uint graceTimeout = 60; public uint GraceTimeout { get => graceTimeout; set { graceTimeout = value; OnPropertyChanged(); } } private uint minimalSessionTime = 120; public uint MinimalSessionTime { get => minimalSessionTime; set { minimalSessionTime = value; OnPropertyChanged(); } } private bool shutdownClients = false; public bool ShutdownClients { get => shutdownClients; set { shutdownClients = value; OnPropertyChanged(); } } private List shutdownPlugins = new List(); public List ShutdownPlugins { get => shutdownPlugins; set { shutdownPlugins = value; OnPropertyChanged(); } } } } ================================================ FILE: source/Playnite/Settings/DetailsVisibilitySettings.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class DetailsVisibilitySettings : ObservableObject { private bool library = true; public bool Library { get { return library; } set { library = value; OnPropertyChanged(); } } private bool playTime = true; public bool PlayTime { get { return playTime; } set { playTime = value; OnPropertyChanged(); } } private bool installSize = true; public bool InstallSize { get { return installSize; } set { installSize = value; OnPropertyChanged(); } } private bool installDirectory = true; public bool InstallDirectory { get { return installDirectory; } set { installDirectory = value; OnPropertyChanged(); } } private bool lastPlayed = false; public bool LastPlayed { get { return lastPlayed; } set { lastPlayed = value; OnPropertyChanged(); } } private bool added = false; public bool Added { get { return added; } set { added = value; OnPropertyChanged(); } } private bool recentActivity = true; public bool RecentActivity { get { return recentActivity; } set { recentActivity = value; OnPropertyChanged(); } } private bool completionStatus = false; public bool CompletionStatus { get { return completionStatus; } set { completionStatus = value; OnPropertyChanged(); } } private bool icon = false; public bool Icon { get { return icon; } set { icon = value; OnPropertyChanged(); } } private bool coverImage = true; public bool CoverImage { get { return coverImage; } set { coverImage = value; OnPropertyChanged(); } } private bool backgroundImage = true; public bool BackgroundImage { get { return backgroundImage; } set { backgroundImage = value; OnPropertyChanged(); } } private bool platform = true; public bool Platform { get { return platform; } set { platform = value; OnPropertyChanged(); } } private bool genres = true; public bool Genres { get { return genres; } set { genres = value; OnPropertyChanged(); } } private bool developers = true; public bool Developers { get { return developers; } set { developers = value; OnPropertyChanged(); } } private bool publishers = true; public bool Publishers { get { return publishers; } set { publishers = value; OnPropertyChanged(); } } private bool releaseDate = true; public bool ReleaseDate { get { return releaseDate; } set { releaseDate = value; OnPropertyChanged(); } } private bool categories = true; public bool Categories { get { return categories; } set { categories = value; OnPropertyChanged(); } } private bool tags = true; public bool Tags { get { return tags; } set { tags = value; OnPropertyChanged(); } } private bool links = true; public bool Links { get { return links; } set { links = value; OnPropertyChanged(); } } private bool description = true; public bool Description { get { return description; } set { description = value; OnPropertyChanged(); } } private bool ageRating = false; public bool AgeRating { get { return ageRating; } set { ageRating = value; OnPropertyChanged(); } } private bool series = false; public bool Series { get { return series; } set { series = value; OnPropertyChanged(); } } private bool source = false; public bool Source { get { return source; } set { source = value; OnPropertyChanged(); } } private bool region = false; public bool Region { get { return region; } set { region = value; OnPropertyChanged(); } } private bool version = false; public bool Version { get { return version; } set { version = value; OnPropertyChanged(); } } private bool communityScore = false; public bool CommunityScore { get { return communityScore; } set { communityScore = value; OnPropertyChanged(); } } private bool criticScore = false; public bool CriticScore { get { return criticScore; } set { criticScore = value; OnPropertyChanged(); } } private bool userScore = false; public bool UserScore { get { return userScore; } set { userScore = value; OnPropertyChanged(); } } private bool features = true; public bool Features { get { return features; } set { features = value; OnPropertyChanged(); } } private bool notes = true; public bool Notes { get { return notes; } set { notes = value; OnPropertyChanged(); } } private bool name = true; public bool Name { get { return name; } set { name = value; OnPropertyChanged(); } } } } ================================================ FILE: source/Playnite/Settings/FilterSettings.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Playnite.Common; using SdkModels = Playnite.SDK.Models; namespace Playnite { public class FilterChangedEventArgs : EventArgs { public List Fields { get; set; } public FilterChangedEventArgs() { } public FilterChangedEventArgs(List fields) { Fields = fields; } } public class StringFilterItemProperties : ObservableObject { [JsonIgnore] public bool IsSet => Values.HasNonEmptyItems(); private List values; public List Values { get => values; set { values = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsSet)); } } public StringFilterItemProperties() { } public StringFilterItemProperties(List values) { Values = values; } public StringFilterItemProperties(string value) { Values = new List() { value }; } public bool Equals(StringFilterItemProperties obj) { if (obj == null) { return false; } return Values.IsListEqual(obj?.Values); } public bool Equals(SdkModels.StringFilterItemProperties obj) { if (obj == null) { return false; } return Values.IsListEqual(obj?.Values); } public SdkModels.StringFilterItemProperties ToSdkModel() { if (Values.HasItems()) { return new SdkModels.StringFilterItemProperties(Values); } else { return null; } } public static StringFilterItemProperties FromSdkModel(SdkModels.StringFilterItemProperties sdk) { if (sdk != null) { if (sdk.Values.HasItems()) { return new StringFilterItemProperties(sdk.Values); } } return null; } } public class EnumFilterItemProperties : ObservableObject { [JsonIgnore] public bool IsSet => Values.HasItems(); private List values; public List Values { get => values; set { values = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsSet)); } } public EnumFilterItemProperties() { } public EnumFilterItemProperties(List values) { Values = values; } public EnumFilterItemProperties(int value) { Values = new List() { value }; } public bool Equals(EnumFilterItemProperties obj) { if (obj == null) { return false; } return Values.IsListEqual(obj?.Values); } public bool Equals(SdkModels.EnumFilterItemProperties obj) { if (obj == null) { return false; } return Values.IsListEqual(obj?.Values); } public SdkModels.EnumFilterItemProperties ToSdkModel() { if (Values.HasItems()) { return new SdkModels.EnumFilterItemProperties(Values); } else { return null; } } public static EnumFilterItemProperties FromSdkModel(SdkModels.EnumFilterItemProperties sdk) { if (sdk != null) { if (sdk.Values.HasItems()) { return new EnumFilterItemProperties(sdk.Values); } } return null; } } public class IdItemFilterItemProperties : ObservableObject { [JsonIgnore] public List Texts { get; private set; } [JsonIgnore] public bool IsSet => !Text.IsNullOrEmpty() || Ids?.Any() == true; private List ids; public List Ids { get => ids; set { ids = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsSet)); } } private string text; public string Text { get => text; set { text = value; if (text?.Contains(Constants.ListSeparator) == true) { Texts = text.Split(Constants.ListSeparators, StringSplitOptions.RemoveEmptyEntries). Select(a => a.Trim()).Where(a => !a.IsNullOrEmpty()).ToList(); } else { Texts = new List { text }; } OnPropertyChanged(); OnPropertyChanged(nameof(IsSet)); OnPropertyChanged(nameof(Texts)); } } public IdItemFilterItemProperties() { } public IdItemFilterItemProperties(List ids) { Ids = ids; } public IdItemFilterItemProperties(Guid id) { Ids = new List() { id }; } public IdItemFilterItemProperties(string text) { Text = text; } public bool ShouldSerializeText() { return !Text.IsNullOrEmpty(); } public bool ShouldSerializeIds() { return Ids.HasItems(); } public bool Equals(IdItemFilterItemProperties obj) { if (obj == null) { return false; } return Ids.IsListEqual(obj?.Ids) && Text == obj.Text; } public bool Equals(SdkModels.IdItemFilterItemProperties obj) { if (obj == null) { return false; } return Ids.IsListEqual(obj?.Ids) && Text == obj.Text; } public SdkModels.IdItemFilterItemProperties ToSdkModel() { if (!Text.IsNullOrEmpty()) { return new SdkModels.IdItemFilterItemProperties(Text); } else if (Ids.HasItems()) { return new SdkModels.IdItemFilterItemProperties(Ids); } else { return null; } } public static IdItemFilterItemProperties FromSdkModel(SdkModels.IdItemFilterItemProperties sdk) { if (sdk != null) { if (sdk.Ids.HasItems()) { return new IdItemFilterItemProperties(sdk.Ids); } else if (!sdk.Text.IsNullOrEmpty()) { return new IdItemFilterItemProperties(sdk.Text); } } return null; } } public class FilterSettings : ObservableObject { public const string MissingFieldString = "{}"; [JsonIgnore] public bool SearchActive { get => !string.IsNullOrEmpty(Name); } [JsonIgnore] public bool IsActive { get { return IsInstalled || IsUnInstalled || Hidden || Favorite || !string.IsNullOrEmpty(Name) || !string.IsNullOrEmpty(Version) || Series?.IsSet == true || Source?.IsSet == true || AgeRating?.IsSet == true || Region?.IsSet == true || Genre?.IsSet == true || Publisher?.IsSet == true || Developer?.IsSet == true || Category?.IsSet == true || Tag?.IsSet == true || Platform?.IsSet == true || Library?.IsSet == true || CompletionStatuses?.IsSet == true || UserScore?.IsSet == true || CriticScore?.IsSet == true || CommunityScore?.IsSet == true || LastActivity?.IsSet == true || RecentActivity?.IsSet == true || Added?.IsSet == true || Modified?.IsSet == true || ReleaseYear?.IsSet == true || PlayTime?.IsSet == true || InstallSize?.IsSet == true || Feature?.IsSet == true; } } private string name; public string Name { get { return name; } set { if (name != value) { name = value; OnPropertyChanged(); OnFilterChanged(nameof(Name)); OnPropertyChanged(nameof(IsActive)); OnPropertyChanged(nameof(SearchActive)); } } } private IdItemFilterItemProperties genre; public IdItemFilterItemProperties Genre { get { return genre; } set { if (genre != value) { genre = value; OnPropertyChanged(); OnFilterChanged(nameof(Genre)); } } } private IdItemFilterItemProperties platforms; public IdItemFilterItemProperties Platform { get { return platforms; } set { if (platforms != value) { platforms = value; OnPropertyChanged(); OnFilterChanged(nameof(Platform)); } } } private StringFilterItemProperties releaseDate; public StringFilterItemProperties ReleaseYear { get { return releaseDate; } set { if (releaseDate != value) { releaseDate = value; OnPropertyChanged(); OnFilterChanged(nameof(ReleaseYear)); } } } private string version; public string Version { get { return version; } set { if (version != value) { version = value; OnPropertyChanged(); OnFilterChanged(nameof(Version)); } } } private IdItemFilterItemProperties publishers; public IdItemFilterItemProperties Publisher { get { return publishers; } set { if (publishers != value) { publishers = value; OnPropertyChanged(); OnFilterChanged(nameof(Publisher)); } } } private IdItemFilterItemProperties developers; public IdItemFilterItemProperties Developer { get { return developers; } set { if (developers != value) { developers = value; OnPropertyChanged(); OnFilterChanged(nameof(Developer)); } } } private IdItemFilterItemProperties categories; public IdItemFilterItemProperties Category { get { return categories; } set { if (categories != value) { categories = value; OnPropertyChanged(); OnFilterChanged(nameof(Category)); } } } private IdItemFilterItemProperties tags; public IdItemFilterItemProperties Tag { get { return tags; } set { if (tags != value) { tags = value; OnPropertyChanged(); OnFilterChanged(nameof(Tag)); } } } private IdItemFilterItemProperties series; public IdItemFilterItemProperties Series { get { return series; } set { if (series != value) { series = value; OnPropertyChanged(); OnFilterChanged(nameof(Series)); } } } private IdItemFilterItemProperties region; public IdItemFilterItemProperties Region { get { return region; } set { if (region != value) { region = value; OnPropertyChanged(); OnFilterChanged(nameof(Region)); } } } private IdItemFilterItemProperties source; public IdItemFilterItemProperties Source { get { return source; } set { if (source != value) { source = value; OnPropertyChanged(); OnFilterChanged(nameof(Source)); } } } private IdItemFilterItemProperties ageRating; public IdItemFilterItemProperties AgeRating { get { return ageRating; } set { if (ageRating != value) { ageRating = value; OnPropertyChanged(); OnFilterChanged(nameof(AgeRating)); } } } private bool useAndFilteringStyle; public bool UseAndFilteringStyle { get { return useAndFilteringStyle; } set { if (useAndFilteringStyle != value) { useAndFilteringStyle = value; OnPropertyChanged(); OnFilterChanged(nameof(UseAndFilteringStyle)); } } } private bool isInstalled; public bool IsInstalled { get { return isInstalled; } set { if (isInstalled != value) { isInstalled = value; OnPropertyChanged(); OnFilterChanged(nameof(IsInstalled)); } } } private bool isUnInstalled; public bool IsUnInstalled { get { return isUnInstalled; } set { if (isUnInstalled != value) { isUnInstalled = value; OnPropertyChanged(); OnFilterChanged(nameof(IsUnInstalled)); } } } private bool hidden; public bool Hidden { get { return hidden; } set { if (hidden != value) { hidden = value; OnPropertyChanged(); OnFilterChanged(nameof(Hidden)); } } } private bool favorite; public bool Favorite { get { return favorite; } set { if (favorite != value) { favorite = value; OnPropertyChanged(); OnFilterChanged(nameof(Favorite)); } } } private IdItemFilterItemProperties library; public IdItemFilterItemProperties Library { get { return library; } set { if (library != value) { library = value; OnPropertyChanged(); OnFilterChanged(nameof(Library)); } } } private IdItemFilterItemProperties completionStatus; public IdItemFilterItemProperties CompletionStatuses { get { return completionStatus; } set { if (completionStatus != value) { completionStatus = value; OnPropertyChanged(); OnFilterChanged(nameof(CompletionStatuses)); } } } private EnumFilterItemProperties userScore; public EnumFilterItemProperties UserScore { get { return userScore; } set { if (userScore != value) { userScore = value; OnPropertyChanged(); OnFilterChanged(nameof(UserScore)); } } } private EnumFilterItemProperties criticScore; public EnumFilterItemProperties CriticScore { get { return criticScore; } set { if (criticScore != value) { criticScore = value; OnPropertyChanged(); OnFilterChanged(nameof(CriticScore)); } } } private EnumFilterItemProperties communityScore; public EnumFilterItemProperties CommunityScore { get { return communityScore; } set { if (communityScore != value) { communityScore = value; OnPropertyChanged(); OnFilterChanged(nameof(CommunityScore)); } } } private EnumFilterItemProperties lastActivity; public EnumFilterItemProperties LastActivity { get { return lastActivity; } set { if (lastActivity != value) { lastActivity = value; OnPropertyChanged(); OnFilterChanged(nameof(LastActivity)); } } } private EnumFilterItemProperties recentActivity; public EnumFilterItemProperties RecentActivity { get { return recentActivity; } set { if (recentActivity != value) { recentActivity = value; OnPropertyChanged(); OnFilterChanged(nameof(RecentActivity)); } } } private EnumFilterItemProperties added; public EnumFilterItemProperties Added { get { return added; } set { if (added != value) { added = value; OnPropertyChanged(); OnFilterChanged(nameof(Added)); } } } private EnumFilterItemProperties modified; public EnumFilterItemProperties Modified { get { return modified; } set { if (modified != value) { modified = value; OnPropertyChanged(); OnFilterChanged(nameof(Modified)); } } } private EnumFilterItemProperties playTime; public EnumFilterItemProperties PlayTime { get { return playTime; } set { if (playTime != value) { playTime = value; OnPropertyChanged(); OnFilterChanged(nameof(PlayTime)); } } } private EnumFilterItemProperties installSize; public EnumFilterItemProperties InstallSize { get { return installSize; } set { if (installSize != value) { installSize = value; OnPropertyChanged(); OnFilterChanged(nameof(InstallSize)); } } } private IdItemFilterItemProperties feature; public IdItemFilterItemProperties Feature { get { return feature; } set { if (feature != value) { feature = value; OnPropertyChanged(); OnFilterChanged(nameof(Feature)); } } } [JsonIgnore] public bool SuppressFilterChanges { get; set; } = false; public event EventHandler FilterChanged; public void OnFilterChanged(string field) { if (!SuppressFilterChanges) { FilterChanged?.Invoke(this, new FilterChangedEventArgs(new List() { field })); } OnPropertyChanged(nameof(IsActive)); } public void OnFilterChanged(List fields) { if (!SuppressFilterChanges) { FilterChanged?.Invoke(this, new FilterChangedEventArgs(fields)); } OnPropertyChanged(nameof(IsActive)); } public void ClearFilters(bool notify = true) { SuppressFilterChanges = true; var filterChanges = new List(); if (Name != null) { Name = null; filterChanges.Add(nameof(Name)); } if (Genre?.IsSet == true) { Genre = null; filterChanges.Add(nameof(Genre)); } if (Platform?.IsSet == true) { Platform = null; filterChanges.Add(nameof(Platform)); } if (ReleaseYear != null) { ReleaseYear = null; filterChanges.Add(nameof(ReleaseYear)); } if (Version != null) { Version = null; filterChanges.Add(nameof(Version)); } if (Publisher?.IsSet == true) { Publisher = null; filterChanges.Add(nameof(Publisher)); } if (Developer?.IsSet == true) { Developer = null; filterChanges.Add(nameof(Developer)); } if (Category?.IsSet == true) { Category = null; filterChanges.Add(nameof(Category)); } if (Tag?.IsSet == true) { Tag = null; filterChanges.Add(nameof(Tag)); } if (UseAndFilteringStyle != false) { UseAndFilteringStyle = false; filterChanges.Add(nameof(UseAndFilteringStyle)); } if (IsInstalled != false) { IsInstalled = false; filterChanges.Add(nameof(IsInstalled)); } if (IsUnInstalled != false) { IsUnInstalled = false; filterChanges.Add(nameof(IsUnInstalled)); } if (Hidden != false) { Hidden = false; filterChanges.Add(nameof(Hidden)); } if (Favorite != false) { Favorite = false; filterChanges.Add(nameof(Favorite)); } if (Series?.IsSet == true) { Series = null; filterChanges.Add(nameof(Series)); } if (Region?.IsSet == true) { Region = null; filterChanges.Add(nameof(Region)); } if (Source?.IsSet == true) { Source = null; filterChanges.Add(nameof(Source)); } if (AgeRating?.IsSet == true) { AgeRating = null; filterChanges.Add(nameof(AgeRating)); } if (Library != null) { Library = null; filterChanges.Add(nameof(Library)); } if (CompletionStatuses != null) { CompletionStatuses = null; filterChanges.Add(nameof(CompletionStatuses)); } if (UserScore != null) { UserScore = null; filterChanges.Add(nameof(UserScore)); } if (CriticScore != null) { CriticScore = null; filterChanges.Add(nameof(CriticScore)); } if (CommunityScore != null) { CommunityScore = null; filterChanges.Add(nameof(CommunityScore)); } if (LastActivity != null) { LastActivity = null; filterChanges.Add(nameof(LastActivity)); } if (RecentActivity != null) { RecentActivity = null; filterChanges.Add(nameof(RecentActivity)); } if (Added != null) { Added = null; filterChanges.Add(nameof(Added)); } if (Modified != null) { Modified = null; filterChanges.Add(nameof(Modified)); } if (PlayTime != null) { PlayTime = null; filterChanges.Add(nameof(PlayTime)); } if (InstallSize != null) { InstallSize = null; filterChanges.Add(nameof(InstallSize)); } if (Feature?.IsSet == true) { Feature = null; filterChanges.Add(nameof(Feature)); } SuppressFilterChanges = false; if (notify) { OnFilterChanged(filterChanges); } } public SdkModels.FilterPresetSettings AsPresetSettings() { return new SdkModels.FilterPresetSettings { UseAndFilteringStyle = UseAndFilteringStyle, IsInstalled = IsInstalled, IsUnInstalled = IsUnInstalled, Hidden = Hidden, Favorite = Favorite, Name = Name, Version = Version, ReleaseYear = ReleaseYear?.ToSdkModel(), Genre = Genre?.ToSdkModel(), Platform = Platform?.ToSdkModel(), Publisher = Publisher?.ToSdkModel(), Developer = Developer?.ToSdkModel(), Category = Category?.ToSdkModel(), Tag = Tag?.ToSdkModel(), Series = Series?.ToSdkModel(), Region = Region?.ToSdkModel(), Source = Source?.ToSdkModel(), AgeRating = AgeRating?.ToSdkModel(), Library = Library?.ToSdkModel(), CompletionStatuses = CompletionStatuses?.ToSdkModel(), Feature = Feature?.ToSdkModel(), UserScore = UserScore?.ToSdkModel(), CriticScore = CriticScore?.ToSdkModel(), CommunityScore = CommunityScore?.ToSdkModel(), LastActivity = LastActivity?.ToSdkModel(), RecentActivity = RecentActivity?.ToSdkModel(), Added = Added?.ToSdkModel(), Modified = Modified?.ToSdkModel(), PlayTime = PlayTime?.ToSdkModel(), InstallSize = InstallSize?.ToSdkModel() }; } public static FilterSettings FromSdkFilterSettings(SdkModels.FilterPresetSettings settings) { return new FilterSettings { IsInstalled = settings.IsInstalled, IsUnInstalled = settings.IsUnInstalled, Hidden = settings.Hidden, Favorite = settings.Favorite, Name = settings.Name, Version = settings.Version, ReleaseYear = StringFilterItemProperties.FromSdkModel(settings.ReleaseYear), Genre = IdItemFilterItemProperties.FromSdkModel(settings.Genre), Platform = IdItemFilterItemProperties.FromSdkModel(settings.Platform), Publisher = IdItemFilterItemProperties.FromSdkModel(settings.Publisher), Developer = IdItemFilterItemProperties.FromSdkModel(settings.Developer), Category = IdItemFilterItemProperties.FromSdkModel(settings.Category), Tag = IdItemFilterItemProperties.FromSdkModel(settings.Tag), Series = IdItemFilterItemProperties.FromSdkModel(settings.Series), Region = IdItemFilterItemProperties.FromSdkModel(settings.Region), Source = IdItemFilterItemProperties.FromSdkModel(settings.Source), AgeRating = IdItemFilterItemProperties.FromSdkModel(settings.AgeRating), Library = IdItemFilterItemProperties.FromSdkModel(settings.Library), CompletionStatuses = IdItemFilterItemProperties.FromSdkModel(settings.CompletionStatuses), Feature = IdItemFilterItemProperties.FromSdkModel(settings.Feature), UserScore = EnumFilterItemProperties.FromSdkModel(settings.UserScore), CriticScore = EnumFilterItemProperties.FromSdkModel(settings.CriticScore), CommunityScore = EnumFilterItemProperties.FromSdkModel(settings.CommunityScore), LastActivity = EnumFilterItemProperties.FromSdkModel(settings.LastActivity), RecentActivity = EnumFilterItemProperties.FromSdkModel(settings.RecentActivity), Added = EnumFilterItemProperties.FromSdkModel(settings.Added), Modified = EnumFilterItemProperties.FromSdkModel(settings.Modified), PlayTime = EnumFilterItemProperties.FromSdkModel(settings.PlayTime), InstallSize = EnumFilterItemProperties.FromSdkModel(settings.InstallSize) }; } public void ApplyFilter(SdkModels.FilterPresetSettings settings) { var filterChanges = new List(); SuppressFilterChanges = true; if (UseAndFilteringStyle != settings.UseAndFilteringStyle) { UseAndFilteringStyle = settings.UseAndFilteringStyle; filterChanges.Add(nameof(UseAndFilteringStyle)); } if (Name != settings.Name) { Name = settings.Name; filterChanges.Add(nameof(Name)); } if (Genre?.Equals(settings.Genre) != true) { Genre = IdItemFilterItemProperties.FromSdkModel(settings.Genre); filterChanges.Add(nameof(Genre)); } if (Platform?.Equals(settings.Platform) != true) { Platform = IdItemFilterItemProperties.FromSdkModel(settings.Platform); filterChanges.Add(nameof(Platform)); } if (ReleaseYear?.Equals(settings.ReleaseYear) != true) { ReleaseYear = StringFilterItemProperties.FromSdkModel(settings.ReleaseYear); filterChanges.Add(nameof(ReleaseYear)); } if (Version != settings.Version) { Version = settings.Version; filterChanges.Add(nameof(Version)); } if (Publisher?.Equals(settings.Publisher) != true) { Publisher = IdItemFilterItemProperties.FromSdkModel(settings.Publisher); filterChanges.Add(nameof(Publisher)); } if (Developer?.Equals(settings.Developer) != true) { Developer = IdItemFilterItemProperties.FromSdkModel(settings.Developer); filterChanges.Add(nameof(Developer)); } if (Category?.Equals(settings.Category) != true) { Category = IdItemFilterItemProperties.FromSdkModel(settings.Category); filterChanges.Add(nameof(Category)); } if (Tag?.Equals(settings.Tag) != true) { Tag = IdItemFilterItemProperties.FromSdkModel(settings.Tag); filterChanges.Add(nameof(Tag)); } if (IsInstalled != settings.IsInstalled) { IsInstalled = settings.IsInstalled; filterChanges.Add(nameof(IsInstalled)); } if (IsUnInstalled != settings.IsUnInstalled) { IsUnInstalled = settings.IsUnInstalled; filterChanges.Add(nameof(IsUnInstalled)); } if (Hidden != settings.Hidden) { Hidden = settings.Hidden; filterChanges.Add(nameof(Hidden)); } if (Favorite != settings.Favorite) { Favorite = settings.Favorite; filterChanges.Add(nameof(Favorite)); } if (Series?.Equals(settings.Series) != true) { Series = IdItemFilterItemProperties.FromSdkModel(settings.Series); filterChanges.Add(nameof(Series)); } if (Region?.Equals(settings.Region) != true) { Region = IdItemFilterItemProperties.FromSdkModel(settings.Region); filterChanges.Add(nameof(Region)); } if (Source?.Equals(settings.Source) != true) { Source = IdItemFilterItemProperties.FromSdkModel(settings.Source); filterChanges.Add(nameof(Source)); } if (AgeRating?.Equals(settings.AgeRating) != true) { AgeRating = IdItemFilterItemProperties.FromSdkModel(settings.AgeRating); filterChanges.Add(nameof(AgeRating)); } if (Library?.Equals(settings.Library) != true) { Library = IdItemFilterItemProperties.FromSdkModel(settings.Library); filterChanges.Add(nameof(Library)); } if (CompletionStatuses?.Equals(settings.CompletionStatuses) != true) { CompletionStatuses = IdItemFilterItemProperties.FromSdkModel(settings.CompletionStatuses); filterChanges.Add(nameof(CompletionStatuses)); } if (UserScore?.Equals(settings.UserScore) != true) { UserScore = EnumFilterItemProperties.FromSdkModel(settings.UserScore); filterChanges.Add(nameof(UserScore)); } if (CriticScore?.Equals(settings.CriticScore) != true) { CriticScore = EnumFilterItemProperties.FromSdkModel(settings.CriticScore); filterChanges.Add(nameof(CriticScore)); } if (CommunityScore?.Equals(settings.CommunityScore) != true) { CommunityScore = EnumFilterItemProperties.FromSdkModel(settings.CommunityScore); filterChanges.Add(nameof(CommunityScore)); } if (LastActivity?.Equals(settings.LastActivity) != true) { LastActivity = EnumFilterItemProperties.FromSdkModel(settings.LastActivity); filterChanges.Add(nameof(LastActivity)); } if (RecentActivity?.Equals(settings.RecentActivity) != true) { RecentActivity = EnumFilterItemProperties.FromSdkModel(settings.RecentActivity); filterChanges.Add(nameof(RecentActivity)); } if (Added?.Equals(settings.Added) != true) { Added = EnumFilterItemProperties.FromSdkModel(settings.Added); filterChanges.Add(nameof(Added)); } if (Modified?.Equals(settings.Modified) != true) { Modified = EnumFilterItemProperties.FromSdkModel(settings.Modified); filterChanges.Add(nameof(Modified)); } if (PlayTime?.Equals(settings.PlayTime) != true) { PlayTime = EnumFilterItemProperties.FromSdkModel(settings.PlayTime); filterChanges.Add(nameof(PlayTime)); } if (InstallSize?.Equals(settings.InstallSize) != true) { InstallSize = EnumFilterItemProperties.FromSdkModel(settings.InstallSize); filterChanges.Add(nameof(InstallSize)); } if (Feature?.Equals(settings.Feature) != true) { Feature = IdItemFilterItemProperties.FromSdkModel(settings.Feature); filterChanges.Add(nameof(Feature)); } SuppressFilterChanges = false; OnFilterChanged(filterChanges); } #region Serialization Conditions public bool ShouldSerializeName() { return !Name.IsNullOrEmpty(); } public bool ShouldSerializeReleaseYear() { return ReleaseYear?.IsSet == true; } public bool ShouldSerializeVersion() { return !Version.IsNullOrEmpty(); } public bool ShouldSerializeSeries() { return Series?.IsSet == true; } public bool ShouldSerializeSource() { return Source?.IsSet == true; } public bool ShouldSerializeAgeRating() { return AgeRating?.IsSet == true; } public bool ShouldSerializeRegion() { return Region?.IsSet == true; } public bool ShouldSerializeGenre() { return Genre?.IsSet == true; } public bool ShouldSerializePublisher() { return Publisher?.IsSet == true; } public bool ShouldSerializeDeveloper() { return Developer?.IsSet == true; } public bool ShouldSerializeCategory() { return Category?.IsSet == true; } public bool ShouldSerializeTag() { return Tag?.IsSet == true; } public bool ShouldSerializePlatform() { return Platform?.IsSet == true; } public bool ShouldSerializeLibrary() { return Library?.IsSet == true; } public bool ShouldSerializeCompletionStatuses() { return CompletionStatuses?.IsSet == true; } public bool ShouldSerializeUserScore() { return UserScore?.IsSet == true; } public bool ShouldSerializeCriticScore() { return CriticScore?.IsSet == true; } public bool ShouldSerializeCommunityScore() { return CommunityScore?.IsSet == true; } public bool ShouldSerializeLastActivity() { return LastActivity?.IsSet == true; } public bool ShouldSerializeRecentActivity() { return RecentActivity?.IsSet == true; } public bool ShouldSerializeAdded() { return Added?.IsSet == true; } public bool ShouldSerializeModified() { return Modified?.IsSet == true; } public bool ShouldSerializePlayTime() { return PlayTime?.IsSet == true; } public bool ShouldSerializeInstallSize() { return InstallSize?.IsSet == true; } public bool ShouldSerializeFeature() { return Feature?.IsSet == true; } #endregion Serialization Conditions } } ================================================ FILE: source/Playnite/Settings/FullscreenSettings.cs ================================================ using Newtonsoft.Json; using Playnite.Audio; using Playnite.Common; using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Playnite { public enum FullscreenButtonPrompts { Xbox, PlayStation } public enum ActiveFullscreenView : int { RecentlyPlayed = 0, Favorites = 1, MostPlayed = 2, All = 3 //Explore } public class FullscreenViewSettings : ViewSettingsBase { } public class FullscreenFilterSettings : FilterSettings { public FullscreenFilterSettings() : base() { FilterChanged += (s, e) => OnPropertyChanged(nameof(IsSubAdditionalFilterActive)); } [JsonIgnore] public bool IsSubAdditionalFilterActive { get { return Series?.IsSet == true || Source?.IsSet == true || AgeRating?.IsSet == true || Region?.IsSet == true || Genre?.IsSet == true || Publisher?.IsSet == true || Developer?.IsSet == true || Tag?.IsSet == true || Feature?.IsSet == true || CompletionStatuses?.IsSet == true || UserScore?.IsSet == true || CriticScore?.IsSet == true || CommunityScore?.IsSet == true || LastActivity?.IsSet == true || Added?.IsSet == true || Modified?.IsSet == true || ReleaseYear?.IsSet == true || PlayTime?.IsSet == true; } } } public class FullscreenSettings : ObservableObject { [JsonIgnore] public List AvailableScreens => Computer.GetScreens(); [JsonIgnore] public List AvailableThemes => ThemeManager.GetAvailableThemes(ApplicationMode.Fullscreen).OrderBy(a => a.Name).ToList(); [JsonIgnore] public const FullscreenButtonPrompts DefaultButtonPrompts = FullscreenButtonPrompts.Xbox; private bool isMusicMuted = false; [JsonIgnore] public bool IsMusicMuted { get { return isMusicMuted; } set { if (isMusicMuted != value) { isMusicMuted = value; OnPropertyChanged(); } } } private int monitor = Computer.GetGetPrimaryScreenIndex(); public int Monitor { get { return monitor; } set { monitor = value; OnPropertyChanged(); } } private string theme = ThemeManager.DefaultFullscreenThemeId; [RequiresRestart] public string Theme { get { return theme; } set { theme = value; OnPropertyChanged(); } } private int rows = 2; public int Rows { get { return rows; } set { rows = value; OnPropertyChanged(); } } private int columns = 4; public int Columns { get { return columns; } set { columns = value; OnPropertyChanged(); } } private bool horizontalLayout = false; public bool HorizontalLayout { get { return horizontalLayout; } set { horizontalLayout = value; OnPropertyChanged(); } } private bool showBattery = false; public bool ShowBattery { get { return showBattery; } set { showBattery = value; OnPropertyChanged(); } } private bool showClock = true; public bool ShowClock { get { return showClock; } set { showClock = value; OnPropertyChanged(); } } private bool showBatteryPercentage = false; public bool ShowBatteryPercentage { get { return showBatteryPercentage; } set { showBatteryPercentage = value; OnPropertyChanged(); } } private bool showGameTitles = false; public bool ShowGameTitles { get { return showGameTitles; } set { showGameTitles = value; OnPropertyChanged(); } } private FullscreenButtonPrompts buttonPrompts = DefaultButtonPrompts; public FullscreenButtonPrompts ButtonPrompts { get { return buttonPrompts; } set { buttonPrompts = value; OnPropertyChanged(); } } private FullscreenFilterSettings filterSettings = new FullscreenFilterSettings(); public FullscreenFilterSettings FilterSettings { get { return filterSettings; } set { filterSettings = value; OnPropertyChanged(); } } private FullscreenViewSettings viewSettings = new FullscreenViewSettings(); public FullscreenViewSettings ViewSettings { get { return viewSettings; } set { viewSettings = value; OnPropertyChanged(); } } private bool darkenUninstalledGamesGrid = false; public bool DarkenUninstalledGamesGrid { get => darkenUninstalledGamesGrid; set { darkenUninstalledGamesGrid = value; OnPropertyChanged(); } } private bool enableMainBackgroundImage = false; public bool EnableMainBackgroundImage { get { return enableMainBackgroundImage; } set { enableMainBackgroundImage = value; OnPropertyChanged(); } } private int mainBackgroundImageBlurAmount = 0; public int MainBackgroundImageBlurAmount { get { return mainBackgroundImageBlurAmount; } set { mainBackgroundImageBlurAmount = value; OnPropertyChanged(); } } private float mainBackgroundImageDarkAmount = 30; public float MainBackgroundImageDarkAmount { get { return mainBackgroundImageDarkAmount; } set { mainBackgroundImageDarkAmount = value; OnPropertyChanged(); } } private bool usePrimaryDisplay = false; public bool UsePrimaryDisplay { get => usePrimaryDisplay; set { usePrimaryDisplay = value; OnPropertyChanged(); } } private Guid selectedFilterPreset; public Guid SelectedFilterPreset { get => selectedFilterPreset; set { selectedFilterPreset = value; OnPropertyChanged(); } } private bool hideMouserCursor = false; public bool HideMouserCursor { get => hideMouserCursor; set { hideMouserCursor = value; OnPropertyChanged(); } } private bool asyncImageLoading = true; [RequiresRestart] public bool AsyncImageLoading { get { return asyncImageLoading; } set { asyncImageLoading = value; OnPropertyChanged(); } } private bool minimizeAfterGameStartup = true; public bool MinimizeAfterGameStartup { get { return minimizeAfterGameStartup; } set { minimizeAfterGameStartup = value; OnPropertyChanged(); } } private double fontSize = 22; [RequiresRestart] public double FontSize { get { return fontSize; } set { fontSize = value; OnPropertyChanged(); } } private double fontSizeSmall = 18; [RequiresRestart] public double FontSizeSmall { get { return fontSizeSmall; } set { fontSizeSmall = value; OnPropertyChanged(); } } private bool enableGameControllerSupport = true; public bool EnableGameControllerSupport { get { return enableGameControllerSupport; } set { enableGameControllerSupport = value; OnPropertyChanged(); } } private List disabledGameControllers = new List(); public List DisabledGameControllers { get { return disabledGameControllers; } set { disabledGameControllers = value; OnPropertyChanged(); } } private bool mainMenuShowRestart = true; public bool MainMenuShowRestart { get { return mainMenuShowRestart; } set { mainMenuShowRestart = value; OnPropertyChanged(); } } private bool mainMenuShowShutdown = true; public bool MainMenuShowShutdown { get { return mainMenuShowShutdown; } set { mainMenuShowShutdown = value; OnPropertyChanged(); } } private bool mainMenuShowSuspend = true; public bool MainMenuShowSuspend { get { return mainMenuShowSuspend; } set { mainMenuShowSuspend = value; OnPropertyChanged(); } } private bool mainMenuShowHibernate = true; public bool MainMenuShowHibernate { get { return mainMenuShowHibernate; } set { mainMenuShowHibernate = value; OnPropertyChanged(); } } private bool mainMenuShowMinimize = true; public bool MainMenuShowMinimize { get { return mainMenuShowMinimize; } set { mainMenuShowMinimize = value; OnPropertyChanged(); } } private bool mainMenuShowLogout = false; public bool MainMenuShowLogout { get { return mainMenuShowLogout; } set { mainMenuShowLogout = value; OnPropertyChanged(); } } private bool mainMenuShowLock = false; public bool MainMenuShowLock { get { return mainMenuShowLock; } set { mainMenuShowLock = value; OnPropertyChanged(); } } private bool mainMenuShowTools = true; public bool MainMenuShowTools { get { return mainMenuShowTools; } set { mainMenuShowTools = value; OnPropertyChanged(); } } private bool mainMenuExtensions = true; public bool MainMenuShowExtensions { get { return mainMenuExtensions; } set { mainMenuExtensions = value; OnPropertyChanged(); } } private bool mainMenuClients = true; public bool MainMenuShowClients { get { return mainMenuClients; } set { mainMenuClients = value; OnPropertyChanged(); } } private bool swapStartDetailsAction = false; public bool SwapStartDetailsAction { get { return swapStartDetailsAction; } set { swapStartDetailsAction = value; OnPropertyChanged(); } } private bool swapConfirmCancelButtons = false; public bool SwapConfirmCancelButtons { get { return swapConfirmCancelButtons; } set { swapConfirmCancelButtons = value; OnPropertyChanged(); } } private float interfaceVolume = 0.5f; public float InterfaceVolume { get { return interfaceVolume; } set { interfaceVolume = value; OnPropertyChanged(); } } private float musicVolume = 0.3f; public float BackgroundVolume { get { return musicVolume; } set { musicVolume = value; OnPropertyChanged(); } } private bool muteInBackground = true; public bool MuteInBackground { get { return muteInBackground; } set { muteInBackground = value; OnPropertyChanged(); } } private bool guideButtonFocus = false; public bool GuideButtonFocus { get { return guideButtonFocus; } set { guideButtonFocus = value; OnPropertyChanged(); } } private ImageLoadScaling imageScalerMode = ImageLoadScaling.BitmapDotNet; public ImageLoadScaling ImageScalerMode { get => imageScalerMode; set { imageScalerMode = value; OnPropertyChanged(); } } private bool smoothScrolling = true; public bool SmoothScrolling { get => smoothScrolling; set { smoothScrolling = value; OnPropertyChanged(); } } } } ================================================ FILE: source/Playnite/Settings/OldSettings/Plugins.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Settings.OldSettings { public class BattleNetSettings { public bool LibraryDownloadEnabled { get; set; } public bool IntegrationEnabled { get; set; } } public class SteamSettings { public enum SteamIdSource { Name, LocalUser } public SteamIdSource IdSource { get; set; } public ulong AccountId { get; set; } public string AccountName { get; set; } public bool PrivateAccount { get; set; } public string APIKey { get; set; } public bool LibraryDownloadEnabled { get; set; } public bool IntegrationEnabled { get; set; } public bool PreferScreenshotForBackground { get; set; } } public class UplaySettings { public bool LibraryDownloadEnabled { get; set; } public bool IntegrationEnabled { get; set; } } public class OriginSettings { public bool LibraryDownloadEnabled { get; set; } public bool IntegrationEnabled { get; set; } } public class GogSettings { public bool LibraryDownloadEnabled { get; set; } public bool IntegrationEnabled { get; set; } public bool RunViaGalaxy { get; set; } } public class Settings { public SteamSettings SteamSettings { get; set; } public OriginSettings OriginSettings { get; set; } public UplaySettings UplaySettings { get; set; } public BattleNetSettings BattleNetSettings { get; set; } public GogSettings GOGSettings { get; set; } } } ================================================ FILE: source/Playnite/Settings/PlaynitePaths.cs ================================================ using Playnite.Common; using Playnite.SDK; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class PlaynitePaths { public const string ExtensionManifestFileName = "extension.yaml"; public const string ThemeManifestFileName = "theme.yaml"; public const string PackedThemeFileExtention = ".pthm"; public const string PackedExtensionFileExtention = ".pext"; public const string EngLocSourceFileName = "LocSource.xaml"; public const string ThemeSlnFileName = "Theme.sln"; public const string ThemeProjFileName = "Theme.csproj"; public const string AppXamlFileName = "App.xaml"; public const string ExtensionsDirName = "Extensions"; public const string ExtensionsDataDirName = "ExtensionsData"; public const string ThemesDirName = "Themes"; public const string ConfigFileName = "config.json"; public const string FullscreenConfigFileName = "fullscreenConfig.json"; public const string WindowPositionsFileName = "windowPositions.json"; public const string LocalizationsDirName = "Localization"; public static string ProgramPath { get; } public static string ConfigRootPath { get; private set; } public static string LocalizationsPath { get; } public static string DataCachePath { get; private set; } public static string DesktopExecutablePath { get; } public static string FullscreenExecutablePath { get; } public static string PlayniteAssemblyPath { get; } public static string PlayniteSDKAssemblyPath { get; } public static string ExtensionsUserDataPath { get; private set; } public static string ExtensionsProgramPath { get; } public static string ExtensionsDataPath { get; private set; } public static string ExtensionQueueFilePath { get; private set; } public static string AddonLicenseAgreementsFilePath { get; private set; } public static string LocalizationsStatusPath { get; } public static string ThemesProgramPath { get; } public static string ThemesUserDataPath { get; private set; } public static string UninstallerPath { get; } public static string BrowserCachePath { get; private set; } public static string TempPath { get; } public static string LogPath { get; private set; } public static string ConfigFilePath { get; private set; } public static string FullscreenConfigFilePath { get; private set; } public static string WindowPositionsPath { get; private set; } public static string BackupConfigFilePath { get; private set; } public static string BackupFullscreenConfigFilePath { get; private set; } public static string BackupWindowPositionsPath { get; private set; } public static string ImagesCachePath { get; private set; } public static string IconsCachePath { get; private set; } public static string JitProfilesPath { get; private set; } public static string EmulationDatabasePath { get; } public static string SafeStartupFlagFile { get; private set; } public static string BackupActionFile { get; private set; } public static string RestoreBackupActionFile { get; private set; } public static bool IsPortable { get; } static PlaynitePaths() { ProgramPath = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); UninstallerPath = Path.Combine(ProgramPath, "unins000.exe"); IsPortable = !File.Exists(UninstallerPath); LocalizationsPath = Path.Combine(ProgramPath, LocalizationsDirName); DesktopExecutablePath = Path.Combine(ProgramPath, "Playnite.DesktopApp.exe"); FullscreenExecutablePath = Path.Combine(ProgramPath, "Playnite.FullscreenApp.exe"); PlayniteAssemblyPath = Path.Combine(ProgramPath, "Playnite.dll"); PlayniteSDKAssemblyPath = Path.Combine(ProgramPath, "Playnite.SDK.dll"); ExtensionsProgramPath = Path.Combine(ProgramPath, ExtensionsDirName); LocalizationsStatusPath = Path.Combine(LocalizationsPath, "locstatus.json"); ThemesProgramPath = Path.Combine(ProgramPath, ThemesDirName); EmulationDatabasePath = Path.Combine(ProgramPath, "Emulation", "Database"); TempPath = Path.Combine(Path.GetTempPath(), "Playnite"); // We need to always initialize some default set for environments like Blend or Rider UpdateUserDataDir(IsPortable ? ProgramPath : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Playnite")); } public static void UpdateUserDataDir(string dir) { ConfigRootPath = dir; DataCachePath = Path.Combine(ConfigRootPath, "cache"); ThemesUserDataPath = Path.Combine(ConfigRootPath, ThemesDirName); ExtensionsDataPath = Path.Combine(ConfigRootPath, ExtensionsDataDirName); ExtensionQueueFilePath = Path.Combine(ConfigRootPath, "extinstalls.json"); AddonLicenseAgreementsFilePath = Path.Combine(ConfigRootPath, "licenseagreements.json"); ExtensionsUserDataPath = Path.Combine(ConfigRootPath, ExtensionsDirName); BrowserCachePath = Path.Combine(ConfigRootPath, "browsercache"); LogPath = Path.Combine(ConfigRootPath, "playnite.log"); ConfigFilePath = Path.Combine(ConfigRootPath, ConfigFileName); FullscreenConfigFilePath = Path.Combine(ConfigRootPath, FullscreenConfigFileName); WindowPositionsPath = Path.Combine(ConfigRootPath, WindowPositionsFileName); BackupConfigFilePath = Path.Combine(ConfigRootPath, "Backup", ConfigFileName); BackupFullscreenConfigFilePath = Path.Combine(ConfigRootPath, "Backup", FullscreenConfigFileName); BackupWindowPositionsPath = Path.Combine(ConfigRootPath, "Backup", WindowPositionsFileName); ImagesCachePath = Path.Combine(DataCachePath, "images"); IconsCachePath = Path.Combine(DataCachePath, "icons"); JitProfilesPath = Path.Combine(ConfigRootPath, "JITProfiles"); SafeStartupFlagFile = Path.Combine(ConfigRootPath, "safestart.flag"); BackupActionFile = Path.Combine(ConfigRootPath, "backup.json"); RestoreBackupActionFile = Path.Combine(ConfigRootPath, "restoreBackup.json"); } public static string ExpandVariables(string inputString, string emulatorDir = null, bool fixSeparators = false) { if (string.IsNullOrEmpty(inputString) || !inputString.Contains('{')) { return inputString; } var result = inputString; if (!emulatorDir.IsNullOrEmpty()) { emulatorDir = emulatorDir.Replace(ExpandableVariables.PlayniteDirectory, ProgramPath, StringComparison.Ordinal); } result = result.Replace(ExpandableVariables.PlayniteDirectory, ProgramPath, StringComparison.Ordinal); result = result.Replace(ExpandableVariables.EmulatorDirectory, emulatorDir, StringComparison.Ordinal); return fixSeparators ? Paths.FixSeparators(result) : result; } } } ================================================ FILE: source/Playnite/Settings/PlayniteSettings.cs ================================================ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using NLog; using NLog.Config; using NLog.Targets; using System.Configuration; using Playnite.Common; using System.Runtime.CompilerServices; using System.Windows.Controls; using System.Windows.Media; using System.Windows; using Newtonsoft.Json.Serialization; using System.Runtime.Serialization; using Playnite.Metadata; using Playnite.SDK; using Microsoft.Win32; using Playnite.SDK.Models; using System.Collections.ObjectModel; using Playnite.SDK.Plugins; namespace Playnite { public enum DesktopSettingsPage { General = 0, AppearanceGeneral = 1, AppearanceAdvanced = 2, AppearanceDetailsView = 3, AppearanceGridView = 4, AppearanceLayout = 5, GeneralAdvanced = 6, Input = 7, Metadata = 9, Scripting = 11, ClientShutdown = 12, Performance = 13, ImportExlusionList = 14, Development = 19, AppearanceTopPanel = 20, Sorting = 21, Updates = 22, AppearanceListView = 23, Search = 24, Backup = 25 } public enum AccessibilityInterfaceOptions { [Description(LOC.Automatic)] Auto, [Description(LOC.AlwaysOn)] AlwaysOn, [Description(LOC.AlwaysOff)] AlwaysOff } public enum GameSearchItemAction { [Description(LOC.GameSearchItemActionPlay)] Play, [Description(LOC.GameSearchItemActionSwitchTo)] SwitchTo, [Description(LOC.GameSearchItemActionOpenMenu)] OpenMenu, [Description(LOC.GameSearchItemActionEdit)] Edit, [Description(LOC.None)] None } public enum AfterLaunchOptions { [Description(LOC.DoNothing)] None, [Description(LOC.Minimize)] Minimize, [Description(LOC.Close)] Close } public enum AfterGameCloseOptions { [Description(LOC.DoNothing)] None, [Description(LOC.RestoreWindow)] Restore, [Description(LOC.RestoreWindowOnlyFromUI)] RestoreOnlyFromUI, [Description(LOC.ExitPlaynite)] Exit } public enum ApplicationView { Library, Statistics } public enum ImageLoadScaling { [Description(LOC.SettingsImageScalingQuality)] None, [Description(LOC.SettingsImageScalingBalanced)] BitmapDotNet, [Description(LOC.SettingsImageScalingAlternative)] Custom } public enum TrayIconType { [Description("TrayIcon")] Default, [Description("TrayIconWhite")] Bright, [Description("TrayIconBlack")] Dark } public enum DefaultIconSourceOptions { [Description("LOCGameProviderTitle")] Library, [Description("LOCPlatformTitle")] Platform, [Description("Playnite")] General, [Description("LOCNone")] None } public enum DefaultCoverSourceOptions { [Description("LOCPlatformTitle")] Platform, [Description("Playnite")] General, [Description("LOCNone")] None } public enum DefaultBackgroundSourceOptions { [Description("LOCGameProviderTitle")] Library, [Description("LOCPlatformTitle")] Platform, [Description("LOCGameCoverTitle")] Cover, [Description("LOCNone")] None } public enum TextRenderingModeOptions { [Description("LOCSettingsTextRenderingModeOptionAuto")] Auto = 0, [Description("LOCSettingsTextRenderingModeOptionAliased")] Aliased = 1, [Description("LOCSettingsTextRenderingModeOptionGrayscale")] Grayscale = 2, [Description("LOCSettingsTextRenderingModeOptionClearType")] ClearType = 3 } public enum TextFormattingModeOptions { [Description("LOCSettingsTextFormattingModeOptionIdeal")] Ideal = 0, [Description("LOCSettingsTextFormattingModeOptionDisplay")] Display = 1 } public enum UpdateCheckFrequency { [Description(LOC.OptionOnEveryStartup)] OnEveryStartup = 0, [Description(LOC.OptionOnceADay)] OnceADay = 1, [Description(LOC.OptionOnceAWeek)] OnceAWeek = 2, [Description(LOC.OptionOnlyManually)] Manually = 3 } public enum LibraryUpdateCheckFrequency { [Description(LOC.OptionOnlyManually)] Manually = 0, [Description(LOC.OptionOnEveryStartup)] OnEveryStartup = 1, [Description(LOC.OptionOnceADay)] OnceADay = 2, [Description(LOC.OptionOnceAWeek)] OnceAWeek = 3 } public enum AutoBackupFrequency { [Description(LOC.OptionOnceADay)] OnceADay = 1, [Description(LOC.OptionOnceAWeek)] OnceAWeek = 2 } public enum SafeSearchSettings { [Description(LOC.Default)] Default, [Description(LOC.EnabledTitle)] On, [Description(LOC.DisabledTitle)] Off } public class DateFormattingOptions : ObservableObject { private string format; private bool pastWeekRelativeFormat; public string Format { get => format; set => SetValue(ref format, value); } public bool PastWeekRelativeFormat { get => pastWeekRelativeFormat; set => SetValue(ref pastWeekRelativeFormat, value); } public DateFormattingOptions() { } public DateFormattingOptions(string format, bool pastWeekRelativeFormat) { Format = format; PastWeekRelativeFormat = pastWeekRelativeFormat; } } public class ReleaseDateFormattingOptions : DateFormattingOptions { private string partialFormat = Constants.DefaultPartialReleaseDateTimeFormat; public string PartialFormat { get => partialFormat; set => SetValue(ref partialFormat, value); } public ReleaseDateFormattingOptions() : base() { } public ReleaseDateFormattingOptions(string format, bool pastWeekRelativeFormat) : base(format, pastWeekRelativeFormat) { } } public class PlayniteSettings : ObservableObject { private static SDK.ILogger logger = SDK.LogManager.GetLogger(); public int Version { get; set; } = 7; private DetailsVisibilitySettings detailsVisibility = new DetailsVisibilitySettings(); public DetailsVisibilitySettings DetailsVisibility { get { return detailsVisibility; } set { detailsVisibility = value; OnPropertyChanged(); } } private DefaultIconSourceOptions defaultIconSource = DefaultIconSourceOptions.General; public DefaultIconSourceOptions DefaultIconSource { get { return defaultIconSource; } set { defaultIconSource = value; OnPropertyChanged(); } } private DefaultCoverSourceOptions defaultCoverSource = DefaultCoverSourceOptions.General; public DefaultCoverSourceOptions DefaultCoverSource { get { return defaultCoverSource; } set { defaultCoverSource = value; OnPropertyChanged(); } } private DefaultBackgroundSourceOptions defaultBackgroundSource = DefaultBackgroundSourceOptions.None; public DefaultBackgroundSourceOptions DefaultBackgroundSource { get { return defaultBackgroundSource; } set { defaultBackgroundSource = value; OnPropertyChanged(); } } private bool indentGameDetails = true; public bool IndentGameDetails { get { return indentGameDetails; } set { indentGameDetails = value; OnPropertyChanged(); OnPropertyChanged(nameof(CalculatedGameDetailsIndentation)); } } public double CalculatedGameDetailsIndentation { get { return IndentGameDetails ? GameDetailsIndentation : Double.NaN; } } private int gameDetailsIndentation = 400; public int GameDetailsIndentation { get { return gameDetailsIndentation; } set { gameDetailsIndentation = value; OnPropertyChanged(); OnPropertyChanged(nameof(CalculatedGameDetailsIndentation)); } } private Dock gridViewDetailsPosition = Dock.Right; public Dock GridViewDetailsPosition { get { return gridViewDetailsPosition; } set { gridViewDetailsPosition = value; OnPropertyChanged(); } } private Dock filterPanelPosition = Dock.Right; public Dock FilterPanelPosition { get { return filterPanelPosition; } set { filterPanelPosition = value; OnPropertyChanged(); } } private Dock explorerPanelPosition = Dock.Left; public Dock ExplorerPanelPosition { get { return explorerPanelPosition; } set { explorerPanelPosition = value; OnPropertyChanged(); } } private Dock detailsListPosition = Dock.Left; public Dock DetailsListPosition { get { return detailsListPosition; } set { detailsListPosition = value; OnPropertyChanged(); } } private bool explorerPanelVisible = false; public bool ExplorerPanelVisible { get { return explorerPanelVisible; } set { explorerPanelVisible = value; OnPropertyChanged(); } } private double filterPanelWitdh = 240; public double FilterPanelWitdh { get { return filterPanelWitdh; } set { filterPanelWitdh = value; OnPropertyChanged(); } } private double explorerPanelWitdh = 280; public double ExplorerPanelWitdh { get { return explorerPanelWitdh; } set { explorerPanelWitdh = value; OnPropertyChanged(); } } private double grdiDetailsWitdh = 350; public double GrdiDetailsWitdh { get { return grdiDetailsWitdh; } set { grdiDetailsWitdh = value; OnPropertyChanged(); } } private double detailsListWitdh = 350; public double DetailsListWitdh { get { return detailsListWitdh; } set { detailsListWitdh = value; OnPropertyChanged(); } } private bool showGridItemBackground = true; public bool ShowGridItemBackground { get { return showGridItemBackground; } set { showGridItemBackground = value; OnPropertyChanged(); } } [JsonIgnore] public double GridItemHeight { get; private set; } private double gridItemWidth = ViewSettings.DefaultGridItemWidth; public double GridItemWidth { get { return gridItemWidth; } set { gridItemWidth = Math.Round(value); OnPropertyChanged(); UpdateGridItemHeight(); } } [JsonIgnore] public AspectRatio CoverAspectRatio => new AspectRatio(GridItemWidthRatio, GridItemHeightRatio); private int gridItemWidthRatio = 3; public int GridItemWidthRatio { get { return gridItemWidthRatio; } set { gridItemWidthRatio = value; OnPropertyChanged(); UpdateGridItemHeight(); OnPropertyChanged(nameof(CoverAspectRatio)); } } private int gridItemHeightRatio = 4; public int GridItemHeightRatio { get { return gridItemHeightRatio; } set { gridItemHeightRatio = value; OnPropertyChanged(); UpdateGridItemHeight(); OnPropertyChanged(nameof(CoverAspectRatio)); } } private Stretch coverArtStretch = Stretch.UniformToFill; public Stretch CoverArtStretch { get { return coverArtStretch; } set { coverArtStretch = value; OnPropertyChanged(); } } private int gridItemSpacing = 8; public int GridItemSpacing { get { return gridItemSpacing; } set { gridItemSpacing = value; OnPropertyChanged(); ItemSpacingMargin = GetItemSpacingMargin(); OnPropertyChanged(nameof(ItemSpacingMargin)); } } private int gridItemMargin = 2; public int GridItemMargin { get { return gridItemMargin; } set { gridItemMargin = value; OnPropertyChanged(); } } private int fullscreenItemSpacing = 20; public int FullscreenItemSpacing { get { return fullscreenItemSpacing; } set { fullscreenItemSpacing = value; OnPropertyChanged(); FullscreenItemSpacingMargin = GetFullscreenItemSpacingMargin(); OnPropertyChanged(nameof(FullscreenItemSpacingMargin)); } } [JsonIgnore] public Thickness ItemSpacingMargin { get; private set; } [JsonIgnore] public Thickness FullscreenItemSpacingMargin { get; private set; } private bool firstTimeWizardComplete; public bool FirstTimeWizardComplete { get { return firstTimeWizardComplete; } set { firstTimeWizardComplete = value; OnPropertyChanged(); } } private bool disableHwAcceleration = false; [RequiresRestart] public bool DisableHwAcceleration { get { return disableHwAcceleration; } set { disableHwAcceleration = value; OnPropertyChanged(); } } private bool asyncImageLoading = true; [RequiresRestart] public bool AsyncImageLoading { get { return asyncImageLoading; } set { asyncImageLoading = value; OnPropertyChanged(); } } private bool showNameEmptyCover = true; public bool ShowNameEmptyCover { get { return showNameEmptyCover; } set { showNameEmptyCover = value; OnPropertyChanged(); } } private bool showNamesUnderCovers = false; public bool ShowNamesUnderCovers { get { return showNamesUnderCovers; } set { showNamesUnderCovers = value; OnPropertyChanged(); } } private bool showBackgroundImageOnWindow = true; public bool ShowBackgroundImageOnWindow { get { return showBackgroundImageOnWindow; } set { showBackgroundImageOnWindow = value; OnPropertyChanged(); } } private bool highQualityBackgroundBlur = true; public bool HighQualityBackgroundBlur { get { return highQualityBackgroundBlur; } set { highQualityBackgroundBlur = value; OnPropertyChanged(); } } private bool blurWindowBackgroundImage = true; public bool BlurWindowBackgroundImage { get { return blurWindowBackgroundImage; } set { blurWindowBackgroundImage = value; OnPropertyChanged(); } } private double backgroundImageBlurAmount = 60; public double BackgroundImageBlurAmount { get { return backgroundImageBlurAmount; } set { backgroundImageBlurAmount = value; OnPropertyChanged(); } } private bool darkenWindowBackgroundImage = true; public bool DarkenWindowBackgroundImage { get { return darkenWindowBackgroundImage; } set { darkenWindowBackgroundImage = value; OnPropertyChanged(); } } private float backgroundImageDarkAmount = 0.7f; public float BackgroundImageDarkAmount { get { return backgroundImageDarkAmount; } set { backgroundImageDarkAmount = value; OnPropertyChanged(); } } private bool showBackImageOnGridView = false; public bool ShowBackImageOnGridView { get { return showBackImageOnGridView; } set { showBackImageOnGridView = value; OnPropertyChanged(); } } private bool downloadMetadataOnImport = true; public bool DownloadMetadataOnImport { get { return downloadMetadataOnImport; } set { downloadMetadataOnImport = value; OnPropertyChanged(); } } private bool showIconsOnList = true; public bool ShowIconsOnList { get { return showIconsOnList; } set { showIconsOnList = value; OnPropertyChanged(); } } private bool showGroupCount = true; public bool ShowGroupCount { get { return showGroupCount; } set { showGroupCount = value; OnPropertyChanged(); } } private bool startInFullscreen = false; public bool StartInFullscreen { get { return startInFullscreen; } set { startInFullscreen = value; OnPropertyChanged(); } } private string databasePath; [RequiresRestart] public string DatabasePath { get { return databasePath; } set { databasePath = value; OnPropertyChanged(); } } private FilterSettings filterSettings = new FilterSettings(); public FilterSettings FilterSettings { get { return filterSettings; } set { filterSettings = value; } } private ViewSettings desktopViewSettings = new ViewSettings(); public ViewSettings ViewSettings { get { return desktopViewSettings; } set { desktopViewSettings = value; } } private bool gridViewSideBarVisible = false; public bool GridViewSideBarVisible { get { return gridViewSideBarVisible; } set { gridViewSideBarVisible = value; OnPropertyChanged(); } } private bool filterPanelVisible = false; public bool FilterPanelVisible { get { return filterPanelVisible; } set { filterPanelVisible = value; OnPropertyChanged(); } } private bool notificationPanelVisible = false; [JsonIgnore] public bool NotificationPanelVisible { get { return notificationPanelVisible; } set { notificationPanelVisible = value; OnPropertyChanged(); } } private bool showSidebar = true; public bool ShowSidebar { get { return showSidebar; } set { showSidebar = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowMainMenuOnTopPanel)); } } private Dock sidebarPosition = Dock.Left; public Dock SidebarPosition { get { return sidebarPosition; } set { sidebarPosition = value; OnPropertyChanged(); } } private bool mainMenuButtonSidebarMove = true; public bool MainMenuButtonSidebarMove { get { return mainMenuButtonSidebarMove; } set { mainMenuButtonSidebarMove = value; OnPropertyChanged(); OnPropertyChanged(nameof(ShowMainMenuOnTopPanel)); } } [JsonIgnore] public bool ShowMainMenuOnTopPanel => !ShowSidebar || (ShowSidebar && !MainMenuButtonSidebarMove); private bool minimizeToTray = false; public bool MinimizeToTray { get { return minimizeToTray; } set { minimizeToTray = value; OnPropertyChanged(); } } private bool closeToTray = true; public bool CloseToTray { get { return closeToTray; } set { closeToTray = value; OnPropertyChanged(); } } private bool enableTray = true; [RequiresRestart] public bool EnableTray { get { return enableTray; } set { enableTray = value; OnPropertyChanged(); } } private string language = "english"; [RequiresRestart] public string Language { get { return language; } set { language = value; OnPropertyChanged(); } } private AfterLaunchOptions afterLaunch = AfterLaunchOptions.Minimize; public AfterLaunchOptions AfterLaunch { get { return afterLaunch; } set { afterLaunch = value; OnPropertyChanged(); } } private AfterGameCloseOptions afterGameClose = AfterGameCloseOptions.RestoreOnlyFromUI; public AfterGameCloseOptions AfterGameClose { get { return afterGameClose; } set { afterGameClose = value; OnPropertyChanged(); } } private string theme = ThemeManager.DefaultDesktopThemeId; [RequiresRestart] public string Theme { get { return theme; } set { theme = value; OnPropertyChanged(); } } private TrayIconType trayIcon = TrayIconType.Default; [RequiresRestart] public TrayIconType TrayIcon { get { return trayIcon; } set { trayIcon = value; OnPropertyChanged(); } } public string InstallInstanceId { get; set; } private List disabledPlugins = new List(); [RequiresRestart] public List DisabledPlugins { get { return disabledPlugins; } set { disabledPlugins = value; OnPropertyChanged(); } } private bool startMinimized = false; public bool StartMinimized { get { return startMinimized; } set { startMinimized = value; OnPropertyChanged(); } } private bool startOnBoot = false; public bool StartOnBoot { get { return startOnBoot; } set { startOnBoot = value; OnPropertyChanged(); } } private bool startOnBootClosedToTray = false; public bool StartOnBootClosedToTray { get { return startOnBootClosedToTray; } set { startOnBootClosedToTray = value; OnPropertyChanged(); } } private bool enableGameControllerSupport = false; [RequiresRestart] public bool EnableGameControllerSupport { get { return enableGameControllerSupport; } set { enableGameControllerSupport = value; OnPropertyChanged(); } } private List disabledGameControllers = new List(); public List DisabledGameControllers { get { return disabledGameControllers; } set { disabledGameControllers = value; OnPropertyChanged(); } } private bool showPanelSeparators = true; public bool ShowPanelSeparators { get { return showPanelSeparators; } set { showPanelSeparators = value; OnPropertyChanged(); } } private double gameDetailsCoverHeight = 170; public double GameDetailsCoverHeight { get { return gameDetailsCoverHeight; } set { gameDetailsCoverHeight = value; OnPropertyChanged(); } } private string fontFamilyName = "Trebuchet MS"; [RequiresRestart] public string FontFamilyName { get { return fontFamilyName; } set { fontFamilyName = value; OnPropertyChanged(); } } private string monospaceFontFamilyName = "Consolas"; [RequiresRestart] public string MonospaceFontFamilyName { get { return monospaceFontFamilyName; } set { monospaceFontFamilyName = value; OnPropertyChanged(); } } private double fontSize = 14; [RequiresRestart] public double FontSize { get { return fontSize; } set { fontSize = value; OnPropertyChanged(); } } private double fontSizeSmall = 12; [RequiresRestart] public double FontSizeSmall { get { return fontSizeSmall; } set { fontSizeSmall = value; OnPropertyChanged(); } } private double fontSizeLarge = 15; [RequiresRestart] public double FontSizeLarge { get { return fontSizeLarge; } set { fontSizeLarge = value; OnPropertyChanged(); } } private double fontSizeLarger = 20; [RequiresRestart] public double FontSizeLarger { get { return fontSizeLarger; } set { fontSizeLarger = value; OnPropertyChanged(); } } private double fontSizeLargest = 29; [RequiresRestart] public double FontSizeLargest { get { return fontSizeLargest; } set { fontSizeLargest = value; OnPropertyChanged(); } } private double detailsViewListIconSize = 26; public double DetailsViewListIconSize { get { return detailsViewListIconSize; } set { detailsViewListIconSize = value; OnPropertyChanged(); } } private TextFormattingModeOptions textFormattingMode = TextFormattingModeOptions.Ideal; [RequiresRestart] public TextFormattingModeOptions TextFormattingMode { get { return textFormattingMode; } set { textFormattingMode = value; OnPropertyChanged(); } } private TextRenderingModeOptions textRenderingMode = TextRenderingModeOptions.Auto; [RequiresRestart] public TextRenderingModeOptions TextRenderingMode { get { return textRenderingMode; } set { textRenderingMode = value; OnPropertyChanged(); } } private MetadataDownloaderSettings metadataSettings; public MetadataDownloaderSettings MetadataSettings { get { return metadataSettings; } set { metadataSettings = value; OnPropertyChanged(); } } private string preScript; public string PreScript { get => preScript; set { preScript = value; OnPropertyChanged(); } } private string postScript; public string PostScript { get => postScript; set { postScript = value; OnPropertyChanged(); } } private string gameStartedScript; public string GameStartedScript { get => gameStartedScript; set { gameStartedScript = value; OnPropertyChanged(); } } private string appStartupScript; public string AppStartupScript { get => appStartupScript; set { appStartupScript = value; OnPropertyChanged(); } } private string appShutdownScript; public string AppShutdownScript { get => appShutdownScript; set { appShutdownScript = value; OnPropertyChanged(); } } private bool downloadBackgroundsImmediately = true; public bool DownloadBackgroundsImmediately { get => downloadBackgroundsImmediately; set { downloadBackgroundsImmediately = value; OnPropertyChanged(); } } private bool showImagePerformanceWarning = true; public bool ShowImagePerformanceWarning { get => showImagePerformanceWarning; set { showImagePerformanceWarning = value; OnPropertyChanged(); } } private bool backgroundImageAnimation = true; public bool BackgroundImageAnimation { get => backgroundImageAnimation; set { backgroundImageAnimation = value; OnPropertyChanged(); } } private AutoClientShutdownSettings clientAutoShutdown = new AutoClientShutdownSettings(); public AutoClientShutdownSettings ClientAutoShutdown { get => clientAutoShutdown; set { clientAutoShutdown = value; OnPropertyChanged(); } } private bool darkenUninstalledGamesGrid = false; public bool DarkenUninstalledGamesGrid { get => darkenUninstalledGamesGrid; set { darkenUninstalledGamesGrid = value; OnPropertyChanged(); } } private bool usedFieldsOnlyOnFilterLists = true; public bool UsedFieldsOnlyOnFilterLists { get => usedFieldsOnlyOnFilterLists; set { usedFieldsOnlyOnFilterLists = value; OnPropertyChanged(); } } private bool discordPresenceEnabled = false; public bool DiscordPresenceEnabled { get => discordPresenceEnabled; set { discordPresenceEnabled = value; OnPropertyChanged(); } } private bool showHiddenInQuickLaunch = true; public bool ShowHiddenInQuickLaunch { get => showHiddenInQuickLaunch; set { showHiddenInQuickLaunch = value; OnPropertyChanged(); } } private int quickLaunchItems = 10; public int QuickLaunchItems { get => quickLaunchItems; set { quickLaunchItems = value; OnPropertyChanged(); } } private string directoryOpenCommand; public string DirectoryOpenCommand { get => directoryOpenCommand; set { directoryOpenCommand = value; OnPropertyChanged(); } } private AgeRatingOrg ageRatingOrgPriority = AgeRatingOrg.PEGI; public AgeRatingOrg AgeRatingOrgPriority { get => ageRatingOrgPriority; set { ageRatingOrgPriority = value; OnPropertyChanged(); } } private bool traceLogEnabled = false; public bool TraceLogEnabled { get => traceLogEnabled; set { traceLogEnabled = value; OnPropertyChanged(); } } private double gridViewScrollSensitivity = 1.5; public double GridViewScrollSensitivity { get => gridViewScrollSensitivity; set { gridViewScrollSensitivity = value; OnPropertyChanged(); } } private long gridViewScrollSpeed = 250 * TimeSpan.TicksPerMillisecond; public long GridViewScrollSpeed { get => gridViewScrollSpeed; set { gridViewScrollSpeed = value; OnPropertyChanged(); } } private bool gridViewSmoothScrollEnabled = false; public bool GridViewSmoothScrollEnabled { get => gridViewSmoothScrollEnabled; set { gridViewSmoothScrollEnabled = value; OnPropertyChanged(); } } private double detailsViewScrollSensitivity = 1.5; public double DetailsViewScrollSensitivity { get => detailsViewScrollSensitivity; set { detailsViewScrollSensitivity = value; OnPropertyChanged(); } } private long detailsViewScrollSpeed = 250 * TimeSpan.TicksPerMillisecond; public long DetailsViewScrollSpeed { get => detailsViewScrollSpeed; set { detailsViewScrollSpeed = value; OnPropertyChanged(); } } private bool detailsViewSmoothScrollEnabled = false; public bool DetailsViewSmoothScrollEnabled { get => detailsViewSmoothScrollEnabled; set { detailsViewSmoothScrollEnabled = value; OnPropertyChanged(); } } private double listViewScrollSensitivity = 1.5; public double ListViewScrollSensitivity { get => listViewScrollSensitivity; set { listViewScrollSensitivity = value; OnPropertyChanged(); } } private long listViewScrollSpeed = 250 * TimeSpan.TicksPerMillisecond; public long ListViewScrollSpeed { get => listViewScrollSpeed; set { listViewScrollSpeed = value; OnPropertyChanged(); } } private bool listViewSmoothScrollEnabled = false; public bool ListViewSmoothScrollEnabled { get => listViewSmoothScrollEnabled; set { listViewSmoothScrollEnabled = value; OnPropertyChanged(); } } private bool showTopPanelGeneralViewItem = false; public bool ShowTopPanelGeneralViewItem { get => showTopPanelGeneralViewItem; set { showTopPanelGeneralViewItem = value; OnPropertyChanged(); } } private bool showTopPanelGroupingItem = true; public bool ShowTopPanelGroupingItem { get => showTopPanelGroupingItem; set { showTopPanelGroupingItem = value; OnPropertyChanged(); } } private bool showTopPanelSortingItem = true; public bool ShowTopPanelSortingItem { get => showTopPanelSortingItem; set { showTopPanelSortingItem = value; OnPropertyChanged(); } } private bool showTopPanelFilterPresetsItem = true; public bool ShowTopPanelFilterPresetsItem { get => showTopPanelFilterPresetsItem; set { showTopPanelFilterPresetsItem = value; OnPropertyChanged(); } } private bool showTopPanelDetailsViewSwitch = true; public bool ShowTopPanelDetailsViewSwitch { get => showTopPanelDetailsViewSwitch; set { showTopPanelDetailsViewSwitch = value; OnPropertyChanged(); } } private bool showTopPanelGridViewSwitch = true; public bool ShowTopPanelGridViewSwitch { get => showTopPanelGridViewSwitch; set { showTopPanelGridViewSwitch = value; OnPropertyChanged(); } } private bool showTopPanelListViewSwitch = true; public bool ShowTopPanelListViewSwitch { get => showTopPanelListViewSwitch; set { showTopPanelListViewSwitch = value; OnPropertyChanged(); } } private bool showTopPanelExplorerSwitch = true; public bool ShowTopPanelExplorerSwitch { get => showTopPanelExplorerSwitch; set { showTopPanelExplorerSwitch = value; OnPropertyChanged(); } } private bool showTopPanelSelectRandomGameButton = false; public bool ShowTopPanelSelectRandomGameButton { get => showTopPanelSelectRandomGameButton; set { showTopPanelSelectRandomGameButton = value; OnPropertyChanged(); } } private bool showTopPanelViewSelectRandomGameButton = true; public bool ShowTopPanelViewSelectRandomGameButton { get => showTopPanelViewSelectRandomGameButton; set { showTopPanelViewSelectRandomGameButton = value; OnPropertyChanged(); } } private bool showTopPanelSearchButton = true; public bool ShowTopPanelSearchButton { get => showTopPanelSearchButton; set { showTopPanelSearchButton = value; OnPropertyChanged(); } } private bool showTopPanelSearchBox = true; public bool ShowTopPanelSearchBox { get => showTopPanelSearchBox; set { showTopPanelSearchBox = value; OnPropertyChanged(); } } private bool fuzzyMatchingInNameFilter = true; public bool FuzzyMatchingInNameFilter { get => fuzzyMatchingInNameFilter; set { fuzzyMatchingInNameFilter = value; OnPropertyChanged(); } } private double topPanelSectionSeparatorWidth = 15; public double TopPanelSectionSeparatorWidth { get { return topPanelSectionSeparatorWidth; } set { topPanelSectionSeparatorWidth = value; OnPropertyChanged(); } } private Dock pluginTopPanelAlignment = Dock.Right; public Dock PluginTopPanelAlignment { get => pluginTopPanelAlignment; set { pluginTopPanelAlignment = value; OnPropertyChanged(); } } private Guid selectedFilterPreset; public Guid SelectedFilterPreset { get => selectedFilterPreset; set { selectedFilterPreset = value; OnPropertyChanged(); } } private ImageLoadScaling imageScalerMode = ImageLoadScaling.BitmapDotNet; public ImageLoadScaling ImageScalerMode { get => imageScalerMode; set { imageScalerMode = value; OnPropertyChanged(); } } private PlaytimeImportMode playtimeImportMode = PlaytimeImportMode.NewImportsOnly; public PlaytimeImportMode PlaytimeImportMode { get => playtimeImportMode; set { playtimeImportMode = value; OnPropertyChanged(); } } private bool useCompositionWebViewRenderer = false; public bool UseCompositionWebViewRenderer { get => useCompositionWebViewRenderer; set { useCompositionWebViewRenderer = value; OnPropertyChanged(); } } private bool addonsPerfNoticeShown = false; public bool AddonsPerfNoticeShown { get => addonsPerfNoticeShown; set { addonsPerfNoticeShown = value; OnPropertyChanged(); } } private bool gameSortingNameAutofill = true; public bool GameSortingNameAutofill { get => gameSortingNameAutofill; set { gameSortingNameAutofill = value; OnPropertyChanged(); } } private List gameSortingNameRemovedArticles = new List { "The", "A", "An" }; [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)] public List GameSortingNameRemovedArticles { get => gameSortingNameRemovedArticles; set { gameSortingNameRemovedArticles = value; OnPropertyChanged(); } } private bool showNahimicServiceWarning = true; public bool ShowNahimicServiceWarning { get => showNahimicServiceWarning; set { showNahimicServiceWarning = value; OnPropertyChanged(); } } private bool showElevatedRightsWarning = true; public bool ShowElevatedRightsWarning { get => showElevatedRightsWarning; set { showElevatedRightsWarning = value; OnPropertyChanged(); } } private DateFormattingOptions dateTimeFormatAdded = new DateFormattingOptions(Constants.DefaultDateTimeFormat, false); [RequiresRestart] public DateFormattingOptions DateTimeFormatAdded { get => dateTimeFormatAdded; set { dateTimeFormatAdded = value; OnPropertyChanged(); } } private DateFormattingOptions dateTimeFormatModified = new DateFormattingOptions(Constants.DefaultDateTimeFormat, false); [RequiresRestart] public DateFormattingOptions DateTimeFormatModified { get => dateTimeFormatModified; set { dateTimeFormatModified = value; OnPropertyChanged(); } } private DateFormattingOptions dateTimeFormatRecentActivity = new DateFormattingOptions(Constants.DefaultDateTimeFormat, true); [RequiresRestart] public DateFormattingOptions DateTimeFormatRecentActivity { get => dateTimeFormatRecentActivity; set { dateTimeFormatRecentActivity = value; OnPropertyChanged(); } } private ReleaseDateFormattingOptions dateTimeFormatReleaseDate = new ReleaseDateFormattingOptions(Constants.DefaultDateTimeFormat, false); [RequiresRestart] public ReleaseDateFormattingOptions DateTimeFormatReleaseDate { get => dateTimeFormatReleaseDate; set { dateTimeFormatReleaseDate = value; OnPropertyChanged(); } } private DateFormattingOptions dateTimeFormatLastPlayed = new DateFormattingOptions(Constants.DefaultDateTimeFormat, true); [RequiresRestart] public DateFormattingOptions DateTimeFormatLastPlayed { get => dateTimeFormatLastPlayed; set { dateTimeFormatLastPlayed = value; OnPropertyChanged(); } } private bool playtimeUseDaysFormat = false; [RequiresRestart] public bool PlaytimeUseDaysFormat { get => playtimeUseDaysFormat; set { playtimeUseDaysFormat = value; OnPropertyChanged(); } } private bool installSizeScanUseSizeOnDisk = true; public bool InstallSizeScanUseSizeOnDisk { get => installSizeScanUseSizeOnDisk; set { installSizeScanUseSizeOnDisk = value; OnPropertyChanged(); } } private bool scanLibInstallSizeOnLibUpdate = true; public bool ScanLibInstallSizeOnLibUpdate { get => scanLibInstallSizeOnLibUpdate; set { scanLibInstallSizeOnLibUpdate = value; OnPropertyChanged(); } } private UpdateCheckFrequency checkForProgramUpdates = UpdateCheckFrequency.OnEveryStartup; public UpdateCheckFrequency CheckForProgramUpdates { get => checkForProgramUpdates; set { checkForProgramUpdates = value; OnPropertyChanged(); } } private UpdateCheckFrequency checkForAddonUpdates = UpdateCheckFrequency.OnEveryStartup; public UpdateCheckFrequency CheckForAddonUpdates { get => checkForAddonUpdates; set { checkForAddonUpdates = value; OnPropertyChanged(); } } private LibraryUpdateCheckFrequency checkForLibraryUpdates = LibraryUpdateCheckFrequency.OnEveryStartup; public LibraryUpdateCheckFrequency CheckForLibraryUpdates { get => checkForLibraryUpdates; set { checkForLibraryUpdates = value; OnPropertyChanged(); } } private LibraryUpdateCheckFrequency checkForEmulatedLibraryUpdates = LibraryUpdateCheckFrequency.OnEveryStartup; public LibraryUpdateCheckFrequency CheckForEmulatedLibraryUpdates { get => checkForEmulatedLibraryUpdates; set { checkForEmulatedLibraryUpdates = value; OnPropertyChanged(); } } public DateTime LastProgramUpdateCheck { get; set; } public DateTime LastAddonUpdateCheck { get; set; } public DateTime LastLibraryUpdateCheck { get; set; } public DateTime LastEmuLibraryUpdateCheck { get; set; } public DateTime LastAutoBackup { get; set; } private GameSearchItemAction primaryGameSearchItemAction = GameSearchItemAction.SwitchTo; public GameSearchItemAction PrimaryGameSearchItemAction { get => primaryGameSearchItemAction; set => SetValue(ref primaryGameSearchItemAction, value); } private GameSearchItemAction secondaryGameSearchItemAction = GameSearchItemAction.Play; public GameSearchItemAction SecondaryGameSearchItemAction { get => secondaryGameSearchItemAction; set => SetValue(ref secondaryGameSearchItemAction, value); } private bool globalSearchOpenWithLegacySearch = true; public bool GlobalSearchOpenWithLegacySearch { get => globalSearchOpenWithLegacySearch; set => SetValue(ref globalSearchOpenWithLegacySearch, value); } private bool saveGlobalSearchFilterSettings = true; public bool SaveGlobalSearchFilterSettings { get => saveGlobalSearchFilterSettings; set => SetValue(ref saveGlobalSearchFilterSettings, value); } private Dictionary customSearchKeywrods = new Dictionary(); [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)] public Dictionary CustomSearchKeywrods { get => customSearchKeywrods; set => SetValue(ref customSearchKeywrods, value); } private GameSearchFilterSettings gameSearchFilterSettings = new GameSearchFilterSettings(); public GameSearchFilterSettings GameSearchFilterSettings { get => gameSearchFilterSettings; set => SetValue(ref gameSearchFilterSettings, value); } private HotKey systemSearchHotkey; public HotKey SystemSearchHotkey { get => systemSearchHotkey; set => SetValue(ref systemSearchHotkey, value); } private bool includeCommandsInDefaultSearch = true; public bool IncludeCommandsInDefaultSearch { get => includeCommandsInDefaultSearch; set => SetValue(ref includeCommandsInDefaultSearch, value); } private bool autoBackupEnabled = false; public bool AutoBackupEnabled { get => autoBackupEnabled; set => SetValue(ref autoBackupEnabled, value); } private AutoBackupFrequency autoBackupFrequency = AutoBackupFrequency.OnceAWeek; public AutoBackupFrequency AutoBackupFrequency { get => autoBackupFrequency; set => SetValue(ref autoBackupFrequency, value); } private string autoBackupDir; public string AutoBackupDir { get => autoBackupDir; set => SetValue(ref autoBackupDir, value); } private int rotatingBackups; public int RotatingBackups { get => rotatingBackups; set => SetValue(ref rotatingBackups, value); } private bool autoBackupIncludeLibFiles = true; public bool AutoBackupIncludeLibFiles { get => autoBackupIncludeLibFiles; set => SetValue(ref autoBackupIncludeLibFiles, value); } private bool autoBackupIncludeExtensionsData = true; public bool AutoBackupIncludeExtensionsData { get => autoBackupIncludeExtensionsData; set => SetValue(ref autoBackupIncludeExtensionsData, value); } private bool autoBackupIncludeExtensions = false; public bool AutoBackupIncludeExtensions { get => autoBackupIncludeExtensions; set => SetValue(ref autoBackupIncludeExtensions, value); } private bool autoBackupIncludeThemes = false; public bool AutoBackupIncludeThemes { get => autoBackupIncludeThemes; set => SetValue(ref autoBackupIncludeThemes, value); } private bool updateNotificationOnPatchesOnly = false; public bool UpdateNotificationOnPatchesOnly { get => updateNotificationOnPatchesOnly; set => SetValue(ref updateNotificationOnPatchesOnly, value); } private string webImageSarchIconTerm = "\"{Name}\" icon"; public string WebImageSarchIconTerm { get => webImageSarchIconTerm; set => SetValue(ref webImageSarchIconTerm, value); } private string webImageSarchCoverTerm = "\"{Name}\" cover"; public string WebImageSarchCoverTerm { get => webImageSarchCoverTerm; set => SetValue(ref webImageSarchCoverTerm, value); } private string webImageSarchBackgroundTerm = "\"{Name}\" wallpaper"; public string WebImageSarchBackgroundTerm { get => webImageSarchBackgroundTerm; set => SetValue(ref webImageSarchBackgroundTerm, value); } // See OnCreateAutomationPeer comment in WindowBase.cs for why this exists. private AccessibilityInterfaceOptions accessibilityInterface = AccessibilityInterfaceOptions.Auto; [RequiresRestart] public AccessibilityInterfaceOptions AccessibilityInterface { get => accessibilityInterface; set => SetValue(ref accessibilityInterface, value); } private bool partialDescriptionLoading = true; public bool PartialDescriptionLoading { get => partialDescriptionLoading; set => SetValue(ref partialDescriptionLoading, value); } private SafeSearchSettings webImageSafeSearch = SafeSearchSettings.Default; public SafeSearchSettings WebImageSafeSearch { get => webImageSafeSearch; set => SetValue(ref webImageSafeSearch, value); } public Guid LastSelectedGame { get; set; } [JsonIgnore] public static bool IsPortable { get { return PlaynitePaths.IsPortable; } } [JsonIgnore] public WindowPositions WindowPositions { get; private set; } = new WindowPositions(); [JsonIgnore] public FullscreenSettings Fullscreen { get; private set; } = new FullscreenSettings(); private List> develExtenions = new List>(); public List> DevelExtenions { get { return develExtenions; } set { develExtenions = value; OnPropertyChanged(); } } private SearchWindowVisibilitySettings searchWindowVisibility = new SearchWindowVisibilitySettings(); public SearchWindowVisibilitySettings SearchWindowVisibility { get { return searchWindowVisibility; } set { searchWindowVisibility = value; OnPropertyChanged(); } } public PlayniteSettings() { var gpus = Computer.GetGpuVendors(); if (gpus.Contains(HwCompany.Intel) || gpus.Contains(HwCompany.VMware)) { BackgroundImageAnimation = false; } InstallInstanceId = Guid.NewGuid().ToString(); ItemSpacingMargin = GetItemSpacingMargin(); FullscreenItemSpacingMargin = GetFullscreenItemSpacingMargin(); UpdateGridItemHeight(); } private static T LoadSettingFile(string path) where T : class { try { if (File.Exists(path)) { return JsonConvert.DeserializeObject(File.ReadAllText(path)); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to load {path} setting file."); } return null; } private static void SaveSettingFile(object settings, string path) { FileSystem.WriteStringToFile(path, JsonConvert.SerializeObject(settings, Formatting.Indented)); } public static PlayniteSettings GetDefaultSettings() { var settings = new PlayniteSettings(); settings.ViewSettings.ListViewColumsOrder = new List { GameField.Icon, GameField.Name, GameField.ReleaseDate, GameField.Genres, GameField.LastActivity, GameField.Playtime, GameField.PluginId }; var columns = new ListViewColumnsProperties(); columns.Icon.Visible = true; columns.Name.Visible = true; columns.ReleaseDate.Visible = true; columns.Genres.Visible = true; columns.LastActivity.Visible = true; columns.Playtime.Visible = true; columns.PluginId.Visible = true; settings.ViewSettings.ListViewColumns = columns; settings.MetadataSettings = MetadataDownloaderSettings.GetDefaultSettings(); return settings; } public static PlayniteSettings LoadSettings() { var settings = LoadSettingFile(PlaynitePaths.ConfigFilePath); if (settings == null) { logger.Warn("No existing settings found."); settings = LoadSettingFile(PlaynitePaths.BackupConfigFilePath); if (settings == null) { logger.Warn("No settings backup found, creating default ones."); settings = new PlayniteSettings(); } } if (settings.ViewSettings.ListViewColumsOrder == null) { settings.ViewSettings.ListViewColumsOrder = new List { GameField.Icon, GameField.Name, GameField.ReleaseDate, GameField.Genres, GameField.LastActivity, GameField.Playtime, GameField.PluginId }; } if (settings.ViewSettings.ListViewColumns == null) { var columns = new ListViewColumnsProperties(); columns.Icon.Visible = true; columns.Name.Visible = true; columns.ReleaseDate.Visible = true; columns.Genres.Visible = true; columns.LastActivity.Visible = true; columns.Playtime.Visible = true; columns.PluginId.Visible = true; settings.ViewSettings.ListViewColumns = columns; } if (settings.MetadataSettings == null) { settings.MetadataSettings = MetadataDownloaderSettings.GetDefaultSettings(); } if (settings.Version == 1) { settings.BackgroundImageBlurAmount = 17; settings.Version = 2; } if (settings.Version == 2) { settings.BackgroundImageBlurAmount = 60; settings.Version = 3; } if (settings.Version == 3) { settings.MetadataSettings.Feature = new MetadataFieldSettings( true, new List { Guid.Empty, BuiltinExtensions.GetIdFromExtension(BuiltinExtension.IgdbMetadata) }); settings.Version = 4; } if (settings.Version == 4) { settings.MetadataSettings.AgeRating = new MetadataFieldSettings( true, new List { Guid.Empty, BuiltinExtensions.GetIdFromExtension(BuiltinExtension.IgdbMetadata) }); settings.MetadataSettings.Series = new MetadataFieldSettings( true, new List { Guid.Empty, BuiltinExtensions.GetIdFromExtension(BuiltinExtension.IgdbMetadata) }); settings.MetadataSettings.Platform = new MetadataFieldSettings( true, new List { Guid.Empty }); settings.MetadataSettings.Region = new MetadataFieldSettings( true, new List { Guid.Empty }); settings.Version = 5; } if (settings.Version == 5) { if (settings.DisabledPlugins.HasItems()) { // P9 saves disabled list based on add-on IDs, not directory names. var idsMigration = new Dictionary { { "AmazonGamesLibrary", "AmazonLibrary_Builtin" }, { "BattleNetLibrary", "BattlenetLibrary_Builtin" }, { "BethesdaLibrary", "BethesdaLibrary_Builtin" }, { "EpicLibrary", "EpicGamesLibrary_Builtin" }, { "GogLibrary", "GogLibrary_Builtin" }, { "HumbleLibrary", "HumbleLibrary_Builtin" }, { "IGDBMetadata", "IGDBMetadata_Builtin" }, { "ItchioLibrary", "ItchioLibrary_Builtin" }, { "LibraryExporter", "LibraryExporterPS_Builtin" }, { "OriginLibrary", "OriginLibrary_Builtin" }, { "PSNLibrary", "PlayStationLibrary_Builtin" }, { "SteamLibrary", "SteamLibrary_Builtin" }, { "TwitchLibrary", "TwitchLibrary_Builtin" }, { "UplayLibrary", "UplayLibrary_Builtin" }, { "XboxLibrary", "XboxLibrary_Builtin" } }; for (int i = 0; i < settings.DisabledPlugins.Count; i++) { if (idsMigration.TryGetValue(settings.DisabledPlugins[i], out var newValue)) { settings.DisabledPlugins[i] = newValue; } } } settings.ViewSettings.ListViewColumns.AgeRating.Field = GameField.AgeRatings; settings.ViewSettings.ListViewColumns.Platform.Field = GameField.Platforms; settings.ViewSettings.ListViewColumns.Series.Field = GameField.Series; settings.ViewSettings.ListViewColumns.Region.Field = GameField.Regions; settings.Version = 6; } if (settings.Version == 6) { var oldSettings = LoadSettingFile>(PlaynitePaths.ConfigFilePath); if (oldSettings != null) { if (oldSettings.TryGetValue("UpdateLibStartup", out var oldUpdateLibStartup) && (bool)oldUpdateLibStartup == false) { settings.CheckForLibraryUpdates = LibraryUpdateCheckFrequency.Manually; } if (oldSettings.TryGetValue("UpdateEmulatedLibStartup", out var oldUpdateEmulatedLibStartup) && (bool)oldUpdateEmulatedLibStartup == false) { settings.CheckForEmulatedLibraryUpdates = LibraryUpdateCheckFrequency.Manually; } if (oldSettings.TryGetValue("ForcePlayTimeSync", out var oldForcePlayTimeSync) && (bool)oldForcePlayTimeSync == true) { settings.PlaytimeImportMode = PlaytimeImportMode.Always; } } settings.Version = 7; } settings.WindowPositions = LoadExternalConfig(PlaynitePaths.WindowPositionsPath, PlaynitePaths.BackupWindowPositionsPath); settings.Fullscreen = LoadExternalConfig(PlaynitePaths.FullscreenConfigFilePath, PlaynitePaths.BackupFullscreenConfigFilePath); settings.BackupSettings(); return settings; } private static T LoadExternalConfig(string origPath, string backupPath, bool generateDefault = true) where T : class, new() { var name = Path.GetFileName(origPath); var config = LoadSettingFile(origPath); if (config == null) { logger.Warn($"No existing {name} settings found."); config = LoadSettingFile(backupPath); if (config == null) { logger.Warn($"No {name} settings backup found, creating default ones."); if (generateDefault) { config = new T(); } } } return config; } public void SaveSettings() { try { FileSystem.CreateDirectory(PlaynitePaths.ConfigRootPath); SaveSettingFile(this, PlaynitePaths.ConfigFilePath); SaveSettingFile(WindowPositions, PlaynitePaths.WindowPositionsPath); SaveSettingFile(Fullscreen, PlaynitePaths.FullscreenConfigFilePath); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to save application settings."); } } public void BackupSettings() { try { FileSystem.CreateDirectory(PlaynitePaths.ConfigRootPath); SaveSettingFile(this, PlaynitePaths.BackupConfigFilePath); SaveSettingFile(WindowPositions, PlaynitePaths.BackupWindowPositionsPath); SaveSettingFile(Fullscreen, PlaynitePaths.BackupFullscreenConfigFilePath); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to backup application settings."); } } public static void ConfigureLogger() { var config = new LoggingConfiguration(); config.DefaultCultureInfo = new System.Globalization.CultureInfo("en-US"); #if DEBUG var consoleTarget = new ColoredConsoleTarget() { Layout = @"${level:uppercase=true:padding=-5}|${logger}:${message}${onexception:${newline}${exception}}" }; config.AddTarget("console", consoleTarget); var rule1 = new LoggingRule("*", LogLevel.Trace, consoleTarget); config.LoggingRules.Add(rule1); #endif var coreFileTarget = new FileTarget() { FileName = Path.Combine(PlaynitePaths.ConfigRootPath, "playnite.log"), Layout = "${date:format=dd-MM HH\\:mm\\:ss.fff}|${level:uppercase=true:padding=-5}|${logger}:${message}${onexception:${newline}${exception:format=toString}}", KeepFileOpen = false, ArchiveFileName = Path.Combine(PlaynitePaths.ConfigRootPath, "playnite.{#####}.log"), ArchiveAboveSize = 4096000, ArchiveNumbering = ArchiveNumberingMode.Sequence, MaxArchiveFiles = 2, Encoding = Encoding.UTF8 }; var extensionFileTarget = new FileTarget() { FileName = Path.Combine(PlaynitePaths.ConfigRootPath, "extensions.log"), Layout = "${date:format=dd-MM HH\\:mm\\:ss.fff}|${level:uppercase=true:padding=-5}|${logger}:${message}${onexception:${newline}${exception:format=toString}}", KeepFileOpen = false, ArchiveFileName = Path.Combine(PlaynitePaths.ConfigRootPath, "extensions.{#####}.log"), ArchiveAboveSize = 4096000, ArchiveNumbering = ArchiveNumberingMode.Sequence, MaxArchiveFiles = 2, Encoding = Encoding.UTF8 }; var allRule = new LoggingRule("*", LogLevel.Trace, coreFileTarget); allRule.Filters.Add(new NLog.Filters.ConditionBasedFilter() { Condition = "contains('${logger}', '#')", Action = NLog.Filters.FilterResult.Ignore }); config.LoggingRules.Add(allRule); config.LoggingRules.Add(new LoggingRule("*#*", LogLevel.Trace, extensionFileTarget)); NLog.LogManager.Configuration = config; SDK.LogManager.Init(new NLogLogProvider()); logger = SDK.LogManager.GetLogger(); } public static string GetAppConfigValue(string key) { return ConfigurationManager.AppSettings[key]; } public static bool GetAppConfigBoolValue(string key) { if (bool.TryParse(ConfigurationManager.AppSettings[key], out var result)) { return result; } else { return false; } } public static void MigrateSettingsConfig() { } private Thickness GetItemSpacingMargin() { return new Thickness(GridItemSpacing / 2, GridItemSpacing / 2, GridItemSpacing / 2, GridItemSpacing / 2);; } private Thickness GetFullscreenItemSpacingMargin() { double marginX = FullscreenItemSpacing / 2; double marginY = CoverAspectRatio.GetWidth(FullscreenItemSpacing) / 2; return new Thickness(marginY / 2, marginX / 2, marginY / 2, marginX / 2); } private void UpdateGridItemHeight() { if (GridItemWidth != 0) { GridItemHeight = Math.Round(GridItemWidth * ((double)gridItemHeightRatio / GridItemWidthRatio)); } else { GridItemHeight = 0; } OnPropertyChanged(nameof(GridItemHeight)); } public bool ShouldCheckProgramUpdatePeriodic() { switch (CheckForProgramUpdates) { case UpdateCheckFrequency.Manually: return false; case UpdateCheckFrequency.OnceADay: return DateTimes.Now.Date > LastProgramUpdateCheck.Date; case UpdateCheckFrequency.OnceAWeek: return (DateTimes.Now - LastProgramUpdateCheck).TotalDays > 6; case UpdateCheckFrequency.OnEveryStartup: default: return false; } } public bool ShouldCheckAddonUpdatePeriodic() { switch (CheckForAddonUpdates) { case UpdateCheckFrequency.Manually: return false; case UpdateCheckFrequency.OnceADay: return DateTimes.Now.Date > LastAddonUpdateCheck.Date; case UpdateCheckFrequency.OnceAWeek: return (DateTimes.Now - LastAddonUpdateCheck).TotalDays > 6; case UpdateCheckFrequency.OnEveryStartup: default: return false; } } public bool ShouldCheckProgramUpdateStartup() { switch (CheckForProgramUpdates) { case UpdateCheckFrequency.Manually: return false; case UpdateCheckFrequency.OnceADay: return DateTimes.Now.Date > LastProgramUpdateCheck.Date; case UpdateCheckFrequency.OnceAWeek: return (DateTimes.Now - LastProgramUpdateCheck).TotalDays > 6; case UpdateCheckFrequency.OnEveryStartup: default: return true; } } public bool ShouldCheckAddonUpdateStartup() { switch (CheckForAddonUpdates) { case UpdateCheckFrequency.Manually: return false; case UpdateCheckFrequency.OnceADay: return DateTimes.Now.Date > LastAddonUpdateCheck.Date; case UpdateCheckFrequency.OnceAWeek: return (DateTimes.Now - LastAddonUpdateCheck).TotalDays > 6; case UpdateCheckFrequency.OnEveryStartup: default: return true; } } public bool ShouldCheckLibraryOnStartup() { switch (CheckForLibraryUpdates) { case LibraryUpdateCheckFrequency.OnceADay: return DateTimes.Now.Date > LastLibraryUpdateCheck.Date; case LibraryUpdateCheckFrequency.OnceAWeek: return (DateTimes.Now - LastLibraryUpdateCheck).TotalDays > 6; case LibraryUpdateCheckFrequency.Manually: return false; case LibraryUpdateCheckFrequency.OnEveryStartup: default: return true; } } public bool ShouldCheckEmuLibraryOnStartup() { switch (CheckForEmulatedLibraryUpdates) { case LibraryUpdateCheckFrequency.OnceADay: return DateTimes.Now.Date > LastEmuLibraryUpdateCheck.Date; case LibraryUpdateCheckFrequency.OnceAWeek: return (DateTimes.Now - LastEmuLibraryUpdateCheck).TotalDays > 6; case LibraryUpdateCheckFrequency.Manually: return false; case LibraryUpdateCheckFrequency.OnEveryStartup: default: return true; } } public bool ShouldDataBackupOnStartup() { if (!AutoBackupEnabled) { return false; } switch (AutoBackupFrequency) { case AutoBackupFrequency.OnceADay: return DateTimes.Now.Date > LastAutoBackup.Date; case AutoBackupFrequency.OnceAWeek: return (DateTimes.Now - LastAutoBackup).TotalDays > 6; default: return false; } } #region Serialization Conditions public bool ShouldSerializeDisabledPlugins() { return DisabledPlugins.HasItems(); } #endregion Serialization Conditions } } ================================================ FILE: source/Playnite/Settings/SearchWindowVisibilitySettings.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class SearchWindowVisibilitySettings : ObservableObject { private bool gameIcon = true; private bool libraryIcon = true; private bool hiddenStatus = true; private bool platform = true; private bool playTime = true; private bool completionStatus = true; private bool releaseDate = false; public bool GameIcon { get => gameIcon; set => SetValue(ref gameIcon, value); } public bool LibraryIcon { get => libraryIcon; set => SetValue(ref libraryIcon, value); } public bool HiddenStatus { get => hiddenStatus; set => SetValue(ref hiddenStatus, value); } public bool Platform { get => platform; set => SetValue(ref platform, value); } public bool PlayTime { get => playTime; set => SetValue(ref playTime, value); } public bool CompletionStatus { get => completionStatus; set => SetValue(ref completionStatus, value); } public bool ReleaseDate { get => releaseDate; set => SetValue(ref releaseDate, value); } } } ================================================ FILE: source/Playnite/Settings/SettingsAttributes.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { [AttributeUsage(AttributeTargets.Property)] public class RequiresRestartAttribute : Attribute { } } ================================================ FILE: source/Playnite/Settings/ViewProperties.cs ================================================ using Newtonsoft.Json; using Playnite.Database; using Playnite.SDK; using Playnite.SDK.Models; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; namespace Playnite { public class ListViewColumnProperty : ObservableObject { public GameField Field { get; set; } private bool visible = false; public bool Visible { get { return visible; } set { visible = value; OnPropertyChanged(); } } private double width = double.NaN; public double Width { get { return width; } set { width = value; // Don't allow exteremly small size because it could lead to a user accidentely hiding the column by resizing: #2257 if (width < 25) { width = 25; } OnPropertyChanged(); } } public ListViewColumnProperty() { } public ListViewColumnProperty(GameField field) { Field = field; } } public class ListViewColumnsProperties : ObservableObject { private ListViewColumnProperty icon = new ListViewColumnProperty(GameField.Icon); public ListViewColumnProperty Icon { get { return icon; } set { icon = value; OnPropertyChanged(); } } private ListViewColumnProperty name = new ListViewColumnProperty(GameField.Name); public ListViewColumnProperty Name { get { return name; } set { name = value; OnPropertyChanged(); } } private ListViewColumnProperty platform = new ListViewColumnProperty(GameField.Platforms); public ListViewColumnProperty Platform { get { return platform; } set { platform = value; OnPropertyChanged(); } } private ListViewColumnProperty developers = new ListViewColumnProperty(GameField.Developers); public ListViewColumnProperty Developers { get { return developers; } set { developers = value; OnPropertyChanged(); } } private ListViewColumnProperty publishers = new ListViewColumnProperty(GameField.Publishers); public ListViewColumnProperty Publishers { get { return publishers; } set { publishers = value; OnPropertyChanged(); } } private ListViewColumnProperty releaseDate = new ListViewColumnProperty(GameField.ReleaseDate); public ListViewColumnProperty ReleaseDate { get { return releaseDate; } set { releaseDate = value; OnPropertyChanged(); } } private ListViewColumnProperty genres = new ListViewColumnProperty(GameField.Genres); public ListViewColumnProperty Genres { get { return genres; } set { genres = value; OnPropertyChanged(); } } private ListViewColumnProperty lastActivity = new ListViewColumnProperty(GameField.LastActivity); public ListViewColumnProperty LastActivity { get { return lastActivity; } set { lastActivity = value; OnPropertyChanged(); } } private ListViewColumnProperty recentActivity = new ListViewColumnProperty(GameField.RecentActivity); public ListViewColumnProperty RecentActivity { get { return recentActivity; } set { recentActivity = value; OnPropertyChanged(); } } private ListViewColumnProperty isInstalled = new ListViewColumnProperty(GameField.IsInstalled); public ListViewColumnProperty IsInstalled { get { return isInstalled; } set { isInstalled = value; OnPropertyChanged(); } } private ListViewColumnProperty installDirectory = new ListViewColumnProperty(GameField.InstallDirectory); public ListViewColumnProperty InstallDirectory { get { return installDirectory; } set { installDirectory = value; OnPropertyChanged(); } } private ListViewColumnProperty categories = new ListViewColumnProperty(GameField.Categories); public ListViewColumnProperty Categories { get { return categories; } set { categories = value; OnPropertyChanged(); } } private ListViewColumnProperty playtime = new ListViewColumnProperty(GameField.Playtime); public ListViewColumnProperty Playtime { get { return playtime; } set { playtime = value; OnPropertyChanged(); } } private ListViewColumnProperty added = new ListViewColumnProperty(GameField.Added); public ListViewColumnProperty Added { get { return added; } set { added = value; OnPropertyChanged(); } } private ListViewColumnProperty modified = new ListViewColumnProperty(GameField.Modified); public ListViewColumnProperty Modified { get { return modified; } set { modified = value; OnPropertyChanged(); } } private ListViewColumnProperty playCount = new ListViewColumnProperty(GameField.PlayCount); public ListViewColumnProperty PlayCount { get { return playCount; } set { playCount = value; OnPropertyChanged(); } } private ListViewColumnProperty installSize = new ListViewColumnProperty(GameField.InstallSize); public ListViewColumnProperty InstallSize { get { return installSize; } set { installSize = value; OnPropertyChanged(); } } private ListViewColumnProperty series = new ListViewColumnProperty(GameField.Series); public ListViewColumnProperty Series { get { return series; } set { series = value; OnPropertyChanged(); } } private ListViewColumnProperty version = new ListViewColumnProperty(GameField.Version); public ListViewColumnProperty Version { get { return version; } set { version = value; OnPropertyChanged(); } } private ListViewColumnProperty ageRating = new ListViewColumnProperty(GameField.AgeRatings); public ListViewColumnProperty AgeRating { get { return ageRating; } set { ageRating = value; OnPropertyChanged(); } } private ListViewColumnProperty region = new ListViewColumnProperty(GameField.Regions); public ListViewColumnProperty Region { get { return region; } set { region = value; OnPropertyChanged(); } } private ListViewColumnProperty source = new ListViewColumnProperty(GameField.Source); public ListViewColumnProperty Source { get { return source; } set { source = value; OnPropertyChanged(); } } private ListViewColumnProperty completionStatus = new ListViewColumnProperty(GameField.CompletionStatus); public ListViewColumnProperty CompletionStatus { get { return completionStatus; } set { completionStatus = value; OnPropertyChanged(); } } private ListViewColumnProperty userScore = new ListViewColumnProperty(GameField.UserScore); public ListViewColumnProperty UserScore { get { return userScore; } set { userScore = value; OnPropertyChanged(); } } private ListViewColumnProperty criticScore = new ListViewColumnProperty(GameField.CriticScore); public ListViewColumnProperty CriticScore { get { return criticScore; } set { criticScore = value; OnPropertyChanged(); } } private ListViewColumnProperty communityScore = new ListViewColumnProperty(GameField.CommunityScore); public ListViewColumnProperty CommunityScore { get { return communityScore; } set { communityScore = value; OnPropertyChanged(); } } private ListViewColumnProperty tags = new ListViewColumnProperty(GameField.Tags); public ListViewColumnProperty Tags { get { return tags; } set { tags = value; OnPropertyChanged(); } } private ListViewColumnProperty pluginId = new ListViewColumnProperty(GameField.PluginId); public ListViewColumnProperty PluginId { get { return pluginId; } set { pluginId = value; OnPropertyChanged(); } } private ListViewColumnProperty features = new ListViewColumnProperty(GameField.Features); public ListViewColumnProperty Features { get { return features; } set { features = value; OnPropertyChanged(); } } private ListViewColumnProperty roms = new ListViewColumnProperty(GameField.Roms); public ListViewColumnProperty Roms { get { return roms; } set { roms = value; OnPropertyChanged(); } } } public class ViewSettingsBase : ObservableObject { private SortOrder sortingOrder = SortOrder.Name; public SortOrder SortingOrder { get { return sortingOrder; } set { sortingOrder = value; OnPropertyChanged(); } } private SortOrderDirection sortingOrderDirection = SortOrderDirection.Ascending; public SortOrderDirection SortingOrderDirection { get { return sortingOrderDirection; } set { sortingOrderDirection = value; OnPropertyChanged(); } } } public class ViewSettings : ViewSettingsBase { public const double MinGridItemWidth = 60; public const double DefaultGridItemWidth = 200; public const double MaxGridItemWidth = 700; private GroupableField groupingOrder = GroupableField.None; public GroupableField GroupingOrder { get { return groupingOrder; } set { groupingOrder = value; OnPropertyChanged(); } } private DesktopView gamesViewType = DesktopView.Details; public DesktopView GamesViewType { get { return gamesViewType; } set { gamesViewType = value; OnPropertyChanged(); } } private ExplorerField selectedExplorerField = ExplorerField.Library; public ExplorerField SelectedExplorerField { get { return selectedExplorerField; } set { selectedExplorerField = value; OnPropertyChanged(); } } private ListViewColumnsProperties listViewColumns; public ListViewColumnsProperties ListViewColumns { get { return listViewColumns; } set { listViewColumns = value; OnPropertyChanged(); } } private List listViewColumsOrder; public List ListViewColumsOrder { get { return listViewColumsOrder; } set { listViewColumsOrder = value; OnPropertyChanged(); } } private Dictionary> collapsedGroups = new Dictionary>(); public Dictionary> CollapsedGroups { get { return collapsedGroups; } set { collapsedGroups = value; OnPropertyChanged(); } } public bool IsGroupCollapsed(GroupableField field, string groupName) { if (collapsedGroups.ContainsKey(field) && collapsedGroups[field].ContainsString(groupName, StringComparison.OrdinalIgnoreCase)) { return true; } return false; } public void ExpandAllGroups(GroupableField field) { if (collapsedGroups.ContainsKey(field)) { collapsedGroups.Remove(field); OnPropertyChanged(nameof(CollapsedGroups)); } } public void CollapseGroups(GroupableField field, List groupNames) { if (!collapsedGroups.ContainsKey(field)) { collapsedGroups.Add(field, new List()); } foreach (var groupName in groupNames) { if (!collapsedGroups[field].ContainsString(groupName, StringComparison.OrdinalIgnoreCase)) { collapsedGroups[field].Add(groupName); } } OnPropertyChanged(nameof(CollapsedGroups)); } public void SetGroupCollapseState(GroupableField field, string groupName, bool collapsed) { if (collapsed) { if (!collapsedGroups.ContainsKey(field)) { collapsedGroups.Add(field, new List()); } if (!collapsedGroups[field].ContainsString(groupName, StringComparison.OrdinalIgnoreCase)) { collapsedGroups[field].Add(groupName); OnPropertyChanged(nameof(CollapsedGroups)); } } else { if (collapsedGroups.ContainsKey(field)) { var existing = collapsedGroups[field].FirstOrDefault(a => string.Equals(a, groupName, StringComparison.OrdinalIgnoreCase)); if (existing != null) { collapsedGroups[field].Remove(existing); } if (collapsedGroups[field].Count == 0) { collapsedGroups.Remove(field); } OnPropertyChanged(nameof(CollapsedGroups)); } } } public ViewSettings() { } } } ================================================ FILE: source/Playnite/Settings/WindowPositions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class WindowPosition { public class Point { public double X { get; set; } public double Y { get; set; } } public Point Position { get; set; } public Point Size { get; set; } public System.Windows.WindowState State { get; set; } = System.Windows.WindowState.Normal; } public class WindowPositions { public Dictionary Positions { get; set; } = new Dictionary(); public WindowPositions() { } } } ================================================ FILE: source/Playnite/SortableNameConverter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; namespace Playnite { public class SortableNameConverter { private readonly Regex regex; private readonly Regex ignoredEndWordsRegex; /// /// The minimum string length of numbers. If 4, XXIII or 23 will turn into 0023. /// private static int numberLength = 2; private static string[] excludedRomanNumerals = new[] { "XL", "XD", "DX", "XXX", "L", "C", "D", "M", "MII", "MIX", "MX", "MC", "DC" }; //Haven't observed game titles with zero, or four and above that would benefit from making those words sortable numbers private static Dictionary numberWordValues = new Dictionary(StringComparer.InvariantCultureIgnoreCase) { { "one", 1 }, { "two", 2 }, { "three", 3 } }; private static Dictionary romanNumeralValues = new Dictionary { { 'I', 1 }, { 'V', 5 }, { 'X', 10 }, { 'L', 50 }, { 'C', 100 }, { 'D', 500 }, { 'M', 1000 }, //unicode uppercase {'Ⅰ', 1}, {'Ⅱ', 2}, {'Ⅲ', 3}, {'Ⅳ', 4}, {'Ⅴ', 5}, {'Ⅵ', 6}, {'Ⅶ', 7}, {'Ⅷ', 8}, {'Ⅸ', 9}, {'Ⅹ', 10}, {'Ⅺ', 11}, {'Ⅻ', 12}, {'Ⅼ', 50}, {'Ⅽ', 100}, {'Ⅾ', 500}, {'Ⅿ', 1000}, //unicode lowercase {'ⅰ', 1}, {'ⅱ', 2}, {'ⅲ', 3}, {'ⅳ', 4}, {'ⅴ', 5}, {'ⅵ', 6}, {'ⅶ', 7}, {'ⅷ', 8}, {'ⅸ', 9}, {'ⅹ', 10}, {'ⅺ', 11}, {'ⅻ', 12}, {'ⅼ', 50}, {'ⅽ', 100}, {'ⅾ', 500}, {'ⅿ', 1000}, //unicode big/exotic numbers {'ↀ', 1000}, {'ↁ', 5000}, {'ↂ', 10000}, {'Ↄ', 100}, {'ↄ', 100}, {'ↅ', 6}, {'ↆ', 50 }, {'ↇ', 50000}, {'ↈ', 100000 } }; /// /// /// /// Words to remove from the start of the title. Suggested: the contents of PlayniteSettings.GameSortingNameRemovedArticles, or "The", "A", "An". /// Optimize for larger amounts of throughput. Slower for small amounts. public SortableNameConverter(IEnumerable articles, bool batchOperation = false) { if (articles == null) { throw new ArgumentNullException(nameof(articles)); } //(? S.T.A.50.K.E.R.) //(?!\.) prevents matching roman numerals with a period right after (again for cases like abbreviations with periods, but that start with a roman numeral character) //\u2160-\u2188 is the unicode range of roman numerals listed in RomanNumeralValues //using [0-9] here instead of \d because \d also matches ٠١٢٣٤٥٦٧٨٩ and I don't know what to do with those //the (?i) is a modifier that makes the rest of the regex (to the right of it) case insensitive //see https://www.regular-expressions.info/modifiers.html var articlesPattern = string.Join("|", articles.Select(Regex.Escape)); var articlesGroup = string.IsNullOrEmpty(articlesPattern) ? string.Empty : $@"^(?
{articlesPattern})\s+|"; var regexStr = $@"(?[IVXLCDM\u2160-\u2188]+(?!\.))|(?[0-9]+))(?=\W|$)|(?i){articlesGroup}\b(?{string.Join("|", numberWordValues.Keys)})\b"; var options = RegexOptions.ExplicitCapture; if (batchOperation) { options |= RegexOptions.Compiled; } regex = new Regex(regexStr, options); ignoredEndWordsRegex = new Regex(@"(\s*[:-])?(\s+([a-z']+\s+(edition|cut)|hd|collection|remaster(ed)?|remake|ultimate|anthology|game of the))+$", options | RegexOptions.IgnoreCase); } public string Convert(string input) { if (string.IsNullOrWhiteSpace(input)) { return input; } input = StripEdition(input, out string edition); string output = regex.Replace(input, match => { if (match.Groups["roman"].Success) { if (match.Value == "I") { if (MatchComesAfterChapterOrEpisodeOrAtEndOfString(input, match)) { return "1".PadLeft(numberLength, '0'); } else { return match.Value; } } else if (match.Value == "X") { if (MatchComesAfterChapterOrEpisodeOrAtEndOfString(input, match, maxDistanceFromEnd: 4) && !MatchComesBeforeDashAndWord(input, match)) { return "10".PadLeft(numberLength, '0'); } else { return match.Value; } } else if (excludedRomanNumerals.Contains(match.Value)) { return match.Value; } return ConvertRomanNumeralToInt(match.Value)?.ToString(new string('0', numberLength)) ?? match.Value; } else if (match.Groups["arabic"].Success) { return match.Value.PadLeft(numberLength, '0'); } else if (match.Groups["article"].Success) { return string.Empty; } else if (match.Groups["numberword"].Success) { if (MatchComesAfterChapterOrEpisodeOrAtEndOfString(input, match)) { return numberWordValues[match.Value].ToString(new string('0', numberLength)); } else { return match.Value; } } return match.Value; }); return output + edition; } /// /// Convert a number from Roman numerals to an integer /// /// The roman numeral. /// If false, parse any roman numeral. If true, reject invalid ones. /// An integer form of the supplied roman numeral, or NULL if the supplied roman numeral is invalid and is true. /// When the input contains non-numeral characters. public static int? ConvertRomanNumeralToInt(string input, bool validate = true) { if (string.IsNullOrWhiteSpace(input)) { return null; } int output = 0; int biggestNumberToTheRight = 0; int prevCharGroupLength = 0; int lastNumericValue = 0; for (int i = input.Length - 1; i >= 0; i--) { char c = input[i]; if (!romanNumeralValues.TryGetValue(c, out int value)) { return null; } bool subtract = value < biggestNumberToTheRight; if (subtract) { output -= value; } else { output += value; biggestNumberToTheRight = value; } #region validation if (!validate) { continue; } //reject things like IVX and VIX and IIX //subtractive numerals are only ever singular if (subtract && lastNumericValue < biggestNumberToTheRight) { return null; } //reject things like VX or LC or DM //subtractions can't be half the bigger value //IV is as close as the two numbers get in value if (subtract && value * 5 > biggestNumberToTheRight) { return null; } if (value == lastNumericValue) { //Numerals that aren't 1 or 10ⁿ can't repeat if (!IsOneOrPowerOfTen(value)) { return null; } //No numeral can repeat 4 times prevCharGroupLength++; if (prevCharGroupLength == 4) { return null; } } else { prevCharGroupLength = 1; } lastNumericValue = value; #endregion validation } return output; } private string StripEdition(string input, out string edition) { var match = ignoredEndWordsRegex.Match(input); if (match.Success) { edition = match.Value; return input.Remove(match.Index); } else { edition = string.Empty; return input; } } private static bool MatchComesAfterChapterOrEpisodeOrAtEndOfString(string input, Match match, int maxDistanceFromEnd = 0) { bool matchIsAtEndOfString = MatchIsNearEndOfString(input, match, maxDistanceFromEnd); string theBitImmediatelyPriorToTheMatch = input.Substring(Math.Max(0, match.Index - 9), length: Math.Min(9, match.Index)); return matchIsAtEndOfString || theBitImmediatelyPriorToTheMatch.Contains("chapter", StringComparison.InvariantCultureIgnoreCase) || theBitImmediatelyPriorToTheMatch.Contains("season", StringComparison.InvariantCultureIgnoreCase) || theBitImmediatelyPriorToTheMatch.Contains("episode", StringComparison.InvariantCultureIgnoreCase); } private static bool MatchIsNearEndOfString(string input, Match match, int maxDistanceFromEnd) { int distance = input.Length - (match.Index + match.Length); return distance <= maxDistanceFromEnd; } private static bool MatchComesBeforeDashAndWord(string input, Match match) { if (MatchIsNearEndOfString(input, match, maxDistanceFromEnd: 1)) { return false; } char nextChar = input[match.Index + match.Length]; if (nextChar != '-') { return false; } string nextWord = ""; for (int i = match.Index + match.Length + 1; i < input.Length; i++) { char c = input[i]; if (char.IsWhiteSpace(c)) { break; } if (!char.IsLetter(c)) { return false; } nextWord += c; } return excludedRomanNumerals.Contains(nextWord) || ConvertRomanNumeralToInt(nextWord) == null; } private static bool IsOneOrPowerOfTen(int x) { while (x > 9 && x % 10 == 0) { x /= 10; } return x == 1; } } } ================================================ FILE: source/Playnite/SystemIntegration.cs ================================================ using Microsoft.Win32; using Playnite.Common; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class SystemIntegration { public static void RegisterPlayniteUriProtocol() { var view = RegistryView.Registry32; if (Environment.Is64BitOperatingSystem) { view = RegistryView.Registry64; } using (var root = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, view)) using (var classes = root.OpenSubKey(@"Software\Classes", true)) { var openString = $"\"{PlaynitePaths.DesktopExecutablePath}\" --uridata \"%1\""; var existing = classes.OpenSubKey(@"Playnite\shell\open\command"); if (existing != null && existing.GetValue(string.Empty)?.ToString() == openString) { existing.Dispose(); return; } using (var newEntry = classes.CreateSubKey("Playnite")) { newEntry.SetValue(string.Empty, "URL:playnite"); newEntry.SetValue("URL Protocol", string.Empty); using (var command = newEntry.CreateSubKey(@"shell\open\command")) { command.SetValue(string.Empty, openString); } } } } public static void SetBootupStateRegistration(bool runOnBootup, bool startClosed) { var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup, Environment.SpecialFolderOption.Create); var shortcutPath = Path.Combine(startupPath, "Playnite.lnk"); if (runOnBootup) { var args = new CmdLineOptions() { HideSplashScreen = true, StartClosedToTray = startClosed }.ToString(); if (File.Exists(shortcutPath)) { var existLnk = Programs.GetLnkShortcutData(shortcutPath); if (existLnk.Path == PlaynitePaths.DesktopExecutablePath && existLnk.Arguments == args) { return; } } FileSystem.DeleteFile(shortcutPath); Programs.CreateShortcut(PlaynitePaths.DesktopExecutablePath, args, "", shortcutPath); } else { FileSystem.DeleteFile(shortcutPath); } } public static void RegisterFileExtensions() { var view = RegistryView.Registry32; if (Environment.Is64BitOperatingSystem) { view = RegistryView.Registry64; } using (var root = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, view)) using (var classes = root.OpenSubKey(@"Software\Classes", true)) { var openString = $"\"{PlaynitePaths.DesktopExecutablePath}\" --installext \"%1\""; var existing = classes.OpenSubKey(@"Playnite.ext\shell\open\command"); if (existing != null && existing.GetValue(string.Empty)?.ToString() == openString) { existing.Dispose(); return; } using (var newEntry = classes.CreateSubKey("Playnite.ext")) { newEntry.SetValue(string.Empty, "Playnite extension file"); using (var command = newEntry.CreateSubKey(@"DefaultIcon")) { var icoPath = Path.Combine(PlaynitePaths.ProgramPath, "Resources", "playnite_extension.ico"); command.SetValue(string.Empty, $"\"{icoPath}\""); } using (var command = newEntry.CreateSubKey(@"shell\open\command")) { command.SetValue(string.Empty, openString); } } using (var newEntry = classes.CreateSubKey(PlaynitePaths.PackedExtensionFileExtention)) using (var command = newEntry.CreateSubKey(@"OpenWithProgids")) { command.SetValue("Playnite.ext", string.Empty); } using (var newEntry = classes.CreateSubKey(PlaynitePaths.PackedThemeFileExtention)) using (var command = newEntry.CreateSubKey(@"OpenWithProgids")) { command.SetValue("Playnite.ext", string.Empty); } } } } } ================================================ FILE: source/Playnite/Themes.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using Playnite; using System.Windows; using System.Windows.Markup; using System.Text.RegularExpressions; using Playnite.Settings; using Playnite.Common; using Playnite.SDK; using YamlDotNet.Serialization; using Playnite.API; using Playnite.Extensions.Markup; using System.Windows.Input; using Playnite.Plugins; namespace Playnite { public class ThemeManager { private static ILogger logger = LogManager.GetLogger(); public static System.Version DesktopApiVersion => new System.Version("2.9.0"); public static System.Version FullscreenApiVersion => new System.Version("2.9.0"); public static ThemeManifest CurrentTheme { get; private set; } public static ThemeManifest DefaultTheme { get; private set; } public const string DefaultDesktopThemeId = "Playnite_builtin_DefaultDesktop"; public const string DefaultFullscreenThemeId = "Playnite_builtin_DefaultFullscreen"; public const string DefaultThemeDirName = "Default"; public static System.Version GetApiVersion(ApplicationMode mode) { return mode == ApplicationMode.Desktop ? DesktopApiVersion : FullscreenApiVersion; } public static string GetThemeRootDir(ApplicationMode mode) { return mode == ApplicationMode.Desktop ? "Desktop" : "Fullscreen"; } public static void SetCurrentTheme(ThemeManifest theme) { CurrentTheme = theme; } public static void SetDefaultTheme(ThemeManifest theme) { DefaultTheme = theme; } public static void ApplyFullscreenButtonPrompts(Application app, FullscreenButtonPrompts prompts) { if (prompts == FullscreenSettings.DefaultButtonPrompts) { var defaultXaml = $"{FullscreenSettings.DefaultButtonPrompts.ToString()}.xaml"; foreach (var dir in PlayniteApplication.CurrentNative.Resources.MergedDictionaries.ToList()) { if (dir.Source == null) { continue; } if (dir.Source.OriginalString.Contains("ButtonPrompts") && !dir.Source.OriginalString.EndsWith(defaultXaml)) { PlayniteApplication.CurrentNative.Resources.MergedDictionaries.Remove(dir); } } } else { var promptsPath = Path.Combine(ThemeManager.DefaultTheme.DirectoryPath, "Images", "ButtonPrompts"); foreach (var dir in Directory.GetDirectories(promptsPath)) { var dirInfo = new DirectoryInfo(dir); var promptXaml = Path.Combine(dir, $"{dirInfo.Name}.xaml"); if (File.Exists(promptXaml) && dirInfo.Name == prompts.ToString()) { var xaml = Xaml.FromFile(promptXaml); if (xaml is ResourceDictionary xamlDir) { xamlDir.Source = new Uri(promptXaml, UriKind.Absolute); PlayniteApplication.CurrentNative.Resources.MergedDictionaries.Add(xamlDir); } } } } } public static AddonLoadError ApplyTheme(Application app, ThemeManifest theme, ApplicationMode mode) { if (theme.Id.IsNullOrEmpty()) { logger.Error($"Theme {theme.Name}, doesn't have ID."); return AddonLoadError.Uknown; } var apiVesion = mode == ApplicationMode.Desktop ? DesktopApiVersion : FullscreenApiVersion; if (!theme.ThemeApiVersion.IsNullOrEmpty()) { var themeVersion = new Version(theme.ThemeApiVersion); if (themeVersion.Major != apiVesion.Major || themeVersion > apiVesion) { logger.Error($"Failed to apply {theme.Name} theme, unsupported API version {theme.ThemeApiVersion}."); return AddonLoadError.SDKVersion; } } var acceptableXamls = new List(); var defaultRoot = $"Themes/{mode.GetDescription()}/{DefaultTheme.DirectoryName}/"; foreach (var dict in app.Resources.MergedDictionaries) { if (dict.Source.OriginalString.StartsWith(defaultRoot)) { acceptableXamls.Add(dict.Source.OriginalString.Replace(defaultRoot, "").Replace('/', '\\')); } } var allLoaded = true; foreach (var accXaml in acceptableXamls) { var xamlPath = Path.Combine(theme.DirectoryPath, accXaml); if (!File.Exists(xamlPath)) { continue; } try { var xaml = Xaml.FromFile(xamlPath); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to load xaml {xamlPath}"); allLoaded = false; break; } } if (!allLoaded) { return AddonLoadError.Uknown; } try { var cursorFile = ThemeFile.GetFilePath("cursor.cur"); if (cursorFile.IsNullOrEmpty()) { cursorFile = ThemeFile.GetFilePath("cursor.ani"); } if (!cursorFile.IsNullOrEmpty()) { Mouse.OverrideCursor = new Cursor(cursorFile, true); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to set custom mouse cursor."); } var themeRoot = $"Themes\\{mode.GetDescription()}\\{theme.DirectoryName}\\"; // This is sad that we have to do this, but it fixes issues like #2328 // We need to remove all loaded theme resources and reload them in specific order: // default/1.xaml -> theme/1.xaml -> default/2.xaml -> theme/2.xaml etc. // // We can't just load custom theme files at the end or insert them in already loaded pool of resources // because styling with static references won't reload data from custom theme files. // That's why we also have to create new instances of default styles. foreach (var defaultRes in app.Resources.MergedDictionaries.ToList()) { if (defaultRes.Source.OriginalString.StartsWith(defaultRoot)) { app.Resources.MergedDictionaries.Remove(defaultRes); } } foreach (var themeXamlFile in acceptableXamls) { var defaultPath = Path.Combine(PlaynitePaths.ThemesProgramPath, mode.GetDescription(), DefaultThemeDirName, themeXamlFile); var defaultXaml = Xaml.FromFile(defaultPath); if (defaultXaml is ResourceDictionary xamlDir) { xamlDir.Source = new Uri(defaultPath, UriKind.Absolute); app.Resources.MergedDictionaries.Add(xamlDir); } var xamlPath = Path.Combine(theme.DirectoryPath, themeXamlFile); if (!File.Exists(xamlPath)) { continue; } var xaml = Xaml.FromFile(xamlPath); if (xaml is ResourceDictionary themeDir) { themeDir.Source = new Uri(xamlPath, UriKind.Absolute); app.Resources.MergedDictionaries.Add(themeDir); } else { logger.Error($"Skipping theme file {xamlPath}, it's not resource dictionary."); } } try { Localization.LoadAddonLocalization(theme.DirectoryPath); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to load theme's localization files."); return AddonLoadError.Uknown; } return AddonLoadError.None; } public static IEnumerable GetAvailableThemes() { foreach (var theme in GetAvailableThemes(ApplicationMode.Desktop)) { yield return theme; } foreach (var theme in GetAvailableThemes(ApplicationMode.Fullscreen)) { yield return theme; } } public static List GetAvailableThemes(ApplicationMode mode) { var modeDir = GetThemeRootDir(mode); var user = new List(); var install = new List(); var userPath = Path.Combine(PlaynitePaths.ThemesUserDataPath, modeDir); if (Directory.Exists(userPath)) { foreach (var dir in Directory.GetDirectories(userPath)) { try { var descriptorPath = Path.Combine(dir, PlaynitePaths.ThemeManifestFileName); if (File.Exists(descriptorPath)) { var info = new FileInfo(descriptorPath); var man = new ThemeManifest(descriptorPath); if (!man.Id.IsNullOrEmpty()) { user.Add(man); } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to load theme info {dir}"); } } } var programPath = Path.Combine(PlaynitePaths.ThemesProgramPath, modeDir); if (Directory.Exists(programPath)) { foreach (var dir in Directory.GetDirectories(programPath)) { try { var descriptorPath = Path.Combine(dir, PlaynitePaths.ThemeManifestFileName); if (File.Exists(descriptorPath)) { var info = new FileInfo(descriptorPath); var man = new ThemeManifest(descriptorPath); if (!man.Id.IsNullOrEmpty()) { if (user.Any(a => a.Id == man.Id)) { continue; } else { install.Add(man); } } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to load theme info {dir}"); } } } var result = new List(); result.AddRange(ExtensionFactory.DeduplicateExtList(user).Cast()); result.AddRange(ExtensionFactory.DeduplicateExtList(install).Cast()); return result; } } } ================================================ FILE: source/Playnite/ThirdPartyClients/ThirdPartyToolsList.cs ================================================ using Playnite.Common; using Playnite.SDK; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public class ThirdPartyTool { public LibraryClient Client { get; set; } public string Name { get; set; } public System.Windows.Controls.Image Icon { get; set; } public void Start() { Client.Open(); } } public class ThirdPartyToolsList { private static readonly ILogger logger = LogManager.GetLogger(); public static List GetTools(IEnumerable plugins) { var tools = new List(); if (plugins?.Any() == true) { foreach (var plugin in plugins.OrderBy(a => a.Name)) { try { if (plugin.Client != null && plugin.Client.IsInstalled) { var tool = new ThirdPartyTool() { Client = plugin.Client, Name = plugin.Name }; if (plugin.Client?.Icon != null && File.Exists(plugin.Client.Icon)) { tool.Icon = Images.GetImageFromFile( plugin.Client.Icon, System.Windows.Media.BitmapScalingMode.Fant, double.NaN, double.NaN); } tools.Add(tool); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to get client info from {plugin.Name}."); } } } return tools; } } } ================================================ FILE: source/Playnite/UrlConstants.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite { public static class UrlConstants { public const string IssuesTesting = @"https://github.com/JosefNemec/Playnite.Testing/issues/new"; public const string Issues = @"https://github.com/JosefNemec/Playnite/issues/new/choose"; public const string Patreon = @"https://www.patreon.com/playnite"; public const string Kofi = @"https://ko-fi.com/playnite"; public const string Discord = @"https://playnite.link/discord"; public const string Reddit = @"https://www.reddit.com/r/playnite/"; public const string SdkDocs = @"https://playnite.link/docs/"; public const string Wiki = @"https://github.com/JosefNemec/Playnite/wiki"; } } ================================================ FILE: source/Playnite/ViewModels/AddonsViewModelBase_Online.cs ================================================ using Playnite.Common; using Playnite.Common.Web; using Playnite.Plugins; using Playnite.SDK; using Playnite.Windows; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Playnite.ViewModels { public class AddonsViewModelBase : ObservableObject { private static ILogger logger = LogManager.GetLogger(); public readonly IDialogsFactory dialogs; public readonly IResourceProvider resources; private bool isRestartRequired = false; public bool IsRestartRequired { get => isRestartRequired; set { isRestartRequired = value; OnPropertyChanged(); } } private List updateAddonList; public List UpdateAddonList { get => updateAddonList; set { updateAddonList = value; OnPropertyChanged(); } } public RelayCommand UpdateAddonsCommand { get => new RelayCommand((a) => { UpdateAddons(); }); } public AddonsViewModelBase( IDialogsFactory dialogs, IResourceProvider resources) { this.dialogs = dialogs; this.resources = resources; } public void UpdateAddons() { var addons = UpdateAddonList.Where(a => a.Selected == true); if (!addons.HasItems()) { return; } dialogs.ActivateGlobalProgress((prg) => { prg.ProgressMaxValue = addons.Count(); prg.CurrentProgressValue = -1; foreach (var update in addons) { try { prg.CurrentProgressValue++; prg.Text = string.Format(resources.GetString(LOC.AddonDownloadingAddon), update.Item.Name); Thread.Sleep(500); var licenseRes = update.Item.CheckAddonLicense(); if (licenseRes == null) { update.Status = AddonUpdateStatus.Failed; continue; } if (licenseRes == false) { update.Status = AddonUpdateStatus.LicenseRejected; continue; } var locaPath = update.Item.GetTargetDownloadPath(); FileSystem.DeleteFile(locaPath); if (update.Package.PackageUrl.IsHttpUrl()) { FileSystem.PrepareSaveFile(locaPath); HttpDownloader.DownloadFile(update.Package.PackageUrl, locaPath); } else { File.Copy(update.Package.PackageUrl, locaPath); } ExtensionInstaller.QueuePackageInstall(locaPath); update.Status = AddonUpdateStatus.Downloaded; IsRestartRequired = true; } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, $"Failed to download addon update. {update.Item.AddonId}"); update.Status = AddonUpdateStatus.Failed; update.StatusMessage = e.Message; } } }, new GlobalProgressOptions("") { IsIndeterminate = false }); } } } ================================================ FILE: source/Playnite/ViewModels/CrashHandlerViewModel.cs ================================================ using Playnite.Common; using Playnite.SDK; using Playnite.Services; using Playnite.Windows; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; namespace Playnite.ViewModels { public class CrashHandlerViewModel : ObservableObject { private static ILogger logger = LogManager.GetLogger(); private IWindowFactory window; private IDialogsFactory dialogs; private IResourceProvider resources; private ApplicationMode mode; private ExceptionInfo exInfo; private PlayniteSettings settings; private string description; public string Description { get => description; set { description = value; OnPropertyChanged(); } } private bool disableExtension; public bool DisableExtension { get => disableExtension; set { disableExtension = value; OnPropertyChanged(); } } private bool showDisableCheck; public bool ShowDisableCheck { get => showDisableCheck; set { showDisableCheck = value; OnPropertyChanged(); } } public string ExtCrashDescription { get; set; } public RelayCommand CreateDiagPackageCommand { get => new RelayCommand((a) => { CreateDiagPackage(new DiagnosticPackageInfo { IsCrashPackage = true, PlayniteVersion = Updater.CurrentVersion.ToString(4) }); }); } public RelayCommand CloseCommand { get => new RelayCommand((a) => { CloseView(); }); } public RelayCommand ReportIssueCommand { get => new RelayCommand((a) => { ReportIssue(); }); } public RelayCommand RestartCommand { get => new RelayCommand((a) => { RestartApp(); }); } public RelayCommand RestartSafeCommand { get => new RelayCommand((a) => { RestartAppSafe(); }); } public RelayCommand SaveLogCommand { get => new RelayCommand((a) => { SaveLog(); }); } public CrashHandlerViewModel( CrashHandlerWindowFactory window, IDialogsFactory dialogs, IResourceProvider resources, ApplicationMode mode) { this.window = window; this.dialogs = dialogs; this.resources = resources; this.mode = mode; } public CrashHandlerViewModel( ExtensionCrashHandlerWindowFactory window, IDialogsFactory dialogs, IResourceProvider resources, ApplicationMode mode, ExceptionInfo exInfo, PlayniteSettings settings) { this.window = window; this.dialogs = dialogs; this.resources = resources; this.mode = mode; this.exInfo = exInfo; this.settings = settings; if (exInfo.CrashExtension == null) { ShowDisableCheck = false; ExtCrashDescription = resources.GetString(LOC.ExtCrashDescriptionUknown); } else { ShowDisableCheck = true; ExtCrashDescription = resources. GetString(mode == ApplicationMode.Desktop ? LOC.ExtCrashDescription : LOC.ExtCrashDescriptionFS). Format(exInfo.CrashExtension.Name); } } public void OpenView() { window.CreateAndOpenDialog(this); } public void CloseView() { window.Close(); } public static void ReportIssue() { try { if (PlayniteEnvironment.ReleaseChannel == ReleaseChannel.Beta) { ProcessStarter.StartUrl(UrlConstants.IssuesTesting); } else { ProcessStarter.StartUrl(UrlConstants.Issues); } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to open report issue url."); } } public void RestartApp() { if (exInfo?.CrashExtension != null && exInfo?.IsExtensionCrash == true && DisableExtension) { settings.DisabledPlugins.AddMissing(exInfo.CrashExtension.Id); settings.SaveSettings(); } if (mode == ApplicationMode.Desktop) { Process.Start(PlaynitePaths.DesktopExecutablePath); } else { Process.Start(PlaynitePaths.FullscreenExecutablePath); } CloseView(); } public void RestartAppSafe() { var options = new CmdLineOptions { SafeStartup = true, UserDataDir = PlayniteApplication.Current.CmdLine.UserDataDir }; if (mode == ApplicationMode.Desktop) { Process.Start(PlaynitePaths.DesktopExecutablePath, options.ToString()); } else { Process.Start(PlaynitePaths.FullscreenExecutablePath, options.ToString()); } CloseView(); } private void CreateDiagPackage(DiagnosticPackageInfo packageInfo) { CreateDiagPackage(dialogs, Description, packageInfo); } public static void CreateDiagPackage( IDialogsFactory dialogs, string crashDescription = null, DiagnosticPackageInfo packageInfo = null) { try { var diagPath = Path.Combine(PlaynitePaths.TempPath, "diag.zip"); if (packageInfo == null) { packageInfo = new DiagnosticPackageInfo { IsCrashPackage = false, PlayniteVersion = Updater.CurrentVersion.ToString(4) }; } var genResult = GlobalProgress.ActivateProgress((_) => Diagnostic.CreateDiagPackage(diagPath, crashDescription, packageInfo), new GlobalProgressOptions("LOCDiagGenerating")); if (genResult.Result != true) { logger.Error(genResult.Error, "Failed to created diagnostics package."); dialogs.ShowErrorMessage(ResourceProvider.GetString("LOCDiagPackageCreationError"), ""); return; } var mode = PlayniteApplication.Current.Mode; if (PlayniteEnvironment.InOfflineMode && mode == ApplicationMode.Desktop) { Explorer.NavigateToFileSystemEntry(diagPath); return; } var uploadedId = Guid.Empty; var uploadResult = GlobalProgress.ActivateProgress((_) => uploadedId = new ServicesClient().UploadDiagPackage(diagPath), new GlobalProgressOptions("LOCDiagUploading")); if (uploadedId == Guid.Empty) { Explorer.NavigateToFileSystemEntry(diagPath); return; } if (uploadResult.Result == true) { if (mode == ApplicationMode.Desktop) { dialogs.ShowSelectableString(ResourceProvider.GetString("LOCDiagPackageCreationSuccess"), "", uploadedId.ToString()); } else { dialogs.ShowMessage(ResourceProvider.GetString("LOCDiagPackageSentSuccess")); } } else { logger.Error(uploadResult.Error, "Failed to upload diag package."); dialogs.ShowErrorMessage(ResourceProvider.GetString("LOCDiagPackageUploadError"), ""); if (mode == ApplicationMode.Desktop) { Explorer.NavigateToFileSystemEntry(diagPath); } } } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to process CreateDiagPackage"); dialogs.ShowErrorMessage(ResourceProvider.GetString("LOCDiagPackageCreationError"), ""); } } private void SaveLog() { var targetPath = dialogs.SaveFile("zip file|*.zip", true); if (!targetPath.IsNullOrEmpty()) { Diagnostic.CreateLogPackage(targetPath); } } } } ================================================ FILE: source/Playnite/ViewModels/ItemSelectionViewModel.cs ================================================ using Playnite.SDK; using Playnite.Windows; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.ViewModels { public class SingleItemSelectionViewModel : ObservableObject { private static readonly ILogger logger = LogManager.GetLogger(); private readonly IWindowFactory window; private SelectableNamedObject selectedItem; public List> Items { get; set; } public int StartIndex { get; private set; } public string HeaderText { get; set; } public string MessageText { get; set; } public RelayCommand> SelectItemCommand { get; } public RelayCommand CancelCommand => new RelayCommand(() => window.Close(null)); public SingleItemSelectionViewModel(IWindowFactory window, string header, string message) { this.window = window; HeaderText = header?.GetLocalized(); MessageText = message?.GetLocalized(); SelectItemCommand = new RelayCommand>((item) => { selectedItem = item; window.Close(true); }); } public bool SelectItem(List> items, out TItem selected) { if (!items.HasItems()) { selected = default; return false; } Items = items; // Selected property is actually not used for selection it's setting focus. // So some item has to be selected (and have focus) otherwise the view would not be controllable. if (Items.All(a => a.Selected == false)) { Items[0].Selected = true; } // This is for the virtualized view to know where to scroll on view load. StartIndex = Items.IndexOf(Items.First(a => a.Selected)); if (window.CreateAndOpenDialog(this) == true) { selected = selectedItem.Value; return true; } else { selected = default; return false; } } } public class MultiItemSelectionViewModel : ObservableObject { private static readonly ILogger logger = LogManager.GetLogger(); private IWindowFactory window; public List> Items { get; set; } public string HeaderText { get; set; } public string MessageText { get; set; } public RelayCommand CancelCommand => new RelayCommand(() => window.Close(null)); public RelayCommand ConfirmCommand => new RelayCommand(() => window.Close(true)); public RelayCommand ToggleSelectionCommand => new RelayCommand(() => ToggleSelection()); public MultiItemSelectionViewModel(IWindowFactory window, string header, string message) { this.window = window; HeaderText = header?.GetLocalized(); MessageText = message?.GetLocalized(); } public bool SelectItem(List> items, out List selected) { Items = items; if (window.CreateAndOpenDialog(this) == true) { selected = Items.Where(a => a.Selected).Select(a => a.Value).ToList(); return true; } else { selected = null; return false; } } private void ToggleSelection() { if (!Items.HasItems()) { return; } var toggle = !Items[0].Selected; Items.ForEach(a => a.Selected = toggle); } } } ================================================ FILE: source/Playnite/ViewModels/LicenseAgreementViewModel.cs ================================================ using Playnite.SDK; using Playnite.Windows; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.ViewModels { public class LicenseAgreementViewModel : ObservableObject { private IWindowFactory window; public string License { get; set; } public string LicenseTitle { get; set; } public RelayCommand AcceptCommnad { get => new RelayCommand((a) => { window.Close(true); }); } public RelayCommand DeclineCommand { get => new RelayCommand((a) => { window.Close(false); }); } public LicenseAgreementViewModel(IWindowFactory window, string license, string addonName) { this.window = window; License = license; LicenseTitle = string.Format(ResourceProvider.GetString(LOC.AddonLicenseWindowTitle), addonName); } public bool? OpenView() { return window.CreateAndOpenDialog(this); } } } ================================================ FILE: source/Playnite/ViewModels/MainViewModelBase.cs ================================================ using Playnite.API; using Playnite.Common; using Playnite.Database; using Playnite.Emulators; using Playnite.Metadata; using Playnite.Plugins; using Playnite.Scripting.PowerShell; using Playnite.SDK; using Playnite.SDK.Exceptions; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using Playnite.Windows; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace Playnite.ViewModels { public interface IMainViewModelBase { string ProgressStatus { get; set; } double ProgressValue { get; set; } double ProgressTotal { get; set; } bool ProgressActive { get; set; } BaseCollectionView GamesView { get; set; } GamesCollectionViewEntry SelectedGame { get; } List SelectedGames { get; set; } } public abstract class MainViewModelBase : ObservableObject { public static ILogger Logger = LogManager.GetLogger(); private DatabaseFilter databaseFilters; public DatabaseFilter DatabaseFilters { get => databaseFilters; set { databaseFilters = value; OnPropertyChanged(); } } private bool gameAdditionAllowed = true; public bool GameAdditionAllowed { get => gameAdditionAllowed; set { gameAdditionAllowed = value; OnPropertyChanged(); } } private string progressStatus; public string ProgressStatus { get => progressStatus; set { progressStatus = value; OnPropertyChanged(); } } private double progressValue; public double ProgressValue { get => progressValue; set { progressValue = value; OnPropertyChanged(); } } private double progressTotal; public double ProgressTotal { get => progressTotal; set { progressTotal = value; OnPropertyChanged(); } } private bool progressActive; public bool ProgressActive { get => progressActive; set { progressActive = value; OnPropertyChanged(); } } private bool updatesAvailable = false; public bool UpdatesAvailable { get => updatesAvailable; set { updatesAvailable = value; OnPropertyChanged(); } } private BaseCollectionView gamesView; public BaseCollectionView GamesView { get => gamesView; set { gamesView = value; OnPropertyChanged(); } } public List SortedFilterPresets { get { return Database.GetSortedFilterPresets(); } } public List SortedFilterFullscreenPresets { get { return Database.GetSortedFilterPresets() .Where(a => a.ShowInFullscreeQuickSelection) .ToList(); } } private List thirdPartyTools = new List(); public List ThirdPartyTools { get => thirdPartyTools; set { thirdPartyTools = value; OnPropertyChanged(); } } public bool IsDisposing { get; set; } = false; public RelayCommand AddFilterPresetCommand { get; private set; } public RelayCommand RenameFilterPresetCommand { get; private set; } public RelayCommand RemoveFilterPresetCommand { get; private set; } public RelayCommand ApplyFilterPresetCommand { get; private set; } public RelayCommand CancelProgressCommand { get; private set; } public RelayCommand OpenUpdatesCommand { get; private set; } public RelayCommand StartInteractivePowerShellCommand { get; private set; } public RelayCommand RestartApp { get; private set; } public RelayCommand RestartInSafeMode { get; private set; } public RelayCommand BackupDataCommand { get; private set; } public RelayCommand RestoreDataBackupCommand { get; private set; } public RelayCommand ThirdPartyToolOpenCommand { get; private set; } public IGameDatabaseMain Database { get; } public PlayniteApplication App { get; } public IDialogsFactory Dialogs { get; } public IResourceProvider Resources { get; } public ExtensionFactory Extensions { get; set; } public bool IgnoreFilterChanges { get; set; } = false; public IWindowFactory Window { get; } public MainViewModelBase( IGameDatabaseMain database, PlayniteApplication app, IDialogsFactory dialogs, IResourceProvider resources, ExtensionFactory extensions, IWindowFactory window) { Database = database; App = app; Dialogs = dialogs; Resources = resources; Extensions = extensions; Window = window; ApplyFilterPresetCommand = new RelayCommand((a) => { ApplyFilterPreset(a); }); RemoveFilterPresetCommand = new RelayCommand((a) => { RemoveFilterPreset(a); }, (a) => a != null); RenameFilterPresetCommand = new RelayCommand((a) => { RenameFilterPreset(a); }, (a) => a != null); AddFilterPresetCommand = new RelayCommand((a) => { AddFilterPreset(); }); OpenUpdatesCommand = new RelayCommand((_) => { OpenUpdates(); }); CancelProgressCommand = new RelayCommand(() => { CancelProgress(); }, () => GlobalTaskHandler.CancelToken?.IsCancellationRequested == false); StartInteractivePowerShellCommand = new RelayCommand(() => { try { Scripting.PowerShell.PowerShellRuntime.StartInteractiveSession(new Dictionary { { "PlayniteApi", App.PlayniteApiGlobal } }); } catch (Exception e) { Dialogs.ShowErrorMessage("Failed to start interactive PowerShell.\n" + e.Message); } }); ThirdPartyToolOpenCommand = new RelayCommand((tool) => { StartThirdPartyTool(tool); }); RestartApp = new RelayCommand(() => RestartAppSkipLibUpdate()); RestartInSafeMode = new RelayCommand(() => RestartAppSafe()); BackupDataCommand = new RelayCommand(() => BackupData()); RestoreDataBackupCommand = new RelayCommand(() => RestoreDataBackup()); var filterPresetsCollection = (database.FilterPresets as FilterPresetsCollection); if (filterPresetsCollection != null) { filterPresetsCollection.OnSettingsUpdated += FilterPresetsCollection_OnSettingsUpdated; database.FilterPresets.ItemCollectionChanged += FilterPresets_ItemCollectionChanged; } } private void FilterPresetsCollection_OnSettingsUpdated(object sender, FilterPresetsSettingsUpdateEvent e) { App.SyncContext.Send((_) => { OnPropertyChanged(nameof(SortedFilterPresets)); OnPropertyChanged(nameof(SortedFilterFullscreenPresets)); }, null); } private void FilterPresets_ItemCollectionChanged(object sender, ItemCollectionChangedEventArgs e) { App.SyncContext.Send((_) => { OnPropertyChanged(nameof(SortedFilterPresets)); OnPropertyChanged(nameof(SortedFilterFullscreenPresets)); }, null); } private PlayniteSettings appSettings; public PlayniteSettings AppSettings { get => appSettings; set { appSettings = value; OnPropertyChanged(); } } private FilterPreset activeFilterPreset; public FilterPreset ActiveFilterPreset { get => activeFilterPreset; set { if (activeFilterPreset == value) { return; } activeFilterPreset = value; if (App.Mode == ApplicationMode.Desktop) { AppSettings.SelectedFilterPreset = value?.Id ?? Guid.Empty; } else { AppSettings.Fullscreen.SelectedFilterPreset = value?.Id ?? Guid.Empty; } ApplyFilterPreset(value); OnPropertyChanged(); } } public void ApplyFilterPreset(Guid presetId) { var preset = Database.FilterPresets[presetId]; if (preset == null) { Logger.Error($"Cannot apply filter, filter preset {presetId} not found."); } else { ActiveFilterPreset = preset; } } private void ApplyFilterPreset(FilterPreset preset) { if (preset == null) { return; } if (ActiveFilterPreset != preset) { ActiveFilterPreset = preset; return; } if (GamesView != null) { GamesView.IgnoreViewConfigChanges = true; } IgnoreFilterChanges = true; var filter = App.Mode == ApplicationMode.Desktop ? AppSettings.FilterSettings : AppSettings.Fullscreen.FilterSettings; var view = App.Mode == ApplicationMode.Desktop ? AppSettings.ViewSettings : (ViewSettingsBase)AppSettings.Fullscreen.ViewSettings; filter.ApplyFilter(preset.Settings); if (preset.SortingOrder != null) { view.SortingOrder = preset.SortingOrder.Value; } if (preset.SortingOrderDirection != null) { view.SortingOrderDirection = preset.SortingOrderDirection.Value; } if (App.Mode == ApplicationMode.Desktop && preset.GroupingOrder != null) { AppSettings.ViewSettings.GroupingOrder = preset.GroupingOrder.Value; } if (GamesView != null) { IgnoreFilterChanges = false; GamesView.IgnoreViewConfigChanges = false; GamesView.RefreshView(); if (GamesView.CollectionView.Count > 0) { SelectGame((GamesView.CollectionView.GetItemAt(0) as GamesCollectionViewEntry).Id); } } } private void RenameFilterPreset(FilterPreset preset) { if (preset == null) { return; } var options = new List { new MessageBoxToggle(LOC.FilterPresetShowOnFSTopPanel, preset.ShowInFullscreeQuickSelection) }; var res = Dialogs.SelectString(LOC.EnterName, string.Empty, preset.Name, options); if (res.Result && !res.SelectedString.IsNullOrEmpty()) { preset.Name = res.SelectedString; preset.ShowInFullscreeQuickSelection = options[0].Selected; Database.FilterPresets.Update(preset); } OnPropertyChanged(nameof(SortedFilterPresets)); OnPropertyChanged(nameof(SortedFilterFullscreenPresets)); } private void RemoveFilterPreset(FilterPreset preset) { if (preset == null) { return; } if (Dialogs.ShowMessage(LOC.AskRemoveItemMessage, LOC.AskRemoveItemTitle, MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes) { Database.FilterPresets.Remove(preset); if (ActiveFilterPreset == preset) { ActiveFilterPreset = null; } } } private void AddFilterPreset() { var options = new List { new MessageBoxToggle(LOC.FilterPresetSaveViewOptions, true), new MessageBoxToggle(LOC.FilterPresetShowOnFSTopPanel, false) }; var overwriteExisting = false; var res = Dialogs.SelectString(LOC.EnterName, string.Empty, string.Empty, options); if (res.Result && !res.SelectedString.IsNullOrEmpty()) { var existingPreset = Database.FilterPresets.FirstOrDefault(a => string.Equals(a.Name, res.SelectedString, StringComparison.InvariantCultureIgnoreCase)); if (existingPreset != null) { var dialogRes = Dialogs.ShowMessage(LOC.FilterPresetNameConflict, "", MessageBoxButton.YesNoCancel, MessageBoxImage.Question); if (dialogRes == MessageBoxResult.Cancel) { return; } if (dialogRes == MessageBoxResult.Yes) { overwriteExisting = true; } } var filter = App.Mode == ApplicationMode.Desktop ? AppSettings.FilterSettings : AppSettings.Fullscreen.FilterSettings; var preset = new FilterPreset { Name = res.SelectedString, Settings = filter.AsPresetSettings(), ShowInFullscreeQuickSelection = options[1].Selected }; if (options[0].Selected) { var view = App.Mode == ApplicationMode.Desktop ? AppSettings.ViewSettings : (ViewSettingsBase)AppSettings.Fullscreen.ViewSettings; preset.SortingOrder = view.SortingOrder; preset.SortingOrderDirection = view.SortingOrderDirection; if (App.Mode == ApplicationMode.Desktop) { preset.GroupingOrder = AppSettings.ViewSettings.GroupingOrder; } } if (existingPreset != null && overwriteExisting) { preset.Id = existingPreset.Id; Database.FilterPresets.Update(preset); ActiveFilterPreset = existingPreset; } else { Database.FilterPresets.Add(preset); ActiveFilterPreset = preset; } } } private void OpenUpdates() { new UpdateViewModel( new Updater(App), new UpdateWindowFactory(), new ResourceProvider(), Dialogs, App.Mode).OpenView(); } public abstract NotificationMessage GetAddonUpdatesFoundMessage(List updates); private List ImportLibraryGames(LibraryPlugin plugin, CancellationToken token) { var addedGames = new List(); if (token.IsCancellationRequested) { return addedGames; } Logger.Info($"Importing games from {plugin.Name} plugin."); ProgressStatus = Resources.GetString(LOC.ProgressImportinGames).Format(plugin.Name); try { addedGames.AddRange(Database.ImportGames(plugin, token, AppSettings.PlaytimeImportMode)); App.Notifications.Remove($"{plugin.Id} - download"); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { Logger.Error(e, $"Failed to import games from plugin: {plugin.Name}"); App.Notifications.Add(new NotificationMessage( $"{plugin.Id} - download", Resources.GetString(LOC.LibraryImportError).Format(plugin.Name) + $"\n{e.Message}", NotificationType.Error)); } return addedGames; } public async Task ProcessStartupLibUpdate() { if (App.CmdLine.SkipLibUpdate) { Logger.Warn("Startup library update disabled via cmdline."); return; } if (!await Common.Network.GetIsConnectedToInternet()) { Logger.Warn("Startup library update disabled because of no internet connection."); return; } var updateLibs = AppSettings.ShouldCheckLibraryOnStartup(); var updateEmu = AppSettings.ShouldCheckEmuLibraryOnStartup(); if (!updateLibs && !updateEmu) { return; } await UpdateLibrary(AppSettings.DownloadMetadataOnImport, updateLibs, updateEmu); } public async Task UpdateLibrary(bool metaForNewGames, bool updateIntegrations, bool updateEmu) { if (!GameAdditionAllowed) { return; } await UpdateLibraryData((token) => { var addedGames = new List(); if (updateIntegrations) { foreach (var plugin in Extensions.LibraryPlugins) { if (token.IsCancellationRequested) { return addedGames; } addedGames.AddRange(ImportLibraryGames(plugin, token)); } AppSettings.LastLibraryUpdateCheck = DateTimes.Now; } if (updateEmu) { foreach (var scanConfig in Database.GameScanners.Where(a => a.InGlobalUpdate).ToList()) { if (token.IsCancellationRequested) { return addedGames; } addedGames.AddRange(ImportEmulatedGames(scanConfig, token)); } AppSettings.LastEmuLibraryUpdateCheck = DateTimes.Now; } if (AppSettings.ScanLibInstallSizeOnLibUpdate) { UpdateGamesInstallSizes(token, Database.Games, LOC.ProgressScanningGamesInstallSize); } return addedGames; }, metaForNewGames); } public async Task UpdateLibrary(LibraryPlugin plugin) { if (!GameAdditionAllowed) { return; } await UpdateLibraryData((token) => { var addedGames = ImportLibraryGames(plugin, token); if (AppSettings.ScanLibInstallSizeOnLibUpdate) { UpdateGamesInstallSizes(token, Database.Games, LOC.ProgressScanningGamesInstallSize); } return addedGames; }, AppSettings.DownloadMetadataOnImport); } public void UpdateGamesInstallSizes(CancellationToken token, IEnumerable games, string progressMessageLocKey) { if (token.IsCancellationRequested) { return; } try { ProgressActive = true; ProgressValue = 0; ProgressTotal = games.Count() + 1; Logger.Info($"Starting Library Install Size scan"); ProgressStatus = Resources.GetString(progressMessageLocKey); var errorStrings = new List(); var errorsCount = 0; using (Database.Games.BufferedUpdate()) { foreach (var game in games) { if (token.IsCancellationRequested) { Logger.Info($"Library Install Size scan was cancelled"); break; } if (Database.Games[game.Id] == null) { continue; // This can happen if a user deleted game after import before we got here } try { App.GamesEditor.UpdateGameSize(game, false, true, true); } catch (Exception e) { errorsCount++; if (errorStrings.Count < 10) { errorStrings.Add($"{game.Name}: {e.Message}"); } } ProgressValue++; } } Logger.Info($"Finished Library Install Size scan"); if (errorsCount > 0) { var errorMessage = ResourceProvider.GetString("LOCCalculateGamesSizeErrorMessage").Format(errorsCount) + $"\n\n" + string.Join("\n", errorStrings); if (errorsCount > 10) { errorMessage += "\n..."; } App.Notifications.Add(new NotificationMessage( $"LibUpdateScanSizeError - {DateTime.Now}", ResourceProvider.GetString("LOCCalculateGamesSizeErrorMessage").Format(errorsCount), NotificationType.Error, () => { Dialogs.ShowMessage( errorMessage, Resources.GetString("LOCCalculateGameSizeErrorCaption"), MessageBoxButton.OK, MessageBoxImage.Error); } ) ); } } finally { ProgressActive = false; } } private List ImportEmulatedGames(GameScannerConfig scanConfig, CancellationToken token) { var addedGames = new List(); if (token.IsCancellationRequested) { return addedGames; } Logger.Info($"Importing emulated games from {scanConfig.Name} config."); ProgressStatus = Resources.GetString(LOC.ProgressImportinEmulatedGames).Format(scanConfig.Directory); try { var scanned = new GameScanner(scanConfig, Database).Scan( token, out var newPlatforms, out var newRegions).Select(a => a.ToGame()).ToList(); if (scanned.HasItems()) { var statusSettings = Database.GetCompletionStatusSettings(); if (newPlatforms.HasItems()) { Database.Platforms.Add(newPlatforms); } if (newRegions.HasItems()) { Database.Regions.Add(newRegions); } if (statusSettings.DefaultStatus != Guid.Empty) { scanned.ForEach(g => g.CompletionStatusId = statusSettings.DefaultStatus); } addedGames.AddRange(scanned); Database.Games.Add(scanned); } App.Notifications.Remove($"{scanConfig.Id} - import"); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { Logger.Error(e, $"Failed to import emulated games from config:\n{scanConfig.Directory}\n{scanConfig.EmulatorId}\n{scanConfig.EmulatorProfileId}"); App.Notifications.Add(new NotificationMessage( $"{scanConfig.Id} - import", Resources.GetString(LOC.LibraryImportEmulatedError).Format(scanConfig.Name) + $"\n{e.Message}", NotificationType.Error)); } return addedGames; } public async Task UpdateEmulationLibrary(GameScannerConfig config) { await UpdateLibraryData((token) => { var addedGames = ImportEmulatedGames(config, token); if (AppSettings.ScanLibInstallSizeOnLibUpdate) { UpdateGamesInstallSizes(token, addedGames, LOC.ProgressScanningImportedGamesInstallSize); } return addedGames; }, AppSettings.DownloadMetadataOnImport); } public async Task UpdateEmulationLibrary() { await UpdateLibraryData((token) => { var addedGames = new List(); foreach (var scanConfig in Database.GameScanners.Where(a => a.InGlobalUpdate)) { addedGames.AddRange(ImportEmulatedGames(scanConfig, token)); } if (AppSettings.ScanLibInstallSizeOnLibUpdate) { UpdateGamesInstallSizes(token, addedGames, LOC.ProgressScanningImportedGamesInstallSize); } return addedGames; }, AppSettings.DownloadMetadataOnImport); } private async Task UpdateLibraryData(Func> updateAction, bool downloadMetadata) { if (GlobalTaskHandler.ProgressTask != null && GlobalTaskHandler.ProgressTask.Status == TaskStatus.Running) { GlobalTaskHandler.CancelToken.Cancel(); await GlobalTaskHandler.ProgressTask; } GameAdditionAllowed = false; try { GlobalTaskHandler.CancelToken = new CancellationTokenSource(); GlobalTaskHandler.ProgressTask = Task.Run(() => { DatabaseFilters.IgnoreDatabaseUpdates = true; ProgressActive = true; ProgressValue = 0; ProgressTotal = 1; var addedGames = updateAction(GlobalTaskHandler.CancelToken.Token); if (GlobalTaskHandler.CancelToken.IsCancellationRequested) { return; } ProgressActive = true; ProgressStatus = Resources.GetString(LOC.ProgressLibImportFinish); Thread.Sleep(1000); if (addedGames.Any() && downloadMetadata) { Logger.Info($"Downloading metadata for {addedGames.Count} new games."); ProgressValue = 0; ProgressTotal = addedGames.Count; string progressBaseStr = ProgressStatus = Resources.GetString(LOC.ProgressMetadata); using (var downloader = new MetadataDownloader(Database, Extensions.MetadataPlugins, Extensions.LibraryPlugins)) { downloader.DownloadMetadataAsync(addedGames, AppSettings.MetadataSettings, AppSettings, (g, i, t) => { ProgressValue = i + 1; ProgressStatus = $"{progressBaseStr} [{ProgressValue}/{ProgressTotal}]"; }, GlobalTaskHandler.CancelToken.Token).Wait(); } } if (addedGames.Any() && AppSettings.GameSortingNameAutofill) { Logger.Info($"Setting Sorting Name for {addedGames.Count} new games."); ProgressStatus = Resources.GetString(LOC.SortingNameAutofillProgress); var c = new SortableNameConverter(AppSettings.GameSortingNameRemovedArticles, batchOperation: addedGames.Count > 20); using (Database.BufferedUpdate()) { foreach (var game in addedGames) { if (GlobalTaskHandler.CancelToken.IsCancellationRequested) { break; } if (Database.Games[game.Id] == null) { continue; // This can happen if a user deleted game after import before we got here } string sortingName = c.Convert(game.Name); if (sortingName != game.Name) { game.SortingName = sortingName; Database.Games.Update(game); } } } } }); await GlobalTaskHandler.ProgressTask; Extensions.NotifiyOnLibraryUpdated(); } finally { GameAdditionAllowed = true; ProgressActive = false; DatabaseFilters.IgnoreDatabaseUpdates = false; } } public async void CancelProgress() { await GlobalTaskHandler.CancelAndWaitAsync(); } public virtual void SelectGame(Guid id, bool restoreView = false) { } private void RunAppScript(string script, string eventName) { if (script.IsNullOrWhiteSpace()) { return; } try { if (!PowerShellRuntime.IsInstalled) { throw new Exception(ResourceProvider.GetString(LOC.ErrorPowerShellNotInstalled)); } using (var runtime = new PowerShellRuntime($"app {eventName} script")) { runtime.Execute( script, PlaynitePaths.ProgramPath, new Dictionary { { "PlayniteApi", App.PlayniteApiGlobal } }); } } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { Logger.Error(exc, $"Failed to execute {eventName} script."); Logger.Debug(script); var message = ResourceProvider.GetString(LOC.ErrorApplicationScript) + Environment.NewLine + exc.Message; if (exc is ScriptRuntimeException scriptExc) { message = message + Environment.NewLine + Environment.NewLine + scriptExc.ScriptStackTrace; } Dialogs.ShowMessage( message, "", MessageBoxButton.OK, MessageBoxImage.Error); } } public void RunStartupScript() { if (!App.CmdLine.SafeStartup) { RunAppScript(AppSettings.AppStartupScript, "startup"); } } public void RunShutdowScript() { RunAppScript(AppSettings.AppShutdownScript, "shutdown"); } public void RestartAppSkipLibUpdate() { CloseView(); App.Restart(new CmdLineOptions { SkipLibUpdate = true }); } public void RestartAppSafe() { CloseView(); App.Restart(new CmdLineOptions { SafeStartup = true }); } public abstract void CloseView(); public virtual IEnumerable GetSearchCommands() { yield break; } public abstract void OpenSettings(int settingsPageIndex); public void StartGame(Game game, bool launchedFromUI) { if (game.IsLaunching || game.IsRunning) { if (Dialogs.ShowMessage( LOC.CancelMonitoringExecutionAsk, LOC.CancelMonitoringAskTitle, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { App.GamesEditor.CancelGameMonitoring(game); App.GamesEditor.PlayGame(game, launchedFromUI); } } else { App.GamesEditor.PlayGame(game, launchedFromUI); } } public void InstallGame(Game game) { App.GamesEditor.InstallGame(game); } public abstract void EditGame(Game game); public abstract void AssignCategories(Game game); public void StartSoftwareTool(AppSoftware app) { try { if (app.AppType == AppSoftwareType.Standard) { ProcessStarter.StartProcess( PlaynitePaths.ExpandVariables(app.Path, fixSeparators: true), PlaynitePaths.ExpandVariables(app.Arguments), PlaynitePaths.ExpandVariables(app.WorkingDir, fixSeparators: true)); } else { using (var runtime = new PowerShellRuntime($"Software tool {app.Name} runtime")) { var scriptVars = new Dictionary { ["PlayniteApi"] = App.PlayniteApiGlobal }; runtime.Execute(PlaynitePaths.ExpandVariables(app.Script), variables: scriptVars); } } } catch (ScriptRuntimeException e) when (!PlayniteEnvironment.ThrowAllErrors) { Logger.Error(e, "Failed to start app script."); var message = e.Message + Environment.NewLine + Environment.NewLine + e.ScriptStackTrace; Dialogs.ShowErrorMessage( message, "LOCScriptError"); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { Logger.Error(e, "Failed to start app tool."); Dialogs.ShowErrorMessage( Resources.GetString("LOCAppStartupError") + "\n\n" + e.Message, "LOCStartupError"); } } private void RestoreDataBackup() { var backupFile = Dialogs.SelectFile("Playnite Backup|*.zip"); if (backupFile.IsNullOrEmpty()) { return; } List restoreOptions = null; try { restoreOptions = Backup.GetRestoreSelections(backupFile); } catch (Exception e) { Logger.Error(e, $"Failed to read backup file {backupFile}"); return; } var restoreItems = restoreOptions.Select(a => new SelectableNamedObject(a, a.GetDescription(), true)).ToList(); if (ItemSelector.SelectMultiple( LOC.MenuRestoreBackup, LOC.BackupRestoreMessage, restoreItems, out var selectedRestoreItems)) { var options = new BackupRestoreOptions { BackupFile = backupFile, DataDir = PlaynitePaths.ConfigRootPath, LibraryDir = GameDatabase.GetFullDbPath(AppSettings.DatabasePath), RestoreItems = selectedRestoreItems, RestoreLibrarySettingsPath = AppSettings.DatabasePath }; FileSystem.WriteStringToFile(PlaynitePaths.RestoreBackupActionFile, Serialization.ToJson(options)); App.Restart(new CmdLineOptions { RestoreBackup = PlaynitePaths.RestoreBackupActionFile, SkipLibUpdate = true }); } } private void BackupData() { var backupFile = Dialogs.SaveFile("Playnite Backup|*.zip", true); if (backupFile.IsNullOrEmpty()) { return; } List backupOptions = new List { BackupDataItem.LibraryFiles, BackupDataItem.Extensions, BackupDataItem.ExtensionsData, BackupDataItem.Themes }; var restoreItems = backupOptions.Select(a => new SelectableNamedObject(a, a.GetDescription(), true)).ToList(); if (ItemSelector.SelectMultiple( LOC.MenuBackupData, LOC.BackupDataBackupMessage, restoreItems, out var selectedBackupItems)) { var options = new BackupOptions { OutputFile = backupFile, DataDir = PlaynitePaths.ConfigRootPath, LibraryDir = GameDatabase.GetFullDbPath(AppSettings.DatabasePath), BackupItems = selectedBackupItems }; FileSystem.WriteStringToFile(PlaynitePaths.BackupActionFile, Serialization.ToJson(options)); App.Restart(new CmdLineOptions { Backup = PlaynitePaths.BackupActionFile, SkipLibUpdate = true }); } } public void StartThirdPartyTool(ThirdPartyTool tool) { try { tool.Start(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { Logger.Error(e, "Failed to start 3rd party tool."); Dialogs.ShowErrorMessage(Resources.GetString("LOCAppStartupError") + "\n\n" + e.Message, Resources.GetString("LOCStartupError")); } } } } ================================================ FILE: source/Playnite/ViewModels/ProgressViewViewModel.cs ================================================ using Playnite.SDK; using Playnite.Windows; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Threading; namespace Playnite.ViewModels { public class ProgressViewViewModel : ObservableObject { private static ILogger logger = LogManager.GetLogger(); private IWindowFactory window; private GlobalProgressOptions args; private CancellationTokenSource cancellationToken; private bool canCancel = false; private bool wasCancelled = false; private bool wasProcessed = false; private GlobalProgressActionArgs progressArgs; public GlobalProgressActionArgs ProgressArgs { get => progressArgs; set { progressArgs = value; OnPropertyChanged(); } } private bool cancelable; public bool Cancelable { get => cancelable; set { cancelable = value; OnPropertyChanged(); } } public RelayCommand CancelCommand { get => new RelayCommand((a) => { if (!canCancel) { return; } canCancel = false; wasCancelled = true; cancellationToken.Cancel(); }, (a) => Cancelable && canCancel && !cancellationToken.IsCancellationRequested); } public Exception FailException { get; private set; } public ProgressViewViewModel(IWindowFactory window, GlobalProgressOptions args) { this.window = window; this.args = args; cancellationToken = new CancellationTokenSource(); Cancelable = args.Cancelable; canCancel = Cancelable; ProgressArgs = new GlobalProgressActionArgs( PlayniteApplication.Current.SyncContext, PlayniteApplication.CurrentNative.Dispatcher, cancellationToken.Token) { Text = args.Text, IsIndeterminate = args.IsIndeterminate }; } public GlobalProgressResult ActivateProgress(Action progresAction) { if (wasProcessed) { throw new Exception("Progress can be shown only once per instance."); } Task.Run(() => { try { progresAction(ProgressArgs); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { FailException = exc; window.Close(false); return; } finally { canCancel = false; cancellationToken.Dispose(); } window.Close(true); }); var res = window.CreateAndOpenDialog(this); wasProcessed = true; return new GlobalProgressResult(res, wasCancelled, FailException); } public GlobalProgressResult ActivateProgress(Func progresAction) { if (wasProcessed) { throw new Exception("Progress can be shown only once per instance."); } Task.Run(async () => { try { await progresAction(ProgressArgs); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { FailException = exc; window.Close(false); return; } finally { canCancel = false; cancellationToken.Dispose(); } window.Close(true); }); var res = window.CreateAndOpenDialog(this); wasProcessed = true; return new GlobalProgressResult(res, wasCancelled, FailException); } public GlobalProgressResult ActivateProgress(Action progresAction, int delay) { if (wasProcessed) { throw new Exception("Progress can be shown only once per instance."); } bool? res = null; var progressTask = Task.Run(() => { try { progresAction(ProgressArgs); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { FailException = exc; if (window.Window?.IsShown == true) { window.Close(false); } res = false; return; } finally { canCancel = false; cancellationToken.Dispose(); } if (window.Window?.IsShown == true) { window.Close(true); } res = true; }); if (!progressTask.Wait(delay)) { window.CreateAndOpenDialog(this); } if (window.Window?.IsShown == true) { window.Close(true); } wasProcessed = true; return new GlobalProgressResult(res, wasCancelled, FailException); } } public class GlobalProgress { public static GlobalProgressResult ActivateProgress(Action progresAction, GlobalProgressOptions progressArgs) { var progressModel = new ProgressViewViewModel(new ProgressWindowFactory(), progressArgs); return progressModel.ActivateProgress(progresAction); } public static GlobalProgressResult ActivateProgress(Func progresAction, GlobalProgressOptions progressArgs) { var progressModel = new ProgressViewViewModel(new ProgressWindowFactory(), progressArgs); return progressModel.ActivateProgress(progresAction); } } } ================================================ FILE: source/Playnite/ViewModels/RandomGameSelectViewModel.cs ================================================ using Playnite.Common; using Playnite.Database; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.Windows; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.ViewModels { public enum RandomGameSelectAction { None, Play, Navigate } public class RandomGameSelectViewModel : ObservableObject { private static ILogger logger = LogManager.GetLogger(); private readonly IWindowFactory window; private readonly IResourceProvider resources; private readonly BaseCollectionView collection; private readonly IGameDatabaseMain database; public RandomGameSelectAction SelectedAction { get; private set; } = RandomGameSelectAction.None; private bool isLimitedToFilter = true; public bool IsLimitedToFilter { get => isLimitedToFilter; set { isLimitedToFilter = value; OnPropertyChanged(); } } private Game selectedGame; public Game SelectedGame { get => selectedGame; set { selectedGame = value; OnPropertyChanged(); } } public RelayCommand PickAnotherCommand { get => new RelayCommand((a) => { PickGame(); }); } public RelayCommand PlayGameCommand { get => new RelayCommand((a) => { PlayGame(); }, (a) => SelectedGame != null); } public RelayCommand CloseCommand { get => new RelayCommand((a) => { CloseView(); }); } public RelayCommand NavigateToGameCommand { get => new RelayCommand(() => { NavigateToGame(); }, () => SelectedGame != null); } public RandomGameSelectViewModel( IGameDatabaseMain database, BaseCollectionView collection, IWindowFactory window, IResourceProvider resources) { this.database = database; this.window = window; this.resources = resources; this.collection = collection; } public bool? OpenView() { PickGame(); return window.CreateAndOpenDialog(this); } public void PlayGame() { SelectedAction = RandomGameSelectAction.Play; window.Close(true); } public void CloseView() { SelectedAction = RandomGameSelectAction.None; window.Close(false); } public void NavigateToGame() { SelectedAction = RandomGameSelectAction.Navigate; window.Close(false); } public void PickGame() { var lastSelection = SelectedGame; if (IsLimitedToFilter) { var count = collection.CollectionView.Count; if (count == 1) { SelectedGame = (collection.CollectionView.GetItemAt(0) as GamesCollectionViewEntry).Game; } else if (count > 1) { var newSelection = lastSelection; while (newSelection == lastSelection) { var index = GlobalRandom.Next(0, count); newSelection = (collection.CollectionView.GetItemAt(index) as GamesCollectionViewEntry).Game; } SelectedGame = newSelection; } else { SelectedGame = null; } } else { var count = database.Games.Count; if (count == 1) { SelectedGame = database.Games.First(); } else if (count > 1) { var newSelection = lastSelection; while (newSelection == lastSelection) { var index = GlobalRandom.Next(0, count); newSelection = database.Games.ElementAt(index); } SelectedGame = newSelection; } else { SelectedGame = null; } } } } } ================================================ FILE: source/Playnite/ViewModels/SearchViewModel.cs ================================================ using Playnite.Converters; using Playnite.Database; using Playnite.Plugins; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using Playnite.Windows; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; namespace Playnite.ViewModels { public class GameMenuContext : SearchContext { private readonly MainViewModelBase mainModel; private readonly Game game; public GameMenuContext(Game game, MainViewModelBase mainModel) { this.game = game; this.mainModel = mainModel; UseAutoSearch = true; Label = game.Name; } public override IEnumerable GetSearchResults(GetSearchResultsArgs args) { return MenuItems.GetSearchGameMenuItems(game, mainModel); } } public class DefaultSearchContext : SearchContext { private readonly MainViewModelBase mainModel; private readonly List searchProviders; private readonly bool commandsInDefault; private List commands; public DefaultSearchContext(MainViewModelBase mainModel, List searchProviders, bool commandsInDefault) { this.mainModel = mainModel; this.searchProviders = searchProviders; this.commandsInDefault = commandsInDefault; Description = ResourceProvider.GetString(LOC.DefaultSearchDescription); Hint = ResourceProvider.GetString(LOC.DefaultSearchHint); } private SearchItemAction GetGameAction(Game game, GameSearchItemAction actionSettings) { switch (actionSettings) { case GameSearchItemAction.Play: return new SearchItemAction(game.IsInstalled ? LOC.PlayGame : LOC.InstallGame, () => mainModel.App.GamesEditor.StartContextAction(game)); case GameSearchItemAction.SwitchTo: return new SearchItemAction(LOC.GameSearchItemActionSwitchTo, () => { if (mainModel.AppSettings.ViewSettings.GamesViewType == DesktopView.List) { mainModel.AppSettings.ViewSettings.GamesViewType = DesktopView.Details; } else if (mainModel.AppSettings.ViewSettings.GamesViewType == DesktopView.Grid) { if (!mainModel.AppSettings.GridViewSideBarVisible) { mainModel.AppSettings.GridViewSideBarVisible = true; } } mainModel.SelectGame(game.Id, true); }); case GameSearchItemAction.OpenMenu: return new ContextSwitchSearchItemAction(LOC.GameSearchItemActionOpenMenu, new GameMenuContext(game, mainModel)); case GameSearchItemAction.Edit: return new SearchItemAction(LOC.GameSearchItemActionEdit, () => mainModel.EditGame(game)); case GameSearchItemAction.None: default: return null; } } private SearchItemAction GetPrimaryGameAction(Game game) { return GetGameAction(game, mainModel.AppSettings.PrimaryGameSearchItemAction); } private SearchItemAction GetSecondaryGameAction(Game game) { return GetGameAction(game, mainModel.AppSettings.SecondaryGameSearchItemAction); } private ContextSwitchSearchItemAction GetGameMenuAction(Game game) { return new ContextSwitchSearchItemAction(LOC.GameSearchItemActionOpenMenu, new GameMenuContext(game, mainModel)); } private bool GameFilter(Game game, string searchTerm, GameSearchFilterSettings settings, bool matchTargetAcronymStart) { if (game.Hidden && !settings.Hidden) { return false; } if (!game.IsInstalled && !settings.Uninstalled) { return false; } if (!SearchViewModel.MatchTextFilter(searchTerm, game.Name, matchTargetAcronymStart)) { return false; } return true; } public override IEnumerable GetSearchResults(GetSearchResultsArgs args) { IEnumerable searchCommnands(string keyword) { if (commands == null) { commands = mainModel.GetSearchCommands().ToList(); } foreach (var command in commands.Where(a => SearchViewModel.MatchTextFilter(keyword, a.Name, true))) { yield return command; } } if (args.SearchTerm.StartsWith("#")) { var commandSearch = args.SearchTerm.Substring(1).Trim(); foreach (var cmd in searchCommnands(commandSearch)) { yield return cmd; } yield break; } if (args.SearchTerm.EndsWith(" ")) { var providerTest = args.SearchTerm.Trim().TrimStart('/'); var provider = searchProviders.FirstOrDefault(a => a.Keyword.Equals(providerTest, StringComparison.InvariantCultureIgnoreCase)); if (provider != null) { args.SwitchContext(provider.Context); yield break; } } if (args.SearchTerm.StartsWith("/")) { var pluginSearch = args.SearchTerm.Substring(1).Trim(); foreach (var provider in searchProviders.Where(a => SearchViewModel.MatchTextFilter(pluginSearch, a.Name, false))) { yield return new SearchItem(provider.Name, new ContextSwitchSearchItemAction(LOC.Activate, provider.Context)) { Description = "/" + provider.Keyword }; } yield break; } if (args.SearchTerm.IsNullOrWhiteSpace()) { foreach (var game in mainModel.Database.Games. Where(a => a.LastActivity != null && GameFilter(a, string.Empty, args.GameFilterSettings, true)). OrderByDescending(a => a.LastActivity). Take(20)) { yield return new GameSearchItem(game, GetPrimaryGameAction(game)) { SecondaryAction = GetSecondaryGameAction(game), MenuAction = GetGameMenuAction(game) }; } yield break; } var searchTerm = args.SearchTerm.Trim(); if (commandsInDefault) { foreach (var cmd in searchCommnands(searchTerm)) { yield return cmd; } } foreach (var game in mainModel.Database.Games. Where(g => GameFilter(g, searchTerm, args.GameFilterSettings, true)) .OrderBy(a => a.Name.GetLevenshteinDistanceIgnoreCase(searchTerm)) .ThenBy(x => x.Name) .ThenByDescending(x => x.IsInstalled) .Take(60)) { yield return new GameSearchItem(game, GetPrimaryGameAction(game)) { SecondaryAction = GetSecondaryGameAction(game), MenuAction = GetGameMenuAction(game) }; } foreach (var tool in mainModel.Database.SoftwareApps.Where(a => SearchViewModel.MatchTextFilter(searchTerm, a.Name, true))) { yield return new SearchItem(tool.Name, LOC.Open, () => mainModel.StartSoftwareTool(tool), tool.Icon); } } } // TODO replace this in future by exposing GamesCollectionViewEntry to SDK and let plugins create them directly. // Also remove all plugin and setting dependencies from View class. public class GameSearchItemWrapper : GameSearchItem { public GamesCollectionViewEntry GameView { get; set; } public List AdditionalInfo { get; set; } = new List(); public GameSearchItemWrapper(GameSearchItem item, LibraryPlugin plugin, PlayniteSettings settings) : base(item.Game, item.PrimaryAction) { GameView = new GamesCollectionViewEntry(item.Game, plugin, settings, true); SecondaryAction = item.SecondaryAction; MenuAction = item.MenuAction; if (settings.SearchWindowVisibility.CompletionStatus && item.Game.CompletionStatus != null) { AdditionalInfo.Add(item.Game.CompletionStatus.Name); } if (settings.SearchWindowVisibility.PlayTime) { AdditionalInfo.Add(PlayTimeToStringConverter.Instance.Convert(item.Game.Playtime, typeof(string), settings.PlaytimeUseDaysFormat, CultureInfo.CurrentCulture) as string); } if (settings.SearchWindowVisibility.Platform && item.Game.Platforms.HasItems()) { item.Game.Platforms.ForEach(a => AdditionalInfo.Add(a.Name)); } if (settings.SearchWindowVisibility.ReleaseDate && item.Game.ReleaseDate != null) { AdditionalInfo.Add(item.Game.ReleaseDate.Value.Year.ToString()); } } public override string ToString() { return GameView?.Name; } } public class SearchItemWrapper : SearchItem { private readonly SynchronizationContext syncContext; public SearchItem Item { get; } private object itemIcon = null; public object ItemIcon { get { if (itemIcon != null) { return itemIcon; } if (Item.Icon == null) { return null; } if (Item.Icon is string stringIcon && stringIcon.IsHttpUrl()) { Task.Run(() => { itemIcon = SdkHelpers.ResolveUiItemIcon(Item.Icon, syncContext); if (itemIcon == null) { itemIcon = DependencyProperty.UnsetValue; } else { syncContext.Send((_) => OnPropertyChanged(nameof(ItemIcon)), null); } }); return null; } else { return SdkHelpers.ResolveUiItemIcon(Item.Icon); } } } public SearchItemWrapper(SearchItem item, SynchronizationContext syncContext) : base(item.Name, item.PrimaryAction) { this.syncContext = syncContext; Item = item; Description = item.Description; MenuAction = item.MenuAction; SecondaryAction = item.SecondaryAction; } public override string ToString() { return Item?.Name; } } public class SearchViewModel : ObservableObject { public class ItemAction : ObservableObject { private bool selected; public bool Selected { get => selected; set => SetValue(ref selected, value); } public string Name { get; set; } public Action Action { get; set; } public bool CloseView { get; set; } public ItemAction(string name, Action action, bool closeView) { Name = name; Action = action; CloseView = closeView; } public ItemAction(SearchItemAction action) { Name = action.Name; Action = action.Action; CloseView = action.CloseSearch; } } #region backing fields private List searchResults; private SearchItem selectedSearchItem; private string currentSearchProviderDescription; private bool slowAnimationActive; private ItemAction primaryAction; private ItemAction secondaryAction; private ItemAction menuAction; private string currentContextHint; private bool contextHintVisible; private bool filterHintVisible = false; private string filterHint; private string currentContextLabel; #endregion backing fields private static readonly char[] textMatchSplitter = new char[] { ' ' }; private static readonly ILogger logger = LogManager.GetLogger(); private readonly IWindowFactory window; private readonly IGameDatabaseMain database; private readonly ExtensionFactory extensions; private readonly MainViewModelBase mainModel; private int currentSearchDelay = 0; private readonly SynchronizationContext syncContext; private readonly System.Timers.Timer searchDelayTimer = new System.Timers.Timer { AutoReset = false }; private readonly System.Timers.Timer longSearchTimer = new System.Timers.Timer { AutoReset = false, Interval = 700 }; private CancellationTokenSource currentSearchToken; private int customProviderDeleteAttemps = 0; private readonly Stack searchContextStack = new Stack(); private const double defaultMinimumJaronWinklerSimilarity = 0.90; private bool isClosing = false; private string searchTerm; public string SearchTerm { get => searchTerm; set { searchTerm = value; OnPropertyChanged(); if (currentSearchDelay == 0) { #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed PerformSearch(); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } else { searchDelayTimer.Stop(); searchDelayTimer.Interval = currentSearchDelay; searchDelayTimer.Start(); } } } public bool FilterHintVisible { get => filterHintVisible; set => SetValue(ref filterHintVisible, value); } public string FilterHint { get => filterHint; set => SetValue(ref filterHint, value); } public GameSearchFilterSettings GameFilterSettings { get; set; } public string CurrentContextHint { get => currentContextHint; set => SetValue(ref currentContextHint, value); } public string CurrentContextLabel { get => currentContextLabel; set => SetValue(ref currentContextLabel, value); } public bool ContextHintVisible { get => contextHintVisible; set => SetValue(ref contextHintVisible, value); } public bool SlowAnimationActive { get => slowAnimationActive; set => SetValue(ref slowAnimationActive, value); } public string CurrentSearchProviderDescription { get => currentSearchProviderDescription; set => SetValue(ref currentSearchProviderDescription, value); } public List SearchResults { get => searchResults; set =>SetValue(ref searchResults, value); } public SearchItem SelectedSearchItem { get => selectedSearchItem; set { SetValue(ref selectedSearchItem, value); InitActions(selectedSearchItem); } } public RelayCommand CloseCommand => new RelayCommand(() => Close()); public RelayCommand TextBoxKeyDownCommand => new RelayCommand((keyArgs) => TextBoxKeyDown(keyArgs)); public RelayCommand TextBoxKeyUpCommand => new RelayCommand((keyArgs) => TextBoxKeyUp(keyArgs)); public RelayCommand WindowClosedCommand => new RelayCommand((_) => WindowClosed(_)); public RelayCommand WindowDeactivatedCommand => new RelayCommand((_) => WindowDeactivated(_)); public RelayCommand WindowClosingCommand => new RelayCommand((_) => WindowClosing(_)); public RelayCommand ToggleHintCommand => new RelayCommand(() => ToggleHint()); public RelayCommand OpenSearchSettingsCommand => new RelayCommand(() => OpenSearchSettings()); public RelayCommand DeactiveCurrentContextCommand => new RelayCommand(() => DeactiveCurrentContext()); public RelayCommand PrimaryActionCommand { get; } public RelayCommand SecondaryActionCommand { get; } public RelayCommand OpenMenuCommand { get; } public ItemAction PrimaryAction { get => primaryAction; set => SetValue(ref primaryAction, value); } public ItemAction SecondaryAction { get => secondaryAction; set => SetValue(ref secondaryAction, value); } public ItemAction MenuAction { get => menuAction; set => SetValue(ref menuAction, value); } public event EventHandler SearchClosed; public bool Active => window.Window.IsActive; public SearchViewModel( IWindowFactory window, IGameDatabaseMain database, ExtensionFactory extensions, MainViewModelBase mainModel) { this.window = window; this.database = database; this.extensions = extensions; this.mainModel = mainModel; PrimaryActionCommand = new RelayCommand(() => syncContext.Send(_ => { if (PrimaryAction.CloseView) { Close(); } PrimaryAction.Action(); }, null)); SecondaryActionCommand = new RelayCommand(() => syncContext.Send(_ => { if (SecondaryAction.CloseView) { Close(); } SecondaryAction.Action(); }, null)); OpenMenuCommand = new RelayCommand(() => syncContext.Send(_ => { if (MenuAction.CloseView) { Close(); } MenuAction.Action(); }, null)); var searchProviders = new List(); foreach (var plugin in extensions.Plugins) { foreach (var search in plugin.Value.Plugin.Searches ?? new List()) { var searchId = plugin.Value.Description.Id + search.DefaultKeyword; if (mainModel.AppSettings.CustomSearchKeywrods.TryGetValue(searchId, out var customKeywrod)) { search.Keyword = customKeywrod; } else { search.Keyword = search.DefaultKeyword; } searchProviders.Add(search); } } syncContext = SynchronizationContext.Current; searchDelayTimer.Elapsed += (_, __) => syncContext.Post((___) => { #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed PerformSearch(); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed }, null); longSearchTimer.Elapsed += (_, __) => { syncContext.Send((___) => SlowAnimationActive = true, null); }; if (mainModel.AppSettings.SaveGlobalSearchFilterSettings) { GameFilterSettings = mainModel.AppSettings.GameSearchFilterSettings.GetClone(); } else { GameFilterSettings = new GameSearchFilterSettings(); } GameFilterSettings.PropertyChanged += GameFilterSettings_PropertyChanged; SetCurrentContext(new DefaultSearchContext(mainModel, searchProviders, mainModel.AppSettings.IncludeCommandsInDefaultSearch)); } private void GameFilterSettings_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { SearchTerm = SearchTerm; } public void OpenSearch() { window.Show(this); } public void OpenSearch(string search) { window.Show(this); SearchTerm = search; } public void OpenSearch(SearchContext context, string search) { window.Show(this); SetCurrentContext(context); if (!search.IsNullOrEmpty()) { SearchTerm = search; } } public void Close() { // This is used because deactivate event is called before close event // so WindowDeactivated and WindowClosed would conflict. isClosing = true; window.Close(); } private void WindowDeactivated(EventArgs args) { // The view would get automatically closed once you switch to debugger... if (Debugger.IsAttached) { return; } if (isClosing) { return; } Close(); } private void WindowClosing(EventArgs args) { isClosing = true; } private void WindowClosed(EventArgs args) { // Don't call this in Close method because that's not invoked when closing using ALT-F4 currentSearchToken?.Cancel(); searchDelayTimer.Dispose(); longSearchTimer.Dispose(); if (mainModel.AppSettings.SaveGlobalSearchFilterSettings) { mainModel.AppSettings.GameSearchFilterSettings = GameFilterSettings.GetClone(); } SearchClosed?.Invoke(this, EventArgs.Empty); } private void SetCurrentContext(SearchContext context) { syncContext.Send((_) => { if (searchContextStack.Count == 0 || searchContextStack.Peek() != context) { searchContextStack.Push(context); } ContextHintVisible = false; CurrentContextHint = context.Hint; CurrentSearchProviderDescription = context.Description; if (searchContextStack.Count == 1) { CurrentContextLabel = null; } else { CurrentContextLabel = context.Label.IsNullOrEmpty() ? "search" : context.Label; } customProviderDeleteAttemps = 0; currentSearchDelay = 0; searchDelayTimer.Stop(); longSearchTimer.Stop(); SlowAnimationActive = false; // Not clearing results immediately will prevent "flashing" when switching contexts // because results list is being cleared completely and then populated again. if (context.Delay > 0) { SearchResults = null; } SearchTerm = string.Empty; // This is called AFTER initial search is set for case where's there's a delay set by search provider. // This makes it so the first search has no delay at all. customProviderDeleteAttemps = 1; currentSearchDelay = context.Delay; }, null); } private void DeactiveCurrentContext() { if (searchContextStack.Count == 1) { return; } searchContextStack.Pop(); SetCurrentContext(searchContextStack.Peek()); } private List FilterSearchResults(List toFilter, string filter, bool matchTargetAcronymStart) { if (toFilter is null) return new List(); var results = new List(); foreach (var item in toFilter) { if (MatchTextFilter(filter, item.Name, matchTargetAcronymStart)) { results.Add(item); } } return results; } public static bool MatchTextFilter(string filter, string toMatch, bool matchTargetAcronymStart, double minimumJaronWinklerSimilarity = defaultMinimumJaronWinklerSimilarity) { if (filter.IsNullOrWhiteSpace()) { return true; } if (!filter.IsNullOrWhiteSpace() && toMatch.IsNullOrWhiteSpace()) { return false; } if (filter.IsNullOrWhiteSpace() && toMatch.IsNullOrWhiteSpace()) { return true; } if (filter.GetJaroWinklerSimilarityIgnoreCase(toMatch) >= minimumJaronWinklerSimilarity) { return true; } if (filter.Length > toMatch.Length) { return false; } if (matchTargetAcronymStart && filter.IsStartOfStringAcronym(toMatch)) { return true; } var filterSplit = filter.Split(textMatchSplitter, StringSplitOptions.RemoveEmptyEntries); var toMatchSplit = toMatch.Split(textMatchSplitter, StringSplitOptions.RemoveEmptyEntries); var allMatch = true; // This is pretty crude, but it works for most cases and provides relatively good results. // TODO definitely could use some improvements for better fuzzy results. foreach (var word in filterSplit) { if (!toMatchSplit.Any(a => a.ContainsInvariantCulture(word, CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace))) { allMatch = false; break; } } return allMatch; } public async Task PerformSearch() { SlowAnimationActive = false; longSearchTimer.Stop(); longSearchTimer.Start(); searchDelayTimer.Stop(); currentSearchToken?.Cancel(); var searchToken = new CancellationTokenSource(); currentSearchToken = searchToken; var searchArgs = new GetSearchResultsArgs { CancelToken = searchToken.Token, SearchTerm = SearchTerm, SwitchContextAction = SetCurrentContext, GameFilterSettings = GameFilterSettings }; var results = await Task.Run(() => { try { var context = searchContextStack.Peek(); if (context.UseAutoSearch) { if (context.CacheAutoSearchResults) { if (context.AutoSearchCache == null) { context.AutoSearchCache = context.GetSearchResults(searchArgs)?.ToList() ?? new List(); } if (SearchTerm.IsNullOrWhiteSpace()) { return context.AutoSearchCache; } else { return FilterSearchResults(context.AutoSearchCache, SearchTerm, true); } } return FilterSearchResults(context.GetSearchResults(searchArgs)?.ToList(), SearchTerm, true); } else { return context.GetSearchResults(searchArgs)?.ToList() ?? new List(); } } catch (Exception e) { logger.Error(e, "Failed to get search results."); return null; } }); if (searchToken.IsCancellationRequested) { return; } if (results.HasItems()) { foreach (var item in results.ToList()) { if (item is GameSearchItemWrapper || item is SearchItemWrapper) { continue; } else if (item is GameSearchItem gameItem) { var index = results.IndexOf(item); results[index] = new GameSearchItemWrapper(gameItem, mainModel.Extensions.GetLibraryPlugin(gameItem.Game.PluginId), mainModel.AppSettings); } else { var index = results.IndexOf(item); results[index] = new SearchItemWrapper(item, syncContext); } } SearchResults = results; SelectedSearchItem = SearchResults[0]; } else { SearchResults = null; SelectedSearchItem = null; } currentSearchToken = null; searchToken.Dispose(); longSearchTimer.Stop(); SlowAnimationActive = false; } private void TextBoxKeyDown(KeyEventArgs keyArgs) { if (!SearchResults.HasItems()) { return; } if (SearchResults.Count > 0) { var selected = false; var curIndex = SearchResults.IndexOf(SelectedSearchItem); if (keyArgs.Key == Key.Up) { if (curIndex > 0) { SelectedSearchItem = SearchResults[curIndex - 1]; selected = true; } } else if (keyArgs.Key == Key.Down) { if (curIndex < SearchResults.Count - 1) { SelectedSearchItem = SearchResults[curIndex + 1]; selected = true; } } else if (keyArgs.Key == Key.PageUp) { if (curIndex - 5 > 0) { SelectedSearchItem = SearchResults[curIndex - 5]; } else { SelectedSearchItem = SearchResults[0]; } selected = true; } else if (keyArgs.Key == Key.PageDown) { if (curIndex + 5 < SearchResults.Count) { SelectedSearchItem = SearchResults[curIndex + 5]; } else { SelectedSearchItem = SearchResults[SearchResults.Count - 1]; } selected = true; } if (selected) { keyArgs.Handled = true; return; } } if (keyArgs.Key == Key.Tab) { ToggleSelectedAction(); keyArgs.Handled = true; return; } } private void TextBoxKeyUp(KeyEventArgs keyArgs) { if ((keyArgs.Key == Key.Return || keyArgs.Key == Key.Enter) && keyArgs.KeyboardDevice.Modifiers == ModifierKeys.Shift && MenuAction != null) { MenuAction.Action(); return; } if ((keyArgs.Key == Key.Return || keyArgs.Key == Key.Enter) && keyArgs.KeyboardDevice.Modifiers == ModifierKeys.Shift && MenuAction == null) { return; } if ((keyArgs.Key == Key.Return || keyArgs.Key == Key.Enter) && SelectedSearchItem != null) { InvokeSelectedAction(); return; } if (keyArgs.Key == Key.Back && SearchTerm.IsNullOrEmpty()) { if (customProviderDeleteAttemps >= 1) { DeactiveCurrentContext(); } else { // This is to switch contexts only after user uses backspace on already cleared search. // We don't want to switch contexts if user is just deleting current search. customProviderDeleteAttemps++; } return; } if (keyArgs.Key == Key.F1) { ToggleHint(); return; } if (keyArgs.Key == Key.F2) { ToggleInstalledFilter(); return; } if (keyArgs.Key == Key.F3) { ToggleHiddenFilter(); return; } if (!SearchTerm.IsNullOrEmpty()) { customProviderDeleteAttemps = 0; } } private void ToggleSelectedAction() { if (PrimaryAction?.Selected == true && SecondaryAction != null) { PrimaryAction.Selected = false; SecondaryAction.Selected = true; } else if (PrimaryAction?.Selected == true && MenuAction != null) { PrimaryAction.Selected = false; MenuAction.Selected = true; } else if (SecondaryAction?.Selected == true && MenuAction != null) { SecondaryAction.Selected = false; MenuAction.Selected = true; } else if (SecondaryAction?.Selected == true && PrimaryAction != null) { SecondaryAction.Selected = false; PrimaryAction.Selected = true; } else if (MenuAction?.Selected == true && PrimaryAction != null) { MenuAction.Selected = false; PrimaryAction.Selected = true; } else if (MenuAction?.Selected == true && SecondaryAction != null) { MenuAction.Selected = false; SecondaryAction.Selected = true; } } private void InvokeSelectedAction() { try { syncContext.Send(_ => { if (PrimaryAction?.Selected == true) { if (PrimaryAction.CloseView) { Close(); } PrimaryAction.Action(); } else if (SecondaryAction?.Selected == true) { if (SecondaryAction.CloseView) { Close(); } SecondaryAction.Action(); } else if (MenuAction?.Selected == true) { if (MenuAction.CloseView) { Close(); } MenuAction.Action(); } }, null); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to invoke search item action."); } } private void InitActions(SearchItem selectedSearchItem) { if (selectedSearchItem == null) { PrimaryAction = null; SecondaryAction = null; MenuAction = null; return; } ItemAction getAction(SearchItemAction action) { if (action != null) { if (action is ContextSwitchSearchItemAction contextAction) { return new ItemAction(contextAction.Name, () => SetCurrentContext(contextAction.Context), false); } else { return new ItemAction(action); } } else { return null; } } PrimaryAction = getAction(selectedSearchItem.PrimaryAction); SecondaryAction = getAction(selectedSearchItem.SecondaryAction); MenuAction = getAction(selectedSearchItem.MenuAction); if (PrimaryAction != null) { PrimaryAction.Selected = true; } else if (SecondaryAction != null) { SecondaryAction.Selected = true; } else if (MenuAction != null) { MenuAction.Selected = true; } } private void ToggleHint() { var context = searchContextStack.Peek(); if (!context.Hint.IsNullOrWhiteSpace()) { ContextHintVisible = !ContextHintVisible; } } private void OpenSearchSettings() { Close(); if (mainModel.App.Mode == ApplicationMode.Desktop) { mainModel.OpenSettings((int)DesktopSettingsPage.Search); } else { throw new NotSupportedInFullscreenException(); } } private void ToggleInstalledFilter() { FilterHint = GameFilterSettings.Uninstalled ? ResourceProvider.GetString(LOC.SearchFilterUninstalledExcluded) : ResourceProvider.GetString(LOC.SearchFilterUninstalledIncluded); GameFilterSettings.Uninstalled = !GameFilterSettings.Uninstalled; FilterHintVisible = true; FilterHintVisible = false; } private void ToggleHiddenFilter() { FilterHint = GameFilterSettings.Hidden ? ResourceProvider.GetString(LOC.SearchFilterHiddenalledExcluded) : ResourceProvider.GetString(LOC.SearchFilterHiddenIncluded); GameFilterSettings.Hidden = !GameFilterSettings.Hidden; FilterHintVisible = true; FilterHintVisible = false; } public void Focus() { window.RestoreWindow(); } } } ================================================ FILE: source/Playnite/ViewModels/UpdateViewModel.cs ================================================ using Playnite.SDK; using Playnite.Commands; using System; using System.Collections.Generic; using System.Threading; using System.Windows; using Playnite.Windows; namespace Playnite.ViewModels { public class UpdateViewModel : ObservableObject { private static ILogger logger = LogManager.GetLogger(); private IWindowFactory window; private Updater updater; private IResourceProvider resources; private IDialogsFactory dialogs; private ApplicationMode mode; private readonly SynchronizationContext context; private int updateProgress; public int UpdateProgress { get => updateProgress; set { updateProgress = value; OnPropertyChanged(); } } private bool showProgress; public bool ShowProgress { get => showProgress; set { showProgress = value; OnPropertyChanged(); } } public RelayCommand CloseCommand { get => new RelayCommand((a) => { CloseView(); }); } public RelayCommand InstallUpdateCommand { get => new RelayCommand((a) => { InstallUpdate(); }); } public List ReleaseNotes { get; private set; } public UpdateViewModel( Updater updater, IWindowFactory window, IResourceProvider resources, IDialogsFactory dialogs, ApplicationMode mode) { context = SynchronizationContext.Current; this.window = window; this.updater = updater; this.resources = resources; this.dialogs = dialogs; this.mode = mode; try { ReleaseNotes = updater.GetReleaseNotes(); } catch (Exception e) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(e, "Failed to download release notes."); } } public bool? OpenView() { return window.CreateAndOpenDialog(this); } public void CloseView() { window.Close(); } public async void InstallUpdate() { if (GlobalTaskHandler.IsActive) { if (dialogs.ShowMessage(resources.GetString("LOCUpdateProgressCancelAsk"), "", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { dialogs.ActivateGlobalProgress((_) => { try { GlobalTaskHandler.CancelAndWait(); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { logger.Error(exc, "Failed to cancel global progress task."); throw; } }, new GlobalProgressOptions("LOCProgressReleasingResources")); } else { window.Close(false); return; } } try { ShowProgress = true; await updater.DownloadUpdate((e) => { context.Post((a) => UpdateProgress = e.ProgressPercentage, null); }); updater.InstallUpdate(mode); window.Close(true); } catch (Exception exc) when (!PlayniteEnvironment.ThrowAllErrors) { ShowProgress = false; logger.Error(exc, "Failed to download and install update."); dialogs.ShowMessage( resources.GetString("LOCGeneralUpdateFailMessage") + $"\n{exc.Message}", resources.GetString("LOCUpdateError"), MessageBoxButton.OK, MessageBoxImage.Error); window.Close(false); return; } } } } ================================================ FILE: source/Playnite/WebView/CookieDestroyer.cs ================================================ using CefSharp; using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace Playnite.WebView { public class CookieDestroyer : ICookieVisitor { private readonly string domainName; private readonly bool useRegex; internal readonly AutoResetEvent Finished = new AutoResetEvent(false); public CookieDestroyer(string domainName, bool useRegex) { this.domainName = domainName; this.useRegex = useRegex; } public void Dispose() { } public bool Visit(Cookie cookie, int count, int total, ref bool deleteCookie) { if (useRegex && Regex.IsMatch(cookie.Domain, domainName)) { deleteCookie = true; } else if (cookie.Domain == domainName) { deleteCookie = true; } if (count == total - 1) { Finished.Set(); } return true; } } public class StandardCookieVisitor : ICookieVisitor { public readonly List Cookies = new List(); internal readonly AutoResetEvent Finished = new AutoResetEvent(false); public StandardCookieVisitor() { } public void Dispose() { } public bool Visit(Cookie cookie, int count, int total, ref bool deleteCookie) { Cookies.Add(new HttpCookie { Name = cookie.Name, Value = cookie.Value, Domain = cookie.Domain, Path = cookie.Path, Expires = cookie.Expires, Creation = cookie.Creation, HttpOnly = cookie.HttpOnly, LastAccess = cookie.LastAccess, Priority = (CookiePriority)(int)cookie.Priority, SameSite = (CookieSameSite)(int)cookie.SameSite, Secure = cookie.Secure }); if (count == total - 1) { Finished.Set(); } return true; } } public class DeleteCookiesHandler : IDeleteCookiesCallback { internal readonly AutoResetEvent Finished = new AutoResetEvent(false); public bool IsDisposed { get; private set; } public void Dispose() { IsDisposed = true; } public void OnComplete(int numDeleted) { Finished.Set(); } } public class SetCookieHandler : ISetCookieCallback { internal readonly AutoResetEvent Finished = new AutoResetEvent(false); public bool IsDisposed { get; private set; } public void Dispose() { IsDisposed = true; } public void OnComplete(bool success) { Finished.Set(); } } } ================================================ FILE: source/Playnite/WebView/OffscreenWebView.cs ================================================ using CefSharp; using Playnite.SDK; using Playnite.SDK.Events; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace Playnite.WebView { public class OffscreenWebView : WebViewBase, IWebView { private static readonly ILogger logger = LogManager.GetLogger(); private AutoResetEvent browserInitializedEvent = new AutoResetEvent(false); private AutoResetEvent loadCompleteEvent = new AutoResetEvent(false); private CefSharp.OffScreen.ChromiumWebBrowser browser; private readonly WebViewSettings settings; public bool CanExecuteJavascriptInMainFrame => browser.CanExecuteJavascriptInMainFrame; public Window WindowHost { get; } public event EventHandler NavigationChanged; public event EventHandler LoadingChanged; public OffscreenWebView() { this.settings = new WebViewSettings(); Initialize(); } public OffscreenWebView(WebViewSettings settings) { this.settings = settings; Initialize(new BrowserSettings { Javascript = settings.JavaScriptEnabled ? CefState.Enabled : CefState.Disabled }); } private void Initialize(BrowserSettings browserSettings = null) { browser = new CefSharp.OffScreen.ChromiumWebBrowser(automaticallyCreateBrowser: false); if (!settings.UserAgent.IsNullOrWhiteSpace() || settings.ResourceLoadedCallback != null) { browser.RequestHandler = new CustomRequestHandler(settings); } browser.LoadingStateChanged += Browser_LoadingStateChanged; browser.BrowserInitialized += Browser_BrowserInitialized; if (browserSettings != null) { browser.CreateBrowser(null, browserSettings); } else { browser.CreateBrowser(); } if (!browserInitializedEvent.WaitOne(30000)) { logger.Error("Failed to initialize OffscreenWebView in timely manner."); } } private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e) { if (e.IsLoading == false) { loadCompleteEvent.Set(); } LoadingChanged?.Invoke(this, new WebViewLoadingChangedEventArgs { IsLoading = e.IsLoading }); NavigationChanged?.Invoke(this, new EventArgs()); } private async void Browser_BrowserInitialized(object sender, EventArgs e) { browserInitializedEvent.Set(); if (!settings.UserAgent.IsNullOrWhiteSpace()) { using (var client = browser.GetDevToolsClient()) { await client.Network.SetUserAgentOverrideAsync(settings.UserAgent); await client.Emulation.SetUserAgentOverrideAsync(settings.UserAgent); } } } public void Close() { } public void Dispose() { browser.LoadingStateChanged -= Browser_LoadingStateChanged; browser.BrowserInitialized -= Browser_BrowserInitialized; browser.Dispose(); } public string GetCurrentAddress() { return browser.Address; } public string GetPageText() { return browser.GetTextAsync().GetAwaiter().GetResult(); } public Task GetPageTextAsync() { return browser.GetTextAsync(); } public string GetPageSource() { return browser.GetSourceAsync().GetAwaiter().GetResult(); } public Task GetPageSourceAsync() { return browser.GetSourceAsync(); } public void NavigateAndWait(string url) { browser.Load(url); loadCompleteEvent.WaitOne(20000); } public void Navigate(string url) { browser.Load(url); } public void Open() { throw new NotImplementedException(); } public bool? OpenDialog() { throw new NotImplementedException(); } public async Task EvaluateScriptAsync(string script) { var res = await browser.EvaluateScriptAsync(script); return new JavaScriptEvaluationResult { Message = res.Message, Result = res.Result, Success = res.Success }; } } } ================================================ FILE: source/Playnite/WebView/WebView.cs ================================================ using CefSharp; using Playnite.Windows; using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Media; using System.Windows; using Playnite.SDK.Events; using CefSharp.Wpf.Rendering.Experimental; namespace Playnite.WebView { public class WebView : WebViewBase, IWebView { private readonly SynchronizationContext context; private AutoResetEvent loadCompleteEvent = new AutoResetEvent(false); private WebViewWindow window; private readonly WebViewSettings settings; public bool CanExecuteJavascriptInMainFrame => window.Browser.CanExecuteJavascriptInMainFrame; public event EventHandler NavigationChanged; public event EventHandler LoadingChanged; public Window WindowHost => window; public WebView(WebViewSettings settings, bool useCompositionRenderer = false) { context = SynchronizationContext.Current; window = new WebViewWindow(); this.settings = settings; window.Browser.LoadingStateChanged += Browser_LoadingStateChanged; window.Browser.TitleChanged += Browser_TitleChanged; if (!settings.UserAgent.IsNullOrWhiteSpace() || settings.ResourceLoadedCallback != null) { window.Browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged; window.Browser.RequestHandler = new CustomRequestHandler(settings); } if (useCompositionRenderer) { window.Browser.RenderHandler = new CompositionTargetRenderHandler(window.Browser, window.Browser.DpiScaleFactor, window.Browser.DpiScaleFactor); } window.Owner = WindowManager.CurrentWindow; window.Width = settings.WindowWidth; window.Height = settings.WindowHeight; window.PanelContent.Background = new SolidColorBrush(settings.WindowBackground); } private async void Browser_IsBrowserInitializedChanged(object sender, DependencyPropertyChangedEventArgs e) { if ((e.NewValue is bool init) && init == true && !settings.UserAgent.IsNullOrWhiteSpace()) { using (var client = window.Browser.GetDevToolsClient()) { await client.Network.SetUserAgentOverrideAsync(settings.UserAgent); await client.Emulation.SetUserAgentOverrideAsync(settings.UserAgent); } } } private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e) { if (e.IsLoading == false) { loadCompleteEvent.Set(); } LoadingChanged?.Invoke(this, new WebViewLoadingChangedEventArgs { IsLoading = e.IsLoading }); NavigationChanged?.Invoke(this, new EventArgs()); } private void Browser_TitleChanged(object sender, DependencyPropertyChangedEventArgs args) { string titlePrefix = args.NewValue as string; string titleSuffix = "Playnite"; window.Title = string.IsNullOrEmpty(titlePrefix) ? titleSuffix : string.Format("{0} - {1}", titlePrefix, titleSuffix); } public void Close() { context.Send(a => window.Close(), null); } public void Dispose() { window.Browser.LoadingStateChanged -= Browser_LoadingStateChanged; window.Browser.TitleChanged -= Browser_TitleChanged; window.Browser.IsBrowserInitializedChanged -= Browser_IsBrowserInitializedChanged; window.Close(); window.Browser.Dispose(); } public string GetCurrentAddress() { var address = string.Empty; context.Send(a => address = window.Browser.Address, null); return address; } public Task GetPageTextAsync() { return window.Browser.GetTextAsync(); } public string GetPageText() { var text = string.Empty; context.Send(a => text = window.Browser.GetTextAsync().GetAwaiter().GetResult(), null); return text; } public string GetPageSource() { var text = string.Empty; context.Send(a => text = window.Browser.GetSourceAsync().GetAwaiter().GetResult(), null); return text; } public Task GetPageSourceAsync() { return window.Browser.GetSourceAsync(); } public void NavigateAndWait(string url) { context.Send(a => window.Browser.Address = url, null); loadCompleteEvent.WaitOne(20000); } public void Navigate(string url) { context.Send(a => window.Browser.Address = url, null); } public void Open() { window.Show(); } public bool? OpenDialog() { return window.ShowDialog(); } public async Task EvaluateScriptAsync(string script) { var res = await window.Browser.EvaluateScriptAsync(script); return new JavaScriptEvaluationResult { Message = res.Message, Result = res.Result, Success = res.Success }; } } } ================================================ FILE: source/Playnite/WebView/WebViewBase.cs ================================================ using CefSharp; using DiscordRPC; using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; namespace Playnite.WebView { public class CustomResourceRequestHandler : CefSharp.Handler.ResourceRequestHandler { private static readonly ILogger logger = LogManager.GetLogger(); private readonly WebViewSettings settings; private readonly MemoryStream contentStream; public CustomResourceRequestHandler(WebViewSettings settings) { this.settings = settings; if (settings.PassResourceContentStreamToCallback) contentStream = new MemoryStream(); } public static Playnite.SDK.WebViewModels.Request ConvertRequest(IRequest request) { var result = new SDK.WebViewModels.Request { Method = request.Method, ResourceType = (Playnite.SDK.WebViewModels.ResourceType)request.ResourceType, Url = request.Url, Headers = new Dictionary() }; foreach (string header in request.Headers) result.Headers.Add(header, request.Headers[header]); return result; } public static Playnite.SDK.WebViewModels.Response ConvertResponse(IResponse response) { var result = new SDK.WebViewModels.Response { Charset = response.Charset, MimeType = response.MimeType, StatusCode = response.StatusCode, StatusText = response.StatusText, Headers = new Dictionary() }; foreach (string header in response.Headers) result.Headers.Add(header, response.Headers[header]); return result; } protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback) { if (!settings.UserAgent.IsNullOrWhiteSpace()) request.SetHeaderByName("user-agent", settings.UserAgent, true); return CefReturnValue.Continue; } protected override IResponseFilter GetResourceResponseFilter(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response) { if (settings.PassResourceContentStreamToCallback) return new CefSharp.ResponseFilter.StreamResponseFilter(contentStream); else return base.GetResourceResponseFilter(chromiumWebBrowser, browser, frame, request, response); } protected override void OnResourceLoadComplete(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength) { if (settings.ResourceLoadedCallback != null) { var args = new WebViewResourceLoadedCallback( ConvertRequest(request), ConvertResponse(response), (SDK.WebViewModels.UrlRequestStatus)status, receivedContentLength); if (settings.PassResourceContentStreamToCallback) args.ResponseContent = contentStream; try { settings.ResourceLoadedCallback(args); } catch (Exception e) { logger.Error(e, "Web view resource callback failed."); } } base.OnResourceLoadComplete(chromiumWebBrowser, browser, frame, request, response, status, receivedContentLength); } protected override void Dispose() { base.Dispose(); contentStream?.Dispose(); } } public class CustomRequestHandler : CefSharp.Handler.RequestHandler { private readonly WebViewSettings settings; public CustomRequestHandler(WebViewSettings settings) { this.settings = settings; } protected override IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) { return new CustomResourceRequestHandler(settings); } } public class WebViewBase { // This needs to be done before calling cookie visit methods because we won't get any callbacks if no cookies exist, // which would lead to a deadlock. There's no way how to tell beforehand if any cookies exist or not. private void MakeSureCookiesExist(ICookieManager manager) { using (var setHandler = new SetCookieHandler()) { if (manager.SetCookie("https://playnite.test", new Cookie { Domain = "playnite.test", Name = "playnite.test", Value = "playnite.test" }, setHandler)) { setHandler.Finished.WaitOne(); } } } public List GetCookies() { using (var manager = Cef.GetGlobalCookieManager()) using (var visitor = new StandardCookieVisitor()) { MakeSureCookiesExist(manager); if (manager.VisitAllCookies(visitor)) { visitor.Finished.WaitOne(); } return visitor.Cookies; } } public void DeleteDomainCookies(string domain) { using (var manager = Cef.GetGlobalCookieManager()) using (var destoyer = new CookieDestroyer(domain, false)) { MakeSureCookiesExist(manager); if (manager.VisitAllCookies(destoyer)) { destoyer.Finished.WaitOne(); } } } public void DeleteDomainCookiesRegex(string domainRegex) { using (var manager = Cef.GetGlobalCookieManager()) using (var destoyer = new CookieDestroyer(domainRegex, true)) { MakeSureCookiesExist(manager); if (manager.VisitAllCookies(destoyer)) { destoyer.Finished.WaitOne(); } } } public void DeleteCookies(string url, string name) { using (var manager = Cef.GetGlobalCookieManager()) using (var deleteHandle = new DeleteCookiesHandler()) { MakeSureCookiesExist(manager); if (manager.DeleteCookies(url, name, deleteHandle)) { deleteHandle.Finished.WaitOne(); } } } public void SetCookies(string url, string domain, string name, string value, string path, DateTime expires) { using (var manager = Cef.GetGlobalCookieManager()) using (var setHandler = new SetCookieHandler()) { if (manager.SetCookie(url, new Cookie { Domain = domain, Name = name, Value = value, Expires = expires, HttpOnly = false, Secure = false, Path = path }, setHandler)) { setHandler.Finished.WaitOne(); } } } public void SetCookies(string url, HttpCookie cookie) { using (var manager = Cef.GetGlobalCookieManager()) using (var setHandler = new SetCookieHandler()) { if (manager.SetCookie(url, new Cookie() { Domain = cookie.Domain, Expires = cookie.Expires, HttpOnly = cookie.HttpOnly, Secure = cookie.Secure, SameSite = (CefSharp.Enums.CookieSameSite)(int)cookie.SameSite, Priority = (CefSharp.Enums.CookiePriority)(int)cookie.Priority, Name = cookie.Name, Path = cookie.Path, Value = cookie.Value }, setHandler)) { setHandler.Finished.WaitOne(); } } } } } ================================================ FILE: source/Playnite/WebView/WebViewFactory.cs ================================================ using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Media; namespace Playnite.WebView { public class WebViewFactory : IWebViewFactory { private PlayniteSettings appSettings; public WebViewFactory(PlayniteSettings settings) { appSettings = settings; } public IWebView CreateOffscreenView() { return new OffscreenWebView(); } public IWebView CreateOffscreenView(WebViewSettings settings) { return new OffscreenWebView(settings); } public IWebView CreateView(int width, int height) { return new WebView( new WebViewSettings() { WindowWidth = width, WindowHeight = height }, appSettings.UseCompositionWebViewRenderer); } public IWebView CreateView(int width, int height, Color background) { return new WebView( new WebViewSettings() { WindowWidth = width, WindowHeight = height, WindowBackground = background }, appSettings.UseCompositionWebViewRenderer); } public IWebView CreateView(WebViewSettings settings) { return new WebView(settings, appSettings.UseCompositionWebViewRenderer); } } } ================================================ FILE: source/Playnite/Windows/CrashHandlerWindowFactory.cs ================================================ using Playnite.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Windows { public class CrashHandlerWindowFactory : WindowFactory { public static Type WindowType { get; private set; } public static void SetWindowType() where TType : WindowBase { WindowType = typeof(TType); } public override WindowBase CreateNewWindowInstance() { return WindowType.CrateInstance(); } } } ================================================ FILE: source/Playnite/Windows/ExtensionCrashHandlerWindowFactory.cs ================================================ using Playnite.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Windows { public class ExtensionCrashHandlerWindowFactory : WindowFactory { public static Type WindowType { get; private set; } public static void SetWindowType() where TType : WindowBase { WindowType = typeof(TType); } public override WindowBase CreateNewWindowInstance() { return WindowType.CrateInstance(); } } } ================================================ FILE: source/Playnite/Windows/ItemSelectorWindowFactory.cs ================================================ using Playnite.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Windows { public class SingleItemSelectionWindowFactory : WindowFactory { public static Type WindowType { get; private set; } public static void SetWindowType() where TType : WindowBase { WindowType = typeof(TType); } public override WindowBase CreateNewWindowInstance() { return WindowType.CrateInstance(); } } public class MultiItemSelectionWindowFactory : WindowFactory { public static Type WindowType { get; private set; } public static void SetWindowType() where TType : WindowBase { WindowType = typeof(TType); } public override WindowBase CreateNewWindowInstance() { return WindowType.CrateInstance(); } } } ================================================ FILE: source/Playnite/Windows/LicenseAgreementWindowFactory.cs ================================================ using Playnite.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Windows { public class LicenseAgreementWindowFactory : WindowFactory { public static Type WindowType { get; private set; } public static void SetWindowType() where TType : WindowBase { WindowType = typeof(TType); } public override WindowBase CreateNewWindowInstance() { return WindowType.CrateInstance(); } } } ================================================ FILE: source/Playnite/Windows/ProgressWindowFactory.cs ================================================ using Playnite.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Windows { public class ProgressWindowFactory : WindowFactory { public static Type WindowType { get; private set; } public static void SetWindowType() where TType : WindowBase { WindowType = typeof(TType); } public override WindowBase CreateNewWindowInstance() { return WindowType.CrateInstance(); } } } ================================================ FILE: source/Playnite/Windows/UpdateWindowFactory.cs ================================================ using Playnite.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Playnite.Windows { public class UpdateWindowFactory : WindowFactory { public static Type WindowType { get; private set; } public static void SetWindowType() where TType : WindowBase { WindowType = typeof(TType); } public override WindowBase CreateNewWindowInstance() { return WindowType.CrateInstance(); } } } ================================================ FILE: source/Playnite/Windows/WebViewWindow.xaml ================================================  ================================================ FILE: source/Playnite/Windows/WebViewWindow.xaml.cs ================================================ using CefSharp; using Playnite.Controls; using System.Windows; namespace Playnite.Windows { /// /// Interaction logic for WebViewWindow.xaml /// public partial class WebViewWindow : WindowBase { public WebViewWindow() : base() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { Browser.Focus(); } private void WindowBase_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) { if (Browser.IsInitialized && e.Key == System.Windows.Input.Key.F12) { Browser.ShowDevTools(); } } } } ================================================ FILE: source/Playnite/Windows/WindowFactory.cs ================================================ using Playnite.Common; using Playnite.Controls; using Playnite.Native; using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Interop; namespace Playnite.Windows { public interface IWindowFactory { bool WasClosed { get; } bool? CreateAndOpenDialog(object dataContext); void Show(object dataContext); void RestoreWindow(); void Close(); void Close(bool? resutl); WindowBase Window { get; } } public abstract class WindowFactory : IWindowFactory { private static ILogger logger = LogManager.GetLogger(); private readonly SynchronizationContext context; private bool asDialog = false; private AutoResetEvent initFinishedEvent { get; } = new AutoResetEvent(false); public bool WasClosed { get; private set; } = false; public WindowBase Window { get; private set; } public abstract WindowBase CreateNewWindowInstance(); public WindowFactory() { context = SynchronizationContext.Current ?? PlayniteApplication.Current.SyncContext; } public bool? CreateAndOpenDialog(object dataContext) { bool? result = null; context.Send((a) => { Window = CreateNewWindowInstance(); Window.Closed += Window_Closed; Window.Loaded += Window_Loaded; Window.DataContext = dataContext; logger.Debug($"Show dialog window {GetType()}: {Window.Id}"); var currentWindow = WindowManager.CurrentWindow; if (currentWindow != null && Window != currentWindow) { if (typeof(WindowBase).IsAssignableFrom(currentWindow.GetType()) && ((WindowBase)currentWindow).IsShown) { Window.Owner = currentWindow; } } if (Window.Owner == null) { Window.WindowStartupLocation = WindowStartupLocation.CenterScreen; Window.ShowInTaskbar = true; } asDialog = true; WasClosed = false; Window.ShowDialog(); result = Window.DialogResultFixed; }, null); return result; } public void Show(object dataContext) { context.Send((a) => { asDialog = false; if (WasClosed) { logger.Debug($"Opening window that was closed previously {GetType()}, old Id: {Window.Id}"); Window = CreateNewWindowInstance(); Window.Closed += Window_Closed; Window.Loaded += Window_Loaded; } if (Window == null) { Window = CreateNewWindowInstance(); Window.Closed += Window_Closed; Window.Loaded += Window_Loaded; } logger.Debug($"Show window {GetType()}: {Window.Id}"); Window.DataContext = dataContext; WasClosed = false; Window.Show(); }, null); } private void Window_Closed(object sender, EventArgs e) { WasClosed = true; Window.Closed -= Window_Closed; Window.Loaded -= Window_Loaded; } private void Window_Loaded(object sender, RoutedEventArgs e) { Window.Loaded -= Window_Loaded; // Loaded can be in theory called more than once so we remove handler on first hit initFinishedEvent.Set(); } public void RestoreWindow() { context.Send((a) => { WindowUtils.RestoreWindow(Window); }, null); } public void Close() { Close(null); } public void Close(bool? result) { // This needs to be here in case Close is called too early. // This can happen in async scenarios like with ProgressViewViewModel and progress dialogs if progress code is too fast to complete. initFinishedEvent.WaitOne(); logger.Debug($"Closing window {GetType()}: {Window.Id}, {result}"); context.Send(async (_) => { // This is a workaround for WPF bug which causes deadlock in ShowDialog // if parent of modal window is closed before the child window itself is closed. // To prevent this we need to make sure that window parenting other windows is only // closed after all children are closed. // https://github.com/dotnet/wpf/issues/277 // https://stackoverflow.com/questions/40304161/showdialog-method-hangs-without-showing-the-window-deadlock#48208699 while (Window.GetHasChild()) { await Task.Delay(100); } if (asDialog) { // This is a workaround for issue with original DialogResult. // For some reason, setting DialogResult fails in rare cases on // "you need to open this window first" exception even when we are for sure // setting this after the window was already opened, see initFinishedEvent. Window.DialogResultFixed = result; try { Window.DialogResult = result; } catch (Exception e) { logger.Error(e, $"DialogResult fail {GetType()}, {Window.Id}, {result}"); } } Window.Close(); }, null); } } public static class WindowUtils { private static ILogger logger = LogManager.GetLogger(); public static void RestoreWindow(this Window window) { // This is the only reliable method that also doesn't result in issues like this: // https://www.reddit.com/r/playnite/comments/f6d73l/bug_full_screen_ui_wont_respond_to_left_stick/ // Adapted from https://ask.xiaolee.net/questions/1040342 try { if (PlayniteApplication.Current?.Mode == ApplicationMode.Fullscreen) { // Show() call is needed when restoring from minimized state otherwise restored window will // not render properly for some reason (will display just black). // BUT we can't call it always otherwise it will bug out restore if atl-tabbing was // used in the past for switching windows, see next comment... if (window.WindowState == WindowState.Minimized || window.Visibility != Visibility.Visible) { window.Show(); } // This needs to be set always otherwise restore will not work if user alt-tabbed out of Playnite. // Yeah apparently switching windows is something Windows can't do reliably in 2023... window.WindowState = WindowState.Normal; if (!window.Activate()) { window.Topmost = true; window.Topmost = false; } } else { window.Show(); if (!window.Activate()) { window.Topmost = true; window.Topmost = false; } if (window.WindowState == WindowState.Minimized) { window.WindowState = WindowState.Normal; } } //Get the process ID for this window's thread var interopHelper = new WindowInteropHelper(window); var thisWindowThreadId = User32.GetWindowThreadProcessId(interopHelper.Handle, IntPtr.Zero); //Get the process ID for the foreground window's thread var currentForegroundWindow = User32.GetForegroundWindow(); var currentForegroundWindowThreadId = User32.GetWindowThreadProcessId(currentForegroundWindow, IntPtr.Zero); //Attach this window's thread to the current window's thread User32.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true); //Set the window position User32.SetWindowPos(interopHelper.Handle, new IntPtr(0), 0, 0, 0, 0, SWP.NOSIZE | SWP.NOMOVE | SWP.SHOWWINDOW); //Detach this window's thread from the current window's thread User32.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false); } catch (Exception e) { logger.Error(e, "Failed to restore window."); } } } } ================================================ FILE: source/Playnite/Windows/WindowManager.cs ================================================ using Playnite.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; namespace Playnite.Windows { public static class WindowManager { static WindowManager() { EventManager.RegisterClassHandler(typeof(WindowBase), WindowBase.ActivatedRoutedEvent, new RoutedEventHandler(ActivatedRoutedEventHandler)); EventManager.RegisterClassHandler(typeof(WindowBase), WindowBase.ClosedRoutedEvent, new RoutedEventHandler(WindowBaseCloseHandler)); } private static void ActivatedRoutedEventHandler(object sender, RoutedEventArgs e) { LastActiveWindow = (WindowBase)sender; } private static void WindowBaseCloseHandler(object sender, RoutedEventArgs e) { if (LastActiveWindow == (WindowBase)sender) { LastActiveWindow = null; } } public static WindowBase LastActiveWindow { get; private set; } public static Window CurrentWindow { get { Window window = null; for (int i = PlayniteApplication.CurrentNative.Windows.Count - 1; i >= 0; i--) { window = PlayniteApplication.CurrentNative.Windows[i]; if (window.IsActive) { return window; } } return window ?? PlayniteApplication.CurrentNative.MainWindow; } } public static bool GetHasChild(this Window window) { return window.OwnedWindows.Count > 0; } public static void NotifyChildOwnershipChanges() { foreach (var wnd in PlayniteApplication.CurrentNative.Windows) { if (wnd is WindowBase window) { window.OnPropertyChanged(nameof(WindowBase.HasChildWindow)); } } } public static void SetEnableMouseInput(bool enable) { foreach (var wnd in PlayniteApplication.CurrentNative.Windows) { if (wnd is WindowBase window) { window.IsHitTestVisible = enable; } } } } } ================================================ FILE: source/Playnite/Windows/WindowPositionHandler.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; using System.Windows.Media; using Playnite; using Playnite.Common; using Playnite.Settings; namespace Playnite.Windows { public class WindowPositionHandler { private Window window; private readonly string windowName; private readonly WindowPositions configuration; private bool ignoreChanges = false; private readonly bool saveSize; public WindowPositionHandler(Window window, string windowName, WindowPositions settings, bool saveSize = true) { this.window = window; this.windowName = windowName; this.saveSize = saveSize; configuration = settings; window.SizeChanged += Window_SizeChanged; window.LocationChanged += Window_LocationChanged; window.StateChanged += Window_StateChanged; window.Loaded += Window_Loaded; window.Closed += Window_Closed; } private void Window_Closed(object sender, EventArgs e) { window.SizeChanged -= Window_SizeChanged; window.LocationChanged -= Window_LocationChanged; window.StateChanged -= Window_StateChanged; window.Loaded -= Window_Loaded; window.Closed -= Window_Closed; window = null; } private void Window_Loaded(object sender, RoutedEventArgs e) { RestoreSizeAndLocation(); window.Loaded -= Window_Loaded; } private void Window_StateChanged(object sender, EventArgs e) { if (window.IsLoaded) { SaveState(); } } private void Window_LocationChanged(object sender, EventArgs e) { if (window.IsLoaded) { SavePosition(); } } private void Window_SizeChanged(object sender, SizeChangedEventArgs e) { if (window.IsLoaded) { SaveSize(); } } private void MakeSureConfigEntryExists() { if (!configuration.Positions.ContainsKey(windowName)) { configuration.Positions[windowName] = new WindowPosition(); } } private void SaveState() { if (configuration == null || ignoreChanges) { return; } // Don't save minimized state. It would not be very user friendly if user exit Playnite while minimized // and it would then open minimized on next startup. if (window.WindowState == WindowState.Minimized) { return; } MakeSureConfigEntryExists(); configuration.Positions[windowName].State = window.WindowState; } private void SaveSize() { if (!saveSize) { return; } if (configuration == null || ignoreChanges) { return; } // Don't save size if windows is maximized, it would be too large when it would restore back to normal state. // Don't save size if windows is minimized becuase it has no size :) if (window.WindowState != WindowState.Normal) { return; } MakeSureConfigEntryExists(); configuration.Positions[windowName].Size = new WindowPosition.Point() { X = window.Width, Y = window.Height }; } private void SavePosition() { if (configuration == null || ignoreChanges) { return; } MakeSureConfigEntryExists(); configuration.Positions[windowName].Position = new WindowPosition.Point() { X = window.Left, Y = window.Top }; } // TODO: this needs complete rework when per display DPI support is enabled in P11. // Stored position must be saved in DPI independent (in 100% scaling) coordinates and relative to active display, // not relative to the whole desktop. Otherwise it gets messy in mixed DPI multi-monitor environments. private void ConstrainWindow(int x, int y) { var dpi = VisualTreeHelper.GetDpi(window); var positioned = false; // Make sure that position is part of at least one connected screen foreach (var monitor in Computer.GetScreens()) { var xTest = (int)(x * dpi.DpiScaleX); var yTest = (int)(y * dpi.DpiScaleY); if (monitor.WorkingArea.Contains(xTest, yTest)) { window.Left = x; window.Top = y; positioned = true; break; } else if (monitor.WorkingArea.Contains(xTest + 8, yTest + 8)) { // 8 pixel offset is there for cases where a window is maximized using drag to top of the screen. // Window's position is then, for some reason, set with -8,-8 pixel offset which would make constrain check to fail. window.Left = x + 8; window.Top = y + 8; positioned = true; break; } } if (!positioned) { window.Left = 0; window.Top = 0; } } private void RestoreSizeAndLocation() { if (!configuration.Positions.ContainsKey(windowName)) { ConstrainWindow((int)window.Left, (int)window.Top); return; } ignoreChanges = true; try { var data = configuration.Positions[windowName]; if (data.Position != null) { ConstrainWindow((int)data.Position.X, (int)data.Position.Y); } if (saveSize) { if (data.Size != null) { // If a window has some constrains set (like min size), then size change event is called when settings new value // which overrides saved data because of Window_SizeChanged callback. var width = data.Size.X; var height = data.Size.Y; if (width >= window.MinWidth) { window.Width = width; } if (height >= window.MinHeight) { window.Height = height; } } } window.WindowState = data.State; } finally { ignoreChanges = false; } } public bool HasSavedData() { return configuration.Positions.ContainsKey(windowName); } } } ================================================ FILE: source/Playnite/WindowsNotifyIconManager.cs ================================================ using System; using System.Drawing; using System.Windows.Forms; namespace Playnite { public static class WindowsNotifyIconManager { public static void Notify(Icon icon, string title, string body, Action clickAction = null) { var notifyIcon = new NotifyIcon { Icon = icon, BalloonTipTitle = title, BalloonTipText = body, Visible = true }; notifyIcon.BalloonTipClicked += (o, ea) => { clickAction?.Invoke(); notifyIcon.Dispose(); }; notifyIcon.BalloonTipClosed += (o, ea) => { notifyIcon.Dispose(); }; notifyIcon.ShowBalloonTip(0); // Windows Vista and up timeout is 5sec by default, only Windows Accessibility Settings can override this } } } ================================================ FILE: source/Playnite/crash_reporter.cfg ================================================  [Config] ProductName=Playnite ProductVersion=1.0.0 AppName=Playnite ExternalHandler=CefSharp.BrowserSubprocess.exe ================================================ FILE: source/Playnite/gamecontrollerdb.txt ================================================ # Game Controller DB for SDL in 2.0.16 format # Source: https://github.com/mdqinc/SDL_GameControllerDB # Windows 03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, 03000000fa190000918d000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, 03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, 03000000d0160000040d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, 03000000d0160000050d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, 03000000d0160000060d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, 03000000d0160000070d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, 03000000d0160000600a000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows, 03000000c82d00001930000000000000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,platform:Windows, 03000000c82d00000031000000000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000531000000000000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000951000000000000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows, 03000000008000000210000000000000,8BitDo F30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 030000003512000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000c82d00001028000000000000,8BitDo F30 Arcade Joystick,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000801000000900000000000000,8BitDo F30 Arcade Stick,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00006a28000000000000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Windows, 03000000c82d00001251000000000000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001151000000000000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000150000000000000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00002090000000000000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000451000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows, 03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d0000e002000000000000,8BitDo N30,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b6,platform:Windows, 03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000290000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows, 03000000c82d00003038000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows, 03000000c82d00006928000000000000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,platform:Windows, 03000000c82d00002590000000000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 030000003512000012ab000000000000,8BitDo NES30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Windows, 03000000c82d000012ab000000000000,8BitDo NES30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000022000000090000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000203800000900000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000751000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000851000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000361000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000660000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000960000000000000,8BitDo Pro 3,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000131000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000231000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000331000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000431000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00002867000000000000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b10,x:b3,y:b4,platform:Windows, 03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000102800000900000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00003028000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 030000003512000020ab000000000000,8BitDo SN30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000351000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a2,rightshoulder:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000021000000000000,8BitDo SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001260000000000000,8BitDo Ultimate 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001c30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001730000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001130000000000000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001330000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000121000000000000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows, 030000008f0e00001200000000000000,Acme GA02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000c01100000355000000000000,Acrux,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000fa190000f0ff000000000000,Acteck AGJ 3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000d1180000402c000000000000,ADT1,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a5,x:b3,y:b4,platform:Windows, 030000006f0e00008801000000000000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001301000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006f0e00001302000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006f0e00001304000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006f0e00001413000000000000,Afterglow Xbox Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00003901000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000ab1200000103000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000ad1b000000f9000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000100000008200000000000000,Akishop Customs PS360,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000007c1800000006000000000000,Alienware Dual Compatible PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, 03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows, 0300000008100000e501000000000000,Anbernic Game Pad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000020500000913000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000373500000710000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000373500004610000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000190e00000110000000000000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Windows, 03000000830500000160000000000000,Arcade,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b4,platform:Windows, 03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, 03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000050b00000579000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows, 03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 03000000380800001889000000000000,AtGames Legends Gamer Pro,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b14,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, 030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, 030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, 030000008a3500000402000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, 03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, 030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ad1b000001f9000000000000,BB 070,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000bc2000005250000000000000,Beitong G3,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a3,righty:a4,start:b15,x:b3,y:b4,platform:Windows, 030000000d0500000208000000000000,Belkin Nostromo N40,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000bc2000000055000000000000,Betop BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000790000000700000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000808300000300000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000bc2000006321000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000006f0e00006401000000000000,BF One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, 03000000300f00000202000000000000,Bigben,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a5,righty:a2,start:b7,x:b2,y:b3,platform:Windows, 030000006b1400000209000000000000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, 03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, 030000005a1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows, 030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows, 030000005b1c00002500000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows, 030000006d04000042c2000000000000,ChillStream,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000457500000401000000000000,Cobra,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000b0400003365000000000000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows, 030000004c050000c505000000000000,CronusMax Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000d814000007cd000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000d8140000cefa000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows, 030000003807000002cb000000000000,Cyborg,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000a306000022f6000000000000,Cyborg V.3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000f806000000a3000000000000,DA Leader,a:b7,b:b6,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b8,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:b3,rightx:a2,righty:a3,start:b12,x:b4,y:b5,platform:Windows, 030000001a1c00000001000000000000,Datel Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000791d00000103000000000000,Dual Box Wii,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000004f040000070f000000000000,Dual Power,a:b8,b:b9,back:b4,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,leftshoulder:b13,leftstick:b6,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b12,rightstick:b7,righttrigger:b15,start:b5,x:b10,y:b11,platform:Windows, 030000004f04000012b3000000000000,Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, 030000004f04000020b3000000000000,Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, 03000000bd12000002e0000000000000,Dual Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows, 03000000ff1100003133000000000000,DualForce,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b1,platform:Windows, 030000006f0e00003001000000000000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, 03000000bc2000000091000000000000,EasySMX Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000006e0500000a20000000000000,Elecom DUX60 MMO,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows, 03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, 03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, 03000095090000010000000000000000,Elecom JC-U609,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows, 0300004112000000e500000000000000,Elecom JC-U909Z,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows, 03000041120000001050000000000000,Elecom JC-U911,a:b1,b:b2,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b0,x:b4,y:b5,platform:Windows, 030000006e0500000520000000000000,Elecom P301U PlayStation Controller Adapter,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, 03000000250900000218000000000000,Elecom PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000411200004450000000000000,Elecom U1012,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, 030000006e0500000320000000000000,Elecom U3613M,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, 030000006e0500000e20000000000000,Elecom U3912T,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, 030000006e0500000f20000000000000,Elecom U4013S,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows, 030000006e0500001320000000000000,Elecom U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006e0500001020000000000000,Elecom U4113S,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, 030000006e0500000720000000000000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, 030000007d0400000640000000000000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform:Windows, 03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000430b00000300000000000000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000062000001801000000000000,EMS TrioLinker Plus II,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Windows, 03000000242f000000b7000000000000,ESM 9110,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Windows, 03000000101c0000181c000000000000,Essential,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b4,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, 03000000341a00000108000000000000,EXEQ RF Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006f0e00008401000000000000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00008101000000000000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00008001000000000000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000021000000090000000000000,FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 0300000011040000c600000000000000,FC801,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, 03000000852100000201000000000000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ad1b000028f0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000ad1b00002ef0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000ad1b000038f0000000000000,Fightpad TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03005036852100000000000000000000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000f806000001a3000000000000,Firestorm,a:b9,b:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b10,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b12,x:b8,y:b4,platform:Windows, 03000000b50700000399000000000000,Firestorm 2,a:b2,b:b4,back:b10,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b9,start:b11,x:b3,y:b5,platform:Windows, 03000000b50700001302000000000000,Firestorm D3,a:b0,b:b2,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,x:b1,y:b3,platform:Windows, 03000000b40400001024000000000000,Flydigi Apex,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000151900004000000000000000,Flydigi Vader 2,a:b27,b:b26,back:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b23,leftstick:b17,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b22,rightstick:b16,righttrigger:b20,rightx:a3,righty:a4,start:b18,x:b25,y:b24,platform:Windows, 03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b14,paddle1:b4,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows, 03000000b40400001224000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows, 0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, 03000000260900002625000000000000,GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows, 03000000341a000005f7000000000000,GameCube Controller,a:b2,b:b3,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b1,y:b0,platform:Windows, 03000000430b00000500000000000000,GameCube Controller,a:b0,b:b2,dpdown:b10,dpleft:b8,dpright:b9,dpup:b11,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a3,rightx:a5,righty:a2,start:b7,x:b1,y:b3,platform:Windows, 03000000790000004718000000000000,GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 030000008f0e00000d31000000000000,Gamepad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ac0500003d03000000000000,GameSir G3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000ac0500005b05000000000000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000ac0500002d02000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000ac0500004d04000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000373500002210000000000000,GameSir G7 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000ac0500001a06000000000000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000373500000410000000000000,GameSir T4 Kaleid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000373500009410000000000000,GameSir Tegenaria Lite,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 030000000d0f00001110000000000000,GameStick Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 0300000047530000616d000000000000,GameStop,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,platform:Windows, 030000008f0e00001411000000000000,Gamo2 Divaller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000300f00000b01000000000000,GGE909 Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000f0250000c283000000000000,Gioteck PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000f025000031c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000f0250000c383000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000f0250000c483000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000d11800000094000000000000,Google Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 030000004f04000026b3000000000000,GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 0300000079000000d418000000000000,GPD Win,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000c6240000025b000000000000,GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000007d0400000840000000000000,Gravis Destroyer Tilt,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows, 030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000280400000140000000000000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a3,dpup:-a4,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000008f0e00000610000000000000,GreenAsia,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a5,righty:a2,start:b11,x:b3,y:b0,platform:Windows, 03000000ac0500006b05000000000000,GT2a,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000008a2e0000dd10000000000000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Windows, 030000008a2e0000df10000000000000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Windows, 030000000d0f00004900000000000000,Hatsune Miku Sho PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000001008000001e1000000000000,Havit HV G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, 030000000d0f00000c00000000000000,HEXT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000d81400000862000000000000,HitBox Edition Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, 03000000632500002605000000000000,HJD X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 030000000d0f00000a00000000000000,Hori DOA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00008500000000000000,Hori Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002500000000000000,Hori Fighting Commander 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00008400000000000000,Hori Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006201000000000000,Hori Fighting Commander Octa,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006401000000000000,Hori Fighting Commander Octa,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00005100000000000000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00008800000000000000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, 030000000d0f00008700000000000000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00001000000000000000,Hori Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00003200000000000000,Hori Fightstick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000c000000000000000,Hori Fightstick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00000d00000000000000,Hori Fightstick EX2,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 030000000d0f00003701000000000000,Hori Fightstick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Windows, 030000000d0f00004000000000000000,Hori Fightstick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002100000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002700000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000a000000000000000,Hori Grip TAC4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b13,x:b0,y:b3,platform:Windows, 030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00000101000000000000,Hori Mini Hatsune Miku FT,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00003801000000000000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Windows, 030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002301000000000000,Hori PS4 Controller Light,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 030000000d0f00001100000000000000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00002600000000000000,Hori Real Arcade Pro 3P,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00004b00000000000000,Hori Real Arcade Pro 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006f00000000000000,Hori Real Arcade Pro 4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00007000000000000000,Hori Real Arcade Pro 4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00003d00000000000000,Hori Real Arcade Pro N3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b4,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b6,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000ae00000000000000,Hori Real Arcade Pro N4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00008c00000000000000,Hori Real Arcade Pro P4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f0000aa00000000000000,Hori Real Arcade Pro S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000d800000000000000,Hori Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Windows, 030000000d0f00002200000000000000,Hori Real Arcade Pro V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005b00000000000000,Hori Real Arcade Pro V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005c00000000000000,Hori Real Arcade Pro V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000af00000000000000,Hori Real Arcade Pro VHS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00001b00000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000ad1b000002f5000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Windows, 030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000c900000000000000,Hori Taiko Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006400000000000000,Horipad 3TP,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00001300000000000000,Horipad 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006e00000000000000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00006600000000000000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000000d0f00004200000000000000,Horipad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000ad1b000001f5000000000000,Horipad EXT2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000c100000000000000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f0000f600000000000000,Horipad Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000000d0f00000202000000000000,Horipad O Nintendo Switch 2 Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, 030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00009601000000000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc2:b2,paddle1:b5,paddle2:b15,paddle3:b18,paddle4:b19,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000242e00000b20000000000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Windows, 03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows, 03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows, 03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows, 03000000242f00000a20000000000000,Hyperkin Scout,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000242e00000a20000000000000,Hyperkin Scout Premium SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows, 03000000f00300008d04000000000000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:-a2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:+a5,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows, 03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,platform:Windows, 03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000511d00000230000000000000,iGUGU Gamecore,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b1,leftstick:b4,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b2,platform:Windows, 03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, 03000000ef0500000300000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, 03000000fd0500000230000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows, 03000000fd0500000030000000000000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows, 03000000fd0500003902000000000000,InterAct Hammerhead,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,rightshoulder:b7,rightstick:b5,righttrigger:b9,start:b10,x:b0,y:b1,platform:Windows, 03000000fd0500002a26000000000000,InterAct Hammerhead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows, 03000000fd0500002f26000000000000,InterAct Hammerhead FX,a:b4,b:b5,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b1,y:b2,platform:Windows, 03000000fd0500005302000000000000,InterAct ProPad,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows, 03000000ac0500002c02000000000000,Ipega Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000491900000204000000000000,Ipega PG9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000491900000304000000000000,Ipega PG9087,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows, 030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows, 030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000250900000017000000000000,Joypad Adapter,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows, 03000000bd12000003c0000000000000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ff1100004033000000000000,JPD FFB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a2,start:b15,x:b3,y:b0,platform:Windows, 03000000242f00002d00000000000000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000242f00008a00000000000000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, 03000000c4100000c082000000000000,KADE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000828200000180000000000000,Keio,a:b4,b:b5,back:b8,leftshoulder:b2,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b1,platform:Windows, 03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000bd12000001e0000000000000,Leadership,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000006f0e00000103000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006f0e00000104000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000008f0e00001300000000000000,Logic3,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows, 030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d0400001dc2000000000000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d0400001ec2000000000000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006d04000019c2000000000000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006d0400001ac2000000000000,Logitech Precision,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000006d04000009c2000000000000,Logitech WingMan,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, 030000006d0400000bc2000000000000,Logitech WingMan Action Pad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:a5~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:a2~,start:b8,x:b3,y:b4,platform:Windows, 030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows, 03000000380700005645000000000000,Lynx,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000222200006000000000000000,Macally,a:b1,b:b2,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700003888000000000000,Mad Catz Arcade Fightstick TE S Plus PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008532000000000000,Mad Catz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700006352000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700006652000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000380700005032000000000000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700005082000000000000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700008031000000000000,Mad Catz FightStick Alpha PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000003807000038b7000000000000,Mad Catz Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03000000380700008433000000000000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700008134000000000000,Mad Catz Fightstick TE2 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700006252000000000000,Mad Catz Micro CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008232000000000000,Mad Catz PlayStation Brawlpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008731000000000000,Mad Catz PlayStation Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000003807000056a8000000000000,Mad Catz PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700001888000000000000,Mad Catz SFIV Fightstick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000380700008081000000000000,Mad Catz SFV Arcade Fightstick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000380700001847000000000000,Mad Catz Street Fighter 4 Xbox 360 FightStick,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, 030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, 030000008f0e00001330000000000000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000242f00003700000000000000,Mayflash F101,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000790000003018000000000000,Mayflash F300 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000242f00003900000000000000,Mayflash F300 Elite Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, 0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000242f0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, 03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows, 03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows, 030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, 0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, 03000000790000000318000000000000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows, 03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000790000002418000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows, 0300000079000000ae18000000000000,Mega Drive Controller,a:b0,b:b1,back:b7,dpdown:b14,dpleft:b15,dpright:b13,dpup:b2,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, 03000000c0160000990a000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,righttrigger:b2,start:b3,platform:Windows, 030000005e0400002800000000000000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Windows, 030000005e0400000300000000000000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, 030000005e0400000700000000000000,Microsoft SideWinder,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, 030000005e0400000e00000000000000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows, 030000005e0400002700000000000000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Windows, 03000000280d00000202000000000000,Miller Lite Cantroller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b5,x:b2,y:b3,platform:Windows, 03000000ad1b000023f0000000000000,MLG,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a6,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 03000000ad1b00003ef0000000000000,MLG Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03000000380700006382000000000000,MLG PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000004523000015e0000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000491900000904000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000ffff00000000000000000000,Mocute M053,a:b3,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b11,leftstick:b7,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b6,righttrigger:b4,rightx:a3,righty:a4,start:b8,x:b1,y:b0,platform:Windows, 03000000d6200000e589000000000000,Moga 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, 03000000d62000007162000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, 03000000d6200000ad0d000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000c62400002a89000000000000,Moga XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c62400002b89000000000000,Moga XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c62400001a89000000000000,Moga XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c62400001b89000000000000,Moga XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows, 03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows, 03000000c9110000f055000000000000,Nacon GC100XF,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000c21100000791000000000000,Nacon GC101 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000006b140000010c000000000000,Nacon GC400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 0300000085320000170d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 0300000085320000190d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000006b140000100d000000000000,Nacon Revolution Infinity PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000006b140000080d000000000000,Nacon Revolution Unlimited Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000bd12000001c0000000000000,Nebular,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000eb0300000000000000000000,NeGcon Adapter,a:a2,b:b13,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,lefttrigger:a4,leftx:a1,righttrigger:b11,start:b3,x:a3,y:b12,platform:Windows, 0300000038070000efbe000000000000,NEO SE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 0300000092120000474e000000000000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Windows, 03000000921200004b46000000000000,NES 2 port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows, 03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows, 03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows, 03000000790000004518000000000000,NEXILUX GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows, 03000000050b00000045000000000000,Nexus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Windows, 03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000ec110000e1a7000000000000,Nintendo Switch,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000007e0500006920000000000000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Windows, 030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows, 030000007e0500007320000000000000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Windows, 030000007e0500001920000000000000,NSO N64 Controller,+rightx:b8,+righty:b2,-rightx:b3,-righty:b7,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Windows, 030000007e0500001720000000000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Windows, 03000000550900001472000000000000,NVIDIA Controller,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows, 03000000550900001072000000000000,NVIDIA Shield,a:b9,b:b8,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b3,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b2,righttrigger:a4,rightx:a2,righty:a5,start:b0,x:b7,y:b6,platform:Windows, 030000005509000000b4000000000000,NVIDIA Virtual,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000120c00000288000000000000,Nyko Air Flo Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 030000004b120000014d000000000000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000d62000001d57000000000000,Nyko Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000791d00000900000000000000,Nyko Playpad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000782300000a10000000000000,Onlive Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows, 030000000d0f00000401000000000000,Onyx,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000008916000001fd000000000000,Onza CE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a3,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000008916000000fd000000000000,Onza TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows, 03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, 03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000006f0e00008501000000000000,PDP Fightpad Pro GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006f0e00000901000000000000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00008901000000000000,PDP Realmz Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000008f0e00004100000000000000,PlaySega,a:b1,b:b0,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b4,y:b3,platform:Windows, 03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d620000013a7000000000000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d62000003340000000000000,PowerA OPS Pro Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000d62000002640000000000000,PowerA OPS Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 0300000062060000d570000000000000,PowerA PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d620000014a7000000000000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d04000084ca000000000000,Precision,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000c62400001a53000000000000,Pro Ex Mini,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000120c0000110e000000000000,Pro5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000250900000088000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000250900006888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b6,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000151a00006222000000000000,PS2 Dual Plus Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000120a00000100000000000000,PS3 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000120c00001307000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000120c00001cf1000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000120c0000f90e000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000250900000118000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows, 030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows, 030000004f1f00000800000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows, 03000000888800000804000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows, 030000008f0e00000300000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000ba2200002010000000000000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Windows, 03000000120c00000807000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000111e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000121e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000130e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000150e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000180e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000181e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000191e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000f51c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120c0000f70e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000120e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000160e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000001a1e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c0500005f0e000000000000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000f20d000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000830500005020000000000000,PSX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Windows, 03000000300f00000111000000000000,Qanba 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00000211000000000000,Qanba 2P,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000300f00000011000000000000,Qanba Arcade Stick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, 03000000300f00001611000000000000,Qanba Arcade Stick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, 03000000222c00000025000000000000,Qanba Dragon Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000222c00000020000000000000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001211000000000000,Qanba Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001210000000000000,Qanba Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 03000000341a00000104000000000000,Qanba Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows, 03000000222c00000223000000000000,Qanba Obsidian Arcade Stick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000008a2400006682000000000000,R1 Mobile Controller,a:b3,b:b1,back:b7,leftx:a0,lefty:a1,start:b6,x:b4,y:b0,platform:Windows, 03000000086700006626000000000000,RadioShack,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 03000000ff1100004733000000000000,Ramox FPS Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows, 030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, 030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, 030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows, 030000009b280000d000000000000000,Raphnet Dreamcast Adapter,a:b1,b:b0,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,lefttrigger:+a5,leftx:a0,lefty:a1,righttrigger:+a2,start:b3,x:b5,y:b4,platform:Windows, 030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, 030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, 030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, 030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows, 030000009b2800003c00000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, 030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, 030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, 030000009b2800006400000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, 030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows, 030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows, 030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, 030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, 030000009b2800000300000000000000,Raphnet SNES Adapter,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, 030000009b2800002600000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800002e00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800002f00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows, 030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows, 03000000790000008f18000000000000,Rapoo Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 0300000032150000a602000000000000,Razer Huntsman V3 Pro,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b12,dpright:b13,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,platform:Windows, 03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000321500000010000000000000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000321500000710000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000a10000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000410000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000910000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,x:b3,y:b4,platform:Windows, 03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, 03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03000000632500007805000000000000,Retro Fighters Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, 03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows, 03000000bd12000015d0000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows, 0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, 0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows, 03000000830500000960000000000000,Revenger,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b5,platform:Windows, 030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006b140000020d000000000000,Revolution Pro Controller 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001f01000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006f0e00004601000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000c6240000fefa000000000000,Rock Candy Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006f0e00008701000000000000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000830500007030000000000000,Rockfire Space Ranger,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b9,righttrigger:b8,start:b2,x:b3,y:b4,platform:Windows, 03000000050b0000e318000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, 03000000050b0000e518000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, 03000000050b00005819000000000000,ROG Chakram Core,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, 03000000050b0000181a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, 03000000050b00001a1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, 03000000050b00001c1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, 030000004f04000001d0000000000000,Rumble Force,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, 030000000d0f0000ad00000000000000,RX Gamepad,a:b0,b:b4,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b6,start:b9,x:b2,y:b1,platform:Windows, 030000008916000000fe000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000c6240000045d000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006f0e00001311000000000000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,platform:Windows, 03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000a306000023f6000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001201000000000000,Saitek Dual Analog,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Windows, 03000000a30600000cff000000000000,Saitek P2500 Force Rumble,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,platform:Windows, 03000000a30600000d5f000000000000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows, 03000000a30600000dff000000000000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b8,x:b0,y:b3,platform:Windows, 03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000a306000018f5000000000000,Saitek P3200,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001001000000000000,Saitek P480 Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 03000000a30600000901000000000000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b5,rightx:a3,righty:a2,x:b0,y:b1,platform:Windows, 03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000a30600002106000000000000,Saitek PS1000 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000a306000020f6000000000000,Saitek PS2700 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000300f00001101000000000000,Saitek Rumble,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 03000000e804000000a0000000000000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c01100000252000000000000,Sanwa Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, 03000000c01100004350000000000000,Sanwa Micro Grip P3,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,x:b3,y:b2,platform:Windows, 03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows, 03000000c01100004150000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, 03000000c01100004450000000000000,Sanwa Online Grip,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b14,x:b3,y:b4,platform:Windows, 03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows, 030000009d0d00001130000000000000,Sanwa PlayStation Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,platform:Windows, 03000000c01100000051000000000000,Satechi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows, 03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a2,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, 0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, 0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows, 03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, 03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, 030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows, 03000000632500002705000000000000,ShanWan Q36,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Windows, 03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, 0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows, 03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, 030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, 03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, 030000008f0e00000910000000000000,Sony DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows, 03000000317300000100000000000000,Sony DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000666600006706000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows, 03000000e30500009605000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000fe1400002a23000000000000,Sony PlayStation Adapter,a:b0,b:b1,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,x:b2,y:b3,platform:Windows, 030000004c050000da0c000000000000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000632500002306000000000000,Sony PlayStation Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, 03000000f0250000c183000000000000,Sony PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000d9040000160f000000000000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000ff000000cb01000000000000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, 030000004c0500003713000000000000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000341a00000208000000000000,Speedlink 6555,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows, 03000000341a00000908000000000000,Speedlink 6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000380700001722000000000000,Speedlink Competition Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,x:b2,y:b3,platform:Windows, 030000008f0e00000800000000000000,Speedlink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000de280000fc11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000de280000ff11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows, 03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows, 03000000380700003847000000000000,Street Fighter Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows, 030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, 03000000341200001300000000000000,Super Racer,a:b2,b:b3,back:b8,leftshoulder:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b7,x:b0,y:b1,platform:Windows, 03000000457500002211000000000000,Szmy Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000004f0400000ab1000000000000,T16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows, 030000000d0f00007b00000000000000,TAC GEAR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000e40a00000307000000000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows, 03000000e40a00000207000000000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows, 03000000d814000001a0000000000000,TE Kitty,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 03000000ba2200000701000000000000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b2,platform:Windows, 03000000c61100001000000000000000,Tencent Xianyou Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows, 03000000790000001c18000000000000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000790000002601000000000000,TGZ Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, 03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,platform:Windows, 030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, 030000004f04000023b3000000000000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004f0400000ed0000000000000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004f04000008d0000000000000,Thrustmaster Ferrari 150 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows, 030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, 030000004f04000003d0000000000000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000004f04000009d0000000000000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006d04000088ca000000000000,Thunderpad,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 03000000666600000288000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000666600000488000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 030000004f04000007d0000000000000,TMini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000571d00002100000000000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Windows, 03000000571d00002000000000000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, 03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000c01100000055000000000000,Tronsmart,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows, 03000000411200000450000000000000,Twin Shock,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows, 03000000d90400000200000000000000,TwinShock PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000151900005678000000000000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 030000000b0400003065000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 03000000242f00006e00000000000000,USB Controller,a:b1,b:b4,back:b10,leftshoulder:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b3,platform:Windows, 03000000300f00000701000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000341a00002308000000000000,USB Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000666600000188000000000000,USB Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 030000006b1400000203000000000000,USB Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000790000000a00000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000b404000081c6000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows, 03000000b50700001503000000000000,USB Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b0,y:b1,platform:Windows, 03000000bd12000012d0000000000000,USB Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows, 03000000ff1100004133000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000632500002305000000000000,USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, 03000000882800000305000000000000,V5 Game Pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,x:b2,y:b3,platform:Windows, 03000000790000001a18000000000000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 03000000790000001b18000000000000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000006f0e00000302000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows, 03000000120c0000ab57000000000000,Warrior Joypad JS083,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000007e0500003003000000000000,Wii U Pro,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b6,leftstick:b11,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b12,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows, 0300000032150000030a000000000000,Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 0300000032150000140a000000000000,Wolverine,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000002e160000efbe000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows, 03000000380700001647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000380700002045000000000000,Xbox 360 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 03000000380700002644000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a5,start:b8,x:b2,y:b3,platform:Windows, 03000000380700002647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000003807000026b7000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000380700003647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a7,righty:a5,start:b7,x:b2,y:b3,platform:Windows, 030000005e0400001907000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e0400009102000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000ad1b000000fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000ad1b000001fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000ad1b000016f0000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000ad1b00008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000c62400000053000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000c6240000fdfa000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000380700002847000000000000,Xbox 360 Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000a102000000000000,Xbox 360 Wireless Receiver,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000120c00000a88000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 03000000120c00001088000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2~,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5~,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000002a0600002000000000000000,Xbox Controller,a:b0,b:b1,back:b13,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b5,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b15,righttrigger:b7,rightx:a2,righty:a5,start:b12,x:b2,y:b3,platform:Windows, 03000000380700001645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 03000000380700002645000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000380700003645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 03000000380700008645000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e0400000202000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 030000005e0400008502000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e0400008702000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 030000005e0400008902000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b8,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b9,righttrigger:b4,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 030000005e0400000c0b000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000fd02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000ff02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006f0e0000a802000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006f0e0000c802000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000c62400003a54000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000450c00002043000000000000,Xeox SL6556BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006f0e00000300000000000000,XGear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 03000000e0ff00000201000000000000,Xiaomi Black Shark (L),back:b0,dpdown:b11,dpleft:b9,dpright:b10,dpup:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,platform:Windows, 03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows, 03000000c0160000e105000000000000,XinMo Dual Arcade,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows, 030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,platform:Windows, 03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000073500000400000000000000,Zenaim Arcade Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, 03000000120c0000101e000000000000,Zeroplus P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, # Mac OS X 030000008f0e00000300000009010000,2 In 1 Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000c82d00001930000000000000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, 03000000c82d00001930000000020000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, 03000000c82d00001930000001000000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, 03000000c82d00000031000001000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00000531000000020000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X, 03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Mac OS X, 03000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001251000000020000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001151000000020000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X, 03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X, 03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, 03000000c82d00002590000000010000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00002690000000010000,8BitDo NEOGEO,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000022000000090000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000190000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000960000000000000,8BitDo Pro 3,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000131000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Mac OS X, 03000000c82d00003028000000010000,8Bitdo SFC30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00004028000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000260000001000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001d30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001530000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001630000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001730000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001130000000020000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001330000000020000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001330000001000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000a00500003232000009010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, 0300000008100000e501000019040000,Anbernic Handheld,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, 03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, 03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X, 030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, 030000008a3500000201000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, 030000008a3500000402000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000008a3500000302000000010000,Backbone One PlayStation Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, 03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000120c0000200e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000120c0000210e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000d8140000cecf000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000a306000022f6000001030000,Cyborg V3 Rumble Pad PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000791d00000103000009010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000006e0500000720000010020000,Elecom JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Mac OS X, 030000006f0e00008401000003010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000151900004000000001000000,Flydigi Vader 2,a:b14,b:b15,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Mac OS X, 03000000b40400001124000001040000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000b40400001224000003030000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X, 03000000ac0500001a06000002020000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000373500000411000023000000,GameSir X4A Xbox Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000ad1b000001f9000000000000,Gamestop BB070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Mac OS X, 03000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000280400000140000000020000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000008f0e00000300000007010000,GreenAsia Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X, 030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005f00000000010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00005e00000000010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008400000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008500000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000341a00000302000014010000,Hori Fighting Stick Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008800000000010000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00008700000000010000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X, 030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000c100000072050000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X, 03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X, 03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, 03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X, 03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Mac OS X, 030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000019c2000000020000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000016c2000000000000,Logitech F310,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 0300000049190000020400001b010000,Manba One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000008f0e00001330000011010000,Mayflash Controller Adapter,a:b2,b:b4,back:b16,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b12,lefttrigger:b16,leftx:a0,lefty:a2,rightshoulder:b14,rightx:a6~,righty:a4,start:b18,x:b0,y:b6,platform:Mac OS X, 03000000790000004318000000010000,Mayflash GameCube Adapter,a:b4,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X, 03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X, 0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000008f0e00001030000011010000,Mayflash Saturn Adapter,a:b0,b:b2,dpdown:b28,dpleft:b30,dpright:b26,dpup:b24,leftshoulder:b10,lefttrigger:b14,rightshoulder:b12,righttrigger:b4,start:b18,x:b6,y:b8,platform:Mac OS X, 0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X, 03000000790000000318000000010000,Mayflash Wii DolphinBar,a:b8,b:b12,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b44,leftshoulder:b16,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b4,platform:Mac OS X, 03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, 03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, 030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Mac OS X, 030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Mac OS X, 030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Mac OS X, 030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X, 030000004523000015e0000072050000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, 03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c62400002b89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000853200008906000000010000,Nacon Revolution X Unlimited,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000632500007505000000020000,NeoGeo mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X, 030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000ec110000e1a7000001010000,Nintendo Switch,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 030000007e0500006920000001010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Mac OS X, 030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 030000007e0500000920000010020000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X, 050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X, 030000007e0500007320000001010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Mac OS X, 030000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Mac OS X, 030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X, 030000004b120000014d000000010000,Nyko Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Mac OS X, 0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,righttrigger:a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Mac OS X, 030000006f0e00000901000002010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000d620000011a7000000020000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000d620000011a7000010050000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c0500006802000072050000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 0300004b4c0500005f0e000000010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000005e040000e002000001000000,PXN P30 Pro Mobile,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000222c00000225000000010000,Qanba Dragon Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000222c00000020000000010000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000009b2800005600000020020000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Mac OS X, 030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Mac OS X, 030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000321500000204000000010000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000104000000010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000321500000010000000010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000321500000011000000010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000632500008005000000010000,Redgear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000632500002305000000010000,Redragon Saturn,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,lefttrigger:b14,rightshoulder:b10,righttrigger:b4,start:b12,x:b6,y:b8,platform:Mac OS X, 03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Mac OS X, 0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Mac OS X, 03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Mac OS X, 030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004c0500006802000002100000,Rii RK707,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b3,righttrigger:b9,rightx:a2,righty:a3,start:b1,x:b15,y:b12,platform:Mac OS X, 030000006f0e00008701000005010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000e804000000a000001b010000,Samsung EIGP20,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b11,leftx:a1,lefty:a3,rightshoulder:b12,rightx:a4,righty:a5,start:b16,x:b7,y:b9,platform:Mac OS X, 03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Mac OS X, 03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Mac OS X, 03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X, 03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X, 030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X, 030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 03000000666600006706000088020000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X, 030000004c050000da0c000000010000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 030000004c0500003713000000010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X, 030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, 03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, 03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X, 05000000484944204465766963650000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X, 050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X, 03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X, 03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X, 030000000d0f0000f600000000010000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000e40a00000307000001000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X, 03000000e40a00000207000001000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X, 03000000790000001c18000000010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000790000001c18000003100000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Mac OS X, 030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X, 030000004f0400000ed0000000020000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X, 03000000571d00002100000021000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Mac OS X, 03000000bd12000015d0000000010000,Tomee Retro Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000bd12000015d0000000000000,Tomee SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000571d00002000000021000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005f140000c501000000020000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X, 03000000632500002605000000010000,Uberwith Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c0160000e105000000040000,Ultimate Atari Fight Stick,a:b2,b:b4,back:b18,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b8,righttrigger:b10,start:b16,x:b0,y:b6,platform:Mac OS X, 03000000151900005678000010010000,Uniplay U6,a:b3,b:b6,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,leftstick:b31,lefttrigger:b21,leftx:a1,lefty:a3,rightshoulder:b19,rightstick:b33,righttrigger:b23,rightx:a4,righty:a5,start:b27,x:b11,y:b13,platform:Mac OS X, 030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, 050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, 030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4~,start:b8,x:b2,y:b3,platform:Mac OS X, 030000006f0e00000104000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000c6240000045d000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000050b000003090000,Xbox Elite Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000200b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000200b000013050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000200b000015050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, 030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, 03000000c62400003a54000000000000,Xbox One PowerA Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000007050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000022050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000220b000021050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000120c0000100e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000120c0000101e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, # Linux 03000000c82d00001930000011010000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, 05000000c82d00001930000001000000,8BitDo 64,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, 03000000c82d00000031000011010000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000631000000010000,8BitDo Adapter 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux, 03000000021000000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Linux, 03000000c82d00001251000011010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000a20000000020000,8BitDo M30 Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux, 03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00006928000011010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, 05000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux, 05000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, 05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux, 03000000022000000090000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000190000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000960000011010000,8BitDo Pro 3,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000431000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Linux, 03000000c82d00000060000011010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 030000003512000012ab000010010000,8BitDo SFC30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux, 030000003512000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000260000011010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000a31000014010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00001d30000011010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000631000010010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000631000014010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00003901000013020000,Afterglow Prismatic Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00001302000000010000,Afterglow Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00003901000020060000,Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000100000008200000011010000,Akishop Customs PS360,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000007c1800000006000010010000,Alienware Dual Compatible Game PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 05000000491900000204000021000000,Amazon Fire Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, 05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 0300000008100000e501000001010000,Anbernic Handheld,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Linux, 03000000020500000913000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000373500000710000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000190e00000110000010010000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Linux, 03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, 05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, 03000000050b00000579000011010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, 03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, 05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, 05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, 05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, 03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, 03000000503200000210000011010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, 05000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, 05000000503200000210000045010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, 05000000503200000210000046010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, 05000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:-a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, 030000008a3500000201000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008a3500000202000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008a3500000302000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008a3500000402000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000c62400001b89000011010000,BDA MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000c21100000791000011010000,Be1 GC101 Controller 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000c31100000791000011010000,Be1 GC101 Controller 1.03,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e0400008e02000003030000,Be1 GC101 Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000bc2000004d50000011010000,Beitong A1T2 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000bc2000000055000001000000,Betop AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006b1400000209000011010000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000120c0000300e000011010000,Brook Audio Fighting Board PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000120c0000310e000011010000,Brook Audio Fighting Board PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000120c0000200e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000120c0000210e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000d81d00000b00000010010000,Buffalo BSGP1601,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Linux, 03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux, 030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux, 03000000632500007a05000001020000,Cosmic Byte Ares Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux, 03000000a306000022f6000011010000,Cyborg V3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000791d00000103000010010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000c11100000191000011010000,EasySMX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000242f00009100000000010000,EasySMX ESM-9101,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006e0500000320000010010000,Elecom U3613M,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, 030000006e0500000720000010010000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux, 030000007d0400000640000010010000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform:Linux, 03000000430b00000300000000010000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 030000006f0e00008401000011010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00008101000011010000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00008001000011010000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000852100000201000010010000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 05000000b40400001224000001010000,Flydigi APEX 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b20,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux, 19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000373500000b10000019010000,GameSir Cyclone 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000ac0500005b05000010010000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000558500001b06000010010000,GameSir G4 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000ac0500007a05000011010000,GameSir G5,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000373500009710000001020000,GameSir Kaleid Flux,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000ac0500001a06000011010000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000bc2000005656000011010000,GameSir T4w,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000373500009410000010010000,GameSir Tegenaria Lite,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000008f0e00000800000010010000,Gasia PlayStation Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000f025000021c1000010010000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000f0250000c283000010010000,Gioteck VX2 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 190000004b4800000010000000010000,GO-Advance Controller,a:b1,b:b0,back:b10,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,leftshoulder:b4,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b13,start:b15,x:b2,y:b3,platform:Linux, 190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,platform:Linux, 190000004b4800000011000000010000,GO-Super Gamepad,a:b0,b:b1,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b3,y:b2,platform:Linux, 03000000f0250000c183000010010000,Goodbetterbest Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d11800000094000011010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 05000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000001010000,GPD Win Max 2 6800U Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000280400000140000000010000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux, 030000008f0e00001200000010010000,GreenAsia Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 0500000047532067616d657061640000,GS Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000008a2e0000dd10000011010000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Linux, 030000008a2e0000df10000011010000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Linux, 06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, 03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00006d00000020010000,Hori EDGE 301,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:+a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008400000011010000,Hori Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00005f00000011010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f00005001000009040000,Hori Fighting Commander Octa Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008500000010010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00003701000013010000,Hori Fighting Stick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b3,y:b2,platform:Linux, 030000000d0f00008800000011010000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00008700000011010000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f00001000000011010000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f00004d00000011010000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00003801000011010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Linux, 030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00001100000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00002200000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000000d0f00008501000017010000,Hori Split Pad Fit,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f0000c100000011010000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f0000ab01000011010000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000000d0f00009601000091000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000000d0f0000f600000001000000,Horipad Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000341a000005f7000010010000,HuiJia GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 05000000242e00000b20000001000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Linux, 03000000242e0000ff0b000011010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Linux, 03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Linux, 03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000f00300008d04000000010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, 03000000d80400004aea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,back:b4,x:b2,y:b3,platform:Linux, 03000000d80400004bea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,back:b4,x:b2,y:b3,platform:Linux, 030000008a2e0000d910000011010000,icedragon.io STAC2 Dance Pad,a:b0,b:b1,back:b4,x:b2,y:b3,platform:Linux, 030000008a2e0000e910000011010000,icedragon.io STAC2 Dance Pad,a:b8,b:b9,back:b12,x:b10,y:b11,platform:Linux, 030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux, 050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux, 03000000120c00000500000010010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, 03000000ef0500000300000000010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, 03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Linux, 03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, 0500000049190000020400001b010000,Ipega PG 9069,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000632500007505000011010000,Ipega PG 9099,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 0500000049190000030400001b010000,Ipega PG9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000491900000204000000000000,Ipega PG9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000300f00001101000010010000,Jess Tech Colour Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000300f00001001000010010000,Jess Tech Dual Analog Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 03000000ba2200002010000001010000,Jess Technology Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux, 050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, 03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000242f00002d00000011010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000242f00008a00000011010000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, 030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d040000d1ca000000000000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d040000d1ca000011010000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d0400001dc2000014400000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d0400001ec2000019200000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d0400001ec2000020200000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d04000019c2000011010000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d0400001fc2000005030000,Logitech F710,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux, 030000006d0400000ac2000010010000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux, 05000000380700006652000025010000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008532000010010000,Mad Catz Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000380700005032000011010000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700005082000011010000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000380700008031000011010000,Mad Catz FightStick Alpha PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008081000011010000,Mad Catz FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008034000011010000,Mad Catz Fightstick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008084000011010000,Mad Catz Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000380700008433000011010000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700008483000011010000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000380700001888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700003888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000380700001647000010040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000380700003847000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000120c00000500000000010000,Manta DualShock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 030000008f0e00001330000010010000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Linux, 03000000790000004318000010010000,Mayflash GameCube Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, 0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000242f0000f700000001010000,Mayflash Magic S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000008f0e00001030000010010000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux, 0300000025090000e803000001010000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, 03000000790000000318000011010000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux, 03000000790000000018000011010000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000b50700001203000010010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000b50700004f00000000010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux, 03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Linux, 030000005e0400000300000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux, 030000005e0400000700000000010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux, 030000005e0400000e00000000010000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux, 030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux, 030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 030000005e0400008902000021010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 030000005e0400008e02000001000000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.1,dpleft:h0.2,dpright:h0.8,dpup:h0.4,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000004010000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000056210000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000062230000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000d102000001010000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000d102000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000dd02000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000ea02000008040000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000ea0200000f050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b000014050000,Microsoft Xbox One,a:b0,b:b1,x:b2,y:b3,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux 030000005e040000e302000003020000,Microsoft Xbox One Elite,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000016050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000017050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b000001050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, 03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 05000000e80400006e0400001b010000,Mocute 053X M59,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000004d4f435554452d3035305800,Mocute 054X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000d6200000e589000001000000,Moga 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 05000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, 03000000c62400002b89000011010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c62400001a89000000010000,MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000250900006688000000010000,MP8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000f70600000100000000010000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Linux, 030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 0300000085320000030c000011010000,Nacon GC100,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000853200000706000012010000,Nacon GC100,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006b140000010c000010010000,Nacon GC400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 05000000853200000503000000010000,Nacon MGX Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 0300000085320000170d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 0300000085320000190d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004f1f00000800000011010000,NeoGeo PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Linux, 03000000790000004518000010010000,Nexilux GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux, 060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, 03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux, 03000000ec110000e1a7000010010000,Nintendo Switch,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000007e0500006920000011010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Linux, 060000004e696e74656e646f20537700,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, 060000007e0500000620000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, 060000007e0500000820000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, 050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b16,b:b15,back:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,platform:Linux, 030000007e0500000920000000026803,Nintendo Switch Pro Controller,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Linux, 030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, 050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, 050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux, 05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500003003000001000000,Nintendo Wii U Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, 050000005a1d00000218000003000000,Nokia GC 5000,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000000d0500000308000010010000,Nostromo n45 Dual Analog,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux, 030000007e0500007320000011010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Linux, 030000007e0500001920000011810000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux, 050000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Linux, 050000007e0500001920000001800000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux, 030000007e0500001e20000011810000,NSO Sega Genesis Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,misc1:b3,rightshoulder:b2,righttrigger:b4,start:b5,platform:Linux, 030000007e0500001720000011810000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, 050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b8,start:b10,x:b3,y:b2,platform:Linux, 050000007e0500001720000001800000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, 03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000550900001472000011010000,NVIDIA Controller,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, 05000000550900001472000001000000,NVIDIA Controller,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, 030000004b120000014d000000010000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 19000000010000000100000001010000,ODROID Go 2,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux, 19000000010000000200000011000000,ODROID Go 2,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux, 05000000362800000100000002010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, 05000000362800000100000003010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, 05000000362800000100000004010000,OUYA Controller,a:b0,b:b3,back:b14,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b16,x:b1,y:b2,platform:Linux, 03000000830500005020000010010000,Padix Rockfire PlayStation Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux, 03000000ff1100003133000010010000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006f0e0000b802000001010000,PDP Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000b802000013020000,PDP Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000d702000006640000,PDP Black Camo Wired Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b13,dpup:b14,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00008501000011010000,PDP Fightpad Pro Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00002f01000011010000,PDP Wired PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000f102000000000000,PDP Xbox Atomic,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000d802000006640000,PDP Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000ef02000007640000,PDP Xbox Series Kinetic Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000540000001010000,PowerA Advantage Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d620000011a7000011010000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000c62400001a53000000010000,PowerA Mini Pro Ex,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d620000013a7000011010000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000d620000014a7000011010000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000c62400001a58000001010000,PowerA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000220000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, 03000000d62000000228000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c62400001a54000001010000,PowerA Xbox One Mini Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000240000001010000,PowerA Xbox One Spectra Infinity,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000520000050010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000b20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux, 03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000120c0000160e000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000005f1400003102000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 0300004b4c0500005f0e000011010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000e60c000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c050000f20d000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000f20d000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 050000004c050000e60c000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 050000004c050000f20d000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux, 03000000222c00000225000011010000,Qanba Dragon Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000222c00000025000011010000,Qanba Dragon Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000222c00001220000011010000,Qanba Drone 2 Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000222c00001020000011010000,Qanba Drone 2 Arcade Joystick PS5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,platform:Linux, 03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, 030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux, 0300132d9b2800006500000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 0300132d9b2800006500000001010000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800003c00000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, 030000009b2800006100000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, 030000009b2800006300000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, 030000009b2800006400000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux, 030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, 030000009b2800008000000001010000,Raphnet Wii Classic Adapter V3,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, 03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000321500000010000011010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000321500000a10000001000000,Razer Raiju Tournament Edition,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000321500000011000011010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 0300000032150000140a000001010000,Razer Wolverine Ultimate Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000000d0f0000c100000010010000,Retro Bit Legacy16,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b12,leftshoulder:b4,lefttrigger:b6,misc1:b13,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux, 0300000003040000c197000011010000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, 190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux, 0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, 00000000526574726f53746f6e653200,RetroStone 2 Controller,a:b1,b:b0,back:b10,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Linux, 03000000341200000400000000010000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Linux, 030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00008701000011010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000c6240000fefa000000010000,Rock Candy Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00001311000011010000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,platform:Linux, 03000000a306000023f6000011010000,Saitek Cyborg PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000a30600001005000000010000,Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux, 03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Linux, 03000000a30600000cff000010010000,Saitek P2500 Force Rumble,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,platform:Linux, 03000000a30600000d5f000010010000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux, 03000000a30600000c04000011010000,Saitek P2900,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux, 03000000a306000018f5000010010000,Saitek P3200 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux, 03000000a30600000b04000000010000,Saitek P990 Dual Analog,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux, 03000000a306000020f6000011010000,Saitek PS2700 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 05000000e804000000a000001b010000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux, 03000000952e00004b43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000952e00004d43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, 03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux, 03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux, 03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux, 03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000632500002605000010010000,ShanWan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000632500007505000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000bc2000000055000010010000,ShanWan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000341a00000908000010010000,SL6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 030000004b2900000430000011000000,Snakebyte Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000666600006706000000010000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, 030000004c050000da0c000011010000,Sony PlayStation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000d9040000160f000000010000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 03000000ff000000cb01000010010000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, 030000004c0500003713000011010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000250900000500000000010000,Sony PS2 pad with SmartJoy Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000073050000,Speedlink Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000020200000,SpeedLink Xeox Pro Analog,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000112000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, 03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, 03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, 03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000512000010010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux, 03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux, 03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux, 03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000ad1b000038f0000090040000,Street Fighter IV Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux, 030000001f08000001e4000010010000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux, 0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, 030000008f0e00000d31000010010000,SZMY Power 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000457500000401000011010000,SZMY Power DS4 Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000457500002211000010010000,SZMY Power Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000008f0e00001431000010010000,SZMY Power PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000e40a00000307000011010000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux, 03000000e40a00000207000011010000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux, 03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux, 03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b15,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux, 03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, 03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux, 030000004f04000015b3000001010000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 030000004f04000020b3000010010000,Thrustmaster Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 030000004f04000023b3000000010000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004f0400000ed0000011010000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,platform:Linux, 030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,platform:Linux, 030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux, 030000004f04000004b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 030000004f04000026b3000002040000,Thrustmaster GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c6240000025b000002020000,Thrustmaster GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000004f04000008d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000004f04000009d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004f04000007d0000000010000,Thrustmaster T Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 030000004f04000012b3000010010000,Thrustmaster Vibrating Gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux, 03000000571d00002000000010010000,Tomee SNES Adapter,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, 03000000bd12000015d0000010010000,Tomee SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000d814000007cd000011010000,Toodles 2008 Chimp PC PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux, 030000005e0400008e02000070050000,Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000c01100000591000011010000,Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 03000000680a00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, 03000000780300000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, 03000000e00d00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, 03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux, 030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000100800000100000010010000,Twin PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 03000000c0160000e105000010010000,Ultimate Atari Fight Stick,a:b1,b:b2,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b8,x:b0,y:b3,platform:Linux, 03000000790000002601000011010000,SNES Controller,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Linux, 03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, 03000000790000000600000007010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux, 03000000790000001100000000010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux, 03000000790000001a18000011010000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, 03000000790000001b18000011010000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 030000006f0e00000302000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000006f0e00000702000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 05000000ac0500003232000001000000,VR Box Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 05000000434f4d4d414e440000000000,VX Gaming Command Series,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 0000000058626f782033363020576900,Xbox 360 Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux, 030000005e0400001907000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000002010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000014010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000047010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400008e02000072050000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400009102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000a102000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000a102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000a102000030060000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e00001503000000020000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 0000000058626f782047616d65706100,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 030000005e0400000202000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 030000006f0e00001304000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000ffff0000ffff000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux, 030000005e0400000a0b000005040000,Xbox One Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, 030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000ea02000001030000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000dd02000003020000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000e302000002090000,Xbox One Elite,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000ea02000015050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000ea02000017050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000ea02000016050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000005050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000009050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000011050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000014050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b000015050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000007050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000011050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000130b000017050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000130b000022050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 060000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000200b000023050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux, 03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, 030000005e0400008e02000020010000,XInput Adapter,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000120c0000182e000011010000,Zeroplus PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000790000002201000011010000,ZhiXu GuliKit D,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, # Android 38653964633230666463343334313533,8BitDo Adapter,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 36666264316630653965636634386234,8BitDo Adapter 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38426974446f20417263616465205374,8BitDo Arcade Stick,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 61393962646434393836356631636132,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, 64323139346131306233636562663738,8BitDo Arcade Stick,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, 64643565386136613265663236636564,8BitDo Arcade Stick,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, 33313433353539306634656436353432,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38426974446f20446f67626f6e65204d,8BitDo Dogbone,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, 34343439373236623466343934376233,8BitDo FC30 Pro,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b28,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b29,righttrigger:b7,start:b5,x:b30,y:b2,platform:Android, 38426974446f204e4743204d6f646b69,8BitDo GameCube,a:b0,b:b2,back:b4,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b18,paddle2:b17,rightshoulder:b15,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b1,y:b3,platform:Android, 38426974446f2038426974446f204c69,8BitDo Lite,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 30643332373663313263316637356631,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38426974446f204c6974652032000000,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 62656331626461363634633735353032,8BitDo Lite 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38393936616436383062666232653338,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38426974446f204c6974652053450000,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 39356430616562366466646636643435,8BitDo Lite SE,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000006500000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a5,rightshoulder:b10,righttrigger:a4,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000051060000ffff3f00,8BitDo M30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b17,leftshoulder:b9,lefttrigger:a4,rightshoulder:b10,righttrigger:a5,start:b6,x:b3,y:b2,platform:Android, 32323161363037623637326438643634,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 33656266353630643966653238646264,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:a5,start:b10,x:b19,y:b2,platform:Android, 38426974446f204d3330204d6f646b69,8BitDo M30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 39366630663062373237616566353437,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,start:b6,x:b2,y:b3,platform:Android, 64653533313537373934323436343563,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,start:b6,x:b2,y:b3,platform:Android, 66356438346136366337386437653934,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,start:b18,x:b19,y:b2,platform:Android, 66393064393162303732356665666366,8BitDo M30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,start:b6,x:b2,y:b3,platform:Android, 38426974446f204d6963726f2067616d,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, 61653365323561356263373333643266,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, 62613137616239666338343866326336,8BitDo Micro,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:a4,leftx:b0,lefty:b1,rightshoulder:b10,righttrigger:a5,rightx:b2,righty:b3,start:b6,x:b3,y:b2,platform:Android, 33663431326134333366393233616633,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, 38426974446f204e3330204d6f646b69,8BitDo N30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b6,platform:Android, 05000000c82d000015900000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000065280000ffff3f00,8BitDo N30 Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38323035343766666239373834336637,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, 38426974446f204e3634204d6f646b69,8BitDo N64,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,platform:Android, 32363135613966656338666638666237,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 35363534633333373639386466346631,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38426974446f204e454f47454f204750,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 39383963623932353561633733306334,8BitDo NEOGEO,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000000220000000900000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000002038000009000000ffff3f00,8BitDo NES30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38313433643131656262306631373166,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38326536643339353865323063616339,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38426974446f2050333020636c617373,8BitDo P30,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 35376664343164386333616535333434,8BitDo Pro 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,start:b10,x:b19,y:b2,platform:Android, 38426974446f2038426974446f205072,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38426974446f2050726f203200000000,8BitDo Pro 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 61333362366131643730353063616330,8BitDo Pro 2,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 62373739366537363166326238653463,8BitDo Pro 2,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b3,y:b2,platform:Android, 38386464613034326435626130396565,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38426974446f2038426974446f205265,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 66303230343038613365623964393766,8BitDo Receiver,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38426974446f20533330204d6f646b69,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 66316462353561376330346462316137,8BitDo S30,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b10,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000c82d000000600000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000000610000ffff3f00,8BitDo SF30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38426974646f20534633302050726f00,8BitDo SF30 Pro,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b17,platform:Android, 61623334636338643233383735326439,8BitDo SFC30,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b31,start:b5,x:b30,y:b2,platform:Android, 05000000c82d000012900000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000062280000ffff3f00,8BitDo SN30,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 38316230613931613964356666353839,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38426974446f20534e3330204d6f646b,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 65323563303231646531383162646335,8BitDo SN30,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 35383531346263653330306238353131,8BitDo SN30 PP,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000c82d000001600000ffff3f00,8BitDo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000002600000ffff0f00,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b17,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 36653638656632326235346264663661,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, 38303232393133383836366330346462,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, 38346630346135363335366265656666,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38426974446f20534e33302050726f2b,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 536f6e7920436f6d707574657220456e,8BitDo SN30 Pro Plus,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 66306331643531333230306437353936,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000002028000009000000ffff3f00,8BitDo SNES30,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 050000003512000020ab000000780f00,8BitDo SNES30,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android, 33666663316164653937326237613331,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 38426974646f205a65726f2047616d65,8BitDo Zero,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 33663434393362303033616630346337,8BitDo Zero 2,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, 34656330626361666438323266633963,8BitDo Zero 2,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, 63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, 4c696e757820342e31392e3137322077,Anbernic Handheld,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Android, 417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, 35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,start:b6,platform:Android, 32353831643566306563643065356239,Atari VCS Modern Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 4f64696e20436f6e74726f6c6c657200,AYN Odin,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b14,dpright:b13,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:+a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 32303165626138343962363666346165,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 38383337343564366131323064613561,Brook Mars PS4 Controller,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 34313430343161653665353737323365,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, 4875694a6961204a432d573031550000,Elecom JC-W01U,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, 30363230653635633863366338623265,Evo VR,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftx:a0,lefty:a1,x:b2,y:b3,platform:Android, 05000000b404000011240000dfff3f00,Flydigi Vader 2,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 34323662653333636330306631326233,Google Nexus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 35383633353935396534393230616564,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 476f6f676c65204c4c43205374616469,Google Stadia Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 5374616469614e3848532d6532633400,Google Stadia Controller,a:b0,b:b1,back:b15,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 05000000d6020000e5890000dfff3f80,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, 66633030656131663837396562323935,Hori Battle,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 35623466343433653739346434636330,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 484f524920434f2e2c4c54442e203130,Hori Fighting Commander 3 Pro,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 484f524920434f2e2c4c544420205041,Hori Gem Pad 3,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, 65656436646661313232656661616130,Hori PC Engine Mini Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b18,platform:Android, 31303433326562636431653534636633,Hori Real Arcade Pro 3,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 32656664353964393561366362333636,Hori Switch Split Pad Pro,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 30306539356238653637313730656134,HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, 48797065726b696e2050616400000000,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, 62333331353131353034386136626636,Hyperkin Admiral N64 Controller,+rightx:b6,+righty:b7,-rightx:b17,-righty:b5,a:b1,b:b0,leftshoulder:b3,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b20,start:b18,platform:Android, 31306635363562663834633739396333,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, 5368616e57616e202020202048797065,Hyperkin N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, 0500000083050000602000000ffe0000,iBuffalo SNES Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b15,rightshoulder:b16,start:b10,x:b2,y:b3,platform:Android, 5553422c322d6178697320382d627574,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b3,y:b2,platform:Android, 64306137363261396266353433303531,InterAct GoPad,a:b24,b:b25,leftshoulder:b23,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,x:b21,y:b22,platform:Android, 532e542e442e20496e74657261637420,InterAct HammerHead FX,a:b23,b:b24,back:b30,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b22,lefttrigger:b28,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b25,righttrigger:b29,rightx:a2,righty:a3,start:b31,x:b20,y:b21,platform:Android, 65346535636333663931613264643164,Joy-Con,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b23,y:b24,platform:Android, 33346566643039343630376565326335,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, 35313531613435623366313835326238,Joy-Con (L),a:b0,b:b1,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, 4a6f792d436f6e20284c290000000000,Joy-Con (L),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b17,x:b19,y:b2,platform:Android, 38383665633039363066383334653465,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, 39363561613936303237333537383931,Joy-Con (R),a:b0,b:b1,back:b5,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, 39373064396565646338333134303131,Joy-Con (R),a:b1,b:b2,back:b5,leftstick:b8,leftx:a1~,lefty:a0,start:b6,x:b0,y:b3,platform:Android, 4a6f792d436f6e202852290000000000,Joy-Con (R),a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, 39656136363638323036303865326464,JYS Aapter,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 63316564383539663166353034616434,JYS Adapter,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, 64623163333561643339623235373232,Logitech F310,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 35623364393661626231343866613337,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 4c6f6769746563682047616d65706164,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 64396331333230326333313330336533,Logitech F710,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 39653365373864633935383236363438,Logitech G Cloud,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 416d617a6f6e2047616d6520436f6e74,Luna Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 4c756e612047616d6570616400000000,Luna Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 30363066623539323534363639323363,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 31353762393935386662336365626334,Magic NS,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 39623565346366623931666633323530,Magic NS,a:b1,b:b3,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b0,y:b2,platform:Android, 6d6179666c617368206c696d69746564,Mayflash GameCube Adapter,a:b22,b:b21,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a5,righty:a2,start:b30,x:b23,y:b24,platform:Android, 436f6e74726f6c6c6572000000000000,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, 65666330633838383061313633326461,Mayflash N64 Adapter,a:b1,b:b19,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a2,righty:a3,start:b18,platform:Android, 37316565396364386635383230353365,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, 4875694a696120205553422047616d65,Mayflash Saturn Adapter,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, 535a4d792d706f776572204c54442043,Mayflash Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b31,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, 30653962643666303631376438373532,Mayflash Wii DolphinBar,a:b23,b:b24,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b0,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b22,platform:Android, 39346131396233376535393665363161,Mayflash Wii U Pro Adapter,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftstick:b31,lefttrigger:b27,rightshoulder:b26,rightstick:b0,righttrigger:b28,rightx:a0,righty:a1,start:b30,x:b21,y:b24,platform:Android, 31323564663862633234646330373138,Mega Drive,a:b23,b:b22,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, 37333564393261653735306132613061,Mega Drive,a:b21,b:b22,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,lefttrigger:b28,rightshoulder:b27,righttrigger:b23,start:b30,x:b24,y:b25,platform:Android, 64363363336633363736393038313464,Mega Drive,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b9,x:b2,y:b3,platform:Android, 33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android, 30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b20,lefttrigger:b9,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,platform:Android, 32386235353630393033393135613831,Microsoft Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 7573622067616d657061642020202020,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Android, 050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android, 31316661666466633938376335383661,Nintendo Switch Pro Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,start:b6,x:b3,y:b2,platform:Android, 34323437396534643531326161633738,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,misc1:b5,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 50726f20436f6e74726f6c6c65720000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b2,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b10,rightx:a2,righty:a3,start:b18,y:b3,platform:Android, 36326533353166323965623661303933,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, 4e363420436f6e74726f6c6c65720000,NSO N64 Controller,+rightx:b17,+righty:b10,-rightx:b2,-righty:b19,a:b1,b:b0,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,righttrigger:b15,start:b18,platform:Android, 534e455320436f6e74726f6c6c657200,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, 64623863346133633561626136366634,NSO SNES Controller,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,rightshoulder:b20,start:b18,x:b19,y:b2,platform:Android, 050000005509000003720000cf7f3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000010720000ffff3f00,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005509000014720000df7f3f00,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 050000005509000014720000df7f3f80,NVIDIA Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a3,rightx:a4,righty:a5,start:b6,x:b2,y:b3,platform:Android, 37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 39383335313438623439373538343266,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b16,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, 4f5559412047616d6520436f6e74726f,OUYA Controller,a:b0,b:b2,dpdown:b18,dpleft:b15,dpright:b6,dpup:b17,leftshoulder:b3,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b19,platform:Android, 506572666f726d616e63652044657369,PDP PS3 Rock Candy Controller,a:b1,b:b17,back:h0.2,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b0,y:b2,platform:Android, 61653962353232366130326530363061,Pokken,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,rightshoulder:b20,righttrigger:b10,start:b18,x:b0,y:b2,platform:Android, 32666633663735353234363064386132,PS2,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a3,righty:a2,start:b30,x:b24,y:b21,platform:Android, 050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 536f6e7920504c415953544154494f4e,PS3 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 61363034663839376638653463633865,PS3 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 66366539656564653432353139356536,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 66383132326164626636313737373037,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000c405000000783f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000c4050000fffe3f80,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a3,rightx:a4,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 30303839663330346632363232623138,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 31326235383662333266633463653332,PS4 Controller,a:b1,b:b16,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b17,x:b0,y:b2,platform:Android, 31373231336561636235613666323035,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 31663838336334393132303338353963,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 34613139376634626133336530386430,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 37626233336235343937333961353732,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 37626464343430636562316661643863,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38393161636261653636653532386639,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 63313733393535663339656564343962,PS4 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 63393662363836383439353064663939,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65366465656364636137653363376531,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 66613532303965383534396638613230,PS4 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, 050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000e60c0000fffe3f80,PS5 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a3,rightx:a4,righty:a5,start:b16,x:b2,y:b17,platform:Android, 050000004c050000e60c0000ffff3f00,PS5 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 32346465346533616263386539323932,PS5 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 32633532643734376632656664383733,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, 37363764353731323963323639666565,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, 61303162353165316365336436343139,PS5 Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a5,start:b18,x:b0,y:b2,platform:Android, 64336263393933626535303339616332,Qanba 4RAF,a:b0,b:b1,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b20,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b9,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, 36626666353861663864336130363137,Razer Junglecat,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000f8270000bf0b0000ffff3f00,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 62653861643333663663383332396665,Razer Kishi,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000005070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000007070000ffff3f00,Razer Raiju Mobile,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, 5a6869587520526574726f2042697420,Retro Bit Saturn Controller,a:b21,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b26,rightshoulder:b27,righttrigger:b28,start:b30,x:b23,y:b24,platform:Android, 32417865732031314b6579732047616d,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 36313938306539326233393732613361,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android, 61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android, 526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 64633735616665613536653363336132,Retroid Pocket,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b19,paddle2:b20,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,start:b6,platform:Android, 37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, 5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, 526574726f5553422e636f6d20534e45,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, 64643037633038386238303966376137,RetroUSB SNES RetroPort,a:b1,b:b20,back:b19,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b2,x:b0,y:b3,platform:Android, 37656564346533643138636436356230,Rock Candy Switch Controller,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,misc1:b7,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 33373336396634316434323337666361,RumblePad 2,a:b22,b:b23,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,lefttrigger:b27,leftx:a0,lefty:a1,rightshoulder:b26,righttrigger:b28,rightx:a2,righty:a3,start:b30,x:b21,y:b24,platform:Android, 36363537303435333566386638366333,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 53616d73756e672047616d6520506164,Samsung EIGP20,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 66386565396238363534313863353065,Sanwa PlayOnline Mobile,a:b21,b:b22,back:b23,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b24,platform:Android, 32383165316333383766336338373261,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, 38613865396530353338373763623431,Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b10,rightshoulder:b20,righttrigger:b19,start:b17,x:b2,y:b3,platform:Android, 61316232336262373631343137633631,Saturn,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:a4,righttrigger:a5,x:b2,y:b3,platform:Android, 30353835333338613130373363646337,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, 66386262366536653765333235343634,SG H510,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android, 66633132393363353531373465633064,SG H510,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b17,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b18,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b19,y:b2,platform:Android, 62653761636366393366613135366338,SN30 PP,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, 38376662666661636265313264613039,SNES,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, 5346432f555342205061640000000000,SNES Adapter,a:b0,b:b1,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b3,rightshoulder:b20,start:b10,x:b19,y:b2,platform:Android, 5553422047616d657061642000000000,SNES Controller,a:b1,b:b0,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 62653335326261303663356263626339,Sony PlayStation Classic Controller,a:b19,b:b1,back:b17,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,lefttrigger:b3,rightshoulder:b10,righttrigger:b20,start:b18,x:b2,y:b0,platform:Android, 536f6e7920496e746572616374697665,Sony PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 576972656c65737320436f6e74726f6c,Sony PlayStation Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 63303964303462366136616266653561,Sony PSP,a:b21,b:b22,back:b27,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b23,y:b24,platform:Android, 63376637643462343766333462383235,Sony Vita,a:b1,b:b19,back:b17,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftx:a0,lefty:a1,rightshoulder:b20,rightx:a3,righty:a4,start:b18,x:b0,y:b2,platform:Android, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, 0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android, 35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,platform:Android, 33313930373536613937326534303931,Taito Egret II Mini Control Panel,a:b25,b:b23,back:b27,guide:b30,leftx:a0,lefty:a1,rightshoulder:b21,righttrigger:b22,start:b28,x:b29,y:b24,platform:Android, 54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,platform:Android, 38346162326232346533316164363336,THEGamepad,a:b23,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b28,x:b24,y:b21,platform:Android, 050000004f0400000ed00000fffe3f00,Thrustmaster eSwap Pro Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 5477696e20555342204a6f7973746963,Twin Joystick,a:b22,b:b21,back:b28,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android, 30623739343039643830333266346439,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 31643365666432386133346639383937,Valve Steam Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,leftx:a0,lefty:a1,paddle1:b24,paddle2:b23,rightshoulder:b10,rightstick:b8,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 30386438313564306161393537333663,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, 33333034646336346339646538643633,Wii Classic Adapter,a:b23,b:b22,back:b29,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b27,lefttrigger:b25,leftx:a0,lefty:a1,rightshoulder:b28,righttrigger:b26,rightx:a2,righty:a3,start:b30,x:b24,y:b21,platform:Android, 050000005e0400008e02000000783f00,Xbox 360 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 30396232393162346330326334636566,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38313038323730383864666463383533,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 58626f782033363020576972656c6573,Xbox 360 Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65353331386662343338643939643636,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65613532386633373963616462363038,Xbox 360 Controller,a:b0,b:b1,back:b4,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 47656e6572696320582d426f78207061,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 4d6963726f736f667420582d426f7820,Xbox Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 64633436313965656664373634323364,Xbox Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b19,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e04000091020000ff073f00,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e04000091020000ff073f80,Xbox One Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000e00200000ffe3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, 050000005e040000e00200000ffe3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android, 050000005e040000e0020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, 050000005e040000e0020000ffff3f80,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b4,leftshoulder:b3,leftstick:b8,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b10,x:b17,y:b2,platform:Android, 050000005e040000fd020000ffff3f00,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 33356661323266333733373865656366,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 34356136633366613530316338376136,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android, 35623965373264386238353433656138,Xbox One Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 36616131643361333337396261666433,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 58626f7820576972656c65737320436f,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65316262316265373335666131623538,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e040000000b000000783f80,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000050b0000ffff3f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000e002000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000ea02000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e040000120b000000783f80,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000001727000044310000ffff3f00,XiaoMi Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a6,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, # iOS 05000000ac0500000100000000006d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, 05000000ac050000010000004f066d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, 05000000ac05000001000000cf076d01,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b2,y:b3,platform:iOS, 05000000ac05000001000000df076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 05000000ac05000001000000ff076d01,*,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 05000000ac0500000200000000006d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, 05000000ac050000020000004f066d02,*,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b2,y:b3,platform:iOS, 05000000ac05000004000000a8986d04,8BitDo Micro,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,lefttrigger:b12,rightshoulder:b13,righttrigger:b14,start:b3,x:b6,y:b5,platform:iOS, 05000000ac05000004000000fd216d04,8BitDo Pro 2,a:b3,b:b2,back:b6,dpdown:b9,dpleft:b10,dpright:b11,dpup:b12,guide:b4,leftshoulder:b13,leftstick:b14,lefttrigger:+a2,leftx:a0,lefty:a1~,paddle1:b1,paddle2:b0,rightshoulder:b16,rightstick:b17,righttrigger:+a5,rightx:a3,righty:a4~,start:b5,x:b8,y:b7,platform:iOS, 05000000ac05000004000000209f6d04,8Bitdo SN30 Pro,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,platform:iOS, 05000000ac050000040000003b8a6d04,8BitDo SN30 Pro+,a:b1,b:b0,back:b4,dpdown:b7,dpleft:b8,dpright:b9,dpup:b10,guide:b2,leftshoulder:b11,leftstick:b12,lefttrigger:b13,leftx:a0,lefty:a1~,rightshoulder:b14,rightstick:b15,righttrigger:b16,rightx:a2,righty:a3~,start:b3,x:b6,y:b5,platform:iOS, 050000008a35000003010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 050000008a35000004010000ff070000,Backbone One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, 4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, 050000007e050000062000000f060000,Nintendo Switch Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS, 050000007e050000062000004f060000,Nintendo Switch Joy-Con (L),+leftx:h0.1,+lefty:h0.2,-leftx:h0.4,-lefty:h0.8,dpdown:b2,dpleft:b0,dpright:b3,dpup:b1,leftshoulder:b4,misc1:b6,rightshoulder:b5,platform:iOS, 050000007e05000008200000df070000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:iOS, 050000007e0500000e200000df070000,Nintendo Switch Joy-Con (L/R),a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:iOS, 050000007e050000072000000f060000,Nintendo Switch Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS, 050000007e050000072000004f060000,Nintendo Switch Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b2,guide:b6,leftshoulder:b4,rightshoulder:b5,x:b1,y:b3,platform:iOS, 050000007e05000009200000df870000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b10,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:iOS, 050000007e05000009200000ff870000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b3,y:b2,platform:iOS, 05000000ac050000040000008e586d04,PlayStation VR2 Sense Controller (L),+leftx:+a3,+lefty:+a1,-leftx:+a2,-lefty:+a4,back:b2,leftstick:b4,lefttrigger:+a7,paddle2:b3,x:b0,y:b1,platform:iOS, 05000000ac050000040000000eb86d04,PlayStation VR2 Sense Controller (R),+rightx:+a3,+righty:+a1,-rightx:+a2,-righty:+a4,a:b0,b:b1,paddle1:b3,rightstick:b4,righttrigger:+a7,start:b2,platform:iOS, 050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000004c050000cc090000df870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000004c050000cc090000ff070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 050000004c050000cc090000ff870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,touchpad:b11,x:b2,y:b3,platform:iOS, 050000004c050000cc090000ff876d01,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 050000004c050000e60c0000df870000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,touchpad:b10,x:b2,y:b3,platform:iOS, 050000004c050000e60c0000ff870000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,touchpad:b11,x:b2,y:b3,platform:iOS, 05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, 05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:iOS, 050000005e040000050b0000df070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b10,paddle2:b12,paddle3:b11,paddle4:b13,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000005e040000050b0000ff070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 050000005e040000130b0000df870001,Xbox Series X Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b10,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000005e040000130b0000ff870001,Xbox Series X Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000005e040000e0020000ff070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, ================================================ FILE: source/Playnite/license.txt ================================================ ---------------------------------------------------------------------------------------- Playnite / Playnite.SDK MIT License Copyright (c) 2020 Josef Nemec Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- Audio Background music for default Fullscreen theme obtained from https://www.zapsplat.com (Attribution license) Mavigation sounds for default Fullscreen theme obtained from https://www.kenney.nl (CC0 license) ---------------------------------------------------------------------------------------- corefx The MIT License (MIT) Copyright (c) .NET Foundation and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- YamlDotNet Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Antoine Aubry and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- WindowsAPICodePack License: Custom License MICROSOFT SOFTWARE LICENSE TERMS MICROSOFT WINDOWS API CODE PACK FOR MICROSOFT .NET FRAMEWORK ___________________________________________________ These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. Please read them. They apply to the software named above, which includes the media on which you received it, if any. The terms also apply to any Microsoft • updates, • supplements, • Internet-based services, and • support services for this software, unless other terms accompany those items. If so, those terms apply. _______________________________________________________________________________________ BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. IF YOU DO NOT ACCEPT THEM, DO NOT USE THE SOFTWARE. If you comply with these license terms, you have the rights below. 1. INSTALLATION AND USE RIGHTS. • You may use any number of copies of the software to design, develop and test your programs that run on a Microsoft Windows operating system. • This agreement gives you rights to the software only. Any rights to a Microsoft Windows operating system (such as testing pre-release versions of Windows in a live operating environment) are provided separately by the license terms for Windows. 2. ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS. a. Distributable Code. You may modify, copy, and distribute the software, in source or compiled form, to run on a Microsoft Windows operating system. ii. Distribution Requirements. If you distribute the software, you must • require distributors and external end users to agree to terms that protect it at least as much as this agreement; • if you modify the software and distribute such modified files, include prominent notices in such modified files so that recipients know that they are not receiving the original software; • display your valid copyright notice on your programs; and • indemnify, defend, and hold harmless Microsoft from any claims, including attorneys’ fees, related to the distribution or use of your programs or to your modifications to the software. iii. Distribution Restrictions. You may not • alter any copyright, trademark or patent notice in the software; • use Microsoft’s trademarks in your programs’ names or in a way that suggests your programs come from or are endorsed by Microsoft; • include the software in malicious, deceptive or unlawful programs; or • modify or distribute the source code of the software so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that • the code be disclosed or distributed in source code form; or • others have the right to modify it. 3. SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. 4. EXPORT RESTRICTIONS. The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destinations, end users and end use. For additional information, see . 5. SUPPORT SERVICES. Because this software is “as is,” we may not provide support services for it. 6. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services. 7. APPLICABLE LAW. a. United States. If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort. b. Outside the United States. If you acquired the software in any other country, the laws of that country apply. 8. LEGAL EFFECT. This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so. 9. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS-IS.” YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. YOU MAY HAVE ADDITIONAL CONSUMER RIGHTS UNDER YOUR LOCAL LAWS WHICH THIS AGREEMENT CANNOT CHANGE. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 10. LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES. This limitation applies to • anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and • claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages. Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French. Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français. EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues. LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. Cette limitation concerne : • tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et • les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur. Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci-dessus ne s’appliquera pas à votre égard. EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas. ---------------------------------------------------------------------------------------- System.IO.Abstractions The MIT License (MIT) Copyright (c) Tatham Oddie and Contributors All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- SteamKit GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ---------------------------------------------------------------------------------------- SQLNado MIT License Copyright (c) 2017-2023 Simon Mourier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- protobuf-net The core Protocol Buffers technology is provided courtesy of Google. At the time of writing, this is released under the BSD license. Full details can be found here: http://code.google.com/p/protobuf/ This .NET implementation is Copyright 2008 Marc Gravell Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---------------------------------------------------------------------------------------- Prism The MIT License (MIT) Copyright (c) .NET Foundation All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- Polly New BSD License = Copyright (c) 2015-2020, App vNext All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of App vNext nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------------------- NUnit Copyright (c) 2019 Charlie Poole, Rob Prouse Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- NLog Copyright (c) 2004-2020 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Jaroslaw Kowalski nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------------------- Newtonsoft.Json The MIT License (MIT) Copyright (c) 2007 James Newton-King Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- Nett Copyright (c) 2015 paiden@outlook.com. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- moq BSD 3-Clause License Copyright (c) 2007, Clarius Consulting, Manas Technology Solutions, InSTEDD, and Contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the names of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------------------- markdig Copyright (c) 2018-2019, Alexandre Mutel All rights reserved. Redistribution and use in source and binary forms, with or without modification , are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------------------- InputSimulatorPlus Ms-PL Microsoft Public License (Ms-PL) This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. 1. Definitions The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution. 2. Grant of Rights (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 3. Conditions and Limitations (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. ---------------------------------------------------------------------------------------- wpf-notifyicon The Code Project Open License (CPOL) Preamble This License governs Your use of the Work. This License is intended to allow developers to use the Source Code and Executable Files provided as part of the Work in any application in any form. The main points subject to the terms of the License are: - Source Code and Executable Files can be used in commercial applications; - Source Code and Executable Files can be redistributed; and - Source Code can be modified to create derivative works. - No claim of suitability, guarantee, or any warranty whatsoever is provided. The software is provided "as-is". - The Article(s) accompanying the Work may not be distributed or republished without the Author's consent This License is entered between You, the individual or other entity reading or otherwise making use of the Work licensed pursuant to this License and the individual or other entity which offers the Work under the terms of this License ("Author"). License THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CODE PROJECT OPEN LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HEREIN, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE AUTHOR GRANTS YOU THE RIGHTS CONTAINED HEREIN IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. IF YOU DO NOT AGREE TO ACCEPT AND BE BOUND BY THE TERMS OF THIS LICENSE, YOU CANNOT MAKE ANY USE OF THE WORK. 1. Definitions. a. "Articles" means, collectively, all articles written by Author which describes how the Source Code and Executable Files for the Work may be used by a user. b. "Author" means the individual or entity that offers the Work under the terms of this License. c. "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works. d. "Executable Files" refer to the executables, binary files, configuration and any required data files included in the Work. e. "Publisher" means the provider of the website, magazine, CD-ROM, DVD or other medium from or by which the Work is obtained by You. f. "Source Code" refers to the collection of source code and configuration files used to create the Executable Files. g. "Standard Version" refers to such a Work if it has not been modified, or has been modified in accordance with the consent of the Author, such consent being in the full discretion of the Author. h. "Work" refers to the collection of files distributed by the Publisher, including the Source Code, Executable Files, binaries, data files, documentation, whitepapers and the Articles. i. "You" is you, an individual or entity wishing to use the Work and exercise your rights under this License. 2. Fair Use/Fair Use Rights. Nothing in this License is intended to reduce, limit, or restrict any rights arising from fair use, fair dealing, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws. 3. License Grant. Subject to the terms and conditions of this License, the Author hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: a. You may use the standard version of the Source Code or Executable Files in Your own applications. b. You may apply bug fixes, portability fixes and other modifications obtained from the Public Domain or from the Author. A Work modified in such a way shall still be considered the standard version and will be subject to this License. c. You may otherwise modify Your copy of this Work (excluding the Articles) in any way to create a Derivative Work, provided that You insert a prominent notice in each changed file stating how, when and where You changed that file. d. You may distribute the standard version of the Executable Files and Source Code or Derivative Work in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution. e. The Articles discussing the Work published in any form by the author may not be distributed or republished without the Author's consent. The author retains copyright to any such Articles. You may use the Executable Files and Source Code pursuant to this License but you may not repost or republish or otherwise distribute or make available the Articles, without the prior written consent of the Author. Any subroutines or modules supplied by You and linked into the Source Code or Executable Files of this Work shall not be considered part of this Work and will not be subject to the terms of this License. 4. Patent License. Subject to the terms and conditions of this License, each Author hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, import, and otherwise transfer the Work. 5. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: a. You agree not to remove any of the original copyright, patent, trademark, and attribution notices and associated disclaimers that may appear in the Source Code or Executable Files. b. You agree not to advertise or in any way imply that this Work is a product of Your own. c. The name of the Author may not be used to endorse or promote products derived from the Work without the prior written consent of the Author. d. You agree not to sell, lease, or rent any part of the Work. This does not restrict you from including the Work or any part of the Work inside a larger software distribution that itself is being sold. The Work by itself, though, cannot be sold, leased or rented. e. You may distribute the Executable Files and Source Code only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy of the Executable Files or Source Code You distribute and ensure that anyone receiving such Executable Files and Source Code agrees that the terms of this License apply to such Executable Files and/or Source Code. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute the Executable Files or Source Code with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License. f. You agree not to use the Work for illegal, immoral or improper purposes, or on pages containing illegal, immoral or improper material. The Work is subject to applicable export laws. You agree to comply with all such laws and regulations that may apply to the Work after Your receipt of the Work. 6. Representations, Warranties and Disclaimer. THIS WORK IS PROVIDED "AS IS", "WHERE IS" AND "AS AVAILABLE", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES OR CONDITIONS OR GUARANTEES. YOU, THE USER, ASSUME ALL RISK IN ITS USE, INCLUDING COPYRIGHT INFRINGEMENT, PATENT INFRINGEMENT, SUITABILITY, ETC. AUTHOR EXPRESSLY DISCLAIMS ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS, INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY WARRANTY OF TITLE OR NON-INFRINGEMENT, OR THAT THE WORK (OR ANY PORTION THEREOF) IS CORRECT, USEFUL, BUG-FREE OR FREE OF VIRUSES. YOU MUST PASS THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE WORK OR DERIVATIVE WORKS. 7. Indemnity. You agree to defend, indemnify and hold harmless the Author and the Publisher from and against any claims, suits, losses, damages, liabilities, costs, and expenses (including reasonable legal or attorneys’ fees) resulting from or relating to any use of the Work by You. 8. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL THE AUTHOR OR THE PUBLISHER BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK OR OTHERWISE, EVEN IF THE AUTHOR OR THE PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 9. Termination. a. This License and the rights granted hereunder will terminate automatically upon any breach by You of any term of this License. Individuals or entities who have received Derivative Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 6, 7, 8, 9, 10 and 11 will survive any termination of this License. b. If You bring a copyright, trademark, patent or any other infringement claim against any contributor over infringements You claim are made by the Work, your License from such contributor to the Work ends automatically. c. Subject to the above terms and conditions, this License is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, the Author reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 10. Publisher. The parties hereby confirm that the Publisher shall not, under any circumstances, be responsible for and shall not have any liability in respect of the subject matter of this License. The Publisher makes no warranty whatsoever in connection with the Work and shall not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. The Publisher reserves the right to cease making the Work available to You at any time without notice 11. Miscellaneous a. This License shall be governed by the laws of the location of the head office of the Author or if the Author is an individual, the laws of location of the principal place of residence of the Author. b. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this License, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. c. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. d. This License constitutes the entire agreement between the parties with respect to the Work licensed herein. There are no understandings, agreements or representations with respect to the Work not specified herein. The Author shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Author and You. ---------------------------------------------------------------------------------------- Flurl MIT License Copyright (c) 2018 Todd Menier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- DynamicLanguageRuntime SPDX identifier Apache-2.0 License text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright _____ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---------------------------------------------------------------------------------------- CommonServiceLocator Microsoft Public License (MS-PL) This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. 1. Definitions The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution. 2. Grant of Rights (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 3. Conditions and Limitations (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. ---------------------------------------------------------------------------------------- CommandLineParser The MIT License (MIT) Copyright (c) 2005 - 2015 Giacomo Stelluti Scala & Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- CefSharp // Copyright © The CefSharp Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // * Neither the name of Google Inc. nor the name Chromium Embedded // Framework nor the name CefSharp nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------------------- CEF // Copyright (c) 2008-2020 Marshall A. Greenblatt. Portions Copyright (c) // 2006-2009 Google Inc. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the name Chromium Embedded // Framework nor the names of its contributors may be used to endorse // or promote products derived from this software without specific prior // written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------------------- Castle.Core Copyright 2004-2016 Castle Project - http://www.castleproject.org/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---------------------------------------------------------------------------------------- AngleSharp The MIT License (MIT) Copyright (c) 2013 - 2019 AngleSharp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- HTML-Renderer Copyright (c) 2009, José Manuel Menéndez Poo Copyright (c) 2013, Arthur Teplitzki All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the menendezpoo.com, ArthurHub nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------------------- LiteDB The MIT License (MIT) Copyright (c) 2014-2020 Mauricio David Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------------------------------------- SDL2 Copyright (C) 1997-2023 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ---------------------------------------------------------------------------------------- SDL2-CS /* SDL2# - C# Wrapper for SDL2 * * Copyright (c) 2013-2021 Ethan Lee. * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in a * product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * * Ethan "flibitijibibo" Lee * */ ---------------------------------------------------------------------------------------- SDL_mixer Copyright (C) 1997-2023 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ---------------------------------------------------------------------------------------- Magick.NET Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: source/Playnite/packages.config ================================================  ================================================ FILE: source/Playnite.DesktopApp/Api/MainViewAPI.cs ================================================ using Playnite.DesktopApp.ViewModels; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using Playnite.ViewModels; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Threading; namespace Playnite.DesktopApp.API { public class MainViewAPI : IMainViewAPI { private static readonly ILogger logger = LogManager.GetLogger(); private DesktopAppViewModel mainModel; public IEnumerable SelectedGames { get { return UIDispatcher.Invoke(() => { if (mainModel.SelectedGames == null && mainModel.SelectedGame != null) { return new List() { mainModel.SelectedGame.Game }; } else { return mainModel.SelectedGames?.Select(a => a.Game).ToList(); } }); } } public DesktopView ActiveDesktopView { get => mainModel.AppSettings.ViewSettings.GamesViewType; set => mainModel.AppSettings.ViewSettings.GamesViewType = value; } public FullscreenView ActiveFullscreenView { get; } = FullscreenView.List; public SortOrder SortOrder { get => mainModel.AppSettings.ViewSettings.SortingOrder; set => mainModel.AppSettings.ViewSettings.SortingOrder = value; } public SortOrderDirection SortOrderDirection { get => mainModel.AppSettings.ViewSettings.SortingOrderDirection; set => mainModel.AppSettings.ViewSettings.SortingOrderDirection = value; } public GroupableField Grouping { get => mainModel.AppSettings.ViewSettings.GroupingOrder; set => mainModel.AppSettings.ViewSettings.GroupingOrder = value; } public List FilteredGames => UIDispatcher.Invoke(() => mainModel.GamesView.CollectionView.Cast().Select(a => a.Game).Distinct().ToList()); public Dispatcher UIDispatcher => PlayniteApplication.CurrentNative.Dispatcher; public MainViewAPI(DesktopAppViewModel mainModel) { this.mainModel = mainModel; } public bool OpenPluginSettings(Guid pluginId) { return mainModel.OpenPluginSettings(pluginId); } public void SwitchToLibraryView() { mainModel.SwitchToLibraryView(); } public void SelectGame(Guid gameId) { var game = mainModel.Database.Games.Get(gameId); if (game == null) { logger.Error($"Can't select game, game ID {gameId} not found."); } else { mainModel.SelectGame(game.Id); } } public void SelectGames(IEnumerable gameIds) { mainModel.SelectGames(gameIds); } public void ApplyFilterPreset(Guid filterId) { mainModel.ApplyFilterPreset(filterId); } public void ApplyFilterPreset(FilterPreset preset) { mainModel.ActiveFilterPreset = preset; } public Guid GetActiveFilterPreset() { return mainModel.AppSettings.SelectedFilterPreset; } public FilterPresetSettings GetCurrentFilterSettings() { return mainModel.AppSettings.FilterSettings.AsPresetSettings(); } public void OpenSearch(string searchTerm) { mainModel.OpenSearch(searchTerm); } public void OpenSearch(SearchContext context, string searchTerm) { mainModel.OpenSearch(context, searchTerm); } public bool? OpenEditDialog(Guid gameId) { var game = mainModel.Database.Games.Get(gameId); if (game is null) return null; return mainModel.GamesEditor.EditGame(game); } public bool? OpenEditDialog(List gameIds) { var games = mainModel.Database.Games.Get(gameIds); if (!games.HasItems()) return null; return mainModel.GamesEditor.EditGames(games); } public List GetSortedFilterPresets() { return mainModel.SortedFilterPresets.ToList(); } public List GetSortedFilterFullscreenPresets() { return mainModel.SortedFilterFullscreenPresets.ToList(); } public void ToggleFullscreenView() { throw new NotSupportedInDesktopException(); } } } ================================================ FILE: source/Playnite.DesktopApp/App.config ================================================  ================================================ FILE: source/Playnite.DesktopApp/App.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/App.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Linq; using System.Threading.Tasks; using System.Windows; namespace Playnite.DesktopApp { /// /// Interaction logic for App.xaml /// public partial class App : Application { public App() { } } } ================================================ FILE: source/Playnite.DesktopApp/ControlGalleryView.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Controls/AddonsSections/AddonUpdates.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Controls/AddonsSections/BrowseAddons.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls.AddonsSections { /// /// Interaction logic for Extensions.xaml /// public partial class BrowseAddons : UserControl { public BrowseAddons() { InitializeComponent(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/AddonsSections/InstalledExtensions.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Controls/AddonsSections/InstalledExtensions.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls.AddonsSections { /// /// Interaction logic for Extensions.xaml /// public partial class InstalledExtensions : UserControl { public InstalledExtensions() { InitializeComponent(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/AddonsSections/InstalledThemes.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Controls/AddonsSections/InstalledThemes.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls.AddonsSections { /// /// Interaction logic for Extensions.xaml /// public partial class InstalledThemes : UserControl { public InstalledThemes() { InitializeComponent(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/ComboBoxList.cs ================================================ using Playnite.Common; using Playnite.DesktopApp.ViewModels; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Xml.Linq; namespace Playnite.DesktopApp.Controls { public class ComboBoxList : ComboBoxListBase { #region ItemsList public SelectableObjectList ItemsList { get => (SelectableObjectList)GetValue(ItemsListProperty); set => SetValue(ItemsListProperty, value); } public static readonly DependencyProperty ItemsListProperty = DependencyProperty.Register( nameof(ItemsList), typeof(SelectableObjectList), typeof(ComboBoxList)); #endregion #region ItemsSource public IList ItemsSource { get => (IList)GetValue(ItemsSourceProperty); set => SetValue(ItemsSourceProperty, value); } public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register( nameof(ItemsSource), typeof(IList), typeof(ComboBoxList), new PropertyMetadata(null, ItemsSourcePropertyChangedCallback)); private static void ItemsSourcePropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as ComboBoxList; var newVal = (IList)e.NewValue; if (obj.ItemsList != null) { obj.ItemsList.SelectionChanged -= obj.List_SelectionChanged; } obj.ItemsList = new SelectableObjectList(null); obj.ItemsList.SelectionChanged += obj.List_SelectionChanged; obj.SetSelectedItem(); } #endregion internal void SetSelectedItem() { if (ItemsList == null) { return; } IgnoreChanges = true; ItemsList.SetItems(ItemsSource as IEnumerable, SelectedItems as IEnumerable); UpdateTextStatus(); IgnoreChanges = false; } #region SelectedItems public IList SelectedItems { get => (IList)GetValue(SelectedItemsProperty); set { if (value == null) { SetValue(SelectedItemsProperty, value); return; } // This makes it possible to bind back into generic List collections instead of just IList. var binding = BindingOperations.GetBindingExpression(this, SelectedItemsProperty); if (binding != null) { var targetProp = binding.ResolvedSource.GetType().GetProperty(binding.ResolvedSourcePropertyName); if (targetProp.PropertyType.IsGenericList(out var itemType)) { var newList = targetProp.PropertyType.CrateInstance(); var addMethod = newList.GetType().GetMethod("Add"); foreach (var val in value) { addMethod.Invoke(newList, new object[] { Convert.ChangeType(val, itemType) }); } SetValue(SelectedItemsProperty, newList); return; } } SetValue(SelectedItemsProperty, value); } } public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register( nameof(SelectedItems), typeof(IList), typeof(ComboBoxList), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedItemsPropertyChangedCallback)); private static void SelectedItemsPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as ComboBoxList; if (!obj.IgnoreChanges) { obj.SetSelectedItem(); } } #endregion static ComboBoxList() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ComboBoxList), new FrameworkPropertyMetadata(typeof(ComboBoxList))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); if (ItemsPanel != null) { XNamespace pns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; ItemsPanel.ItemTemplate = Xaml.FromString(new XDocument( new XElement(pns + nameof(DataTemplate), new XElement(pns + nameof(CheckBox), new XAttribute(nameof(CheckBox.IsChecked), "{Binding Selected}"), new XAttribute(nameof(CheckBox.Content), "{Binding Item}"), new XAttribute(nameof(CheckBox.IsThreeState), "{Binding IsThreeState, Mode=OneWay, RelativeSource={RelativeSource AncestorType=ComboBoxList}}"), new XAttribute(nameof(CheckBox.Style), $"{{DynamicResource ComboBoxListItemStyle}}"))) ).ToString()); } UpdateTextStatus(); } public override void ClearButtonAction(RoutedEventArgs e) { SelectedItems = null; } private void List_SelectionChanged(object sender, EventArgs e) { if (!IgnoreChanges) { IgnoreChanges = true; var sel = ItemsList.GetSelectedItems(); SelectedItems = sel; IgnoreChanges = false; UpdateTextStatus(); } } private void UpdateTextStatus() { if (TextFilterString != null) { TextFilterString.Text = ItemsList?.AsString; } } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/ComboBoxListBase.cs ================================================ using Playnite.Common; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Xml.Linq; namespace Playnite.DesktopApp.Controls { [TemplatePart(Name = "PART_ItemsPanel", Type = typeof(ItemsControl))] [TemplatePart(Name = "PART_ButtonClearFilter", Type = typeof(Button))] [TemplatePart(Name = "PART_TextFilterString", Type = typeof(TextBlock))] public abstract class ComboBoxListBase : Control { internal ItemsControl ItemsPanel; internal Button ButtonClearFilter; internal TextBlock TextFilterString; internal bool IgnoreChanges { get; set; } public bool IsThreeState { get => (bool)GetValue(IsThreeStateProperty); set => SetValue(IsThreeStateProperty, value); } public static readonly DependencyProperty IsThreeStateProperty = DependencyProperty.Register( nameof(IsThreeState), typeof(bool), typeof(ComboBoxListBase)); public override void OnApplyTemplate() { base.OnApplyTemplate(); ButtonClearFilter = Template.FindName("PART_ButtonClearFilter", this) as Button; TextFilterString = Template.FindName("PART_TextFilterString", this) as TextBlock; ItemsPanel = Template.FindName("PART_ItemsPanel", this) as ItemsControl; if (ButtonClearFilter != null) { ButtonClearFilter.Click += (_, e) => ClearButtonAction(e); } if (ItemsPanel != null) { BindingTools.SetBinding( ItemsPanel, ItemsControl.ItemsSourceProperty, this, "ItemsList"); XNamespace pns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; ItemsPanel.ItemsPanel = Xaml.FromString(new XDocument( new XElement(pns + nameof(ItemsPanelTemplate), new XElement(pns + nameof(VirtualizingStackPanel))) ).ToString()); ItemsPanel.Template = Xaml.FromString(new XDocument( new XElement(pns + nameof(ControlTemplate), new XElement(pns + nameof(ScrollViewer), new XAttribute(nameof(ScrollViewer.Focusable), false), new XElement(pns + nameof(ItemsPresenter)))) ).ToString()); ItemsPanel.ItemTemplate = Xaml.FromString(new XDocument( new XElement(pns + nameof(DataTemplate), new XElement(pns + nameof(CheckBox), new XAttribute(nameof(CheckBox.IsChecked), "{Binding Selected}"), new XAttribute(nameof(CheckBox.Content), "{Binding Item}"), new XAttribute(nameof(CheckBox.Style), $"{{DynamicResource ComboBoxListItemStyle}}"))) ).ToString()); ScrollViewer.SetCanContentScroll(ItemsPanel, true); KeyboardNavigation.SetDirectionalNavigation(ItemsPanel, KeyboardNavigationMode.Contained); VirtualizingPanel.SetIsVirtualizing(ItemsPanel, true); VirtualizingPanel.SetVirtualizationMode(ItemsPanel, VirtualizationMode.Recycling); } } public virtual void ClearButtonAction(RoutedEventArgs e) { } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/DdItemListSelectionBox.cs ================================================ using Playnite.Common; using Playnite.DesktopApp.ViewModels; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Xml.Linq; namespace Playnite.DesktopApp.Controls { [TemplatePart(Name = "PART_ToggleSelectedOnly", Type = typeof(ToggleButton))] [TemplatePart(Name = "PART_SearchBox", Type = typeof(SearchBox))] [TemplatePart(Name = "PART_ElemSearchHost", Type = typeof(FrameworkElement))] public class DdItemListSelectionBox : ComboBoxListBase { internal ToggleButton ToggleSelectedOnly; internal SearchBox TextSearchBox; internal FrameworkElement ElemSearchHost; public bool ShowSearchBox { get => (bool)GetValue(ShowSearchBoxProperty); set => SetValue(ShowSearchBoxProperty, value); } public static readonly DependencyProperty ShowSearchBoxProperty = DependencyProperty.Register( nameof(ShowSearchBox), typeof(bool), typeof(ComboBoxListBase), new PropertyMetadata(false)); public SelectableDbItemList ItemsList { get { return (SelectableDbItemList)GetValue(ItemsListProperty); } set { SetValue(ItemsListProperty, value); } } public static readonly DependencyProperty ItemsListProperty = DependencyProperty.Register( nameof(ItemsList), typeof(SelectableDbItemList), typeof(DdItemListSelectionBox), new PropertyMetadata(null, ItemsListPropertyChangedCallback)); private static void ItemsListPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as DdItemListSelectionBox; var oldVal = (SelectableDbItemList)e.NewValue; if (oldVal != null) { oldVal.SelectionChanged -= obj.List_SelectionChanged; } var list = (SelectableDbItemList)e.NewValue; list.SelectionChanged += obj.List_SelectionChanged; obj.UpdateTextStatus(); } private void List_SelectionChanged(object sender, EventArgs e) { if (!IgnoreChanges) { IgnoreChanges = true; BoundIds = ItemsList.GetSelectedIds(); IgnoreChanges = false; UpdateTextStatus(); } } public object BoundIds { get { return GetValue(BoundIdsProperty); } set { SetValue(BoundIdsProperty, value); } } public static readonly DependencyProperty BoundIdsProperty = DependencyProperty.Register( nameof(BoundIds), typeof(object), typeof(DdItemListSelectionBox), new PropertyMetadata(null, BoundIdsPropertyChangedCallback)); private static void BoundIdsPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as DdItemListSelectionBox; if (obj.IgnoreChanges) { return; } obj.IgnoreChanges = true; obj.ItemsList?.SetSelection(obj.BoundIds as IEnumerable); obj.IgnoreChanges = false; obj.UpdateTextStatus(); } static DdItemListSelectionBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DdItemListSelectionBox), new FrameworkPropertyMetadata(typeof(DdItemListSelectionBox))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); if (ItemsPanel != null) { BindingTools.ClearBinding(ItemsPanel, ItemsControl.ItemsSourceProperty); BindingTools.SetBinding( ItemsPanel, ItemsControl.ItemsSourceProperty, this, "ItemsList.CollectionView"); XNamespace pns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; ItemsPanel.ItemTemplate = Xaml.FromString(new XDocument( new XElement(pns + nameof(DataTemplate), new XElement(pns + nameof(CheckBox), new XAttribute(nameof(CheckBox.IsChecked), "{Binding Selected}"), new XAttribute(nameof(CheckBox.Content), "{Binding Item.Name}"), new XAttribute(nameof(CheckBox.IsThreeState), "{Binding IsThreeState, Mode=OneWay, RelativeSource={RelativeSource AncestorType=DdItemListSelectionBox}}"), new XAttribute(nameof(CheckBox.Style), $"{{DynamicResource ComboBoxListItemStyle}}"))) ).ToString()); } ToggleSelectedOnly = Template.FindName("PART_ToggleSelectedOnly", this) as ToggleButton; if (ToggleSelectedOnly != null) { BindingTools.SetBinding( ToggleSelectedOnly, ToggleButton.IsCheckedProperty, this, nameof(ItemsList) + "." + nameof(ItemsList.ShowSelectedOnly), BindingMode.TwoWay); } ElemSearchHost = Template.FindName("PART_ElemSearchHost", this) as FrameworkElement; if (ElemSearchHost != null) { BindingTools.SetBinding( ElemSearchHost, FrameworkElement.VisibilityProperty, this, nameof(ShowSearchBox), converter: new Converters.BooleanToVisibilityConverter()); } TextSearchBox = Template.FindName("PART_SearchBox", this) as SearchBox; if (TextSearchBox != null) { BindingTools.SetBinding( TextSearchBox, SearchBox.TextProperty, this, nameof(ItemsList) + "." + nameof(ItemsList.SearchText), BindingMode.TwoWay); } UpdateTextStatus(); if (Template.FindName("Popup", this) is Popup popup) { popup.Opened += (_, __) => { if (ShowSearchBox && TextSearchBox != null) { TextSearchBox.IsFocused = true; } }; popup.Closed += (_, __) => { if (ShowSearchBox && TextSearchBox != null) { TextSearchBox.IsFocused = false; TextSearchBox.Text = string.Empty; } }; popup.PreviewKeyUp += (_, keyArgs) => { if (keyArgs.Key == Key.Escape) { popup.IsOpen = false; } }; } } public override void ClearButtonAction(RoutedEventArgs e) { ItemsList.SetSelection(null); BoundIds = null; } private void UpdateTextStatus() { if (TextFilterString != null) { TextFilterString.Text = ItemsList?.AsString; } } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/ExpanderEx.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace Playnite.DesktopApp.Controls { public class ExpanderEx : Expander { private bool ignoreChanges = false; private readonly PlayniteSettings settings; public string SaveGameGroupId { get { return (string)GetValue(SaveGameGroupIdProperty); } set { SetValue(SaveGameGroupIdProperty, value); } } public static readonly DependencyProperty SaveGameGroupIdProperty = DependencyProperty.Register( nameof(SaveGameGroupId), typeof(string), typeof(ExpanderEx), new FrameworkPropertyMetadata(null)); static ExpanderEx() { DefaultStyleKeyProperty.OverrideMetadata(typeof(ExpanderEx), new FrameworkPropertyMetadata(typeof(ExpanderEx))); } public ExpanderEx() : this(PlayniteApplication.Current?.AppSettings) { } public ExpanderEx(PlayniteSettings settings) : base() { if (settings == null || DesignerProperties.GetIsInDesignMode(this)) { return; } this.settings = settings; Loaded += ExpanderEx_Loaded; Unloaded += ExpanderEx_Unloaded; } private void ViewSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (ignoreChanges) { return; } if (e.PropertyName == nameof(ViewSettings.CollapsedGroups)) { if (SaveGameGroupId != null && settings != null) { ignoreChanges = true; IsExpanded = !settings.ViewSettings.IsGroupCollapsed(settings.ViewSettings.GroupingOrder, SaveGameGroupId); ignoreChanges = false; } } } private void ExpanderEx_Loaded(object sender, RoutedEventArgs e) { Expanded += ExpanderEx_Expanded; Collapsed += ExpanderEx_Collapsed; if (SaveGameGroupId != null && settings != null) { settings.ViewSettings.PropertyChanged += ViewSettings_PropertyChanged; ignoreChanges = true; var newState = !settings.ViewSettings.IsGroupCollapsed(settings.ViewSettings.GroupingOrder, SaveGameGroupId); if (newState != IsExpanded) { IsExpanded = newState; } ignoreChanges = false; } } private void ExpanderEx_Unloaded(object sender, RoutedEventArgs e) { Expanded -= ExpanderEx_Expanded; Collapsed -= ExpanderEx_Collapsed; if (SaveGameGroupId != null && settings != null) { settings.ViewSettings.PropertyChanged -= ViewSettings_PropertyChanged; } } private void ExpanderEx_Collapsed(object sender, RoutedEventArgs e) { if (ignoreChanges) { return; } if (SaveGameGroupId != null && settings != null) { ignoreChanges = true; settings.ViewSettings.SetGroupCollapseState(settings.ViewSettings.GroupingOrder, SaveGameGroupId, true); ignoreChanges = false; } } private void ExpanderEx_Expanded(object sender, RoutedEventArgs e) { if (ignoreChanges) { return; } if (SaveGameGroupId != null && settings != null) { ignoreChanges = true; settings.ViewSettings.SetGroupCollapseState(settings.ViewSettings.GroupingOrder, SaveGameGroupId, false); ignoreChanges = false; } } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/FilterEnumSelectionBox.cs ================================================ using Playnite.Common; using Playnite.DesktopApp.ViewModels; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Xml.Linq; namespace Playnite.DesktopApp.Controls { public class FilterEnumSelectionBox : FilterSelectionBoxBase { public List> ItemsList { get; set; } public class SelectionObject { public string Name { get; } public int Value { get; } public SelectionObject(Enum enumValue) { Value = Convert.ToInt32(enumValue); Name = enumValue.GetDescription(); } public override string ToString() { return Name; } } public Type EnumType { get { return (Type)GetValue(EnumTypeProperty); } set { SetValue(EnumTypeProperty, value); } } public static readonly DependencyProperty EnumTypeProperty = DependencyProperty.Register( nameof(EnumType), typeof(Type), typeof(FilterEnumSelectionBox), new PropertyMetadata(null, EnumTypePropertyChangedCallback)); private static void EnumTypePropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as FilterEnumSelectionBox; var list = (Type)e.NewValue; var items = new List>(); if (obj.ItemsList.HasItems()) { foreach (var item in obj.ItemsList) { item.PropertyChanged -= obj.NewItem_PropertyChanged; } } foreach (Enum en in list.GetEnumValues()) { var newItem = new SelectableItem(new SelectionObject(en)); if (obj.FilterProperties != null) { newItem.Selected = obj.FilterProperties.Values?.Contains(newItem.Item.Value) == true; } newItem.PropertyChanged += obj.NewItem_PropertyChanged; items.Add(newItem); } obj.ItemsList = items; } private void NewItem_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (IgnoreChanges) { return; } if (e.PropertyName == nameof(SelectableItem.Selected)) { var selected = ItemsList.Where(a => a.Selected == true); if (selected.HasItems()) { FilterProperties = new EnumFilterItemProperties(selected.Select(a => a.Item.Value).ToList()); } else { FilterProperties = null; } } } public EnumFilterItemProperties FilterProperties { get { return (EnumFilterItemProperties)GetValue(FilterPropertiesProperty); } set { SetValue(FilterPropertiesProperty, value); } } public static readonly DependencyProperty FilterPropertiesProperty = DependencyProperty.Register( nameof(FilterProperties), typeof(EnumFilterItemProperties), typeof(FilterEnumSelectionBox), new PropertyMetadata(null, FilterPropertiesPropertyChangedCallback)); private static void FilterPropertiesPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as FilterEnumSelectionBox; if (obj.IgnoreChanges) { return; } obj.IgnoreChanges = true; if (obj.FilterProperties?.IsSet != true) { obj.ItemsList?.ForEach(a => a.Selected = false); } else { obj.ItemsList?.ForEach(a => a.Selected = obj.FilterProperties.Values.Contains(a.Item.Value)); } obj.IgnoreChanges = false; obj.UpdateTextStatus(); } static FilterEnumSelectionBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(FilterEnumSelectionBox), new FrameworkPropertyMetadata(typeof(FilterEnumSelectionBox))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); UpdateTextStatus(); } private void UpdateTextStatus() { if (TextFilterString != null) { if (ItemsList.HasItems()) { TextFilterString.Text = string.Join(", ", ItemsList.Where(a => a.Selected == true).Select(a => a.Item.Name).ToArray()); } else { TextFilterString.Text = string.Empty; } } } public override void ClearButtonAction(RoutedEventArgs e) { FilterProperties = null; } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/FilterSelectionBox.cs ================================================ using Playnite.Common; using Playnite.Converters; using Playnite.DesktopApp.ViewModels; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Xml.Linq; namespace Playnite.DesktopApp.Controls { [TemplatePart(Name = "PART_TextFilterInput", Type = typeof(TextBox))] public abstract class FilterSelectionBoxBase : ComboBoxListBase { internal TextBox TextFilterInput; public bool IsFullTextEnabled { get => (bool)GetValue(IsFullTextEnabledProperty); set => SetValue(IsFullTextEnabledProperty, value); } public static readonly DependencyProperty IsFullTextEnabledProperty = DependencyProperty.Register( nameof(IsFullTextEnabled), typeof(bool), typeof(FilterSelectionBoxBase), new PropertyMetadata(false)); public override void OnApplyTemplate() { base.OnApplyTemplate(); TextFilterInput = Template.FindName("PART_TextFilterInput", this) as TextBox; if (ItemsPanel != null) { XNamespace pns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; ItemsPanel.ItemTemplate = Xaml.FromString(new XDocument( new XElement(pns + nameof(DataTemplate), new XElement(pns + nameof(CheckBox), new XAttribute(nameof(CheckBox.IsChecked), "{Binding Selected}"), new XAttribute(nameof(CheckBox.Content), "{Binding Item}"), new XAttribute(nameof(CheckBox.Style), $"{{DynamicResource FilterSelectionBoxItemStyle}}"))) ).ToString()); } if (TextFilterInput != null) { BindingTools.SetBinding( TextFilterInput, TextBox.VisibilityProperty, this, nameof(IsFullTextEnabled), converter: new Converters.BooleanToVisibilityConverter()); } if (TextFilterString != null) { BindingTools.SetBinding( TextFilterString, TextBox.VisibilityProperty, this, nameof(IsFullTextEnabled), converter: new InvertedBooleanToVisibilityConverter()); } } } public class FilterSelectionBox : FilterSelectionBoxBase { private BindingExpressionBase textInputBinding; public SelectableIdItemList ItemsList { get { return (SelectableIdItemList)GetValue(ItemsListProperty); } set { SetValue(ItemsListProperty, value); } } public static readonly DependencyProperty ItemsListProperty = DependencyProperty.Register( nameof(ItemsList), typeof(SelectableIdItemList), typeof(FilterSelectionBox), new PropertyMetadata(null, ItemsListPropertyChangedCallback)); private static void ItemsListPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var box = sender as FilterSelectionBox; if (box.IgnoreChanges) { return; } var oldVal = (SelectableIdItemList)e.OldValue; if (oldVal != null) { oldVal.SelectionChanged -= box.List_SelectionChanged; } var list = (SelectableIdItemList)e.NewValue; box.IgnoreChanges = true; list.SelectionChanged += box.List_SelectionChanged; if (box.FilterProperties != null) { list.SetSelection(box.FilterProperties.Ids); } box.UpdateTextStatus(); box.IgnoreChanges = false; } public void List_SelectionChanged(object sender, EventArgs e) { if (!IgnoreChanges) { IgnoreChanges = true; FilterProperties = new IdItemFilterItemProperties { Ids = ItemsList.GetSelectedIds() }; UpdateTextStatus(); IgnoreChanges = false; } } public IdItemFilterItemProperties FilterProperties { get { return (IdItemFilterItemProperties)GetValue(FilterPropertiesProperty); } set { SetValue(FilterPropertiesProperty, value); } } public static readonly DependencyProperty FilterPropertiesProperty = DependencyProperty.Register( nameof(FilterProperties), typeof(IdItemFilterItemProperties), typeof(FilterSelectionBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, FilterPropertiesPropertyChangedCallback)); private static void FilterPropertiesPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var box = sender as FilterSelectionBox; if (box.IgnoreChanges) { return; } box.IgnoreChanges = true; if (box.FilterProperties != null && box.FilterProperties.Text.IsNullOrEmpty()) { box.ItemsList?.SetSelection(box.FilterProperties.Ids); } else if (box.FilterProperties == null) { box.ItemsList?.SetSelection(null); } box.UpdateTextStatus(); box.IgnoreChanges = false; } public string FullTextText { get { if (FilterProperties == null) { return null; } if (FilterProperties.Text.IsNullOrEmpty()) { return ItemsList?.AsString; } else { return FilterProperties.Text; } } set { if (!IgnoreChanges) { FilterProperties = new IdItemFilterItemProperties() { Text = value }; if (ItemsList != null) { IgnoreChanges = true; ItemsList.SetSelection(null); IgnoreChanges = false; } } UpdateTextStatus(); } } static FilterSelectionBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(FilterSelectionBox), new FrameworkPropertyMetadata(typeof(FilterSelectionBox))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); if (TextFilterInput != null) { textInputBinding = BindingTools.SetBinding( TextFilterInput, TextBox.TextProperty, this, nameof(FullTextText), delay: 200, trigger: System.Windows.Data.UpdateSourceTrigger.PropertyChanged, mode: BindingMode.TwoWay); } UpdateTextStatus(); } public override void ClearButtonAction(RoutedEventArgs e) { FilterProperties = null; IgnoreChanges = true; ItemsList?.SetSelection(null); IgnoreChanges = false; } private void UpdateTextStatus() { if (TextFilterString != null) { TextFilterString.Text = FullTextText; } if (textInputBinding != null) { textInputBinding.UpdateTarget(); } } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/FilterStringSelectionBox.cs ================================================ using Playnite.Common; using Playnite.DesktopApp.ViewModels; using Playnite.SDK; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; namespace Playnite.DesktopApp.Controls { public class FilterStringSelectionBox : FilterSelectionBoxBase { public SelectableObjectList> ItemsList { get { return (SelectableObjectList>)GetValue(ItemsListProperty); } set { SetValue(ItemsListProperty, value); } } public static readonly DependencyProperty ItemsListProperty = DependencyProperty.Register( nameof(ItemsList), typeof(SelectableObjectList>), typeof(FilterStringSelectionBox), new PropertyMetadata(null, ItemsListPropertyChangedCallback)); private static void ItemsListPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as FilterStringSelectionBox; var oldVal = (SelectableObjectList>)e.NewValue; if (oldVal != null) { oldVal.SelectionChanged -= obj.List_SelectionChanged; } var list = (SelectableObjectList>)e.NewValue; obj.IgnoreChanges = true; list.SelectionChanged += obj.List_SelectionChanged; if (obj.FilterProperties != null) { list.SetSelection(obj.FilterProperties.Values?.Select(a => new NamedObject(a))); } obj.IgnoreChanges = false; obj.UpdateTextStatus(); } private void List_SelectionChanged(object sender, EventArgs e) { if (!IgnoreChanges) { IgnoreChanges = true; FilterProperties = new StringFilterItemProperties(ItemsList.GetSelectedItems().Select(a => a.Value).ToList()); IgnoreChanges = false; UpdateTextStatus(); } } public StringFilterItemProperties FilterProperties { get { return (StringFilterItemProperties)GetValue(FilterPropertiesProperty); } set { SetValue(FilterPropertiesProperty, value); } } public static readonly DependencyProperty FilterPropertiesProperty = DependencyProperty.Register( nameof(FilterProperties), typeof(StringFilterItemProperties), typeof(FilterStringSelectionBox), new PropertyMetadata(null, FilterPropertiesPropertyChangedCallback)); private static void FilterPropertiesPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as FilterStringSelectionBox; if (obj.IgnoreChanges) { return; } obj.IgnoreChanges = true; if (obj.FilterProperties?.IsSet != true) { obj.ItemsList?.SetSelection(null); } else { obj.ItemsList?.SetSelection(obj.FilterProperties.Values?.Select(a => new NamedObject(a))); } obj.IgnoreChanges = false; obj.UpdateTextStatus(); } static FilterStringSelectionBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(FilterStringSelectionBox), new FrameworkPropertyMetadata(typeof(FilterStringSelectionBox))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); UpdateTextStatus(); } private void UpdateTextStatus() { if (TextFilterString != null) { TextFilterString.Text = ItemsList?.AsString; } } public override void ClearButtonAction(RoutedEventArgs e) { FilterProperties = null; } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/GameListItem.cs ================================================ using Playnite.Commands; using Playnite.Common; using Playnite.Controls; using Playnite.Converters; using Playnite.DesktopApp.ViewModels; using Playnite.Extensions; using Playnite.SDK; using Playnite.ViewModels; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using BooleanToVisibilityConverter = Playnite.Converters.BooleanToVisibilityConverter; namespace Playnite.DesktopApp.Controls { [TemplatePart(Name = "PART_PanelHost", Type = typeof(FrameworkElement))] [TemplatePart(Name = "PART_ImageIcon", Type = typeof(Image))] [TemplatePart(Name = "PART_ImageCover", Type = typeof(Image))] [TemplatePart(Name = "PART_ButtonPlay", Type = typeof(Button))] [TemplatePart(Name = "PART_ButtonInfo", Type = typeof(Button))] public class GameListItem : Control { private readonly DesktopAppViewModel mainModel; private FrameworkElement PanelHost; private Image ImageIcon; private Image ImageCover; private Button ButtonPlay; private Button ButtonInfo; static GameListItem() { DefaultStyleKeyProperty.OverrideMetadata(typeof(GameListItem), new FrameworkPropertyMetadata(typeof(GameListItem))); } public GameListItem() : this(DesktopApplication.Current?.MainModel) { } public GameListItem(DesktopAppViewModel mainModel) { if (DesignerProperties.GetIsInDesignMode(this)) { this.mainModel = DesignMainViewModel.DesignIntance; } else if (mainModel != null) { this.mainModel = mainModel; } } public override void OnApplyTemplate() { base.OnApplyTemplate(); PanelHost = Template.FindName("PART_PanelHost", this) as FrameworkElement; if (PanelHost != null) { if (!DesignerProperties.GetIsInDesignMode(this)) { var mBinding = new MouseBinding(mainModel.StartGameCommand, new MouseGesture(MouseAction.LeftDoubleClick)); BindingTools.SetBinding(mBinding, MouseBinding.CommandParameterProperty, nameof(GamesCollectionViewEntry.Game)); PanelHost.InputBindings.Add(mBinding); PanelHost.ContextMenu = new GameMenu(mainModel) { ShowStartSection = true }; BindingTools.SetBinding(PanelHost.ContextMenu, Button.DataContextProperty, mainModel, nameof(DesktopAppViewModel.SelectedGames)); } } ImageIcon = Template.FindName("PART_ImageIcon", this) as Image; if (ImageIcon != null) { BindingTools.SetBinding(ImageIcon, Image.VisibilityProperty, mainModel.AppSettings, nameof(PlayniteSettings.ShowIconsOnList), converter: new BooleanToVisibilityConverter()); var sourceBinding = new PriorityBinding(); sourceBinding.Bindings.Add(new Binding() { Path = new PropertyPath(nameof(GamesCollectionViewEntry.DetailsListIconObjectCached)), IsAsync = mainModel.AppSettings.AsyncImageLoading, Converter = new NullToDependencyPropertyUnsetConverter(), Mode = BindingMode.OneWay }); sourceBinding.Bindings.Add(new Binding() { Path = new PropertyPath(nameof(GamesCollectionViewEntry.DefaultDetailsListIconObjectCached)), Converter = new NullToDependencyPropertyUnsetConverter(), Mode = BindingMode.OneWay }); BindingOperations.SetBinding(ImageIcon, Image.SourceProperty, sourceBinding); } ImageCover = Template.FindName("PART_ImageCover", this) as Image; if (ImageCover != null) { var sourceBinding = new PriorityBinding(); sourceBinding.Bindings.Add(new Binding() { Path = new PropertyPath(nameof(GamesCollectionViewEntry.GridViewCoverObjectCached)), IsAsync = mainModel.AppSettings.AsyncImageLoading, Converter = new NullToDependencyPropertyUnsetConverter(), Mode = BindingMode.OneWay }); sourceBinding.Bindings.Add(new Binding() { Path = new PropertyPath(nameof(GamesCollectionViewEntry.DefaultGridViewCoverObjectCached)), Converter = new NullToDependencyPropertyUnsetConverter(), Mode = BindingMode.OneWay }); BindingOperations.SetBinding(ImageCover, Image.SourceProperty, sourceBinding); } ButtonPlay = Template.FindName("PART_ButtonPlay", this) as Button; if (ButtonPlay != null) { ButtonPlay.Command = mainModel.StartGameCommand; BindingTools.SetBinding(ButtonPlay, Button.CommandParameterProperty, nameof(GamesCollectionViewEntry.Game)); } ButtonInfo = Template.FindName("PART_ButtonInfo", this) as Button; if (ButtonInfo != null) { ButtonInfo.Command = mainModel.ShowGameSideBarCommand; BindingTools.SetBinding(ButtonInfo, Button.CommandParameterProperty, string.Empty); } ControlTemplateTools.InitializePluginControls( mainModel.Extensions, Template, this, ApplicationMode.Desktop, this, $"DataContext.{nameof(GamesCollectionViewEntry.Game)}"); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/GameTaskView.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Controls/GameTaskView.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Microsoft.Win32; using Playnite.SDK.Models; using System.ComponentModel; using Playnite.Common; using Playnite.Emulators; namespace Playnite.DesktopApp.Controls { /// /// Interaction logic for GameTaskView.xaml /// public partial class GameTaskView : UserControl, INotifyPropertyChanged { public bool IsCurrentEmulatorProfileCustom => GameTask.EmulatorProfileId?.StartsWith(CustomEmulatorProfile.ProfilePrefix) == true; public bool CanOverrideArgs { get { if (GameTask.EmulatorProfileId?.StartsWith(CustomEmulatorProfile.ProfilePrefix) == true) { return true; } else { var emu = Emulators.FirstOrDefault(a => a.Id == GameTask.EmulatorId); if (emu == null) { return false; } var emuProf = emu.BuiltinProfiles?.FirstOrDefault(a => a.Id == GameTask.EmulatorProfileId); if (emuProf != null) { var def = Emulation.GetProfile(emu.BuiltInConfigId, emuProf.BuiltInProfileName); if (def?.ScriptStartup == false) { return true; } } } return false; } } public bool ShowCustomEmulatorArgsRow { get { if (GameTask == null) { return false; } return GameTask.Type == GameActionType.Emulator && GameTask.OverrideDefaultArgs && CanOverrideArgs; } } public bool ShowArgumentsRow { get { if (GameTask == null) { return false; } return GameTask.Type == GameActionType.File; } } public bool ShowAdditionalArgumentsRow { get { if (GameTask == null) { return false; } else if (GameTask.Type == GameActionType.Emulator && !CanOverrideArgs) { return false; } else if (GameTask.Type == GameActionType.Emulator && !GameTask.OverrideDefaultArgs) { return true; } return false; } } public bool ShowDefaultArgumentsRow { get { if (GameTask == null) { return false; } if (GameTask.Type == GameActionType.Emulator && !CanOverrideArgs) { return false; } else if (GameTask.Type == GameActionType.Emulator && !GameTask.OverrideDefaultArgs) { return true; } return false; } } public bool ShowPathRow { get { if (GameTask == null) { return false; } return GameTask.Type != GameActionType.Emulator; } } public bool ShowWorkingDirRow { get { if (GameTask == null) { return false; } return GameTask.Type == GameActionType.File; } } public bool ShowEmulatorRow { get { if (GameTask == null) { return false; } return GameTask.Type == GameActionType.Emulator; } } public bool ShowOverrideArgsRow { get { if (GameTask == null) { return false; } if (GameTask.Type == GameActionType.Emulator && !CanOverrideArgs) { return false; } return GameTask.Type == GameActionType.Emulator; } } public bool ShowTrackingPathRow { get { if (GameTask == null) { return false; } return (GameTask.TrackingMode == TrackingMode.Directory || GameTask.TrackingMode == TrackingMode.ProcessName) && GameTask.Type != GameActionType.Emulator; } } public bool ShowTrackingModeRow { get { if (GameTask == null) { return false; } return (GameTask.Type == GameActionType.File || GameTask.Type == GameActionType.URL) && GameTask.IsPlayAction; } } public bool ShowTrackingTimeRows { get { if (GameTask == null) { return false; } return (GameTask.TrackingMode == TrackingMode.Directory || GameTask.TrackingMode == TrackingMode.ProcessName) && GameTask.Type != GameActionType.Emulator; } } public bool ShowScriptInput { get { return GameTask?.Type == GameActionType.Script; } } public GameAction GameTask { get { if (DataContext == null) { return null; } else { return ((GameAction)DataContext); } } } private string selectedEmulatorArguments; public string SelectedEmulatorArguments { get => selectedEmulatorArguments; set { selectedEmulatorArguments = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedEmulatorArguments))); } } public List Emulators { get { return (List)GetValue(EmulatorsProperty); } set { SetValue(EmulatorsProperty, value); } } public static readonly DependencyProperty EmulatorsProperty = DependencyProperty.Register(nameof(Emulators), typeof(List), typeof(GameTaskView)); public bool ShowNameRow { get { return (bool)GetValue(ShowNameRowProperty); } set { SetValue(ShowNameRowProperty, value); } } public static readonly DependencyProperty ShowNameRowProperty = DependencyProperty.Register(nameof(ShowNameRow), typeof(bool), typeof(GameTaskView)); public string DefaultSelectionDir { get { return (string)GetValue(DefaultSelectionDirProperty); } set { SetValue(DefaultSelectionDirProperty, value); } } public static readonly DependencyProperty DefaultSelectionDirProperty = DependencyProperty.Register(nameof(DefaultSelectionDir), typeof(string), typeof(GameTaskView)); public event PropertyChangedEventHandler PropertyChanged; public GameTaskView() { InitializeComponent(); } public void OnPropertyChanged(string name) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } private void ButtonBrowsePath_Click(object sender, RoutedEventArgs e) { var path = SystemDialogs.SelectFile(Window.GetWindow(this), "*.*|*.*", DefaultSelectionDir); if (string.IsNullOrEmpty(path)) { return; } TextPath.Text = path; } private void ComboType_SelectionChanged(object sender, SelectionChangedEventArgs e) { NotifyRowChange(); } private void ComboTrackingMode_SelectionChanged(object sender, SelectionChangedEventArgs e) { OnPropertyChanged(nameof(ShowTrackingPathRow)); OnPropertyChanged(nameof(ShowTrackingTimeRows)); } private void NotifyRowChange() { OnPropertyChanged(nameof(ShowArgumentsRow)); OnPropertyChanged(nameof(ShowAdditionalArgumentsRow)); OnPropertyChanged(nameof(ShowDefaultArgumentsRow)); OnPropertyChanged(nameof(ShowPathRow)); OnPropertyChanged(nameof(ShowWorkingDirRow)); OnPropertyChanged(nameof(ShowEmulatorRow)); OnPropertyChanged(nameof(ShowOverrideArgsRow)); OnPropertyChanged(nameof(ShowCustomEmulatorArgsRow)); OnPropertyChanged(nameof(ShowTrackingModeRow)); OnPropertyChanged(nameof(ShowScriptInput)); } private void CheckOverrideArgs_Checked(object sender, RoutedEventArgs e) { if (GameTask == null) { return; } NotifyRowChange(); if (GameTask.OverrideDefaultArgs && !SelectedEmulatorArguments.IsNullOrEmpty() && GameTask.Arguments.IsNullOrEmpty()) { GameTask.Arguments = $"{SelectedEmulatorArguments} {GameTask.AdditionalArguments}".Trim(); } } private void ComboEmulator_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (GameTask == null) { return; } if (Emulators == null || Emulators.Count == 0) { return; } if (GameTask?.EmulatorId != Guid.Empty && Emulators.Any(a => a.Id == GameTask?.EmulatorId)) { ComboEmulatorConfig.SelectedItem = Emulators.First(a => a.Id == GameTask.EmulatorId).CustomProfiles?.FirstOrDefault(); } } private void ComboEmulatorConfig_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (GameTask == null) { return; } if (Emulators == null || Emulators.Count == 0) { SelectedEmulatorArguments = string.Empty; return; } if (GameTask?.EmulatorId != Guid.Empty && Emulators.Any(a => a.Id == GameTask?.EmulatorId)) { var emulator = Emulators.First(a => a.Id == GameTask.EmulatorId); var emulatorProfile = emulator.AllProfiles?.FirstOrDefault(a => a.Id == GameTask.EmulatorProfileId); if (emulatorProfile == null) { SelectedEmulatorArguments = string.Empty; } else { if (emulatorProfile is CustomEmulatorProfile customProfile) { SelectedEmulatorArguments = customProfile.Arguments; } else if (emulatorProfile is BuiltInEmulatorProfile builtInProfile) { if (builtInProfile.OverrideDefaultArgs) { SelectedEmulatorArguments = builtInProfile.CustomArguments; } else { var def = Emulation.GetProfile(emulator.BuiltInConfigId, builtInProfile.BuiltInProfileName); if (def?.ScriptStartup == false) { SelectedEmulatorArguments = def.StartupArguments; } else { SelectedEmulatorArguments = string.Empty; } } } else { SelectedEmulatorArguments = string.Empty; } } } else { SelectedEmulatorArguments = string.Empty; } NotifyRowChange(); } private void CheckBox_Checked(object sender, RoutedEventArgs e) { NotifyRowChange(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/GamesGridView.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Controls/GamesGridView.xaml.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Playnite.Database; using Playnite.SDK.Models; using Playnite; using System.ComponentModel; using Playnite.Settings; namespace Playnite.DesktopApp.Controls { public class GamesGridViewColumn : GridViewColumn { public GameField Field { get; set; } public SortOrder? SortOrder { get; set; } public GamesGridViewColumn() : base() { } } /// /// Interaction logic for GamesGridView.xaml /// public partial class GamesGridView : UserControl { internal bool ignoreSelectedItemsListChanges = false; public IList SelectedItemsList { get { return (IList)GetValue(SelectedItemsListProperty); } set { SetValue(SelectedItemsListProperty, value); } } public static readonly DependencyProperty SelectedItemsListProperty = DependencyProperty.Register(nameof(SelectedItemsList), typeof(IList), typeof(GamesGridView), new PropertyMetadata(null, SelectedItemsListChanged)); public IEnumerable ItemsSource { get { return (IEnumerable)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(IEnumerable), typeof(GamesGridView)); public PlayniteSettings AppSettings { get { return (PlayniteSettings)GetValue(AppSettingsProperty); } set { SetValue(AppSettingsProperty, value); } } public static readonly DependencyProperty AppSettingsProperty = DependencyProperty.Register(nameof(AppSettings), typeof(PlayniteSettings), typeof(GamesGridView)); private bool ignoreColumnChanges = false; private bool initialized = false; public GamesGridView() { InitializeComponent(); GridGames.SelectionChanged += GridGames_SelectionChanged; ActualGridView.Columns.CollectionChanged += Columns_CollectionChanged; Loaded += GamesGridView_Loaded; } private void GamesGridView_Loaded(object sender, RoutedEventArgs e) { if (initialized) { return; } initialized = true; InitializeColumns(); AppSettings.ViewSettings.ListViewColumns.Added.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.AgeRating.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Categories.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.CommunityScore.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.CompletionStatus.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.CriticScore.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Developers.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Features.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Genres.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Icon.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.InstallDirectory.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.IsInstalled.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.LastActivity.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.RecentActivity.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Modified.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Name.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Platform.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.PlayCount.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.InstallSize.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Playtime.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.PluginId.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Publishers.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Region.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.ReleaseDate.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Series.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Source.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Tags.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.UserScore.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Version.PropertyChanged += ListViewColumn_PropertyChanged; AppSettings.ViewSettings.ListViewColumns.Roms.PropertyChanged += ListViewColumn_PropertyChanged; } private void ListViewColumn_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(ListViewColumnProperty.Visible)) { var prop = sender as ListViewColumnProperty; if (AppSettings.ViewSettings.ListViewColumsOrder.Contains(prop.Field) && !prop.Visible) { var column = ActualGridView.Columns.FirstOrDefault(a => ((GamesGridViewColumn)a).Field == prop.Field); if (column != null) { ActualGridView.Columns.Remove(column); } } else if (!AppSettings.ViewSettings.ListViewColumsOrder.Contains(prop.Field) && prop.Visible) { var newColumn = GetColumn(prop.Field); if (newColumn != null) { ActualGridView.Columns.Add(newColumn); } } } } public static void SelectedItemsListChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var list = (GamesGridView)d; if (list.ignoreSelectedItemsListChanges || list.GridGames.SelectionMode == SelectionMode.Single) { return; } list.GridGames.SelectedItems.Clear(); var newValues = e.NewValue as IList; if (newValues.HasItems()) { newValues.ForEach(a => list.GridGames.SelectedItems.Add(a)); } } private void InitializeColumns() { ignoreColumnChanges = true; foreach (var field in AppSettings.ViewSettings.ListViewColumsOrder) { var newColumn = GetColumn(field); if (newColumn != null) { ActualGridView.Columns.Add(newColumn); } } ignoreColumnChanges = false; } private void Columns_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (ignoreColumnChanges) { return; } AppSettings.ViewSettings.ListViewColumsOrder = ActualGridView.Columns.Select(a => ((GamesGridViewColumn)a).Field).ToList(); } private GamesGridViewColumn GetColumn(GameField field) { GamesGridViewColumn newColumn = null; if (field == GameField.Icon) { newColumn = CreateColumn(field, null, "CellTemplateIcon", null); } else if (field == GameField.Name) { newColumn = CreateColumn(field, SortOrder.Name, "CellTemplateName", "HeaderTemplateName"); } else if (field == GameField.Platforms) { newColumn = CreateColumn(field, SortOrder.Platforms, "CellTemplatePlatform", "HeaderTemplatePlatform", "Platform"); } else if (field == GameField.PluginId) { newColumn = CreateColumn(field, SortOrder.Library, "CellTemplateLibrary", "HeaderTemplateLibrary"); } else if (field == GameField.Developers) { newColumn = CreateColumn(field, SortOrder.Developers, "CellTemplateDevelopers", "HeaderTemplateDevelopers"); } else if (field == GameField.Publishers) { newColumn = CreateColumn(field, SortOrder.Publishers, "CellTemplatePublishers", "HeaderTemplatePublishers"); } else if (field == GameField.ReleaseDate) { newColumn = CreateColumn(field, SortOrder.ReleaseDate, "CellTemplateReleaseDate", "HeaderTemplateReleaseDate"); } else if (field == GameField.Genres) { newColumn = CreateColumn(field, SortOrder.Genres, "CellTemplateGenres", "HeaderTemplateGenres"); } else if (field == GameField.Categories) { newColumn = CreateColumn(field, SortOrder.Categories, "CellTemplateCategories", "HeaderTemplateCategories"); } else if (field == GameField.Features) { newColumn = CreateColumn(field, SortOrder.Features, "CellTemplateFeatures", "HeaderTemplateFeatures"); } else if (field == GameField.Tags) { newColumn = CreateColumn(field, SortOrder.Tags, "CellTemplateTags", "HeaderTemplateTags"); } else if (field == GameField.IsInstalled) { newColumn = CreateColumn(field, SortOrder.IsInstalled, "CellTemplateIsInstalled", "HeaderTemplateIsInstalled"); } else if (field == GameField.InstallDirectory) { newColumn = CreateColumn(field, SortOrder.InstallDirectory, "CellTemplateInstallDirectory", "HeaderTemplateInstallDirectory"); } else if (field == GameField.LastActivity) { newColumn = CreateColumn(field, SortOrder.LastActivity, "CellTemplateLastActivity", "HeaderTemplateLastActivity"); } else if (field == GameField.Playtime) { newColumn = CreateColumn(field, SortOrder.Playtime, "CellTemplatePlaytime", "HeaderTemplatePlaytime"); } else if (field == GameField.PlayCount) { newColumn = CreateColumn(field, SortOrder.PlayCount, "CellTemplatePlayCount", "HeaderTemplatePlayCount"); } else if (field == GameField.InstallSize) { newColumn = CreateColumn(field, SortOrder.InstallSize, "CellTemplateInstallSize", "HeaderTemplateInstallSize"); } else if (field == GameField.CompletionStatus) { newColumn = CreateColumn(field, SortOrder.CompletionStatus, "CellTemplateCompletionStatus", "HeaderTemplateCompletionStatus"); } else if (field == GameField.Series) { newColumn = CreateColumn(field, SortOrder.Series, "CellTemplateSeries", "HeaderTemplateSeries"); } else if (field == GameField.Version) { newColumn = CreateColumn(field, SortOrder.Version, "CellTemplateVersion", "HeaderTemplateVersion"); } else if (field == GameField.AgeRatings) { newColumn = CreateColumn(field, SortOrder.AgeRatings, "CellTemplateAgeRating", "HeaderTemplateAgeRating", "AgeRating"); } else if (field == GameField.Regions) { newColumn = CreateColumn(field, SortOrder.Regions, "CellTemplateRegion", "HeaderTemplateRegion", "Region"); } else if (field == GameField.Source) { newColumn = CreateColumn(field, SortOrder.Source, "CellTemplateSource", "HeaderTemplateSource"); } else if (field == GameField.Added) { newColumn = CreateColumn(field, SortOrder.Added, "CellTemplateAdded", "HeaderTemplateAdded"); } else if (field == GameField.Modified) { newColumn = CreateColumn(field, SortOrder.Modified, "CellTemplateModified", "HeaderTemplateModified"); } else if (field == GameField.UserScore) { newColumn = CreateColumn(field, SortOrder.UserScore, "CellTemplateUserScore", "HeaderTemplateUserScore"); } else if (field == GameField.CriticScore) { newColumn = CreateColumn(field, SortOrder.CriticScore, "CellTemplateCriticScore", "HeaderTemplateCriticScore"); } else if (field == GameField.CommunityScore) { newColumn = CreateColumn(field, SortOrder.CommunityScore, "CellTemplateCommunityScore", "HeaderTemplateCommunityScore"); } else if (field == GameField.RecentActivity) { newColumn = CreateColumn(field, SortOrder.RecentActivity, "CellTemplateRecentActivity", "HeaderTemplateRecentActivity"); } else if (field == GameField.Roms) { newColumn = CreateColumn(field, SortOrder.RomList, "CellTemplateRoms", "HeaderTemplateRoms"); } return newColumn; } private GamesGridViewColumn CreateColumn(GameField field, SortOrder? sortOrder, string cellTemplateName, string headerTemplateName, string bindingName = null) { // bindingName is there for backwards compatibility between P8 and P9 var column = new GamesGridViewColumn { Field = field, SortOrder = sortOrder }; BindingOperations.SetBinding( column, GridViewColumn.WidthProperty, new Binding($"{nameof(ViewSettings.ListViewColumns)}.{bindingName ?? field.ToString()}.{nameof(ListViewColumnProperty.Width)}") { Source = AppSettings.ViewSettings, Mode = BindingMode.TwoWay }); column.CellTemplate = Resources[cellTemplateName] as DataTemplate; if (!headerTemplateName.IsNullOrEmpty()) { column.HeaderTemplate = Resources[headerTemplateName] as DataTemplate; } return column; } private void GridGames_SelectionChanged(object sender, SelectionChangedEventArgs e) { ignoreSelectedItemsListChanges = true; SelectedItemsList = (IList)GridGames.SelectedItems; ignoreSelectedItemsListChanges = false; } private void Grid_MouseDoubleClick(object sender, MouseButtonEventArgs e) { if (e.LeftButton != MouseButtonState.Pressed || GridGames.SelectedItems == null || GridGames.SelectedItems.Count == 0) { return; } var entry = (GamesCollectionViewEntry)GridGames.SelectedItems[0]; var game = entry.Game; if (game.IsInstalled) { PlayniteApplication.Current.GamesEditor.PlayGame(game, true); } else { if (game.IsCustomGame) { ((DesktopGamesEditor)DesktopApplication.Current.GamesEditor).EditGame(game); } else { DesktopApplication.Current.GamesEditor.InstallGame(game); } } } private void GridViewColumnHeader_Click(object sender, RoutedEventArgs e) { var header = sender as GridViewColumnHeader; if (header == null) { return; } var column = header.Column as GamesGridViewColumn; if (column == null || column.SortOrder == null) { return; } if (AppSettings.ViewSettings.SortingOrder == column.SortOrder.Value) { AppSettings.ViewSettings.SortingOrderDirection = AppSettings.ViewSettings.SortingOrderDirection == SortOrderDirection.Ascending ? SortOrderDirection.Descending : SortOrderDirection.Ascending; } else { AppSettings.ViewSettings.SortingOrder = column.SortOrder.Value; } } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/GridViewPanel.cs ================================================ using System.Windows.Controls.Primitives; using System.Windows.Controls; using System.Windows; using System.Windows.Media; using System; using System.Diagnostics; using System.Collections.Specialized; using System.ComponentModel; using System.Reflection; using System.Linq; namespace Playnite.DesktopApp.Controls { public class GridViewPanel : VirtualizingPanel, IScrollInfo { private int computedColumns; private double centerMargin; private int itemCount => ((ItemContainerGenerator)ItemContainerGenerator).Items.Count; // Important for grouped virtualization to work protected override bool CanHierarchicallyScrollAndVirtualizeCore => true; private DependencyObject itemsOwner; protected DependencyObject ItemsOwner { get { if (itemsOwner is null) { var getItemsOwnerInternalMethod = typeof(ItemsControl).GetMethod( "GetItemsOwnerInternal", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(DependencyObject) }, null ); itemsOwner = (DependencyObject)getItemsOwnerInternalMethod.Invoke(null, new object[] { this }); } return itemsOwner; } } private IRecyclingItemContainerGenerator itemContainerGenerator; protected new IRecyclingItemContainerGenerator ItemContainerGenerator { get { if (itemContainerGenerator is null) { /* Because of a bug in the framework the ItemContainerGenerator * is null until InternalChildren accessed at least one time. */ var children = InternalChildren; itemContainerGenerator = (IRecyclingItemContainerGenerator)base.ItemContainerGenerator; } return itemContainerGenerator; } } private double cachedItemWith; public double ItemWidth { get { double width = 0; if (InternalChildren.Count > 0) { width = InternalChildren[0].DesiredSize.Width; } if (width > 0) { cachedItemWith = width; return width; } else { return cachedItemWith; } } } private double cachedItemHeight; public double ItemHeight { get { double height = 0; if (InternalChildren.Count > 0) { height = InternalChildren[0].DesiredSize.Height; } if (height > 0) { cachedItemHeight = height; return height; } else { return cachedItemHeight; } } } public GridViewPanel() : base() { } protected override Size MeasureOverride(Size availableSize) { // This is for weird edge case where Measuring can occur and OnItemsChanged was not called by WPF first if (ItemWidth == 0) { var startPosition = ItemContainerGenerator.GeneratorPositionFromIndex(0); using (ItemContainerGenerator.StartAt(startPosition, GeneratorDirection.Forward, true)) { var child = (UIElement)ItemContainerGenerator.GenerateNext(); if (child != null) { AddInternalChild(child); ItemContainerGenerator.PrepareItemContainer(child); child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); } } } double expanderOffset = 0; var groupItem = ItemsOwner as IHierarchicalVirtualizationAndScrollInfo; if (groupItem != null) { // This is a workaround for issues like #2454, caused by some bug in WPF. // Collapsed groups are SOMETIMES treated as expandeded and will force initialization of child items. var expander = ElementTreeHelper.FindVisualChildren(groupItem as GroupItem).FirstOrDefault(); if (expander != null && expander.IsExpanded == false) { UpdateScrollInfo(new Size(0, 0)); CleanUpItems(); return new Size(0, 0); } else { UpdateScrollInfo(groupItem.Constraints.Viewport.Size); } if (expander != null) { var toggle = ElementTreeHelper.FindVisualChildren(groupItem as GroupItem).FirstOrDefault(); if (toggle != null) { expanderOffset = toggle.ActualHeight; } } Offset = groupItem.Constraints.Viewport.Location; } else { UpdateScrollInfo(availableSize); } GetVisibleRange(expanderOffset, out var firstItemIndex, out var lastItemIndex); if (lastItemIndex < 0) { return Extent; } var startPos = ItemContainerGenerator.GeneratorPositionFromIndex(firstItemIndex); var childIndex = (startPos.Offset == 0) ? startPos.Index : startPos.Index + 1; using (ItemContainerGenerator.StartAt(startPos, GeneratorDirection.Forward, true)) { for (int itemIndex = firstItemIndex; itemIndex <= lastItemIndex; ++itemIndex, ++childIndex) { UIElement child = ItemContainerGenerator.GenerateNext(out var newlyRealized) as UIElement; if (child == null) { continue; } if (newlyRealized) { if (childIndex >= InternalChildren.Count) { AddInternalChild(child); } else { InsertInternalChild(childIndex, child); } ItemContainerGenerator.PrepareItemContainer(child); } else if (!InternalChildren.Contains(child)) { InsertInternalChild(childIndex, child); ItemContainerGenerator.PrepareItemContainer(child); } child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); } } CleanUpItems(firstItemIndex, lastItemIndex); return Extent; } protected override Size ArrangeOverride(Size finalSize) { for (int i = 0; i < InternalChildren.Count; i++) { var child = InternalChildren[i]; var itemIndex = ItemContainerGenerator.IndexFromGeneratorPosition(new GeneratorPosition(i, 0)); // Negative index means that child is disconnected item if (itemIndex >= 0) { child.Arrange(GetItemRect(itemIndex)); } } return finalSize; } private Rect GetItemRect(int itemIndex) { if (computedColumns == 0) { return new Rect(); } var column = itemIndex % computedColumns; var row = itemIndex < column ? 0 : (int)Math.Floor(itemIndex / (double)computedColumns); var offset = ItemsOwner is IHierarchicalVirtualizationAndScrollInfo ? 0 : Offset.Y; return new Rect( centerMargin + (column * ItemWidth), (row * ItemHeight) - offset, ItemWidth, ItemHeight); } private void GetVisibleRange(double expanderOffset, out int firstIndex, out int lastIndex) { if (itemCount == 0 || Viewport.Height == 0) { firstIndex = -1; lastIndex = -1; return; } var startRow = 0; double totalHeight = 0; while (true) { if (Offset.Y - expanderOffset > totalHeight + ItemHeight) { totalHeight += ItemHeight; startRow++; } else { break; } } firstIndex = startRow * computedColumns; var newRows = (int)Math.Ceiling(Viewport.Height / ItemHeight) + 1; lastIndex = firstIndex + (newRows * computedColumns); if (lastIndex >= itemCount) { lastIndex = itemCount - 1; } } private void CleanUpItems() { RemoveInternalChildRange(0, InternalChildren.Count - 1); } private void CleanUpItems(int firstIndex, int lastIndex) { for (int i = InternalChildren.Count - 1; i >= 0; i--) { var childGeneratorPos = new GeneratorPosition(i, 0); int itemIndex = ItemContainerGenerator.IndexFromGeneratorPosition(childGeneratorPos); var child = InternalChildren[i]; if ((itemIndex < firstIndex || itemIndex > lastIndex) && itemIndex > 0) { try { ItemContainerGenerator.Recycle(childGeneratorPos, 1); } catch { // There are some weird null-reference crash reports from Generator.Recycle } RemoveInternalChildRange(i, 1); } else if (child.ToString().Contains("{DisconnectedItem}")) { RemoveInternalChildRange(i, 1); } } } protected override void OnItemsChanged(object sender, ItemsChangedEventArgs args) { switch (args.Action) { case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Replace: RemoveInternalChildRange(args.Position.Index, args.ItemUICount); break; case NotifyCollectionChangedAction.Move: RemoveInternalChildRange(args.OldPosition.Index, args.ItemUICount); break; } } internal Size GetExtent() { if (itemCount == 0) { return new Size(0, 0); } if (computedColumns == 0) { return new Size( Viewport.Width, itemCount * ItemHeight); } else { var totalRows = (int)Math.Ceiling(itemCount / (double)computedColumns); return new Size( Viewport.Width, totalRows * ItemHeight); } } private Size Viewport = new Size(0, 0); private Point Offset = new Point(0, 0); private Size Extent = new Size(0, 0); public bool CanVerticallyScroll { get; set; } public bool CanHorizontallyScroll { get; set; } public double ExtentWidth => Extent.Width; public double ExtentHeight => Extent.Height; public double ViewportWidth => Viewport.Width; public double ViewportHeight => Viewport.Height; public double HorizontalOffset => Offset.X; public double VerticalOffset => Offset.Y; public ScrollViewer ScrollOwner { get; set; } internal void UpdateScrollInfo(Size availableSize) { var invalidate = false; if (availableSize != Viewport) { Viewport = availableSize; } var itemWidth = ItemWidth; if (ItemWidth > 0) { computedColumns = (int)Math.Floor(Viewport.Width / itemWidth); centerMargin = (Viewport.Width - (computedColumns * itemWidth)) / 2; } else { computedColumns = 0; centerMargin = 0; } var newExtent = GetExtent(); if (Extent != newExtent) { Extent = newExtent; invalidate = true; } if (Offset.Y > Extent.Height) { Offset.Y = 0; invalidate = true; } if (Offset.X > Extent.Width) { Offset.X = 0; invalidate = true; } if (invalidate) { ScrollOwner?.InvalidateScrollInfo(); } } public void SetHorizontalOffset(double newOffset) { } public void SetVerticalOffset(double newOffset) { if (newOffset < 0 || Viewport.Height >= Extent.Height) { newOffset = 0; } else { if (newOffset + Viewport.Height >= Extent.Height) { newOffset = Extent.Height - Viewport.Height; } } Offset.Y = newOffset; ScrollOwner?.InvalidateScrollInfo(); InvalidateMeasure(); } protected override void BringIndexIntoView(int index) { if (index < 0) { return; } var itemRect = GetItemRect(index); if (itemRect.Y > 0 && itemRect.Bottom < Viewport.Height) { return; } else if (itemRect.Bottom > Viewport.Height) { SetVerticalOffset(Offset.Y + (itemRect.Bottom - Viewport.Height)); return; } else if (itemRect.Y < 0) { SetVerticalOffset(Offset.Y + itemRect.Y); return; } } public Rect MakeVisible(Visual visual, Rect rectangle) { var index = ((ItemContainerGenerator)ItemContainerGenerator).IndexFromContainer(visual); if (index < 0) { return rectangle; } var itemRect = GetItemRect(index); if (itemRect.Y > 0 && itemRect.Bottom < Viewport.Height) { return rectangle; } else if (itemRect.Bottom > Viewport.Height) { SetVerticalOffset(Offset.Y + (itemRect.Bottom - Viewport.Height)); return rectangle; } else if (itemRect.Y < 0) { SetVerticalOffset(Offset.Y + itemRect.Y); return rectangle; } return rectangle; } public void LineLeft() { } public void LineRight() { } public void LineUp() { SetVerticalOffset(VerticalOffset - ItemHeight); } public void LineDown() { SetVerticalOffset(VerticalOffset + ItemHeight); } public void MouseWheelDown() { SetVerticalOffset(VerticalOffset + (ItemHeight / 2)); } public void MouseWheelLeft() { LineLeft(); } public void MouseWheelRight() { LineRight(); } public void MouseWheelUp() { SetVerticalOffset(VerticalOffset - (ItemHeight / 2)); } public void PageLeft() { } public void PageRight() { } public void PageUp() { SetVerticalOffset(VerticalOffset - Viewport.Height); } public void PageDown() { SetVerticalOffset(VerticalOffset + Viewport.Height); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/LibraryStatistics.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Controls/MetadataDownloadSettings.xaml.cs ================================================ using Playnite.Metadata; using Playnite.SDK; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls { /// /// Interaction logic for MetadataDownloadSettings.xaml /// public partial class MetadataDownloadSettings : UserControl, INotifyPropertyChanged { public class MetadataSource : ObservableObject { private bool enabled = true; public bool Enabled { get => enabled; set { enabled = value; OnPropertyChanged(); } } private Guid id = Guid.Empty; public Guid Id { get => id; set { id = value; OnPropertyChanged(); } } private string name; public string Name { get => name; set { name = value; OnPropertyChanged(); } } } public class FieldsSelectionSettings : ObservableObject { public RelayCommand MoveSourceUpCommand { get => new RelayCommand((a) => { var index = Sources.IndexOf(a); if (Sources.Count > 1 && (index - 1) >= 0) { Sources.Remove(a); Sources.Insert(index - 1, a); } }); } public RelayCommand MoveSourceDownCommand { get => new RelayCommand((a) => { var index = Sources.IndexOf(a); if (Sources.Count > 1 && (index + 1) < Sources.Count) { Sources.Remove(a); Sources.Insert(index + 1, a); } }); } public ObservableCollection Sources { get; set; } public string SelectionText { get => string.Join(", ", Sources.Where(a => a.Enabled).Select(a => a.Name).ToArray()); } public event EventHandler SettingsChanged; public FieldsSelectionSettings(ObservableCollection sources) { Sources = sources; Sources.CollectionChanged += (s, e) => { OnSettingsChanged(); }; foreach (var source in Sources) { source.PropertyChanged += (s, e) => { OnSettingsChanged(); }; } } private void OnSettingsChanged() { OnPropertyChanged(nameof(SelectionText)); SettingsChanged?.Invoke(this, EventArgs.Empty); } } #region Setting fields private FieldsSelectionSettings nameSettings; public FieldsSelectionSettings NameSettings { get => nameSettings; set { nameSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings genresSettings; public FieldsSelectionSettings GenresSettings { get => genresSettings; set { genresSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings releaseDateSettings; public FieldsSelectionSettings ReleaseDateSettings { get => releaseDateSettings; set { releaseDateSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings developersSettings; public FieldsSelectionSettings DevelopersSettings { get => developersSettings; set { developersSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings publishersSettings; public FieldsSelectionSettings PublishersSettings { get => publishersSettings; set { publishersSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings tagsSettings; public FieldsSelectionSettings TagsSettings { get => tagsSettings; set { tagsSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings featuresSettings; public FieldsSelectionSettings FeaturesSettings { get => featuresSettings; set { featuresSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings descriptionSettings; public FieldsSelectionSettings DescriptionSettings { get => descriptionSettings; set { descriptionSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings linksSettings; public FieldsSelectionSettings LinksSettings { get => linksSettings; set { linksSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings communityScoreSettings; public FieldsSelectionSettings CommunityScoreSettings { get => communityScoreSettings; set { communityScoreSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings criticScoreSettings; public FieldsSelectionSettings CriticScoreSettings { get => criticScoreSettings; set { criticScoreSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings coverSettings; public FieldsSelectionSettings CoverSettings { get => coverSettings; set { coverSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings iconSettings; public FieldsSelectionSettings IconSettings { get => iconSettings; set { iconSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings backgroundImageSettings; public FieldsSelectionSettings BackgroundImageSettings { get => backgroundImageSettings; set { backgroundImageSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings ageRatingSettings; public FieldsSelectionSettings AgeRatingSettings { get => ageRatingSettings; set { ageRatingSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings seriesSettings; public FieldsSelectionSettings SeriesSettings { get => seriesSettings; set { seriesSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings regionSettings; public FieldsSelectionSettings RegionSettings { get => regionSettings; set { regionSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings platformSettings; public FieldsSelectionSettings PlatformSettings { get => platformSettings; set { platformSettings = value; OnPropertyChanged(); } } private FieldsSelectionSettings installSizeSettings; public FieldsSelectionSettings InstallSizeSettings { get => installSizeSettings; set { installSizeSettings = value; OnPropertyChanged(); } } #endregion Setting fields #region Properties public MetadataDownloaderSettings Settings { get { return (MetadataDownloaderSettings)GetValue(SettingsProperty); } set { SetValue(SettingsProperty, value); } } public static readonly DependencyProperty SettingsProperty = DependencyProperty.Register( nameof(Settings), typeof(MetadataDownloaderSettings), typeof(MetadataDownloadSettings), new PropertyMetadata(new MetadataDownloaderSettings(), SettingsPropertyChangedCallback)); private static void SettingsPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { if (e.NewValue == null || PlayniteApplication.Current == null) { return; } var control = (MetadataDownloadSettings)sender; var settings = e.NewValue as MetadataDownloaderSettings; var plugins = PlayniteApplication.Current.Extensions.MetadataPlugins; control.NameSettings = control.SetupField(settings.Name, MetadataField.Name, plugins); control.NameSettings.SettingsChanged += (_, __) => { control.Settings.Name.Sources = control.NameSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.GenresSettings = control.SetupField(settings.Genre, MetadataField.Genres, plugins); control.GenresSettings.SettingsChanged += (_, __) => { control.Settings.Genre.Sources = control.GenresSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.ReleaseDateSettings = control.SetupField(settings.ReleaseDate, MetadataField.ReleaseDate, plugins); control.ReleaseDateSettings.SettingsChanged += (_, __) => { control.Settings.ReleaseDate.Sources = control.ReleaseDateSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.DevelopersSettings = control.SetupField(settings.Developer, MetadataField.Developers, plugins); control.DevelopersSettings.SettingsChanged += (_, __) => { control.Settings.Developer.Sources = control.DevelopersSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.PublishersSettings = control.SetupField(settings.Publisher, MetadataField.Publishers, plugins); control.PublishersSettings.SettingsChanged += (_, __) => { control.Settings.Publisher.Sources = control.PublishersSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.TagsSettings = control.SetupField(settings.Tag, MetadataField.Tags, plugins); control.TagsSettings.SettingsChanged += (_, __) => { control.Settings.Tag.Sources = control.TagsSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.FeaturesSettings = control.SetupField(settings.Feature, MetadataField.Features, plugins); control.FeaturesSettings.SettingsChanged += (_, __) => { control.Settings.Feature.Sources = control.FeaturesSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.DescriptionSettings = control.SetupField(settings.Description, MetadataField.Description, plugins); control.DescriptionSettings.SettingsChanged += (_, __) => { control.Settings.Description.Sources = control.DescriptionSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.LinksSettings = control.SetupField(settings.Links, MetadataField.Links, plugins); control.LinksSettings.SettingsChanged += (_, __) => { control.Settings.Links.Sources = control.LinksSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.CriticScoreSettings = control.SetupField(settings.CriticScore, MetadataField.CriticScore, plugins); control.CriticScoreSettings.SettingsChanged += (_, __) => { control.Settings.CriticScore.Sources = control.CriticScoreSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.CommunityScoreSettings = control.SetupField(settings.CommunityScore, MetadataField.CommunityScore, plugins); control.CommunityScoreSettings.SettingsChanged += (_, __) => { control.Settings.CommunityScore.Sources = control.CommunityScoreSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.CoverSettings = control.SetupField(settings.CoverImage, MetadataField.CoverImage, plugins); control.CoverSettings.SettingsChanged += (_, __) => { control.Settings.CoverImage.Sources = control.CoverSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.IconSettings = control.SetupField(settings.Icon, MetadataField.Icon, plugins); control.IconSettings.SettingsChanged += (_, __) => { control.Settings.Icon.Sources = control.IconSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.BackgroundImageSettings = control.SetupField(settings.BackgroundImage, MetadataField.BackgroundImage, plugins); control.BackgroundImageSettings.SettingsChanged += (_, __) => { control.Settings.BackgroundImage.Sources = control.BackgroundImageSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.AgeRatingSettings = control.SetupField(settings.AgeRating, MetadataField.AgeRating, plugins); control.AgeRatingSettings.SettingsChanged += (_, __) => { control.Settings.AgeRating.Sources = control.AgeRatingSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.RegionSettings = control.SetupField(settings.Region, MetadataField.Region, plugins); control.RegionSettings.SettingsChanged += (_, __) => { control.Settings.Region.Sources = control.RegionSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.SeriesSettings = control.SetupField(settings.Series, MetadataField.Series, plugins); control.SeriesSettings.SettingsChanged += (_, __) => { control.Settings.Series.Sources = control.SeriesSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.PlatformSettings = control.SetupField(settings.Platform, MetadataField.Platform, plugins); control.PlatformSettings.SettingsChanged += (_, __) => { control.Settings.Platform.Sources = control.PlatformSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; control.InstallSizeSettings = control.SetupField(settings.InstallSize, MetadataField.InstallSize, plugins); control.InstallSizeSettings.SettingsChanged += (_, __) => { control.Settings.InstallSize.Sources = control.InstallSizeSettings.Sources.Where(a => a.Enabled).Select(a => a.Id).ToList(); }; } #endregion Properties private FieldsSelectionSettings allSettings; public FieldsSelectionSettings AllSettings { get => allSettings; set { allSettings = value; OnPropertyChanged(); } } public RelayCommand SetAllPropertiesCommmand => new RelayCommand(() => SetAllProperties()); public RelayCommand SelectAllToImportCommand => new RelayCommand(() => SelectAllSelectionImport(true)); public RelayCommand DeselectAllToImportCommand => new RelayCommand(() => SelectAllSelectionImport(false)); public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged([CallerMemberName]string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } public MetadataDownloadSettings() { InitializeComponent(); if (DesignerProperties.GetIsInDesignMode(this)) { return; } var allSources = new ObservableCollection { new MetadataSource { Id = Guid.Empty, Enabled = false, Name = ResourceProvider.GetString(LOC.MetaSourceStore) }}; foreach (var plugin in PlayniteApplication.Current.Extensions.MetadataPlugins) { allSources.Add(new MetadataSource { Id = plugin.Id, Enabled = false, Name = plugin.Name }); } AllSettings = new FieldsSelectionSettings(allSources); } internal FieldsSelectionSettings SetupField( MetadataFieldSettings settings, MetadataField field, List plugins) { var sources = new ObservableCollection(); var storeAdded = false; foreach (var src in settings.Sources) { if (src == Guid.Empty) { storeAdded = true; sources.Add(new MetadataSource { Id = Guid.Empty, Enabled = true, Name = ResourceProvider.GetString(LOC.MetaSourceStore) }); } else { var plugin = plugins.FirstOrDefault(a => a.Id == src); if (plugin?.SupportedFields.Contains(field) == true) { sources.Add(new MetadataSource { Id = plugin.Id, Enabled = true, Name = plugin.Name }); } } } if (!storeAdded) { sources.Add(new MetadataSource { Id = Guid.Empty, Enabled = false, Name = ResourceProvider.GetString("LOCMetaSourceStore") }); } foreach (var plugin in plugins) { if (plugin.SupportedFields.Contains(field) && sources.Any(a => a.Id == plugin.Id) == false) { sources.Add(new MetadataSource { Id = plugin.Id, Enabled = false, Name = plugin.Name }); } } return new FieldsSelectionSettings(sources); } private void SetAllProperties() { void setSources(FieldsSelectionSettings settings) { settings.Sources.ForEach(a => a.Enabled = AllSettings.Sources.FirstOrDefault(b => a.Id == b.Id)?.Enabled ?? false); foreach (var allSource in AllSettings.Sources.Reverse()) { var toMove = settings.Sources.FirstOrDefault(a => a.Id == allSource.Id); if (toMove != null) { var oldIndex = settings.Sources.IndexOf(toMove); if (oldIndex != 0) { settings.Sources.Move(oldIndex, 0); } } } } foreach (var prop in GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(a => a.PropertyType == typeof(FieldsSelectionSettings) && a.Name != nameof(AllSettings))) { setSources((FieldsSelectionSettings)prop.GetValue(this)); } } private void SelectAllSelectionImport(bool select) { foreach (var prop in Settings.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(a => a.PropertyType == typeof(MetadataFieldSettings))) { ((MetadataFieldSettings)prop.GetValue(Settings)).Import = select; } } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/NullableIntBox.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; namespace Playnite.DesktopApp.Controls { public class NullIntNumericBox : TextBox { public int MinValue { get { return (int)GetValue(MinValueProperty); } set { SetValue(MinValueProperty, value); } } public int MaxValue { get { return (int)GetValue(MaxValueProperty); } set { SetValue(MaxValueProperty, value); } } private int? lastValue; public int? Value { get { return (int?)GetValue(ValueProperty); } set { lastValue = value; SetValue(ValueProperty, value); } } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(int?), typeof(NullIntNumericBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, ValuePropertyChanged, CoerceValue, false, UpdateSourceTrigger.PropertyChanged)); public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register(nameof(MinValue), typeof(int?), typeof(NullIntNumericBox), new PropertyMetadata(0, MinValuePropertyChanged)); public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(nameof(MaxValue), typeof(int?), typeof(NullIntNumericBox), new PropertyMetadata(int.MaxValue, MaxValuePropertyChanged)); static NullIntNumericBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(NullIntNumericBox), new FrameworkPropertyMetadata(typeof(NullIntNumericBox))); } public NullIntNumericBox() { Text = Value.ToString(); LostFocus += NumericBox_LostFocus; Loaded += NumericBox_Loaded; } private void NumericBox_Loaded(object sender, RoutedEventArgs e) { lastValue = Value; } private void NumericBox_LostFocus(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(Text)) { Value = null; } else { if (!int.TryParse(Text, out var result)) { Value = lastValue; } else { if (result >= MinValue && result <= MaxValue) { Value = result; } else { Value = lastValue; } } } } private static object CoerceValue(DependencyObject element, object baseValue) { var box = (NullIntNumericBox)element; var value = (int?)baseValue; if (value == null) { box.Text = string.Empty; } else { box.Text = value.ToString(); } return value; } private static void ValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { } private static void MinValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { } private static void MaxValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/NumericDoubleBox.cs ================================================ using Playnite.Common; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls { public class DoubleNumericBox : TextBox { public double MinValue { get { return (double)GetValue(MinValueProperty); } set { SetValue(MinValueProperty, value); } } public double MaxValue { get { return (double)GetValue(MaxValueProperty); } set { SetValue(MaxValueProperty, value); } } private double lastValue; public double Value { get { return (double)GetValue(ValueProperty); } set { lastValue = value; SetValue(ValueProperty, value); } } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(DoubleNumericBox), new FrameworkPropertyMetadata( (double)0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, DoubleValuePropertyChanged, CoerceDoubleValue, false, UpdateSourceTrigger.PropertyChanged)); public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register(nameof(MinValue), typeof(double), typeof(DoubleNumericBox), new PropertyMetadata((double)0, MinDoubleValuePropertyChanged)); public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register(nameof(MaxValue), typeof(double), typeof(DoubleNumericBox), new PropertyMetadata(double.MaxValue, MaxDoubleValuePropertyChanged)); static DoubleNumericBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DoubleNumericBox), new FrameworkPropertyMetadata(typeof(DoubleNumericBox))); } public DoubleNumericBox() { Text = Value.ToString(); LostFocus += NumericDoubleBox_LostFocus; Loaded += NumericDoubleBox_Loaded; TextChanged += NumericDoubleBox_TextChanged; PreviewKeyDown += NumericDoubleBox_PreviewKeyDown; } private void NumericDoubleBox_PreviewKeyDown(object sender, KeyEventArgs e) { if (e.Key.IsNumericKey() || e.Key == Key.OemPeriod || e.Key == Key.OemComma || e.Key == Key.Delete || e.Key == Key.Back || e.Key == Key.Left || e.Key == Key.Right) { e.Handled = false; } else { e.Handled = true; } } private void NumericDoubleBox_TextChanged(object sender, TextChangedEventArgs e) { NumericDoubleBox_LostFocus(sender, e); } private void NumericDoubleBox_Loaded(object sender, RoutedEventArgs e) { lastValue = Value; } private void NumericDoubleBox_LostFocus(object sender, RoutedEventArgs e) { if (string.IsNullOrEmpty(Text)) { Text = "0"; } if (!TryParseForCurrentCulture(Text, out var result)) { e.Handled = true; Value = lastValue; } else { if (result >= MinValue && result <= MaxValue) { Value = result; } else { e.Handled = true; Value = lastValue; Text = lastValue.ToString(); } } } private static object CoerceDoubleValue(DependencyObject element, object baseValue) { var box = (DoubleNumericBox)element; var current = (double)baseValue; if (current < box.MinValue) { current = box.MinValue; } if (current > box.MaxValue) { current = box.MaxValue; } return current; } private static void DoubleValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = (DoubleNumericBox)sender; if (obj.Text.IsNullOrEmpty()) { obj.Text = e.NewValue.ToString(); } else { if (TryParseForCurrentCulture(obj.Text.Replace(".", ","), out var result) && result == (double)e.NewValue) { } else { obj.Text = e.NewValue.ToString(); } } } private static void MinDoubleValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { } private static void MaxDoubleValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) { } private static bool TryParseForCurrentCulture(string text, out double result) { var numberFormat = CultureInfo.CurrentCulture.NumberFormat; var parsedText = text.Replace(",", numberFormat.NumberDecimalSeparator) .Replace(".", numberFormat.NumberDecimalSeparator); return double.TryParse(parsedText, out result); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/PathSelectionBox.cs ================================================ using Playnite.Common; using Playnite.DesktopApp.ViewModels; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace Playnite.DesktopApp.Controls { [TemplatePart(Name = "PART_ButtonDirectorySelect", Type = typeof(Button))] [TemplatePart(Name = "PART_ButtonFileSelect", Type = typeof(Button))] public class PathSelectionBox : TextBox { private Button ButtonDirectorySelect; private Button ButtonFileSelect; public string FileSelectorFilter { get; set; } = "Any file|*.*"; private bool showFileSelector = false; public bool ShowFileSelector { get => showFileSelector; set { showFileSelector = value; SetButtonVisibility(); } } private bool showDirectorySelector = false; public bool ShowDirectorySelector { get => showDirectorySelector; set { showDirectorySelector = value; SetButtonVisibility(); } } static PathSelectionBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(PathSelectionBox), new FrameworkPropertyMetadata(typeof(PathSelectionBox))); } public PathSelectionBox() { } public override void OnApplyTemplate() { base.OnApplyTemplate(); ButtonDirectorySelect = Template.FindName("PART_ButtonDirectorySelect", this) as Button; if (ButtonDirectorySelect != null) { ButtonDirectorySelect.Click += (_, __) => { var path = Dialogs.SelectFolder(); if (!path.IsNullOrWhiteSpace()) { Clear(); AppendText(path); } }; } ButtonFileSelect = Template.FindName("PART_ButtonFileSelect", this) as Button; if (ButtonFileSelect != null) { ButtonFileSelect.Click += (_, __) => { var path = Dialogs.SelectFile(FileSelectorFilter); if (!path.IsNullOrWhiteSpace()) { Clear(); AppendText(path); } }; } SetButtonVisibility(); } private void SetButtonVisibility() { if (ButtonDirectorySelect != null) { ButtonDirectorySelect.Visibility = ShowDirectorySelector == true ? Visibility.Visible : Visibility.Collapsed; } if (ButtonFileSelect != null) { ButtonFileSelect.Visibility = ShowFileSelector == true ? Visibility.Visible : Visibility.Collapsed; } } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/SearchBox.cs ================================================ using Playnite.Common; using Playnite.DesktopApp.ViewModels; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace Playnite.DesktopApp.Controls { [TemplatePart(Name = "PART_SeachIcon", Type = typeof(FrameworkElement))] [TemplatePart(Name = "PART_ClearTextIcon", Type = typeof(FrameworkElement))] [TemplatePart(Name = "PART_TextInpuText", Type = typeof(TextBox))] public class SearchBox : Control { private FrameworkElement ElemSeachIcon; private FrameworkElement ElemClearTextIcon; private TextBox TextInputText; private int oldCarret; private bool ignoreTextCallback; internal IInputElement previousFocus; public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } } public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string), typeof(SearchBox), new PropertyMetadata(string.Empty, TextPropertyChangedCallback)); public bool ShowImage { get { return (bool)GetValue(ShowImageProperty); } set { SetValue(ShowImageProperty, value); } } public static readonly DependencyProperty ShowImageProperty = DependencyProperty.Register(nameof(ShowImage), typeof(bool), typeof(SearchBox), new PropertyMetadata(true, ShowImagePropertyChangedCallback)); public new bool IsFocused { get { return (bool)GetValue(IsFocusedProperty); } set { SetValue(IsFocusedProperty, value); } } public new static readonly DependencyProperty IsFocusedProperty = DependencyProperty.Register(nameof(IsFocused), typeof(bool), typeof(SearchBox), new PropertyMetadata(false, IsFocusedPropertyChangedCallback)); static SearchBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchBox), new FrameworkPropertyMetadata(typeof(SearchBox))); } public SearchBox() { } public override void OnApplyTemplate() { base.OnApplyTemplate(); ElemSeachIcon = Template.FindName("PART_SeachIcon", this) as FrameworkElement; if (ElemSeachIcon != null) { } ElemClearTextIcon = Template.FindName("PART_ClearTextIcon", this) as FrameworkElement; if (ElemClearTextIcon != null) { ElemClearTextIcon.MouseUp += ClearImage_MouseUp; } TextInputText = Template.FindName("PART_TextInpuText", this) as TextBox; if (TextInputText != null) { TextInputText.TextChanged += TextFilter_TextChanged; TextInputText.KeyUp += TextFilter_KeyUp; TextInputText.GotFocus += TextInputText_GotFocus; TextInputText.LostFocus += TextInputText_GotFocus; BindingTools.SetBinding( TextInputText, TextBox.TextProperty, this, nameof(Text), mode: System.Windows.Data.BindingMode.OneWay, trigger: System.Windows.Data.UpdateSourceTrigger.PropertyChanged); } UpdateIconStates(); } private void TextInputText_GotFocus(object sender, RoutedEventArgs e) { UpdateIconStates(); } private void UpdateIconStates() { if (TextInputText.IsFocused) { ElemSeachIcon.Visibility = Visibility.Collapsed; } if (Text.IsNullOrEmpty()) { ElemClearTextIcon.Visibility = Visibility.Collapsed; if (!TextInputText.IsFocused) { ElemSeachIcon.Visibility = ShowImage ? Visibility.Visible : Visibility.Collapsed; } } else { ElemClearTextIcon.Visibility = Visibility.Visible; if (!TextInputText.IsFocused) { ElemSeachIcon.Visibility = Visibility.Collapsed; } } } public void ClearFocus() { if (previousFocus != null) { Keyboard.Focus(previousFocus); } else { TextInputText.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); } previousFocus = null; IsFocused = false; } private void TextFilter_KeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Escape || e.Key == Key.Enter) { ClearFocus(); } } private void ClearImage_MouseUp(object sender, MouseButtonEventArgs e) { TextInputText.Clear(); } private void TextFilter_TextChanged(object sender, TextChangedEventArgs e) { if (ignoreTextCallback) { return; } ignoreTextCallback = true; Text = TextInputText.Text; ignoreTextCallback = false; UpdateIconStates(); } private static void TextPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as SearchBox; if (obj.ignoreTextCallback) { return; } if (obj.TextInputText != null) { var currentCurret = obj.TextInputText.CaretIndex; if (currentCurret == 0 && obj.TextInputText.Text.Length > 0 && obj.oldCarret != obj.TextInputText.Text.Length) { obj.TextInputText.CaretIndex = obj.oldCarret; } obj.oldCarret = obj.TextInputText.CaretIndex; } } private static void ShowImagePropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as SearchBox; obj.ShowImage = (bool)e.NewValue; } private static void IsFocusedPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) { var obj = sender as SearchBox; var shouldFocus = (bool)e.NewValue; if (!shouldFocus && !obj.TextInputText.IsFocused) { return; } if (shouldFocus == true) { obj.previousFocus = Keyboard.FocusedElement; obj.TextInputText.Focus(); } else { obj.ClearFocus(); } obj.UpdateIconStates(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/AppearanceAdvanced.xaml ================================================  Library Platform General None Platform General None Library Platform Cover None ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/AppearanceGridView.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls.SettingsSections { /// /// Interaction logic for AppearanceGridView.xaml /// public partial class AppearanceGridView : UserControl { public AppearanceGridView() { InitializeComponent(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/AppearanceLayout.xaml ================================================  Left Right ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/AppearanceLayout.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls.SettingsSections { /// /// Interaction logic for AppearanceLayout.xaml /// public partial class AppearanceLayout : UserControl { public AppearanceLayout() { InitializeComponent(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/AppearanceListView.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/AppearanceListView.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls.SettingsSections { /// /// Interaction logic for AppearanceDetailsView.xaml /// public partial class AppearanceListView : UserControl { public AppearanceListView() { InitializeComponent(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/AppearanceTopPanel.xaml ================================================  Left Right ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/AppearanceTopPanel.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls.SettingsSections { /// /// Interaction logic for EmptyParent.xaml /// public partial class AppearanceTopPanel : UserControl { public AppearanceTopPanel() { InitializeComponent(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/Backup.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/ClientShutdown.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Playnite.DesktopApp.Controls.SettingsSections { /// /// Interaction logic for EmptyParent.xaml /// public partial class ClientShutdown : UserControl { public ClientShutdown() { InitializeComponent(); } } } ================================================ FILE: source/Playnite.DesktopApp/Controls/SettingsSections/Development.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/ExpanderEx.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/ExtendedDataGrid.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/ExtendedListBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/ExtendedListView.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/FilterSelectionBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/GameGroupMenu.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/GameMenu.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/HotKeyBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/HtmlTextView.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/PathSelectionBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/SearchBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/SidebarItem.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/SliderEx.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/TopPanelItem.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/TrayContextMenu.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/CustomControls/WindowBase.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/Border.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/Button.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/CheckBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/ComboBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/ContextMenu.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/DataGrid.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/DatePicker.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/Expander.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/GridSplitter.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/GroupBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/Hyperlink.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/Label.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/ListBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/ListView.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/Menu.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/PasswordBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/Popup.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/ProgressBar.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/RadioButton.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/RepeatButton.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/RichTextBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/ScrollViewer.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/Slider.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/TabControl.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/TextBlock.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/TextBox.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/Thumb.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/ToggleButton.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/ToolTip.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DefaultControls/TreeView.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/BottomButton.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/DetailsHyperlink.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/DetailsScrollViewer.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/DetailsViewGroupStyle.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/DetailsViewItemStyle.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/DetailsViewItemTemplate.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/GridViewGroupStyle.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/GridViewItemStyle.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/GridViewItemTemplate.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/HighlightBorder.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/ImageHighlightButton.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/ListViewGroupStyle.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/MainWindowStyle.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/NotificationMessage.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/PlayButton.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/PropertyItemButton.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/SimpleButton.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/StandardWindowStyle.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/TextBlockGameScore.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DerivedStyles/WindowBarButton.xaml ================================================ ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/DescriptionView.html ================================================  Game Description
{text}
================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Media.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/DetailsViewGameOverview.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/ExplorerPanel.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/Library.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/LibraryDetailsView.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/LibraryGridView.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/LibraryListView.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/MainWindow.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/NotificationPanel.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/SearchView.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/Sidebar.xaml ================================================  ================================================ FILE: source/Playnite.DesktopApp/Themes/Desktop/Default/Views/TopPanel.xaml ================================================