Repository: nstlaurent/DoomLauncher Branch: development Commit: 950e7031a434 Files: 563 Total size: 4.4 MB Directory structure: gitextract_dzt83he3/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── BindingListView/ │ ├── AggregateBindingListView.cs │ ├── BindingListView.cs │ ├── BindingListView.csproj │ ├── BindingListView.nuspec │ ├── CompositeItemFilter.cs │ ├── IItemFilter.cs │ ├── INotifyingEditableObject.cs │ ├── InvalidSourceListException.cs │ ├── MultiSourceIndexList.cs │ ├── ObjectView.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ └── ProvidedViewPropertyDescriptor.cs ├── CheckBoxComboBox/ │ ├── CheckBoxComboBox.Designer.cs │ ├── CheckBoxComboBox.cs │ ├── CheckBoxComboBox.csproj │ ├── GripBounds.cs │ ├── NativeMethods.cs │ ├── Popup.Designer.cs │ ├── Popup.cs │ ├── PopupComboBox.Designer.cs │ ├── PopupComboBox.cs │ ├── PopupComboBox.resx │ ├── Properties/ │ │ └── AssemblyInfo.cs │ └── Selection Wrappers/ │ ├── ListSelectionWrapper.cs │ └── ObjectSelectionWrapper.cs ├── DoomLauncher/ │ ├── Adapters/ │ │ ├── DataAccess.cs │ │ ├── DbDataSourceAdapter.cs │ │ ├── DirectoryDataSourceAdapter.cs │ │ ├── GameLauncher.cs │ │ ├── IdGamesDataAdapater.cs │ │ ├── Launch/ │ │ │ ├── ExtraParametersLaunchFeature.cs │ │ │ ├── GameFilesLaunchFeature.cs │ │ │ ├── ILaunchFeature.cs │ │ │ ├── IWadLaunchFeature.cs │ │ │ ├── LaunchParameters.cs │ │ │ ├── LaunchResult.cs │ │ │ ├── LoadSaveLaunchFeature.cs │ │ │ ├── MapSkillLaunchFeature.cs │ │ │ ├── PlayDemoLaunchFeature.cs │ │ │ ├── RecordLaunchFeature.cs │ │ │ ├── SourcePortExtraParametersLaunchFeature.cs │ │ │ ├── StatisticsReaderLaunchFeature.cs │ │ │ └── UtilityFilesLaunchFeature.cs │ │ ├── MSSQLDataAdapter.cs │ │ ├── PlaySession.cs │ │ ├── SqliteDatabaseAdapter.cs │ │ └── WadArchiveDataAdapter.cs │ ├── App.config │ ├── Archive/ │ │ ├── ArchiveReader.cs │ │ ├── Directory/ │ │ │ ├── DirectoryArchiveEntry.cs │ │ │ └── DirectoryArchiveReader.cs │ │ ├── EmptyArchiveReader.cs │ │ ├── FileArchiveReader.cs │ │ ├── IArchiveEntry.cs │ │ ├── IArchiveReader.cs │ │ ├── Rar/ │ │ │ ├── RarArchiveEntry.cs │ │ │ └── RarArchiveReader.cs │ │ ├── RecursiveArchiveReader.cs │ │ ├── SevenZip/ │ │ │ ├── SevenZipArchiveEntry.cs │ │ │ └── SevenZipArchiveReader.cs │ │ ├── Wad/ │ │ │ ├── WadArchiveReader.cs │ │ │ └── WadEntry.cs │ │ └── Zip/ │ │ ├── ZipArchiveReader.cs │ │ └── ZipArchiveReaderEntry.cs │ ├── Config/ │ │ ├── AppConfiguration.cs │ │ ├── AppVersion.cs │ │ ├── ColorTheme.cs │ │ ├── ColorThemeType.cs │ │ ├── ColumnConfig.cs │ │ ├── ConfigType.cs │ │ ├── DataCache.cs │ │ ├── FileManagement.cs │ │ ├── GameFileListEventArgs.cs │ │ ├── IDirectoriesConfiguration.cs │ │ ├── Icons.cs │ │ ├── LauncherPath.cs │ │ ├── ScreenFilter.cs │ │ └── TagMapLookup.cs │ ├── Controls/ │ │ ├── BasicFileView.cs │ │ ├── CCheckBox.cs │ │ ├── CComboBox.cs │ │ ├── CDataGridView.cs │ │ ├── CProgressBar.cs │ │ ├── CRichTextBox.Designer.cs │ │ ├── CRichTextBox.cs │ │ ├── CTabControl.cs │ │ ├── CheckBoxList.Designer.cs │ │ ├── CheckBoxList.cs │ │ ├── CheckBoxList.resx │ │ ├── ColumnField.cs │ │ ├── DownloadView.Designer.cs │ │ ├── DownloadView.cs │ │ ├── DownloadView.resx │ │ ├── DownloadViewItem.Designer.cs │ │ ├── DownloadViewItem.cs │ │ ├── DownloadViewItem.resx │ │ ├── FileType.cs │ │ ├── FilesCtrl.Designer.cs │ │ ├── FilesCtrl.cs │ │ ├── FilesCtrl.resx │ │ ├── FlowLayoutPanelDB.Designer.cs │ │ ├── FlowLayoutPanelDB.cs │ │ ├── FormButton.cs │ │ ├── GameFileAssociationView.Designer.cs │ │ ├── GameFileAssociationView.cs │ │ ├── GameFileAssociationView.resx │ │ ├── GameFileEdit.Designer.cs │ │ ├── GameFileEdit.cs │ │ ├── GameFileEdit.resx │ │ ├── GameFileSummary.Designer.cs │ │ ├── GameFileSummary.cs │ │ ├── GameFileSummary.resx │ │ ├── GameFileTile.Designer.cs │ │ ├── GameFileTile.cs │ │ ├── GameFileTile.resx │ │ ├── GameFileTileBase.cs │ │ ├── GameFileTileExpanded.Designer.cs │ │ ├── GameFileTileExpanded.cs │ │ ├── GameFileTileExpanded.resx │ │ ├── GameFileTileViewControl.Designer.cs │ │ ├── GameFileTileViewControl.cs │ │ ├── GameFileTileViewControl.resx │ │ ├── GameFileViewControl.Designer.cs │ │ ├── GameFileViewControl.cs │ │ ├── GameFileViewControl.resx │ │ ├── GenericFileView.Designer.cs │ │ ├── GenericFileView.cs │ │ ├── GenericFileView.resx │ │ ├── GlowButton.cs │ │ ├── GrowLabel.Designer.cs │ │ ├── GrowLabel.cs │ │ ├── PagingControl.Designer.cs │ │ ├── PagingControl.cs │ │ ├── PagingControl.resx │ │ ├── RatingControl.Designer.cs │ │ ├── RatingControl.cs │ │ ├── RatingControl.resx │ │ ├── ScreenshotView.Designer.cs │ │ ├── ScreenshotView.cs │ │ ├── ScreenshotView.resx │ │ ├── SearchControl.Designer.cs │ │ ├── SearchControl.cs │ │ ├── SearchControl.resx │ │ ├── SlideShowPictureBox.Designer.cs │ │ ├── SlideShowPictureBox.cs │ │ ├── SlideShowPictureBox.resx │ │ ├── SourcePortEdit.Designer.cs │ │ ├── SourcePortEdit.cs │ │ ├── SourcePortEdit.resx │ │ ├── StatBar.cs │ │ ├── StatisticsView.Designer.cs │ │ ├── StatisticsView.cs │ │ ├── StatisticsView.resx │ │ ├── StatsControl.Designer.cs │ │ ├── StatsControl.cs │ │ ├── StatsControl.resx │ │ ├── TableLayoutPanelDB.Designer.cs │ │ ├── TableLayoutPanelDB.cs │ │ ├── TagControl.Designer.cs │ │ ├── TagControl.cs │ │ ├── TagControl.resx │ │ ├── TagEdit.Designer.cs │ │ ├── TagEdit.cs │ │ ├── TagEdit.resx │ │ ├── TagSelectControl.Designer.cs │ │ ├── TagSelectControl.cs │ │ ├── TagSelectControl.resx │ │ ├── TitleBarControl.Designer.cs │ │ ├── TitleBarControl.cs │ │ ├── TitleBarControl.resx │ │ ├── UpdateControl.Designer.cs │ │ ├── UpdateControl.cs │ │ └── UpdateControl.resx │ ├── DataSources/ │ │ ├── CleanupFile.cs │ │ ├── ConfigurationData.cs │ │ ├── CustomParam.cs │ │ ├── FileData.cs │ │ ├── GameFile.cs │ │ ├── GameProfile.cs │ │ ├── ITagDataSource.cs │ │ ├── IWadData.cs │ │ ├── IdGamesGameFile.cs │ │ ├── NewFileData.cs │ │ ├── PreviewImage.cs │ │ ├── SettingsDataSource.cs │ │ ├── SourcePortData.cs │ │ ├── SourcePortLaunchType.cs │ │ ├── StaticTagData.cs │ │ ├── StatsData.cs │ │ ├── TagData.cs │ │ ├── TagMapping.cs │ │ ├── WadArchiveFile.cs │ │ └── WadArchiveGameFile.cs │ ├── Demo/ │ │ ├── CldDemoParser.cs │ │ ├── DemoUtil.cs │ │ └── IDemoParser.cs │ ├── DoomLauncher.VisualElementsManifest.xml │ ├── DoomLauncher.csproj │ ├── Dpi/ │ │ └── DpiScale.cs │ ├── Forms/ │ │ ├── AboutBox.Designer.cs │ │ ├── AboutBox.cs │ │ ├── AboutBox.resx │ │ ├── CumulativeStats.Designer.cs │ │ ├── CumulativeStats.cs │ │ ├── CumulativeStats.resx │ │ ├── DirectoriesForm.Designer.cs │ │ ├── DirectoriesForm.cs │ │ ├── DirectoriesForm.resx │ │ ├── FileDetailsEditForm.Designer.cs │ │ ├── FileDetailsEditForm.cs │ │ ├── FileDetailsEditForm.resx │ │ ├── FileManagementSelect.Designer.cs │ │ ├── FileManagementSelect.cs │ │ ├── FileManagementSelect.resx │ │ ├── FileSelectForm.Designer.cs │ │ ├── FileSelectForm.cs │ │ ├── FileSelectForm.resx │ │ ├── FilterForm.Designer.cs │ │ ├── FilterForm.cs │ │ ├── FilterForm.resx │ │ ├── FilterSettingsForm.Designer.cs │ │ ├── FilterSettingsForm.cs │ │ ├── FilterSettingsForm.resx │ │ ├── GameFileEditForm.Designer.cs │ │ ├── GameFileEditForm.cs │ │ ├── GameFileEditForm.resx │ │ ├── MainForm.Designer.cs │ │ ├── MainForm.cs │ │ ├── MainForm.resx │ │ ├── MainForm_Config.cs │ │ ├── MainForm_Init.cs │ │ ├── MainForm_Meta.cs │ │ ├── MainForm_Play.cs │ │ ├── MainForm_Sync.cs │ │ ├── MessageCheckBox.Designer.cs │ │ ├── MessageCheckBox.cs │ │ ├── MessageCheckBox.resx │ │ ├── MetaDataForm.Designer.cs │ │ ├── MetaDataForm.cs │ │ ├── MetaDataForm.resx │ │ ├── PlayForm.Designer.cs │ │ ├── PlayForm.cs │ │ ├── PlayForm.resx │ │ ├── PlayRandomForm.Designer.cs │ │ ├── PlayRandomForm.cs │ │ ├── PlayRandomForm.resx │ │ ├── ProgressBarForm.Designer.cs │ │ ├── ProgressBarForm.cs │ │ ├── ProgressBarForm.resx │ │ ├── SaveInfo.Designer.cs │ │ ├── SaveInfo.cs │ │ ├── SaveInfo.resx │ │ ├── ScreenshotEditForm.Designer.cs │ │ ├── ScreenshotEditForm.cs │ │ ├── ScreenshotEditForm.resx │ │ ├── ScreenshotViewerForm.Designer.cs │ │ ├── ScreenshotViewerForm.cs │ │ ├── ScreenshotViewerForm.resx │ │ ├── SettingsForm.Designer.cs │ │ ├── SettingsForm.cs │ │ ├── SettingsForm.resx │ │ ├── SimpleFileSelectForm.Designer.cs │ │ ├── SimpleFileSelectForm.cs │ │ ├── SimpleFileSelectForm.resx │ │ ├── SourcePortEditForm.Designer.cs │ │ ├── SourcePortEditForm.cs │ │ ├── SourcePortEditForm.resx │ │ ├── SourcePortViewForm.Designer.cs │ │ ├── SourcePortViewForm.cs │ │ ├── SourcePortViewForm.resx │ │ ├── SpecificFilesForm.Designer.cs │ │ ├── SpecificFilesForm.cs │ │ ├── SpecificFilesForm.resx │ │ ├── SplashScreen.Designer.cs │ │ ├── SplashScreen.cs │ │ ├── SplashScreen.resx │ │ ├── StatsInfo.Designer.cs │ │ ├── StatsInfo.cs │ │ ├── StatsInfo.resx │ │ ├── SyncStatusForm.Designer.cs │ │ ├── SyncStatusForm.cs │ │ ├── SyncStatusForm.resx │ │ ├── TagEditForm.Designer.cs │ │ ├── TagEditForm.cs │ │ ├── TagEditForm.resx │ │ ├── TagForm.Designer.cs │ │ ├── TagForm.cs │ │ ├── TagForm.resx │ │ ├── TagSelectForm.Designer.cs │ │ ├── TagSelectForm.cs │ │ ├── TagSelectForm.resx │ │ ├── TextBoxForm.Designer.cs │ │ ├── TextBoxForm.cs │ │ ├── TextBoxForm.resx │ │ ├── TxtGenerator.Designer.cs │ │ ├── TxtGenerator.cs │ │ ├── TxtGenerator.resx │ │ ├── Welcome.Designer.cs │ │ ├── Welcome.cs │ │ └── Welcome.resx │ ├── GameFileView/ │ │ ├── GameFileViewFactory.cs │ │ └── GameFileViewType.cs │ ├── GameStores/ │ │ ├── AutomaticGameStoreCheck.cs │ │ ├── GameStoreFiles.cs │ │ ├── StoreGame.cs │ │ └── StoreGameLoader.cs │ ├── Handlers/ │ │ ├── AddFileType.cs │ │ ├── ArchiveUtil.cs │ │ ├── AutoCompleteCombo.cs │ │ ├── DemoHandler.cs │ │ ├── DownloadHandler.cs │ │ ├── FIleAddResults.cs │ │ ├── FileLoadHandler.cs │ │ ├── FileLoadHandlerLegacy.cs │ │ ├── FileMapComparer.cs │ │ ├── GameFileTileManager.cs │ │ ├── GameProfileUtil.cs │ │ ├── ImageExtensions.cs │ │ ├── InvalidFile.cs │ │ ├── LaunchData.cs │ │ ├── MenuConstants.cs │ │ ├── NativeMethods.cs │ │ ├── NewFileDetector.cs │ │ ├── PathExtensions.cs │ │ ├── SaveGameHandler.cs │ │ ├── ScreenshotHandler.cs │ │ ├── Sync/ │ │ │ ├── Doom64SyncAction.cs │ │ │ ├── Doom64TitlePicSyncAction.cs │ │ │ ├── GameConfSyncAction.cs │ │ │ ├── GameInfoSyncAction.cs │ │ │ ├── ISyncAction.cs │ │ │ ├── IWadTitlesSyncAction.cs │ │ │ ├── IdGamesTextFileParser.cs │ │ │ ├── IdGamesTextInfo.cs │ │ │ ├── KnownWadsSyncAction.cs │ │ │ ├── MapInfoUtil.cs │ │ │ ├── MapStringSyncAction.cs │ │ │ ├── StartupImageSyncAction.cs │ │ │ ├── SyncResult.cs │ │ │ ├── TextFileSyncAction.cs │ │ │ ├── TitlePicSyncAction.cs │ │ │ └── TitlePicUtil.cs │ │ ├── SyncFileData.cs │ │ ├── SyncFileOption.cs │ │ ├── SyncLibraryHandler.cs │ │ ├── TabHandler.cs │ │ ├── TabKeys.cs │ │ ├── ThumbnailManager.cs │ │ ├── ToolTipDisplayHandler.cs │ │ ├── ToolTipGroup.cs │ │ ├── ToolTipHandler.cs │ │ ├── Util.cs │ │ ├── UtilityHandler.cs │ │ ├── VersionHandler.cs │ │ └── VersionUpdateResults.cs │ ├── Help.docx │ ├── IWad/ │ │ └── IWadInfo.cs │ ├── Interfaces/ │ │ ├── IConfigurationData.cs │ │ ├── IConfigurationDataSourceAdapter.cs │ │ ├── ICustomParam.cs │ │ ├── IDataSourceAdapter.cs │ │ ├── IDatabaseAdapter.cs │ │ ├── IFileAssociationView.cs │ │ ├── IFileData.cs │ │ ├── IGameFile.cs │ │ ├── IGameFileColumnView.cs │ │ ├── IGameFileDataSourceAdapter.cs │ │ ├── IGameFileDownloadable.cs │ │ ├── IGameFileSortableView.cs │ │ ├── IGameFileTile.cs │ │ ├── IGameFileView.cs │ │ ├── IGameProfile.cs │ │ ├── IIWadData.cs │ │ ├── IIWadDataSourceAdapter.cs │ │ ├── INewFileDetector.cs │ │ ├── ISettingsDataSource.cs │ │ ├── ISourcePortData.cs │ │ ├── IStatsData.cs │ │ ├── IStatsDataSourceAdapter.cs │ │ ├── ITabView.cs │ │ ├── ITagData.cs │ │ ├── ITagMapLookup.cs │ │ └── ITagMapping.cs │ ├── LaunchArgs.cs │ ├── Program.Init.cs │ ├── Program.cs │ ├── ProgressBarType.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── RecentFileType.cs │ ├── Resources/ │ │ ├── HereticPalette.pal │ │ ├── HexenPalette.pal │ │ └── PLAYPAL.LMP │ ├── SaveGame/ │ │ ├── DsgSaveGameReader.cs │ │ ├── HelionSaveGameReader.cs │ │ ├── ISaveGameReader.cs │ │ └── ZDoomSaveGameReader.cs │ ├── Search/ │ │ ├── GameFileFieldType.cs │ │ ├── GameFileGetOptions.cs │ │ ├── GameFileSearchField.cs │ │ ├── GameFileSearchOp.cs │ │ ├── IGameFileGetOptions.cs │ │ └── OrderType.cs │ ├── SourcePort/ │ │ ├── DoomsdaySourcePortFlavor.cs │ │ ├── GenericSourcePortFlavor.cs │ │ ├── HelionSourcePortFlavor.cs │ │ ├── ISourcePortFlavor.cs │ │ ├── LevelstatSourcePortFlavor.cs │ │ ├── SpData.cs │ │ ├── StatdumpSourcePortFlavor.cs │ │ └── ZDoomSourcePortFlavor.cs │ ├── Statistics/ │ │ ├── IStatisticsReader.cs │ │ ├── LevelstatReader.cs │ │ ├── MultiLineStatReader.cs │ │ ├── NewStatisticsEventArgs.cs │ │ ├── StatFileScanner.cs │ │ ├── StatdumpReader.cs │ │ └── ZDoomStatsReader.cs │ ├── Stylize/ │ │ ├── CToolStripRenderer.cs │ │ ├── DarkTheme.cs │ │ ├── DefaultTheme.cs │ │ ├── IThemeColors.cs │ │ ├── ImmersiveDarkMode.cs │ │ ├── StyledMessageBox.cs │ │ └── Stylizer.cs │ ├── TabViews/ │ │ ├── BasicTabViewCtrl.Designer.cs │ │ ├── BasicTabViewCtrl.cs │ │ ├── BasicTabViewCtrl.resx │ │ ├── IWadTabViewCtrl.Designer.cs │ │ ├── IWadTabViewCtrl.cs │ │ ├── IdGamesTabViewCtrl.Designer.cs │ │ ├── IdGamesTabViewCtrl.cs │ │ ├── LocalTabViewCtrl.cs │ │ ├── OptionsTabViewCtrl.Designer.cs │ │ ├── OptionsTabViewCtrl.cs │ │ ├── TagTabView.Designer.cs │ │ ├── TagTabView.cs │ │ └── UntaggedTabView.cs │ ├── TextFileParsers/ │ │ └── ZdlParser.cs │ ├── Update/ │ │ ├── ApplicationUpdate.cs │ │ ├── ApplicationUpdateInfo.cs │ │ └── ApplicationUpdater.cs │ ├── WindowsVersion/ │ │ └── WindowsVersion.cs │ ├── app.manifest │ └── packages.config ├── DoomLauncher.sln ├── DoomLauncherRelease/ │ ├── App.config │ ├── DoomLauncherRelease.csproj │ ├── Program.cs │ └── Properties/ │ └── AssemblyInfo.cs ├── LICENSE ├── Newtonsoft.Json.xml ├── README.adoc ├── RELEASENOTES.md ├── Setup/ │ ├── EnableLaunchApplication.js │ └── Setup.vdproj ├── UnitTest/ │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Resources/ │ │ ├── TestSteamInstall/ │ │ │ └── config/ │ │ │ └── libraryfolders.vdf │ │ ├── TestSteamLibrary1/ │ │ │ └── steamapps/ │ │ │ ├── appmanifest_2280.acf │ │ │ ├── appmanifest_2390.acf │ │ │ └── common/ │ │ │ ├── TestDoom/ │ │ │ │ ├── base/ │ │ │ │ │ └── doom.wad │ │ │ │ └── rerelease/ │ │ │ │ ├── doom2.wad │ │ │ │ ├── id1.wad │ │ │ │ ├── masterlevels.wad │ │ │ │ ├── nerve.wad │ │ │ │ ├── plutonia.wad │ │ │ │ ├── sigil.wad │ │ │ │ └── tnt.wad │ │ │ └── TestHeretic/ │ │ │ └── base/ │ │ │ └── heretic.wad │ │ ├── TestSteamLibrary2/ │ │ │ └── steamapps/ │ │ │ ├── appmanifest_2360.acf │ │ │ ├── appmanifest_2390.acf │ │ │ └── common/ │ │ │ ├── TestHeretic/ │ │ │ │ └── base/ │ │ │ │ └── heretic.wad │ │ │ └── TestHexen/ │ │ │ └── base/ │ │ │ └── hexen.wad │ │ ├── chocosave1.dsg │ │ ├── invalid1.wad │ │ ├── invalid2.wad │ │ ├── prboomsave1.dsg │ │ ├── simple.wad │ │ ├── testpk3.pk3 │ │ ├── zandemo.cld │ │ ├── zdoomsave_v1.zds │ │ ├── zdoomsave_v2.zds │ │ ├── zdoomsave_v3.zds │ │ └── zdoomsave_v4.zds │ ├── TestInit.cs │ ├── Tests/ │ │ ├── Helpers/ │ │ │ ├── DirectoriesConfiguration.cs │ │ │ ├── Tree.cs │ │ │ ├── TreeEntry.cs │ │ │ └── TreeReader.cs │ │ ├── TestArchives.cs │ │ ├── TestAutomaticSteamCheck.cs │ │ ├── TestCldDemoParser.cs │ │ ├── TestCnDoomStats.cs │ │ ├── TestDirectoryAdapter.cs │ │ ├── TestDirectoryArchive.cs │ │ ├── TestDoom64SyncAction.cs │ │ ├── TestDoom64TitlePicSyncAction.cs │ │ ├── TestDsgSave.cs │ │ ├── TestFile.cs │ │ ├── TestFileDetector.cs │ │ ├── TestGameConfSyncAction.cs │ │ ├── TestGameFile.cs │ │ ├── TestGameFileTags.cs │ │ ├── TestGameInfoSyncAction.cs │ │ ├── TestGameLauncher.cs │ │ ├── TestGameProfile.cs │ │ ├── TestGameProfileUtil.cs │ │ ├── TestGameStoreFiles.cs │ │ ├── TestIWad.cs │ │ ├── TestIWadTitlesSyncAction.cs │ │ ├── TestIdGamesFileParser.cs │ │ ├── TestIdGamesTextInfo.cs │ │ ├── TestIgnoreRarExploit.cs │ │ ├── TestKnownWadsSyncAction.cs │ │ ├── TestLaunchParameters.cs │ │ ├── TestLauncherPath.cs │ │ ├── TestLevelstat.cs │ │ ├── TestLoadFiles.cs │ │ ├── TestMapLumps.cs │ │ ├── TestMapStringSyncAction.cs │ │ ├── TestRecursiveArchiveReader.cs │ │ ├── TestSourcePort.cs │ │ ├── TestSpecificFiles.cs │ │ ├── TestStartupImageSyncAction.cs │ │ ├── TestStatdump.cs │ │ ├── TestStats.cs │ │ ├── TestSteamLoader.cs │ │ ├── TestSyncLibraryHandler.cs │ │ ├── TestTextFileSyncAction.cs │ │ ├── TestUpdateInfo.cs │ │ ├── TestUtil.cs │ │ ├── TestWadReader.cs │ │ ├── TestZdlParser.cs │ │ ├── TestZdoomSave.cs │ │ └── TestZdoomStats.cs │ ├── UnitTest.csproj │ ├── app.config │ └── packages.config ├── WadReader/ │ ├── DoomImage/ │ │ ├── DoomImage.cs │ │ ├── Palette.cs │ │ └── PaletteReaders.cs │ ├── FileLump.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Util.cs │ ├── WadFileReader.cs │ ├── WadHeader.cs │ ├── WadReader.csproj │ ├── WadType.cs │ └── packages.config ├── WpfControlLibrary/ │ ├── AssemblyInfo.cs │ ├── WpfComboBox.xaml │ ├── WpfComboBox.xaml.cs │ └── WpfControlLibrary.csproj └── appveyor.yml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] custom: https://paypal.me/nstlaurent22 ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # 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/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # 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 # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.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 # Visual Studio Trace Files *.e2e # 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 # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Visual Studio code coverage results *.coverage *.coveragexml # 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 # Note: 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 **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable 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 *.appx # 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 *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # 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 ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # 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/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ ReleaseBuild/ ================================================ FILE: BindingListView/AggregateBindingListView.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Collections; using System.Reflection; using System.Diagnostics; namespace Equin.ApplicationFramework { public class AggregateBindingListView : Component, IBindingListView, IList, IRaiseItemChangedEvents, ICancelAddNew, ITypedList, IEnumerable { #region Constructors public AggregateBindingListView() { _sourceLists = new BindingList(); (_sourceLists as IBindingList).ListChanged += new ListChangedEventHandler(SourceListsChanged); _savedSourceLists = new List(); _sourceIndices = new MultiSourceIndexList(); // Start with a filter that includes all items. _filter = IncludeAllItemFilter.Instance; // Start with no sorts applied. _sorts = new ListSortDescriptionCollection(); _objectViewCache = new Dictionary>(); } public AggregateBindingListView(IContainer container) : this() { container.Add(this); if (Site is ISynchronizeInvoke) { SynchronizingObject = Site as ISynchronizeInvoke; } } #endregion #region Private Member Fields /// /// The list of underlying list of items on which this view is based. /// private IList _sourceLists; /// /// The sorted, filtered list of item indices in _sourceList. /// private MultiSourceIndexList _sourceIndices; /// /// The current filter applied to the view. /// private IItemFilter _filter; /// /// The current sorts applied to the view. /// private ListSortDescriptionCollection _sorts; /// /// The IComparer used to compare items when sorting. /// private IComparer, int>> _comparer; /// /// The item in the process of being added to the view. /// private ObjectView _newItem; /// /// The IList we will add new items to. /// private IList _newItemsList; /// /// The object used to marshal event-handler calls that are invoked on a non-UI thread. /// private ISynchronizeInvoke _synchronizingObject; /// /// A copy of the source lists so when a list is removed from SourceLists /// we still have a reference to use for unhooking events, etc. /// private List _savedSourceLists; /// /// The property on a source list item that contains the actual list to view. /// If null or empty then the source list item is used instead. /// private string _dataMember; /// /// ObjectView cache used to prevent re-creation of existing object wrappers when /// in FilterAndSort(). /// private Dictionary> _objectViewCache; /// /// Controls whether or not the view is automatically re-filtered and re-sorted when /// source lists change. /// private bool _autoFilterAndSortSuspended; #endregion /// /// Gets or sets the list of source lists used by this view. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IList SourceLists { get { return _sourceLists; } set { if (value == null) { throw new ArgumentNullException("SourceLists", Properties.Resources.SourceListsNull); } // Check that every item in each list is of type T. foreach (object obj in value) { if (obj == null) { throw new InvalidSourceListException(); } IList list = null; if (!string.IsNullOrEmpty(DataMember)) { foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(obj)) { if (pd.Name == DataMember) { list = pd.GetValue(obj) as IList; break; } } } else if (obj is IListSource) { IListSource src = obj as IListSource; if (src.ContainsListCollection) { list = src.GetList()[0] as IList; } else { list = (obj as IListSource).GetList(); } } else if (!(obj is ICollection)) { list = obj as IList; } else { // We have a typed collection, so can skip the item-by-item check. continue; } if (list == null) { throw new InvalidSourceListException(); } foreach (object item in list) { if (!(item is T)) { throw new InvalidSourceListException(string.Format(Properties.Resources.InvalidListItemType, typeof(T).FullName)); } } } IBindingList bindingList = _sourceLists as IBindingList; // Un-hook old list changed event. if (bindingList != null && bindingList.SupportsChangeNotification) { bindingList.ListChanged -= new ListChangedEventHandler(SourceListsChanged); } foreach (object list in _sourceLists) { IBindingList bl = list as IBindingList; if (bl != null && bl.SupportsChangeNotification) { bl.ListChanged -= new ListChangedEventHandler(SourceListChanged); } } _sourceLists = value; bindingList = _sourceLists as IBindingList; // Hook new list changed event if (bindingList != null && bindingList.SupportsChangeNotification) { bindingList.ListChanged += new ListChangedEventHandler(SourceListsChanged); } foreach (object list in _sourceLists) { IBindingList bl = list as IBindingList; if (bl != null && bl.SupportsChangeNotification) { bl.ListChanged += new ListChangedEventHandler(SourceListChanged); } } // save new lists BuildSavedList(); FilterAndSort(); OnListChanged(ListChangedType.Reset, -1); } } /// /// Gets the ObjectView<T> of the item at the given index in the view. /// /// The item index. /// The ObjectView<T> of the item. public ObjectView this[int index] { get { return _sourceIndices[index].Key.Item; } } [Browsable(false)] public string DataMember { get { return _dataMember; } set { _dataMember = value; FilterAndSort(); OnListChanged(ListChangedType.Reset, -1); } } private bool ShouldSerializeListMember() { return !string.IsNullOrEmpty(DataMember); } #region Adding New Items /// /// Occurs before an item is added to the list. /// Assign the event argument's NewObject property to provide the object to add. /// public event AddingNewEventHandler AddingNew; /// /// Attempts to get a new object to add to the list, first by raising the /// AddingNew event and then (if no new object was assigned) by using the /// default public constructor. /// /// The new object to add to the list. /// No new object provided by the AddingNew event handler and has no default public constructor. protected virtual T OnAddingNew() { // We allow users of this class to provide the object to add // by raising the AddingNew event. if (AddingNew != null) { AddingNewEventArgs args = new AddingNewEventArgs(); AddingNew(this, args); // Check if we were given an object (and it's the correct type) if ((args.NewObject != null) && (args.NewObject is T)) { return (T)args.NewObject; } } // Otherwise, try the default public constructor instead. // Use reflection to find it. Note: We're not using the generic new() constraint since // we do not want to force the need for a public default constructor when the user // can simply handle the AddingNew event called above. System.Reflection.ConstructorInfo ci = typeof(T).GetConstructor(System.Type.EmptyTypes); if (ci != null) { // Invoke the constructor to create the object. return (T)ci.Invoke(null); } else { throw new InvalidOperationException(Properties.Resources.CannotAddNewItem); } } /// /// Adds a new item to the view. Note that EndNew must be called to commit /// the item to the to the source list. /// /// The new item, wrapped in an ObjectView. public ObjectView AddNew() { // Are we currently adding another item? if (_newItem != null) { // Need to commit previous new item before adding another. EndNew(_sourceIndices.Count - 1); } // Get the new item to add. T item = OnAddingNew(); // Create the ObjectView wrapper for the item. ObjectView objectView = new ObjectView(item, this); _objectViewCache[item] = objectView; HookPropertyChangedEvent(objectView); // Set the _newItem reference so we know what to use when ending/cancelling this add operation. _newItem = objectView; // Add to indicies list, but index of -1 means it's not in the source list yet. _sourceIndices.Add(_newItemsList, objectView, -1); // Tell any data binders that we've added an item to the view. // Put it at the end of the list. OnListChanged(ListChangedType.ItemAdded, _sourceIndices.Count - 1); return objectView; } /// /// Cancels the pending addition of a new item to the source list /// and remove the item from the view. /// /// The index of the new item. public void CancelNew(int itemIndex) { // We must take special care that the item index does refer to the new item. if (itemIndex > -1 && itemIndex < _sourceIndices.Count && _newItem != null && _sourceIndices[itemIndex].Key.Item == _newItem) { // We no longer need to listen to any events from the object. UnHookPropertyChangedEvent(_newItem); // Remove the item from the view. _sourceIndices.RemoveAt(itemIndex); // Data binders need to know the item has gone from the view. OnListChanged(ListChangedType.ItemDeleted, itemIndex); // Done with this adding operation, so clear the _newItem reference. _newItem = null; } } /// /// Commits the pending addition of a new item to the source list. /// /// The index of the new item. public void EndNew(int itemIndex) { // The binding infrastructure tends to call the method // more times than needed and often with itemIndex not even pointing to the // new object! So we have to take special care to check. if (itemIndex > -1 && itemIndex < _sourceIndices.Count && _newItem != null && _sourceIndices[itemIndex].Key.Item == _newItem) { // In order to reuse the SourceListChanged code for adding a new item // we have to first remove all knowledge of the item, then add it // to the source list. // We no longer need to listen to any events from the object. UnHookPropertyChangedEvent(_newItem); // Remove the item from the view. _sourceIndices.RemoveAt(itemIndex); // Add the actual data object to the source list. // The SourceListChanged event handler will take care of correctly inserting this // object into the view (if newItemsList is a IBindingList). _newItemsList.Add(_newItem.Object); // If it is not an IBindingList (or not SupportsChangeNotification) // then we must force the update ourselves. if (!(_newItemsList is IBindingList) || !(_newItemsList as IBindingList).SupportsChangeNotification) { if (!_autoFilterAndSortSuspended) { FilterAndSort(); } OnListChanged(ListChangedType.Reset, -1); } // Done with this adding operation, so clear the _newItem reference. _newItem = null; } } /// /// Gets or sets the source list to which new items are added. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public IList NewItemsList { get { return _newItemsList; } set { if (value != null && !_sourceLists.Contains(value)) { throw new ArgumentException(Properties.Resources.SourceListNotFound); } _newItemsList = value; } } #endregion /// /// Re-applies any current filter and sorts to refresh the current view. /// public void Refresh() { FilterAndSort(); // Get any bound objects to refresh everything as well. OnListChanged(ListChangedType.Reset, -1); } /// /// Gets or sets the object used to marshal event-handler calls that are invoked on a non-UI thread. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public ISynchronizeInvoke SynchronizingObject { get { return _synchronizingObject; } set { _synchronizingObject = value; } } public void SuspendAutoFilterAndSort() { _autoFilterAndSortSuspended = true; } public void ResumeAutoFilterAndSort() { _autoFilterAndSortSuspended = false; } /// /// Updates the _sourceIndices list to contain the items that are current viewed /// according to applied filter and sorts. /// protected void FilterAndSort() { // The view contains items from the source list // and possibly a new items that are not yet committed. // Therefore we can't just clear the list and start over // as we would lose the new items. So we have to to insert // filtered source list items into a new list first. // New items can then be pulled out of the current view // and appended to the new list. MultiSourceIndexList newList = new MultiSourceIndexList(); // Get items from the source list that are included by the current filter. foreach (IList sourceList in GetSourceLists()) { for (int i = 0; i < sourceList.Count; i++) { T item = (T)sourceList[i]; ObjectView editableObject; if (_filter.Include(item)) { if (_objectViewCache.ContainsKey(item)) { editableObject = _objectViewCache[item]; } else { editableObject = new ObjectView(item, this); _objectViewCache.Add(item, editableObject); // Listen to the editing notification and property changed events. HookEditableObjectEvents(editableObject); HookPropertyChangedEvent(editableObject); } // Add the editable object along with the index of the item in the source list. newList.Add(sourceList, editableObject, i); } else { if (_objectViewCache.ContainsKey(item)) { editableObject = _objectViewCache[item]; UnHookEditableObjectEvents(editableObject); UnHookPropertyChangedEvent(editableObject); _objectViewCache.Remove(item); } } } } // If we have sorts to apply, do them now if (_comparer != null) { newList.Sort(_comparer); } // Now we can append any new items to the end of the view. foreach (KeyValuePair, int> kvp in _sourceIndices) { // New items have a source list index of -1 since they are not // yet in the source list. if (kvp.Value == -1) { newList.Add(kvp); } } // Set our view now _sourceIndices = newList; // Note: We do not raise the ListChanged event with ListChangeType.Reset // since the view may not have changed that much. It is better to let // the calling code decide what has happened and raise events accordingly. } #region Editing Items Event Handlers /// /// Currently unused. Here in case we want to perform actions when /// an item edit begins. /// protected virtual void BegunItemEdit(object sender, EventArgs e) { } /// /// Currently unused. Here in case we want to perform actions when /// an item edit is cancelled. /// protected virtual void CancelledItemEdit(object sender, EventArgs e) { } /// /// Handles the EndedEdit event. /// /// The that raised the event. protected virtual void EndedItemEdit(object sender, EventArgs e) { if (_autoFilterAndSortSuspended) { return; } ObjectView editableObject = (ObjectView)sender; // Check if filtering removed the item from view // by getting the index before and after int oldIndex = _sourceIndices.IndexOfItem(editableObject.Object); FilterAndSort(); int newIndex = _sourceIndices.IndexOfItem(editableObject.Object); // if item was filtered out then the newIndex == -1 if (newIndex > -1) { if (oldIndex == newIndex) { OnListChanged(ListChangedType.ItemChanged, newIndex); } else { OnListChanged(ListChangedType.ItemMoved, newIndex, oldIndex); } } else { OnListChanged(ListChangedType.ItemDeleted, oldIndex); } } #endregion /// /// Event handler for when SourceLists is changed. /// protected virtual void SourceListsChanged(object sender, ListChangedEventArgs e) { if (e.ListChangedType == ListChangedType.ItemAdded) { IList list = SourceLists[e.NewIndex] as IList; if (list == null) { SourceLists.RemoveAt(e.NewIndex); throw new InvalidSourceListException(); } if (list is IBindingList) { // We need to know when the source list changes (list as IBindingList).ListChanged += new ListChangedEventHandler(SourceListChanged); } _savedSourceLists.Add(list); if (!_autoFilterAndSortSuspended) { FilterAndSort(); OnListChanged(ListChangedType.Reset, -1); } } else if (e.ListChangedType == ListChangedType.ItemDeleted) { IList list = _savedSourceLists[e.NewIndex] as IList; if (list != null) { if (list is IBindingList) { (list as IBindingList).ListChanged -= new ListChangedEventHandler(SourceListChanged); } _savedSourceLists.RemoveAt(e.NewIndex); if (!_autoFilterAndSortSuspended) { FilterAndSort(); OnListChanged(ListChangedType.Reset, -1); } } } else if (e.ListChangedType == ListChangedType.Reset) { BuildSavedList(); if (!_autoFilterAndSortSuspended) { FilterAndSort(); OnListChanged(ListChangedType.Reset, -1); } } } /// /// Event handler for when a source list changes. /// private void SourceListChanged(object sender, ListChangedEventArgs e) { if (_autoFilterAndSortSuspended) { return; } int oldIndex; int newIndex; IBindingList sourceList = sender as IBindingList; switch (e.ListChangedType) { case ListChangedType.ItemAdded: FilterAndSort(); // Get the index of the newly sorted item newIndex = _sourceIndices.IndexOfSourceIndex(sourceList, e.NewIndex); if (newIndex > -1) { OnListChanged(ListChangedType.ItemAdded, newIndex); // Other items have moved down the list for (int i = newIndex + 1; i < Count; i++) { OnListChanged(ListChangedType.ItemMoved, i - 1, i); } } else { // The item was excluded by the filter, // so to the viewer the item has been "deleted". // The new item will have been added at the end of the view OnListChanged(ListChangedType.ItemDeleted, Math.Max(Count - 1, 0)); } break; case ListChangedType.ItemChanged: // Check if filtering will remove the item from view // by getting the index before and after oldIndex = _sourceIndices.IndexOfSourceIndex(sourceList, e.NewIndex); // Is the object in our view? if (oldIndex < 0) { return; } FilterAndSort(); newIndex = _sourceIndices.IndexOfSourceIndex(sourceList, e.NewIndex); // if item was filtered out then the newIndex == -1 // otherwise we can say that the item was changed. if (newIndex > -1) { if (newIndex == oldIndex) { OnListChanged(ListChangedType.ItemChanged, newIndex); } else { // Two items will have changed places OnListChanged(ListChangedType.ItemMoved, newIndex, oldIndex); } } else { OnListChanged(ListChangedType.ItemDeleted, oldIndex); } break; case ListChangedType.ItemDeleted: // Find the deleted index newIndex = _sourceIndices.IndexOfSourceIndex(sourceList, e.NewIndex); // Did we have the object in our view? if (newIndex < 0) { return; } // Stop listening to it's events UnHookEditableObjectEvents(_sourceIndices[newIndex].Key.Item); UnHookPropertyChangedEvent(_sourceIndices[newIndex].Key.Item); // Remove its index _sourceIndices.RemoveAt(newIndex); // Move up indices after removed item for (int i = 0; i < _sourceIndices.Count; i++) { if (_sourceIndices[i].Value > e.NewIndex) { _sourceIndices[i] = new KeyValuePair, int>(_sourceIndices[i].Key, _sourceIndices[i].Value - 1); } } // Inform listeners that an item has been deleted from this view OnListChanged(ListChangedType.ItemDeleted, newIndex); break; case ListChangedType.ItemMoved: if (!IsSorted && (Filter is IncludeAllItemFilter)) { // We can move the item in the view // note indicies match those in _sourceList OnListChanged(ListChangedType.ItemMoved, e.NewIndex, e.OldIndex); } // Otherwise it makes no sense to move due to sort and/or filter break; case ListChangedType.Reset: // Most of the source list has changed // so re-sort and filter FilterAndSort(); // The view is most likely to have changed lots as well OnListChanged(ListChangedType.Reset, -1); break; } } /// /// Event handler for when an item in the view changes. /// /// The item that changed. private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e) { // The changed item may not actually be present in the view int index = _sourceIndices.IndexOfItem((T)sender); // Test the returned index, -1 => not in the view. if (index > -1) { // Tell listeners that an item has changed. // This is inline with the IRaiseItemChangedEvents implementation. OnListChanged(ListChangedType.ItemChanged, index); } } #region ListChanged Event /// /// Occurs when the list changes or an item in the list changes. /// public event ListChangedEventHandler ListChanged; /// /// Raises the ListChanged event with the given event arguments. /// /// The ListChangedEventArgs to raise the event with. protected virtual void OnListChanged(ListChangedEventArgs e) { if (ListChanged != null) { // Check if we need to invoke on the UI thread or not if (SynchronizingObject != null && SynchronizingObject.InvokeRequired) { SynchronizingObject.Invoke(ListChanged, new object[] { this, e }); } else { ListChanged(this, e); } } } /// /// Helper method to build the ListChangedEventArgs needed for the ListChanged event. /// /// The type of change that occured. /// The index of the changed item. private void OnListChanged(ListChangedType listChangedType, int newIndex) { OnListChanged(new ListChangedEventArgs(listChangedType, newIndex)); } /// /// Helper method to build the ListChangedEventArgs needed for the ListChanged event. /// /// The type of change that occured. /// The index of the item after the change. /// The index of the iem before the change. private void OnListChanged(ListChangedType listChangedType, int newIndex, int oldIndex) { OnListChanged(new ListChangedEventArgs(listChangedType, newIndex, oldIndex)); } #endregion #region Filtering public void ApplyFilter(IItemFilter filter) { Filter = filter; } public void ApplyFilter(Predicate includeItem) { if (includeItem == null) { throw new ArgumentNullException("includeItem", Properties.Resources.IncludeDelegateCannotBeNull); } Filter = AggregateBindingListView.CreateItemFilter(includeItem); } /// /// Gets if this view supports filtering of items. Always returns true. /// bool IBindingListView.SupportsFiltering { get { return true; } } /// Explicitly implemented to expose the stronger Filter property instead. string IBindingListView.Filter { get { return Filter.ToString(); } set { throw new NotSupportedException("Cannot set filter from string expression."); //TODO: Re-instate this line once we have an expression filter //Filter = new ExpressionItemFilter(value); } } /// /// Gets or sets the filter currently applied to the view. /// public IItemFilter Filter { get { return _filter; } set { // Do not allow a null filter. Instead, use the "include all items" filter. if (value == null) value = IncludeAllItemFilter.Instance; if (_filter != value) { _filter = value; FilterAndSort(); // The list has probably changed a lot, so get bound controls to reset. OnListChanged(ListChangedType.Reset, -1); } } } private bool ShouldSerializeFilter() { return (Filter != IncludeAllItemFilter.Instance); } public static IItemFilter CreateItemFilter(Predicate predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return new PredicateItemFilter(predicate); } // Function for LINQ style filtering // e.g. SetFilter(i => i.Items.Count < 42) /* public static void ApplyFilter(Func predicate) { if (predicate == null) { throw new ArgumentNullException("predicate"); } return new FuncItemFilter(predicate); } // Class to wrap a LINQ Func delegate and expose // it as an IItemFilter. private class FuncItemFilter : IItemFilter { private Func _func; public FuncItemFilter(Func func) { _func = func; } public bool Include(T item) { return _func(item); } } */ /// /// Removes any currently applied filter so that all items are displayed by the view. /// public void RemoveFilter() { // Set filter back to including all items. Filter = IncludeAllItemFilter.Instance; } #endregion #region Sorting /// /// Used to signal that a sort on a property is to be descending, not ascending. /// public readonly string SortDescendingModifier = "DESC"; /// /// The character used to seperate sorts by multiple properties. /// public readonly char SortDelimiter = ','; /// /// Gets if this view supports sorting. Always returns true. /// bool IBindingList.SupportsSorting { get { return true; } } /// /// Gets if this view supports advanced sorting. Always returns true. /// bool IBindingListView.SupportsAdvancedSorting { get { return true; } } /// /// Sorts the view by a single property in a given direction. /// This will remove any existing sort. /// /// A property of to sort by. /// The direction to sort in. public void ApplySort(PropertyDescriptor property, ListSortDirection direction) { // Apply sort by setting the current sort descriptions // to be a collection containing just one SortDescription. SortDescriptions = new ListSortDescriptionCollection( new ListSortDescription[] { new ListSortDescription(property, direction)}); } /// /// Sorts the view by the given collection of sort descriptions. /// /// The sorts to apply. public void ApplySort(ListSortDescriptionCollection sorts) { SortDescriptions = sorts; } /// /// Sorts the view according to the properties and directions given in the /// SQL style sort parameter. /// /// /// The SQL ORDER BY clause style sort. /// A comma separated list of properties to sort by. /// Use "DESC" after a property name to sort descending. /// The default direction is ascending. /// /// view.ApplySort("Surname, FirstName, Age DESC"); public void ApplySort(string sort) { if (string.IsNullOrEmpty(sort)) { RemoveSort(); return; } // Parse string for sort descriptions string[] sorts = sort.Split(SortDelimiter); ListSortDescription[] col = new ListSortDescription[sorts.Length]; for (int i = 0; i < sorts.Length; i++) { // Get the sort description. // This will be a name optionally followed by a direction. sort = sorts[i].Trim(); // A space will separate name from direction. int pos = sort.IndexOf(' '); string name; ListSortDirection direction; if (pos == -1) { // No direction specified, default to ascending. name = sort; direction = ListSortDirection.Ascending; } else { // Name is everything before the space. name = sort.Substring(0, pos); // direction is everything after the space. string dir = sort.Substring(pos + 1).Trim(); // Check what kind of direction is specified. // (Ignoring case and culture.) if (string.Compare(dir, SortDescendingModifier, true, System.Globalization.CultureInfo.InvariantCulture) == 0) { direction = ListSortDirection.Descending; } else { // Default to ascending. direction = ListSortDirection.Ascending; } } // Put the sort description into the collection. col[i] = CreateListSortDescription(name, direction); } ApplySort(new ListSortDescriptionCollection(col)); } public void ApplySort(IComparer comparer) { if (comparer == null) { throw new ArgumentNullException("comparer"); } // Clear any current sorts _sorts = new ListSortDescriptionCollection(); // Sort with this new comparer _comparer = new ExternalSortComparer(comparer); FilterAndSort(); OnListChanged(ListChangedType.Reset, -1); } public void ApplySort(Comparison comparison) { if (comparison == null) { throw new ArgumentNullException("comparison"); } // Clear any current sorts _sorts = new ListSortDescriptionCollection(); // Sort with this new comparer _comparer = new ExternalSortComparison(comparison); FilterAndSort(); OnListChanged(ListChangedType.Reset, -1); } /// /// Removes any sort currently applied to the view, restoring it to the order of the source list. /// public void RemoveSort() { // An empty collection of sorts will achieve what we need. SortDescriptions = new ListSortDescriptionCollection(); } /// /// Gets if the view is currently sorted. /// [Browsable(false)] public bool IsSorted { get { // To be sorted there must be some sorts applied. return (SortDescriptions.Count > 0); } } /// /// Gets or sets the string representation of the sort currently applied to the view. /// public string Sort { get { if (IsSorted) { // Build a string of the properties being sorted by System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (ListSortDescription sort in SortDescriptions) { sb.Append(sort.PropertyDescriptor.Name); // Need to signal descending sorts if (sort.SortDirection == ListSortDirection.Descending) { sb.Append(' ').Append(SortDescendingModifier); } // Separate by SortDelimiter sb.Append(SortDelimiter); } // Remove trailing SortDelimiter sb.Remove(sb.Length - 1, 1); // Return the string return sb.ToString(); } return string.Empty; } set { ApplySort(value); } } private bool ShouldSerializeSort() { return !String.IsNullOrEmpty(Sort); } /// /// Gets the direction in which the view is sorted. /// If more than one sort is applied, the direction of the first is returned. /// [Browsable(false)] public ListSortDirection SortDirection { get { if (IsSorted) { return SortDescriptions[0].SortDirection; } else { // We don't really want to throw exceptions. // Calling code should have checked IsSorted to know the true situation. return ListSortDirection.Ascending; } } } /// /// Gets the property the view is currently sorted by. /// If more than one sort is applied, the property of the first is returned. /// [Browsable(false)] public PropertyDescriptor SortProperty { get { if (IsSorted) { return SortDescriptions[0].PropertyDescriptor; } else { // We don't really want to throw exceptions. // Calling code should have checked IsSorted to know the true situation. return null; } } } /// /// Gets the sorts currently applied to the view. /// [Browsable(false)] public ListSortDescriptionCollection SortDescriptions { get { return _sorts; } private set { _sorts = value; _comparer = new SortComparer(value); FilterAndSort(); // Most of the list will have probably changed, so get bound objects to reset. OnListChanged(ListChangedType.Reset, -1); } } /// /// Used to compare items in the view when sorting the _sourceIndices list. /// It supports mutliple sorts by different properties and directions. /// private class SortComparer : IComparer, int>> { private Dictionary> _comparisons; /// /// Creates a new SortComparer that will use the given sorts. /// /// The sorts to apply to the view. public SortComparer(ListSortDescriptionCollection sorts) { _sorts = sorts; // Build the delegates used to compare properties of objects _comparisons = new Dictionary>(); foreach (ListSortDescription sort in sorts) { _comparisons[sort] = BuildComparison(sort.PropertyDescriptor.Name, sort.SortDirection); } } private ListSortDescriptionCollection _sorts; /// /// Compares two items according to the defined sorts. /// /// /// Use of light-weight code generation comparison delegates gives ~10x speed up /// compared to the pure reflection based implementation. /// /// The first item to compare. /// The second item to compare. /// -1 if x < y, 0 if x = y and 1 if x > y. public int Compare(KeyValuePair, int> x, KeyValuePair, int> y) { foreach (ListSortDescription sort in _sorts) { int result = _comparisons[sort](x.Key.Item.Object, y.Key.Item.Object); if (result != 0) { return result; } } return 0; } private static Comparison BuildComparison(string propertyName, ListSortDirection direction) { PropertyInfo pi = typeof(T).GetProperty(propertyName); Debug.Assert(pi != null, string.Format("Property '{0}' is not a member of type '{1}'", propertyName, typeof(T).FullName)); Type pType = pi.PropertyType; bool isNullable = pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>); if (isNullable) pType = pi.PropertyType.GetGenericArguments()[0]; if (typeof(IComparable).IsAssignableFrom(pType)) { if (pType.IsValueType && !isNullable) { return delegate (T x, T y) { int result = (pi.GetValue(x, null) as IComparable).CompareTo(pi.GetValue(y, null)); if (direction == ListSortDirection.Descending) result *= -1; return result; }; } else { return delegate (T x, T y) { int result; object value1 = pi.GetValue(x, null); object value2 = pi.GetValue(y, null); if (value1 != null && value2 != null) result = (value1 as IComparable).CompareTo(value2); else if (value1 == null && value2 != null) result = -1; else if (value1 != null && value2 == null) result = 1; else result = 0; if (direction == ListSortDirection.Descending) result *= -1; return result; }; } } else { return delegate (T o1, T o2) { int result; if (o1.Equals(o2)) result = 0; else result = o1.ToString().CompareTo(o2.ToString()); if (direction == ListSortDirection.Descending) result *= -1; return result; }; } } } private class ExternalSortComparer : IComparer, int>> { public ExternalSortComparer(IComparer comparer) { _comparer = comparer; } private IComparer _comparer; public int Compare(KeyValuePair, int> x, KeyValuePair, int> y) { return _comparer.Compare(x.Key.Item.Object, y.Key.Item.Object); } } private class ExternalSortComparison : IComparer, int>> { public ExternalSortComparison(Comparison comparison) { _comparison = comparison; } private Comparison _comparison; public int Compare(KeyValuePair, int> x, KeyValuePair, int> y) { return _comparison(x.Key.Item.Object, y.Key.Item.Object); } } #endregion #region Searching /// /// Gets if this view supports searching using the Find method. Always returns true. /// bool IBindingList.SupportsSearching { get { return true; } } /// /// Returns the index of the first item in the view who's property equals the given value. /// -1 is returned if no item is found. /// /// The property of each item to check. /// The value being sought. /// The index of the item, or -1 if not found. public int Find(PropertyDescriptor property, object key) { for (int i = 0; i < _sourceIndices.Count; i++) { if (property.GetValue(_sourceIndices[i].Key.Item.Object).Equals(key)) { return i; } } return -1; } /// /// Returns the index of the first item in the view who's property equals the given value. /// -1 is returned if no item is found. /// /// The property name of each item to check. /// The value being sought. /// The index of the item, or -1 if not found. /// /// It is easier for users of this class to enter a property name /// and get the PropertyDescriptor ourselves. /// public int Find(string propertyName, object key) { PropertyDescriptor pd = GetPropertyDescriptor(propertyName); if (pd != null) { return Find(pd, key); } else { throw new ArgumentException(string.Format(Properties.Resources.PropertyNotFound, propertyName, typeof(T).FullName), "propertyName"); } } #endregion #region IBindingList Members /// /// Gets if this view raises the ListChanged event. Always returns true. /// bool IBindingList.SupportsChangeNotification { get { return true; } } /// Explicitly implemented so the type safe AddNew method is exposed instead. object IBindingList.AddNew() { return this.AddNew(); } /// /// Gets if this view allows items to be edited. /// /// Delegates to the source list. bool IBindingList.AllowEdit { get { foreach (object list in SourceLists) { if (list is IBindingList) { if (!(list as IBindingList).AllowEdit) { return false; } } } return true; } } /// /// Gets if this view allows new items to be added using AddNew(). /// /// Delegates to the source list. bool IBindingList.AllowNew { get { if (_newItemsList != null) { if (_newItemsList is IBindingList) { // Respect what the binding list says. return (_newItemsList as IBindingList).AllowNew; } // _newItemsList is a IList, so we can call Add() // it may fail at runtime - but that is the callee's problem return true; } return false; } } /// /// Gets if this view allows items to be removed. /// /// Delegates to the source list. bool IBindingList.AllowRemove { get { foreach (object list in SourceLists) { if (list is IBindingList) { if (!(list as IBindingList).AllowRemove) { return false; } } } return true; } } /// /// Not implemented. /// /// Method not implemented. void IBindingList.AddIndex(PropertyDescriptor property) { throw new NotImplementedException(); } /// /// Not implemented. /// /// Method not implemented. void IBindingList.RemoveIndex(PropertyDescriptor property) { throw new NotImplementedException(); } #endregion #region IRaiseItemChangedEvents Members /// /// Gets if this view raises the ListChanged event when an item changes. Always returns true. /// [Browsable(false)] public bool RaisesItemChangedEvents { get { return true; } } #endregion #region IList Members /// /// value is of the wrong type. /// /// /// is null, so an item cannot be added. /// int IList.Add(object value) { if (value == null) { AddNew(); return Count - 1; } throw new NotSupportedException(Properties.Resources.CannotAddItem); } /// /// Cannot clear this view. /// /// /// Cannot clear this view. /// void IList.Clear() { throw new NotSupportedException(Properties.Resources.CannotClearView); } /// /// Checks if this view contains the given item. /// Note that items excluded by current filter are not searched. /// /// The item to search for. /// True if the item is in the view, else false. bool IList.Contains(object item) { // See if the source indices contain the item if (item is ObjectView) { return _sourceIndices.ContainsKey((ObjectView)item); } else if (item is T) { return _sourceIndices.ContainsItem((T)item); } else { return false; } } /// /// Gets the index in the view of an item. /// /// The item to search for /// The index of the item, or -1 if not found. int IList.IndexOf(object item) { if (item is ObjectView) { return _sourceIndices.IndexOfKey(item as ObjectView); } else if (item is T) { return _sourceIndices.IndexOfItem((T)item); } return -1; } /// /// Cannot insert an external item into this collection. /// /// /// Cannot insert an external item into this collection. /// void IList.Insert(int index, object value) { throw new NotSupportedException(Properties.Resources.CannotInsertItem); } /// /// Gets a value indicating if this view is read-only. /// /// Delegates to the source list. bool IList.IsReadOnly { get { foreach (object list in SourceLists) { if (list is IBindingList) { if (!(list as IBindingList).IsReadOnly) { return false; } } else { return false; } } return true; } } /// /// Always returns false because the view can change size when /// source lists are added. /// bool IList.IsFixedSize { get { return false; } } /// /// Removes the given item from the view and underlying source list. /// /// Either an ObjectView<T> or T to remove. void IList.Remove(object value) { int index = (this as IList).IndexOf(value); (this as IList).RemoveAt(index); } /// /// Removes the item from the view at the given index. /// /// The index of the item to remove. void IList.RemoveAt(int index) { // Get the index in the source list. int sourceIndex = _sourceIndices[index].Value; IList sourceList = _sourceIndices[index].Key.List; if (sourceIndex > -1) { sourceList.RemoveAt(sourceIndex); if (!(sourceList is IBindingList) || !(sourceList as IBindingList).SupportsChangeNotification) { FilterAndSort(); OnListChanged(ListChangedType.Reset, -1); } } else { // The item is not in the source list yet as it is new // So cancel the new operation instead. CancelNew(index); } } /// /// Gets the at the given index. /// /// The index of the item to retrieve. /// An object. /// /// Cannot set an item in the view. /// object IList.this[int index] { get { return this[index]; } set { // The interface requires we supply a setter // But we don't want external code modifying the view // in this manner. throw new NotSupportedException(Properties.Resources.CannotSetItem); } } #endregion #region ICollection Members /// /// Copies the objects of the view to an , starting at a particular System.Array index. /// /// The one-dimensional that is the destination of the elements copied from view. The System.Array must have zero-based indexing. /// The zero-based index in array at which copying begins. void ICollection.CopyTo(Array array, int index) { _sourceIndices.Keys.CopyTo(array, index); } /// /// Gets a value indicating whether access to the is synchronized (thread safe). /// bool ICollection.IsSynchronized { get { return false; } } /// /// Not supported. /// object ICollection.SyncRoot { get { throw new NotSupportedException(Properties.Resources.SyncAccessNotSupported); } } /// /// Gets the number of items currently in the view. This does not include those items /// excluded by the current filter. /// [Browsable(false)] public int Count { get { return _sourceIndices.Count; } } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { for (int i = 0; i < _sourceIndices.Count; i++) yield return _sourceIndices[i].Key.Item.Object; } /// /// Returns an enumerator that iterates through all the items in the view. /// This does not include those items excluded by the current filter. /// /// An IEnumerator to iterate with. IEnumerator IEnumerable.GetEnumerator() { return _sourceIndices.GetKeyEnumerator(); } #endregion #region ITypedList Members /// /// Returns the that represents the properties on each item used to bind data. /// /// Array of property descriptors to navigate object hirerachy to actual item object. It can be null. /// The System.ComponentModel.PropertyDescriptorCollection that represents the properties on each item used to bind data. PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) { PropertyDescriptorCollection originalProps; IEnumerator lists = GetSourceLists().GetEnumerator(); if (lists.MoveNext() && lists.Current is ITypedList) { // Ask the source list for the properties. originalProps = (lists.Current as ITypedList).GetItemProperties(listAccessors); } else { // Get the properties ourself. originalProps = System.Windows.Forms.ListBindingHelper.GetListItemProperties(typeof(T), listAccessors); } if (listAccessors != null && listAccessors.Length > 0) { Type type = originalProps[0].ComponentType; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ObjectView<>)) { originalProps = originalProps[0].GetChildProperties(); } } List newProps = new List(); foreach (PropertyDescriptor pd in AddProvidedViews(originalProps)) { newProps.Add(pd); } return new PropertyDescriptorCollection(newProps.ToArray()); } protected internal bool ShouldProvideView(PropertyDescriptor property) { return ProvidedViewPropertyDescriptor.CanProvideViewOf(property); } protected internal string GetProvidedViewName(PropertyDescriptor sourceListProperty) { return sourceListProperty.Name + "View"; } protected internal object CreateProvidedView(ObjectView @object, PropertyDescriptor sourceListProperty) { object list = sourceListProperty.GetValue(@object); Type viewType = GetProvidedViewType(sourceListProperty); return Activator.CreateInstance(viewType, list); } private static Type GetProvidedViewType(PropertyDescriptor sourceListProperty) { // The source list property type implements IList. // We want to get the type of X. Type typeParam = null; foreach (Type interfaceType in sourceListProperty.PropertyType.GetInterfaces()) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition().Equals(typeof(IList<>))) { typeParam = interfaceType.GetGenericArguments()[0]; } } Debug.Assert(typeParam != null, "Did not get the generic argument for type " + sourceListProperty.PropertyType.FullName); // Now build the BindingListView type. Type viewTypeDef = typeof(BindingListView<>); Type viewType = viewTypeDef.MakeGenericType(typeParam); return viewType; } internal IEnumerable AddProvidedViews(PropertyDescriptorCollection properties) { if (properties.Count < 0) { yield break; } foreach (PropertyDescriptor prop in properties) { if (ShouldProvideView(prop)) { string name = GetProvidedViewName(prop); yield return new ProvidedViewPropertyDescriptor(name, GetProvidedViewType(prop)); } yield return prop; } } /// /// Gets the name of the view. /// /// Unused. Can be null. /// The name of the view. string ITypedList.GetListName(PropertyDescriptor[] listAccessors) { return GetType().Name; } #endregion #region Helper Methods /// /// Creates a new for given property name and sort direction. /// /// The name of the property to sort by. /// The direction in which to sort. /// A ListSortDescription. /// /// Used by external code to simplify sorting the view. /// public ListSortDescription CreateListSortDescription(string propertyName, ListSortDirection direction) { PropertyDescriptor pd = GetPropertyDescriptor(propertyName); if (pd == null) { throw new ArgumentException(string.Format(Properties.Resources.PropertyNotFound, propertyName, typeof(T).FullName), "propertyName"); } return new ListSortDescription(pd, direction); } /// /// Gets the property descriptor for a given property name. /// /// The name of a property of . /// The . private PropertyDescriptor GetPropertyDescriptor(string propertyName) { return TypeDescriptor.GetProperties(typeof(T)).Find(propertyName, false); } /// /// Attaches event handlers to the given 's /// edit life cycle notification events. /// /// The to listen to. private void HookEditableObjectEvents(ObjectView editableObject) { editableObject.EditBegun += new EventHandler(BegunItemEdit); editableObject.EditCancelled += new EventHandler(CancelledItemEdit); editableObject.EditEnded += new EventHandler(EndedItemEdit); } /// /// Detaches event handlers from the given 's /// edit life cycle notification events. /// /// The to stop listening to. private void UnHookEditableObjectEvents(ObjectView editableObject) { editableObject.EditBegun -= new EventHandler(BegunItemEdit); editableObject.EditCancelled -= new EventHandler(CancelledItemEdit); editableObject.EditEnded -= new EventHandler(EndedItemEdit); } /// /// Attaches an event handler to the 's PropertyChanged event. /// /// The to listen to. private void HookPropertyChangedEvent(ObjectView editableObject) { editableObject.PropertyChanged += new PropertyChangedEventHandler(ItemPropertyChanged); } /// /// Detaches the event handler from the 's PropertyChanged event. /// /// The to stop listening to. private void UnHookPropertyChangedEvent(ObjectView editableObject) { editableObject.PropertyChanged -= new PropertyChangedEventHandler(ItemPropertyChanged); } private void BuildSavedList() { _savedSourceLists.Clear(); foreach (object list in GetSourceLists()) { _savedSourceLists.Add(list as IList); } } protected IEnumerable GetSourceLists() { foreach (object obj in _sourceLists) { if (!string.IsNullOrEmpty(DataMember)) { bool found = false; foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(obj)) { if (pd.Name == DataMember) { found = true; yield return pd.GetValue(obj) as IList; break; } } if (!found) { yield return null; } } else if (obj is IListSource) { IListSource src = obj as IListSource; if (src.ContainsListCollection) { IList list = src.GetList() as IList; if (list != null && list.Count > 0) { list = list[0] as IList; yield return list; } else { yield return null; } } else { yield return src.GetList(); } } else { yield return obj as IList; } } } #endregion } } ================================================ FILE: BindingListView/BindingListView.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; namespace Equin.ApplicationFramework { /// /// A searchable, sortable, filterable, data bindable view of a list of objects. /// /// The type of object in the list. public class BindingListView : AggregateBindingListView { /// /// Creates a new of a given IBindingList. /// All items in the list must be of type . /// /// The list of objects to base the view on. public BindingListView(IList list) : base() { DataSource = list; } public BindingListView(IContainer container) : base(container) { DataSource = null; } [DefaultValue(null)] [AttributeProvider(typeof(IListSource))] public IList DataSource { get { IEnumerator e = GetSourceLists().GetEnumerator(); e.MoveNext(); return e.Current; } set { if (value == null) { // Clear all current data SourceLists = new BindingList>(); NewItemsList = null; FilterAndSort(); OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); return; } if (!(value is ICollection)) { // list is not a strongy-type collection. // Check that items in list are all of type T foreach (object item in value) { if (!(item is T)) { throw new ArgumentException(string.Format(Properties.Resources.InvalidListItemType, typeof(T).FullName), "DataSource"); } } } SourceLists = new object[] { value }; NewItemsList = value; } } private bool ShouldSerializeDataSource() { return (SourceLists.Count > 0); } protected override void SourceListsChanged(object sender, ListChangedEventArgs e) { if ((SourceLists.Count > 1 && e.ListChangedType == ListChangedType.ItemAdded) || e.ListChangedType == ListChangedType.ItemDeleted) { throw new Exception("BindingListView allows strictly one source list."); } else { base.SourceListsChanged(sender, e); } } } } ================================================ FILE: BindingListView/BindingListView.csproj ================================================  Debug AnyCPU 8.0.50727 2.0 {75AF36A8-7797-4023-B183-5B63D448420A} Library Properties Equin.ApplicationFramework Equin.ApplicationFramework.BindingListView v4.8 true full false bin\Debug\ DEBUG;TRACE prompt 4 AllRules.ruleset false pdbonly true bin\Release\ TRACE prompt 4 AllRules.ruleset false Component Component ResXFileCodeGenerator Resources.Designer.cs Designer True Resources.resx True SettingsSingleFileGenerator Settings.Designer.cs True Settings.settings True ================================================ FILE: BindingListView/BindingListView.nuspec ================================================ $id$ $version$ $title$ $author$ https://github.com/waynebloss/BindingListView/blob/master/license.txt https://github.com/waynebloss/BindingListView false $description$ The BindingListView .NET library provides a type-safe, sortable, filterable, data-bindable view of one or more lists of objects. It is the business objects equivalent of using a DataView on a DataTable in ADO.NET. If you have a list of objects to display on a Windows Forms UI (e.g. in a DataGridView) and want to allow your user to sort and filter, then this is the library to use! Initial Copyright 2014 .net IBindingListView winforms ================================================ FILE: BindingListView/CompositeItemFilter.cs ================================================ using System; using System.Collections.Generic; namespace Equin.ApplicationFramework { public class CompositeItemFilter : IItemFilter { private List> _filters; public CompositeItemFilter() { _filters = new List>(); } public void AddFilter(IItemFilter filter) { _filters.Add(filter); } public void RemoveFilter(IItemFilter filter) { _filters.Remove(filter); } public bool Include(T item) { foreach (IItemFilter filter in _filters) { if (!filter.Include(item)) { return false; } } return true; } } } ================================================ FILE: BindingListView/IItemFilter.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace Equin.ApplicationFramework { /// /// Defines a general method to test it an item should be included in a . /// /// The type of item to be filtered. public interface IItemFilter { /// /// Tests if the item should be included. /// /// The item to test. /// True if the item should be included, otherwise false. bool Include(T item); } /// /// A dummy filter that is used when no filter is needed. /// It simply includes any and all items tested. /// public class IncludeAllItemFilter : IItemFilter { public bool Include(T item) { // All items are to be included. // So always return true. return true; } public override string ToString() { return Properties.Resources.NoFilter; } #region Singleton Accessor private static IncludeAllItemFilter _instance; /// /// Gets the singleton instance of . /// public static IncludeAllItemFilter Instance { get { if (_instance == null) { _instance = new IncludeAllItemFilter(); } return _instance; } } #endregion } /// /// A filter that uses a user-defined to test items for inclusion in . /// public class PredicateItemFilter : IItemFilter { /// /// Creates a new that uses the specified and default name. /// /// The used to test items. public PredicateItemFilter(Predicate includeDelegate) : this(includeDelegate, null) { // The other constructor is called to do the work. } /// /// Creates a new that uses the specified . /// /// The used to test items. /// The name used for the ToString() return value. public PredicateItemFilter(Predicate includeDelegate, string name) { // We don't allow a null string. Use the default instead. _name = name ?? defaultName; if (includeDelegate != null) { _includeDelegate = includeDelegate; } else { throw new ArgumentNullException("includeDelegate", Properties.Resources.IncludeDelegateCannotBeNull); } } private Predicate _includeDelegate; private string _name; private readonly string defaultName = Properties.Resources.PredicateFilter; public bool Include(T item) { return _includeDelegate(item); } public override string ToString() { return _name; } } // TODO: Implement this class /* public class ExpressionItemFilter : IItemFilter { public ExpressionItemFilter(string expression) { // TODO: Parse expression into predicate } public bool Include(T item) { // TODO: use expression... return true; } } */ // TODO: Implement this class /* public class CSharpItemFilter : IItemFilter { public CSharpItemFilter(string filterSourceCode) { } public bool Include(T item) { // TODO: implement this method... return true; } } */ } ================================================ FILE: BindingListView/INotifyingEditableObject.cs ================================================ using System; using System.ComponentModel; namespace Equin.ApplicationFramework { /// /// Extends by providing events to raise during edit state changes. /// internal interface INotifyingEditableObject : IEditableObject { /// /// An edit has started on the object. /// /// /// This event should be raised from BeginEdit(). /// event EventHandler EditBegun; /// /// The editing of the object was cancelled. /// /// /// This event should be raised from CancelEdit(). /// event EventHandler EditCancelled; /// /// The editing of the object was ended. /// /// /// This event should be raised from EndEdit(). /// event EventHandler EditEnded; } } ================================================ FILE: BindingListView/InvalidSourceListException.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace Equin.ApplicationFramework { [Serializable] public class InvalidSourceListException : Exception { public InvalidSourceListException() : base(Properties.Resources.InvalidSourceList) { } public InvalidSourceListException(string message) : base(message) { } public InvalidSourceListException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } } } ================================================ FILE: BindingListView/MultiSourceIndexList.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Collections; namespace Equin.ApplicationFramework { internal class MultiSourceIndexList : List, int>> { public void Add(IList sourceList, ObjectView item, int index) { Add(new KeyValuePair, int>(new ListItemPair(sourceList, item), index)); } /// /// Searches for a given source index value, returning the list index of the value. /// /// The source index to find. /// Returns the index in this list of the source index, or -1 if not found. public int IndexOfSourceIndex(IList sourceList, int sourceIndex) { for (int i = 0; i < Count; i++) { if (this[i].Key.List == sourceList && this[i].Value == sourceIndex) { return i; } } return -1; } /// /// Searches for a given item, returning the index of the value in this list. /// /// The item to search for. /// Returns the index in this list of the item, or -1 if not found. public int IndexOfItem(T item) { for (int i = 0; i < Count; i++) { if (this[i].Key.Item.Object.Equals(item) && this[i].Value > -1) { return i; } } return -1; } /// /// Searches for a given item's wrapper, returning the index of the value in this list. /// /// The to search for. /// Returns the index in this list of the item, or -1 if not found. public int IndexOfKey(ObjectView item) { for (int i = 0; i < Count; i++) { if (this[i].Key.Item.Equals(item) && this[i].Value > -1) { return i; } } return -1; } /// /// Checks if the list contains a given item. /// /// The item to check for. /// True if the item is contained in the list, otherwise false. public bool ContainsItem(T item) { return (IndexOfItem(item) != -1); } /// /// Checks if the list contains a given key. /// /// The key to search for. /// True if the key is contained in the list, otherwise false. public bool ContainsKey(ObjectView key) { return (IndexOfKey(key) != -1); } /// /// Returns an array of all the keys in the list. /// public ObjectView[] Keys { get { return ConvertAll>(new Converter, int>, ObjectView>( delegate(KeyValuePair, int> kvp) { return kvp.Key.Item; } )).ToArray(); } } /// /// Returns an to iterate over all the keys in this list. /// /// The to use. public IEnumerator> GetKeyEnumerator() { foreach (KeyValuePair, int> kvp in this) { yield return kvp.Key.Item; } } } internal class ListItemPair { private IList _list; private ObjectView _item; public ListItemPair(IList list, ObjectView item) { _list = list; _item = item; } public IList List { get { return _list; } } public ObjectView Item { get { return _item; } } } } ================================================ FILE: BindingListView/ObjectView.cs ================================================ using System; using System.ComponentModel; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; namespace Equin.ApplicationFramework { /// /// Serves a wrapper for items being viewed in a . /// This class implements so will raise the necessary events during /// the item edit life-cycle. /// /// /// If implements this class will call BeginEdit/CancelEdit/EndEdit on the object as well. /// If implements this class will use that implementation as its own. /// /// The type of object being viewed. [Serializable] public class ObjectView : INotifyingEditableObject, IDataErrorInfo, INotifyPropertyChanged, ICustomTypeDescriptor, IProvideViews { /// /// Creates a new wrapper for a object. /// /// The object being wrapped. public ObjectView(T @object, AggregateBindingListView parent) { _parent = parent; Object = @object; if (Object is INotifyPropertyChanged) { ((INotifyPropertyChanged)Object).PropertyChanged += new PropertyChangedEventHandler(ObjectPropertyChanged); } if (typeof(ICustomTypeDescriptor).IsAssignableFrom(typeof(T))) { _isCustomTypeDescriptor = true; _customTypeDescriptor = Object as ICustomTypeDescriptor; Debug.Assert(_customTypeDescriptor != null); } _providedViews = new Dictionary(); CreateProvidedViews(); } /// /// The view containing this ObjectView. /// private AggregateBindingListView _parent; /// /// Flag that signals if we are currently editing the object. /// private bool _editing; /// /// The actual object being edited. /// private T _object; /// /// Flag set to true if type of T implements ICustomTypeDescriptor /// private bool _isCustomTypeDescriptor; /// /// Holds the Object pre-casted ICustomTypeDescriptor (if supported). /// private ICustomTypeDescriptor _customTypeDescriptor; /// /// A collection of BindingListView objects, indexed by name, for views auto-provided for any generic IList members. /// private Dictionary _providedViews; /// /// Gets the object being edited. /// public T Object { get { return _object; } private set { if (value == null) { throw new ArgumentNullException("Object", Properties.Resources.ObjectCannotBeNull); } _object = value; } } public object GetProvidedView(string name) { return _providedViews[name]; } /// /// Casts an ObjectView<T> to a T by getting the wrapped T object. /// /// The ObjectView<T> to cast to a T /// The object that is wrapped. public static explicit operator T(ObjectView eo) { return eo.Object; } public override bool Equals(object obj) { if (obj == null) { return false; } if (obj is T) { return Object.Equals(obj); } else if (obj is ObjectView) { return Object.Equals((obj as ObjectView).Object); } else { return Equals(obj); } } public override int GetHashCode() { return Object.GetHashCode(); } public override string ToString() { return Object.ToString(); } private void ObjectPropertyChanged(object sender, PropertyChangedEventArgs e) { // Raise our own event OnPropertyChanged(sender, new PropertyChangedEventArgs(e.PropertyName)); } private bool ShouldProvideViewOf(PropertyDescriptor listProp) { return _parent.ShouldProvideView(listProp); } private string GetProvidedViewName(PropertyDescriptor listProp) { return _parent.GetProvidedViewName(listProp); } private void CreateProvidedViews() { foreach (PropertyDescriptor prop in (this as ICustomTypeDescriptor).GetProperties()) { if (ShouldProvideViewOf(prop)) { object view = _parent.CreateProvidedView(this, prop); string viewName = GetProvidedViewName(prop); _providedViews.Add(viewName, view); } } } #region INotifyEditableObject Members /// /// Indicates an edit has just begun. /// [field: NonSerialized] public event EventHandler EditBegun; /// /// Indicates the edit was cancelled. /// [field: NonSerialized] public event EventHandler EditCancelled; /// /// Indicated the edit was ended. /// [field: NonSerialized] public event EventHandler EditEnded; protected virtual void OnEditBegun() { if (EditBegun != null) { EditBegun(this, EventArgs.Empty); } } protected virtual void OnEditCancelled() { if (EditCancelled != null) { EditCancelled(this, EventArgs.Empty); } } protected virtual void OnEditEnded() { if (EditEnded != null) { EditEnded(this, EventArgs.Empty); } } #endregion #region IEditableObject Members public void BeginEdit() { // As per documentation, this method may get called multiple times for a single edit. // So we set a flag to only honor the first call. if (!_editing) { _editing = true; // If possible call the object's BeginEdit() method // to let it do what ever it needs e.g. save state if (Object is IEditableObject) { ((IEditableObject)Object).BeginEdit(); } // Raise the EditBegun event. OnEditBegun(); } } public void CancelEdit() { // We can only cancel if currently editing if (_editing) { // If possible call the object's CancelEdit() method // to let it do what ever it needs e.g. rollback state if (Object is IEditableObject) { ((IEditableObject)Object).CancelEdit(); } // Raise the EditCancelled event. OnEditCancelled(); // No longer editing now. _editing = false; } } public void EndEdit() { // We can only end if currently editing if (_editing) { // If possible call the object's EndEdit() method // to let it do what ever it needs e.g. commit state if (Object is IEditableObject) { ((IEditableObject)Object).EndEdit(); } // Raise the EditEnded event. OnEditEnded(); // No longer editing now. _editing = false; } } #endregion #region IDataErrorInfo Members // If the wrapped Object support IDataErrorInfo we forward calls to it. // Otherwise, we just return empty strings that signal "no error". string IDataErrorInfo.Error { get { if (Object is IDataErrorInfo) { return ((IDataErrorInfo)Object).Error; } return string.Empty; } } string IDataErrorInfo.this[string columnName] { get { if (Object is IDataErrorInfo) { return ((IDataErrorInfo)Object)[columnName]; } return string.Empty; } } #endregion #region INotifyPropertyChanged Members [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs args) { if (PropertyChanged != null) { PropertyChanged(sender, args); } } #endregion #region ICustomTypeDescriptor Members AttributeCollection ICustomTypeDescriptor.GetAttributes() { if (_isCustomTypeDescriptor) { return _customTypeDescriptor.GetAttributes(); } else { return TypeDescriptor.GetAttributes(Object); } } string ICustomTypeDescriptor.GetClassName() { if (_isCustomTypeDescriptor) { return _customTypeDescriptor.GetClassName(); } else { return typeof(T).FullName; } } string ICustomTypeDescriptor.GetComponentName() { if (_isCustomTypeDescriptor) { return _customTypeDescriptor.GetComponentName(); } else { return TypeDescriptor.GetFullComponentName(Object); } } TypeConverter ICustomTypeDescriptor.GetConverter() { if (_isCustomTypeDescriptor) { return _customTypeDescriptor.GetConverter(); } else { return TypeDescriptor.GetConverter(Object); } } EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { if (_isCustomTypeDescriptor) { return _customTypeDescriptor.GetDefaultEvent(); } else { return TypeDescriptor.GetDefaultEvent(Object); } } PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { if (_isCustomTypeDescriptor) { return _customTypeDescriptor.GetDefaultProperty(); } else { return TypeDescriptor.GetDefaultProperty(Object); } } object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { if (_isCustomTypeDescriptor) { return _customTypeDescriptor.GetEditor(editorBaseType); } else { return null; } } EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { if (_isCustomTypeDescriptor) { return _customTypeDescriptor.GetEvents(); } else { return TypeDescriptor.GetEvents(Object, attributes); } } EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { return (this as ICustomTypeDescriptor).GetEvents(null); } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { List props; if (_isCustomTypeDescriptor) { props = new List(_parent.AddProvidedViews(_customTypeDescriptor.GetProperties(attributes))); } else { props = new List(_parent.AddProvidedViews(TypeDescriptor.GetProperties(Object, attributes))); } return new PropertyDescriptorCollection(props.ToArray()); } PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return (this as ICustomTypeDescriptor).GetProperties(null); } object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { if (_isCustomTypeDescriptor) { return _customTypeDescriptor.GetPropertyOwner(pd); } else { return Object; } } #endregion } public interface IProvideViews { object GetProvidedView(string name); } } ================================================ FILE: BindingListView/Properties/AssemblyInfo.cs ================================================ using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 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("BindingListView")] [assembly: AssemblyDescription("Advanced, data bindable, object list view.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Equin")] [assembly: AssemblyProduct("BindingListView")] [assembly: AssemblyCopyright("Copyright Andrew Davey 2005")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM componenets. 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("709a6d28-582d-423b-ae59-83e0945c4227")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // [assembly: AssemblyVersion("1.4.*")] [assembly: AssemblyFileVersion("1.4.1.0")] [assembly: CLSCompliant(true)] ================================================ FILE: BindingListView/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Equin.ApplicationFramework.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Equin.ApplicationFramework.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Cannot add an external item to the view. Use AddNew() or add to source list instead.. /// internal static string CannotAddItem { get { return ResourceManager.GetString("CannotAddItem", resourceCulture); } } /// /// Looks up a localized string similar to Cannot add a new item due to no object being provided in the AddingNew event and a lack of default public constructor.. /// internal static string CannotAddNewItem { get { return ResourceManager.GetString("CannotAddNewItem", resourceCulture); } } /// /// Looks up a localized string similar to Cannot add a new item when NewItemsList is null.. /// internal static string CannotAddWhenNewItemsListNull { get { return ResourceManager.GetString("CannotAddWhenNewItemsListNull", resourceCulture); } } /// /// Looks up a localized string similar to Cannot clear this view.. /// internal static string CannotClearView { get { return ResourceManager.GetString("CannotClearView", resourceCulture); } } /// /// Looks up a localized string similar to Cannot insert an external item into this collection.. /// internal static string CannotInsertItem { get { return ResourceManager.GetString("CannotInsertItem", resourceCulture); } } /// /// Looks up a localized string similar to Cannot set an item in the view.. /// internal static string CannotSetItem { get { return ResourceManager.GetString("CannotSetItem", resourceCulture); } } /// /// Looks up a localized string similar to includeDelegate cannot be null.. /// internal static string IncludeDelegateCannotBeNull { get { return ResourceManager.GetString("IncludeDelegateCannotBeNull", resourceCulture); } } /// /// Looks up a localized string similar to Item in list is not of type {0}.. /// internal static string InvalidListItemType { get { return ResourceManager.GetString("InvalidListItemType", resourceCulture); } } /// /// Looks up a localized string similar to Source list does not implement IList.. /// internal static string InvalidSourceList { get { return ResourceManager.GetString("InvalidSourceList", resourceCulture); } } /// /// Looks up a localized string similar to Item was not of type {0}.. /// internal static string ItemTypeIncorrect { get { return ResourceManager.GetString("ItemTypeIncorrect", resourceCulture); } } /// /// Looks up a localized string similar to (no filter). /// internal static string NoFilter { get { return ResourceManager.GetString("NoFilter", resourceCulture); } } /// /// Looks up a localized string similar to Object cannot be null.. /// internal static string ObjectCannotBeNull { get { return ResourceManager.GetString("ObjectCannotBeNull", resourceCulture); } } /// /// Looks up a localized string similar to (predicate filter). /// internal static string PredicateFilter { get { return ResourceManager.GetString("PredicateFilter", resourceCulture); } } /// /// Looks up a localized string similar to Property {0} does not exist in type {1}.. /// internal static string PropertyNotFound { get { return ResourceManager.GetString("PropertyNotFound", resourceCulture); } } /// /// Looks up a localized string similar to Source list already added to the view.. /// internal static string SourceListAlreadyAdded { get { return ResourceManager.GetString("SourceListAlreadyAdded", resourceCulture); } } /// /// Looks up a localized string similar to Source list is not in the view.. /// internal static string SourceListNotFound { get { return ResourceManager.GetString("SourceListNotFound", resourceCulture); } } /// /// Looks up a localized string similar to SourceLists cannot be null.. /// internal static string SourceListsNull { get { return ResourceManager.GetString("SourceListsNull", resourceCulture); } } /// /// Looks up a localized string similar to Synchronized access to the view is not supported.. /// internal static string SyncAccessNotSupported { get { return ResourceManager.GetString("SyncAccessNotSupported", resourceCulture); } } } } ================================================ FILE: BindingListView/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Cannot add an external item to the view. Use AddNew() or add to source list instead. Cannot add a new item due to no object being provided in the AddingNew event and a lack of default public constructor. Cannot add a new item when NewItemsList is null. Cannot clear this view. Cannot insert an external item into this collection. Cannot set an item in the view. includeDelegate cannot be null. Item in list is not of type {0}. Source list does not implement IList. Item was not of type {0}. (no filter) Object cannot be null. (predicate filter) Property {0} does not exist in type {1}. Source list already added to the view. Source list is not in the view. SourceLists cannot be null. Synchronized access to the view is not supported. ================================================ FILE: BindingListView/Properties/Settings.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace Equin.ApplicationFramework.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { get { return defaultInstance; } } } } ================================================ FILE: BindingListView/Properties/Settings.settings ================================================  ================================================ FILE: BindingListView/ProvidedViewPropertyDescriptor.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; namespace Equin.ApplicationFramework { class ProvidedViewPropertyDescriptor : PropertyDescriptor { public ProvidedViewPropertyDescriptor(string name, Type propertyType) : base(name, null) { _propertyType = propertyType; } private Type _propertyType; public override bool CanResetValue(object component) { return false; } public override Type ComponentType { get { return typeof(IProvideViews); } } public override object GetValue(object component) { if (component is IProvideViews) { return (component as IProvideViews).GetProvidedView(Name); } throw new ArgumentException("Type of component is not valid.", "component"); } public override bool IsReadOnly { get { return true; } } public override Type PropertyType { get { return _propertyType; } } public override void ResetValue(object component) { throw new NotSupportedException(); } public override void SetValue(object component, object value) { throw new NotSupportedException(); } public override bool ShouldSerializeValue(object component) { return false; } /// /// Gets if a BindingListView can be provided for given property. /// The property type must implement IList<> i.e. some generic IList. /// public static bool CanProvideViewOf(PropertyDescriptor prop) { Type propType = prop.PropertyType; foreach (Type interfaceType in propType.GetInterfaces()) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition().Equals(typeof(IList<>))) { return true; } } return false; } } } ================================================ FILE: CheckBoxComboBox/CheckBoxComboBox.Designer.cs ================================================ namespace PresentationControls { partial class CheckBoxComboBox { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion } } ================================================ FILE: CheckBoxComboBox/CheckBoxComboBox.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Reflection; using System.Windows.Forms; namespace PresentationControls { /// /// Martin Lottering : 2007-10-27 /// -------------------------------- /// This is a usefull control in Filters. Allows you to save space and can replace a Grouped Box of CheckBoxes. /// Currently used on the TasksFilter for TaskStatusses, which means the user can select which Statusses to include /// in the "Search". /// This control does not implement a CheckBoxListBox, instead it adds a wrapper for the normal ComboBox and Items. /// See the CheckBoxItems property. /// ---------------- /// ALSO IMPORTANT: In Data Binding when setting the DataSource. The ValueMember must be a bool type property, because it will /// be binded to the Checked property of the displayed CheckBox. Also see the DisplayMemberSingleItem for more information. /// ---------------- /// Extends the CodeProject PopupComboBox "Simple pop-up control" "http://www.codeproject.com/cs/miscctrl/simplepopup.asp" /// by Lukasz Swiatkowski. /// public partial class CheckBoxComboBox : PopupComboBox { #region CONSTRUCTOR public CheckBoxComboBox() : base() { InitializeComponent(); _CheckBoxProperties = new CheckBoxProperties(); _CheckBoxProperties.PropertyChanged += new EventHandler(_CheckBoxProperties_PropertyChanged); // Dumps the ListControl in a(nother) Container to ensure the ScrollBar on the ListControl does not // Paint over the Size grip. Setting the Padding or Margin on the Popup or host control does // not work as I expected. I don't think it can work that way. CheckBoxComboBoxListControlContainer ContainerControl = new CheckBoxComboBoxListControlContainer(); _CheckBoxComboBoxListControl = new CheckBoxComboBoxListControl(this); _CheckBoxComboBoxListControl.Items.CheckBoxCheckedChanged += new EventHandler(Items_CheckBoxCheckedChanged); ContainerControl.Controls.Add(_CheckBoxComboBoxListControl); // This padding spaces neatly on the left-hand side and allows space for the size grip at the bottom. ContainerControl.Padding = new Padding(4, 0, 0, 14); // The ListControl FILLS the ListContainer. _CheckBoxComboBoxListControl.Dock = DockStyle.Fill; // The DropDownControl used by the base class. Will be wrapped in a popup by the base class. DropDownControl = ContainerControl; // Must be set after the DropDownControl is set, since the popup is recreated. // NOTE: I made the dropDown protected so that it can be accessible here. It was private. dropDown.Resizable = true; } #endregion #region PRIVATE FIELDS /// /// The checkbox list control. The public CheckBoxItems property provides a direct reference to its Items. /// internal CheckBoxComboBoxListControl _CheckBoxComboBoxListControl; /// /// In DataBinding operations, this property will be used as the DisplayMember in the CheckBoxComboBoxListBox. /// The normal/existing "DisplayMember" property is used by the TextBox of the ComboBox to display /// a concatenated Text of the items selected. This concatenation and its formatting however is controlled /// by the Binded object, since it owns that property. /// private string _DisplayMemberSingleItem = null; internal bool _MustAddHiddenItem = false; #endregion #region PRIVATE OPERATIONS /// /// Builds a CSV string of the items selected. /// internal string GetCSVText(bool skipFirstItem) { var itemText = _CheckBoxComboBoxListControl.Items.Where(x => x.Checked).Select(x => x.Text); if (DropDownStyle == ComboBoxStyle.DropDownList && DataSource == null && skipFirstItem) return string.Join(", ", itemText.Skip(1).ToArray()); else return string.Join(", ", itemText.ToArray()); } #endregion #region PUBLIC PROPERTIES /// /// A direct reference to the Items of CheckBoxComboBoxListControl. /// You can use it to Get or Set the Checked status of items manually if you want. /// But do not manipulate the List itself directly, e.g. Adding and Removing, /// since the list is synchronised when shown with the ComboBox.Items. So for changing /// the list contents, use Items instead. /// [Browsable(false)] public CheckBoxComboBoxItemList CheckBoxItems { get { // Added to ensure the CheckBoxItems are ALWAYS // available for modification via code. if (_CheckBoxComboBoxListControl.Items.Count != Items.Count) _CheckBoxComboBoxListControl.SynchroniseControlsWithComboBoxItems(); return _CheckBoxComboBoxListControl.Items; } } /// /// The DataSource of the combobox. Refreshes the CheckBox wrappers when this is set. /// public new object DataSource { get { return base.DataSource; } set { base.DataSource = value; if (!string.IsNullOrEmpty(ValueMember)) // This ensures that at least the checkboxitems are available to be initialised. _CheckBoxComboBoxListControl.SynchroniseControlsWithComboBoxItems(); } } /// /// The ValueMember of the combobox. Refreshes the CheckBox wrappers when this is set. /// public new string ValueMember { get { return base.ValueMember; } set { base.ValueMember = value; if (!string.IsNullOrEmpty(ValueMember)) // This ensures that at least the checkboxitems are available to be initialised. _CheckBoxComboBoxListControl.SynchroniseControlsWithComboBoxItems(); } } /// /// In DataBinding operations, this property will be used as the DisplayMember in the CheckBoxComboBoxListBox. /// The normal/existing "DisplayMember" property is used by the TextBox of the ComboBox to display /// a concatenated Text of the items selected. This concatenation however is controlled by the Binded /// object, since it owns that property. /// public string DisplayMemberSingleItem { get { if (string.IsNullOrEmpty(_DisplayMemberSingleItem)) return DisplayMember; else return _DisplayMemberSingleItem; } set { _DisplayMemberSingleItem = value; } } /// /// Made this property Browsable again, since the Base Popup hides it. This class uses it again. /// Gets an object representing the collection of the items contained in this /// System.Windows.Forms.ComboBox. /// /// A System.Windows.Forms.ComboBox.ObjectCollection representing the items in /// the System.Windows.Forms.ComboBox. /// [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public new ObjectCollection Items { get { return base.Items; } } #endregion #region EVENTS & EVENT HANDLERS public event EventHandler CheckBoxCheckedChanged; private void Items_CheckBoxCheckedChanged(object sender, EventArgs e) { OnCheckBoxCheckedChanged(sender, e); } #endregion #region EVENT CALLERS and OVERRIDES e.g. OnResize() protected void OnCheckBoxCheckedChanged(object sender, EventArgs e) { string ListText = GetCSVText(true); // The DropDownList style seems to require that the text // part of the "textbox" should match a single item. if (DropDownStyle != ComboBoxStyle.DropDownList) Text = ListText; // This refreshes the Text of the first item (which is not visible) else if (DataSource == null) { Items[0] = ListText; // Keep the hidden item and first checkbox item in // sync in order to ensure the Synchronise process // can match the items. CheckBoxItems[0].ComboBoxItem = ListText; } EventHandler handler = CheckBoxCheckedChanged; if (handler != null) handler(sender, e); } /// /// Will add an invisible item when the style is DropDownList, /// to help maintain the correct text in main TextBox. /// /// protected override void OnDropDownStyleChanged(EventArgs e) { base.OnDropDownStyleChanged(e); if (DropDownStyle == ComboBoxStyle.DropDownList && DataSource == null && !DesignMode) _MustAddHiddenItem = true; } protected override void OnResize(EventArgs e) { // When the ComboBox is resized, the width of the dropdown // is also resized to match the width of the ComboBox. I think it looks better. Size Size = new Size(Width, DropDownControl.Height); dropDown.Size = Size; base.OnResize(e); } #endregion #region PUBLIC OPERATIONS /// /// A function to clear/reset the list. /// (Ubiklou : http://www.codeproject.com/KB/combobox/extending_combobox.aspx?msg=2526813#xx2526813xx) /// public void Clear() { this.Items.Clear(); if (DropDownStyle == ComboBoxStyle.DropDownList && DataSource == null) _MustAddHiddenItem = true; } /// /// Uncheck all items. /// public void ClearSelection() { foreach (CheckBoxComboBoxItem Item in CheckBoxItems) if (Item.Checked) Item.Checked = false; } #endregion #region CHECKBOX PROPERTIES (DEFAULTS) private CheckBoxProperties _CheckBoxProperties; /// /// The properties that will be assigned to the checkboxes as default values. /// [Description("The properties that will be assigned to the checkboxes as default values.")] [Browsable(true)] public CheckBoxProperties CheckBoxProperties { get { return _CheckBoxProperties; } set { _CheckBoxProperties = value; _CheckBoxProperties_PropertyChanged(this, EventArgs.Empty); } } private void _CheckBoxProperties_PropertyChanged(object sender, EventArgs e) { foreach (CheckBoxComboBoxItem Item in CheckBoxItems) Item.ApplyProperties(CheckBoxProperties); } #endregion protected override void WndProc(ref Message m) { // 323 : Item Added // 331 : Clearing if (m.Msg == 331 && DropDownStyle == ComboBoxStyle.DropDownList && DataSource == null) { _MustAddHiddenItem = true; } base.WndProc(ref m); } } /// /// A container control for the ListControl to ensure the ScrollBar on the ListControl does not /// Paint over the Size grip. Setting the Padding or Margin on the Popup or host control does /// not work as I expected. /// [ToolboxItem(false)] public class CheckBoxComboBoxListControlContainer : UserControl { #region CONSTRUCTOR public CheckBoxComboBoxListControlContainer() : base() { BackColor = SystemColors.Window; BorderStyle = BorderStyle.FixedSingle; AutoScaleMode = AutoScaleMode.Inherit; ResizeRedraw = true; // If you don't set this, then resize operations cause an error in the base class. MinimumSize = new Size(1, 1); MaximumSize = new Size(500, 500); } #endregion #region RESIZE OVERRIDE REQUIRED BY THE POPUP CONTROL /// /// Prescribed by the Popup class to ensure Resize operations work correctly. /// /// protected override void WndProc(ref Message m) { if ((Parent as Popup).ProcessResizing(ref m)) { return; } base.WndProc(ref m); } #endregion } /// /// This ListControl that pops up to the User. It contains the CheckBoxComboBoxItems. /// The items are docked DockStyle.Top in this control. /// [ToolboxItem(false)] public class CheckBoxComboBoxListControl : ScrollableControl { #region CONSTRUCTOR public CheckBoxComboBoxListControl(CheckBoxComboBox owner) : base() { DoubleBuffered = true; _CheckBoxComboBox = owner; _Items = new CheckBoxComboBoxItemList(_CheckBoxComboBox); BackColor = SystemColors.Window; AutoScroll = true; ResizeRedraw = true; // if you don't set this, a Resize operation causes an error in the base class. MinimumSize = new Size(1, 1); MaximumSize = new Size(500, 500); } #endregion #region PRIVATE PROPERTIES /// /// Simply a reference to the CheckBoxComboBox. /// private CheckBoxComboBox _CheckBoxComboBox; /// /// A Typed list of ComboBoxCheckBoxItems. /// private CheckBoxComboBoxItemList _Items; #endregion public CheckBoxComboBoxItemList Items { get { return _Items; } } #region RESIZE OVERRIDE REQUIRED BY THE POPUP CONTROL /// /// Prescribed by the Popup control to enable Resize operations. /// /// protected override void WndProc(ref Message m) { if ((Parent.Parent as Popup).ProcessResizing(ref m)) { return; } base.WndProc(ref m); } #endregion #region PROTECTED MEMBERS protected override void OnVisibleChanged(EventArgs e) { // Synchronises the CheckBox list with the items in the ComboBox. SynchroniseControlsWithComboBoxItems(); base.OnVisibleChanged(e); } /// /// Maintains the controls displayed in the list by keeping them in sync with the actual /// items in the combobox. (e.g. removing and adding as well as ordering) /// public void SynchroniseControlsWithComboBoxItems() { SuspendLayout(); if (_CheckBoxComboBox._MustAddHiddenItem) { _CheckBoxComboBox.Items.Insert( 0, _CheckBoxComboBox.GetCSVText(false)); // INVISIBLE ITEM _CheckBoxComboBox.SelectedIndex = 0; _CheckBoxComboBox._MustAddHiddenItem = false; } Controls.Clear(); #region Disposes all items that are no longer in the combo box list for (int Index = _Items.Count - 1; Index >= 0; Index--) { CheckBoxComboBoxItem Item = _Items[Index]; if (!_CheckBoxComboBox.Items.Contains(Item.ComboBoxItem)) { _Items.Remove(Item); Item.Dispose(); } } #endregion #region Recreate the list in the same order of the combo box items bool HasHiddenItem = _CheckBoxComboBox.DropDownStyle == ComboBoxStyle.DropDownList && _CheckBoxComboBox.DataSource == null && !DesignMode; CheckBoxComboBoxItemList NewList = new CheckBoxComboBoxItemList(_CheckBoxComboBox); for(int Index0 = 0; Index0 <= _CheckBoxComboBox.Items.Count - 1; Index0 ++) { object Object = _CheckBoxComboBox.Items[Index0]; CheckBoxComboBoxItem Item = null; // The hidden item could match any other item when only // one other item was selected. if (Index0 == 0 && HasHiddenItem && _Items.Count > 0) Item = _Items[0]; else { int StartIndex = HasHiddenItem ? 1 // Skip the hidden item, it could match : 0; for (int Index1 = StartIndex; Index1 <= _Items.Count - 1; Index1++) { if (_Items[Index1].ComboBoxItem == Object) { Item = _Items[Index1]; break; } } } if (Item == null) { Item = new CheckBoxComboBoxItem(_CheckBoxComboBox, Object); Item.ApplyProperties(_CheckBoxComboBox.CheckBoxProperties); } NewList.Add(Item); Item.Dock = DockStyle.Top; } _Items.Clear(); _Items.AddRange(NewList); #endregion #region Add the items to the controls in reversed order to maintain correct docking order if (NewList.Count > 0) { // This reverse helps to maintain correct docking order. NewList.Reverse(); // If you get an error here that "Cannot convert to the desired // type, it probably means the controls are not binding correctly. // The Checked property is binded to the ValueMember property. // It must be a bool for example. Controls.AddRange(NewList.ToArray()); } #endregion // Keep the first item invisible if (_CheckBoxComboBox.DropDownStyle == ComboBoxStyle.DropDownList && _CheckBoxComboBox.DataSource == null && !DesignMode) _CheckBoxComboBox.CheckBoxItems[0].Visible = false; ResumeLayout(); } #endregion } /// /// The CheckBox items displayed in the Popup of the ComboBox. /// [ToolboxItem(false)] public class CheckBoxComboBoxItem : CheckBox { #region CONSTRUCTOR /// /// /// /// A reference to the CheckBoxComboBox. /// A reference to the item in the ComboBox.Items that this object is extending. public CheckBoxComboBoxItem(CheckBoxComboBox owner, object comboBoxItem) : base() { DoubleBuffered = true; _CheckBoxComboBox = owner; _ComboBoxItem = comboBoxItem; if (_CheckBoxComboBox.DataSource != null) AddBindings(); else Text = comboBoxItem.ToString(); } #endregion #region PRIVATE PROPERTIES /// /// A reference to the CheckBoxComboBox. /// private CheckBoxComboBox _CheckBoxComboBox; /// /// A reference to the Item in ComboBox.Items that this object is extending. /// private object _ComboBoxItem; #endregion #region PUBLIC PROPERTIES /// /// A reference to the Item in ComboBox.Items that this object is extending. /// public object ComboBoxItem { get { return _ComboBoxItem; } internal set { _ComboBoxItem = value; } } #endregion #region BINDING HELPER /// /// When using Data Binding operations via the DataSource property of the ComboBox. This /// adds the required Bindings for the CheckBoxes. /// public void AddBindings() { // Note, the text uses "DisplayMemberSingleItem", not "DisplayMember" (unless its not assigned) DataBindings.Add( "Text", _ComboBoxItem, _CheckBoxComboBox.DisplayMemberSingleItem ); // The ValueMember must be a bool type property usable by the CheckBox.Checked. DataBindings.Add( "Checked", _ComboBoxItem, _CheckBoxComboBox.ValueMember, false, // This helps to maintain proper selection state in the Binded object, // even when the controls are added and removed. DataSourceUpdateMode.OnPropertyChanged, false, null, null); // Helps to maintain the Checked status of this // checkbox before the control is visible if (_ComboBoxItem is INotifyPropertyChanged) ((INotifyPropertyChanged)_ComboBoxItem).PropertyChanged += new PropertyChangedEventHandler( CheckBoxComboBoxItem_PropertyChanged); } #endregion #region PROTECTED MEMBERS protected override void OnCheckedChanged(EventArgs e) { // Found that when this event is raised, the bool value of the binded item is not yet updated. if (_CheckBoxComboBox.DataSource != null) { PropertyInfo PI = ComboBoxItem.GetType().GetProperty(_CheckBoxComboBox.ValueMember); PI.SetValue(ComboBoxItem, Checked, null); } base.OnCheckedChanged(e); // Forces a refresh of the Text displayed in the main TextBox of the ComboBox, // since that Text will most probably represent a concatenation of selected values. // Also see DisplayMemberSingleItem on the CheckBoxComboBox for more information. if (_CheckBoxComboBox.DataSource != null) { string OldDisplayMember = _CheckBoxComboBox.DisplayMember; _CheckBoxComboBox.DisplayMember = null; _CheckBoxComboBox.DisplayMember = OldDisplayMember; } } #endregion #region HELPER MEMBERS internal void ApplyProperties(CheckBoxProperties properties) { this.Appearance = properties.Appearance; this.AutoCheck = properties.AutoCheck; this.AutoEllipsis = properties.AutoEllipsis; this.AutoSize = properties.AutoSize; this.CheckAlign = properties.CheckAlign; this.FlatAppearance.BorderColor = properties.FlatAppearanceBorderColor; this.FlatAppearance.BorderSize = properties.FlatAppearanceBorderSize; this.FlatAppearance.CheckedBackColor = properties.FlatAppearanceCheckedBackColor; this.FlatAppearance.MouseDownBackColor = properties.FlatAppearanceMouseDownBackColor; this.FlatAppearance.MouseOverBackColor = properties.FlatAppearanceMouseOverBackColor; this.FlatStyle = properties.FlatStyle; this.ForeColor = properties.ForeColor; this.RightToLeft = properties.RightToLeft; this.TextAlign = properties.TextAlign; this.ThreeState = properties.ThreeState; } #endregion #region EVENT HANDLERS - ComboBoxItem (DataSource) /// /// Added this handler because the control doesn't seem /// to initialize correctly until shown for the first /// time, which also means the summary text value /// of the combo is out of sync initially. /// private void CheckBoxComboBoxItem_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == _CheckBoxComboBox.ValueMember) Checked = (bool)_ComboBoxItem .GetType() .GetProperty(_CheckBoxComboBox.ValueMember) .GetValue(_ComboBoxItem, null); } #endregion } /// /// A Typed List of the CheckBox items. /// Simply a wrapper for the CheckBoxComboBox.Items. A list of CheckBoxComboBoxItem objects. /// This List is automatically synchronised with the Items of the ComboBox and extended to /// handle the additional boolean value. That said, do not Add or Remove using this List, /// it will be lost or regenerated from the ComboBox.Items. /// [ToolboxItem(false)] public class CheckBoxComboBoxItemList : List { #region CONSTRUCTORS public CheckBoxComboBoxItemList(CheckBoxComboBox checkBoxComboBox) { _CheckBoxComboBox = checkBoxComboBox; } #endregion #region PRIVATE FIELDS private CheckBoxComboBox _CheckBoxComboBox; #endregion #region EVENTS, This could be moved to the list control if needed public event EventHandler CheckBoxCheckedChanged; protected void OnCheckBoxCheckedChanged(object sender, EventArgs e) { EventHandler handler = CheckBoxCheckedChanged; if (handler != null) handler(sender, e); } private void item_CheckedChanged(object sender, EventArgs e) { OnCheckBoxCheckedChanged(sender, e); } #endregion #region LIST MEMBERS & OBSOLETE INDICATORS [Obsolete("Do not add items to this list directly. Use the ComboBox items instead.", false)] public new void Add(CheckBoxComboBoxItem item) { item.CheckedChanged += new EventHandler(item_CheckedChanged); base.Add(item); } public new void AddRange(IEnumerable collection) { foreach (CheckBoxComboBoxItem Item in collection) Item.CheckedChanged += new EventHandler(item_CheckedChanged); base.AddRange(collection); } public new void Clear() { foreach (CheckBoxComboBoxItem Item in this) Item.CheckedChanged -= item_CheckedChanged; base.Clear(); } [Obsolete("Do not remove items from this list directly. Use the ComboBox items instead.", false)] public new bool Remove(CheckBoxComboBoxItem item) { item.CheckedChanged -= item_CheckedChanged; return base.Remove(item); } #endregion #region DEFAULT PROPERTIES /// /// Returns the item with the specified displayName or Text. /// public CheckBoxComboBoxItem this[string displayName] { get { int StartIndex = // An invisible item exists in this scenario to help // with the Text displayed in the TextBox of the Combo _CheckBoxComboBox.DropDownStyle == ComboBoxStyle.DropDownList && _CheckBoxComboBox.DataSource == null ? 1 // Ubiklou : 2008-04-28 : Ignore first item. (http://www.codeproject.com/KB/combobox/extending_combobox.aspx?fid=476622&df=90&mpp=25&noise=3&sort=Position&view=Quick&select=2526813&fr=1#xx2526813xx) : 0; for(int Index = StartIndex; Index <= Count - 1; Index ++) { CheckBoxComboBoxItem Item = this[Index]; string Text; // The binding might not be active yet if (string.IsNullOrEmpty(Item.Text) // Ubiklou : 2008-04-28 : No databinding && Item.DataBindings != null && Item.DataBindings["Text"] != null ) { PropertyInfo PropertyInfo = Item.ComboBoxItem.GetType().GetProperty( Item.DataBindings["Text"].BindingMemberInfo.BindingMember); Text = (string)PropertyInfo.GetValue(Item.ComboBoxItem, null); } else Text = Item.Text; if (Text.CompareTo(displayName) == 0) return Item; } throw new ArgumentOutOfRangeException(String.Format("\"{0}\" does not exist in this combo box.", displayName)); } } #endregion } [TypeConverter(typeof(ExpandableObjectConverter))] public class CheckBoxProperties { public CheckBoxProperties() { } #region PRIVATE PROPERTIES private Appearance _Appearance = Appearance.Normal; private bool _AutoSize = false; private bool _AutoCheck = true; private bool _AutoEllipsis = false; private ContentAlignment _CheckAlign = ContentAlignment.MiddleLeft; private Color _FlatAppearanceBorderColor = Color.Empty; private int _FlatAppearanceBorderSize = 1; private Color _FlatAppearanceCheckedBackColor = Color.Empty; private Color _FlatAppearanceMouseDownBackColor = Color.Empty; private Color _FlatAppearanceMouseOverBackColor = Color.Empty; private FlatStyle _FlatStyle = FlatStyle.Standard; private Color _ForeColor = SystemColors.ControlText; private RightToLeft _RightToLeft = RightToLeft.No; private ContentAlignment _TextAlign = ContentAlignment.MiddleLeft; private bool _ThreeState = false; #endregion #region PUBLIC PROPERTIES [DefaultValue(Appearance.Normal)] public Appearance Appearance { get { return _Appearance; } set { _Appearance = value; OnPropertyChanged(); } } [DefaultValue(true)] public bool AutoCheck { get { return _AutoCheck; } set { _AutoCheck = value; OnPropertyChanged(); } } [DefaultValue(false)] public bool AutoEllipsis { get { return _AutoEllipsis; } set { _AutoEllipsis = value; OnPropertyChanged(); } } [DefaultValue(false)] public bool AutoSize { get { return _AutoSize; } set { _AutoSize = true; OnPropertyChanged(); } } [DefaultValue(ContentAlignment.MiddleLeft)] public ContentAlignment CheckAlign { get { return _CheckAlign; } set { _CheckAlign = value; OnPropertyChanged(); } } [DefaultValue(typeof(Color), "")] public Color FlatAppearanceBorderColor { get { return _FlatAppearanceBorderColor; } set { _FlatAppearanceBorderColor = value; OnPropertyChanged(); } } [DefaultValue(1)] public int FlatAppearanceBorderSize { get { return _FlatAppearanceBorderSize; } set { _FlatAppearanceBorderSize = value; OnPropertyChanged(); } } [DefaultValue(typeof(Color), "")] public Color FlatAppearanceCheckedBackColor { get { return _FlatAppearanceCheckedBackColor; } set { _FlatAppearanceCheckedBackColor = value; OnPropertyChanged(); } } [DefaultValue(typeof(Color), "")] public Color FlatAppearanceMouseDownBackColor { get { return _FlatAppearanceMouseDownBackColor; } set { _FlatAppearanceMouseDownBackColor = value; OnPropertyChanged(); } } [DefaultValue(typeof(Color), "")] public Color FlatAppearanceMouseOverBackColor { get { return _FlatAppearanceMouseOverBackColor; } set { _FlatAppearanceMouseOverBackColor = value; OnPropertyChanged(); } } [DefaultValue(FlatStyle.Standard)] public FlatStyle FlatStyle { get { return _FlatStyle; } set { _FlatStyle = value; OnPropertyChanged(); } } [DefaultValue(typeof(SystemColors), "ControlText")] public Color ForeColor { get { return _ForeColor; } set { _ForeColor = value; OnPropertyChanged(); } } [DefaultValue(RightToLeft.No)] public RightToLeft RightToLeft { get { return _RightToLeft; } set { _RightToLeft = value; OnPropertyChanged(); } } [DefaultValue(ContentAlignment.MiddleLeft)] public ContentAlignment TextAlign { get { return _TextAlign; } set { _TextAlign = value; OnPropertyChanged(); } } [DefaultValue(false)] public bool ThreeState { get { return _ThreeState; } set { _ThreeState = value; OnPropertyChanged(); } } #endregion #region EVENTS AND EVENT CALLERS /// /// Called when any property changes. /// public event EventHandler PropertyChanged; protected void OnPropertyChanged() { EventHandler handler = PropertyChanged; if (handler != null) handler(this, EventArgs.Empty); } #endregion } } ================================================ FILE: CheckBoxComboBox/CheckBoxComboBox.csproj ================================================  Debug AnyCPU 9.0.20404 2.0 {70A25201-8EA4-48F8-A4A6-ED13ADF8823C} Library Properties CheckBoxComboBox CheckBoxComboBox v4.8 512 3.5 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false 3.5 3.5 Component CheckBoxComboBox.cs Component Popup.cs Component PopupComboBox.cs PopupComboBox.cs Designer ================================================ FILE: CheckBoxComboBox/GripBounds.cs ================================================ using System.Drawing; namespace PresentationControls { /// /// CodeProject.com "Simple pop-up control" "http://www.codeproject.com/cs/miscctrl/simplepopup.asp". /// internal struct GripBounds { private const int GripSize = 6; private const int CornerGripSize = GripSize << 1; public GripBounds(Rectangle clientRectangle) { this.clientRectangle = clientRectangle; } private Rectangle clientRectangle; public Rectangle ClientRectangle { get { return clientRectangle; } //set { clientRectangle = value; } } public Rectangle Bottom { get { Rectangle rect = ClientRectangle; rect.Y = rect.Bottom - GripSize + 1; rect.Height = GripSize; return rect; } } public Rectangle BottomRight { get { Rectangle rect = ClientRectangle; rect.Y = rect.Bottom - CornerGripSize + 1; rect.Height = CornerGripSize; rect.X = rect.Width - CornerGripSize + 1; rect.Width = CornerGripSize; return rect; } } public Rectangle Top { get { Rectangle rect = ClientRectangle; rect.Height = GripSize; return rect; } } public Rectangle TopRight { get { Rectangle rect = ClientRectangle; rect.Height = CornerGripSize; rect.X = rect.Width - CornerGripSize + 1; rect.Width = CornerGripSize; return rect; } } public Rectangle Left { get { Rectangle rect = ClientRectangle; rect.Width = GripSize; return rect; } } public Rectangle BottomLeft { get { Rectangle rect = ClientRectangle; rect.Width = CornerGripSize; rect.Y = rect.Height - CornerGripSize + 1; rect.Height = CornerGripSize; return rect; } } public Rectangle Right { get { Rectangle rect = ClientRectangle; rect.X = rect.Right - GripSize + 1; rect.Width = GripSize; return rect; } } public Rectangle TopLeft { get { Rectangle rect = ClientRectangle; rect.Width = CornerGripSize; rect.Height = CornerGripSize; return rect; } } } } ================================================ FILE: CheckBoxComboBox/NativeMethods.cs ================================================ using System; using System.Drawing; using System.Runtime.InteropServices; namespace PresentationControls { /// /// CodeProject.com "Simple pop-up control" "http://www.codeproject.com/cs/miscctrl/simplepopup.asp". /// internal static class NativeMethods { internal const int WM_NCHITTEST = 0x0084, WM_NCACTIVATE = 0x0086, WS_EX_NOACTIVATE = 0x08000000, HTTRANSPARENT = -1, HTLEFT = 10, HTRIGHT = 11, HTTOP = 12, HTTOPLEFT = 13, HTTOPRIGHT = 14, HTBOTTOM = 15, HTBOTTOMLEFT = 16, HTBOTTOMRIGHT = 17, WM_USER = 0x0400, WM_REFLECT = WM_USER + 0x1C00, WM_COMMAND = 0x0111, CBN_DROPDOWN = 7, WM_GETMINMAXINFO = 0x0024; internal static int HIWORD(int n) { return (n >> 16) & 0xffff; } internal static int HIWORD(IntPtr n) { return HIWORD(unchecked((int)(long)n)); } internal static int LOWORD(int n) { return n & 0xffff; } internal static int LOWORD(IntPtr n) { return LOWORD(unchecked((int)(long)n)); } [StructLayout(LayoutKind.Sequential)] internal struct MINMAXINFO { public Point reserved; public Size maxSize; public Point maxPosition; public Size minTrackSize; public Size maxTrackSize; } } } ================================================ FILE: CheckBoxComboBox/Popup.Designer.cs ================================================ namespace PresentationControls { partial class Popup { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } if (content != null) { System.Windows.Forms.Control _content = content; content = null; _content.Dispose(); } } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion } } ================================================ FILE: CheckBoxComboBox/Popup.cs ================================================ using System; using System.ComponentModel; using System.Drawing; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Windows.Forms; using VS = System.Windows.Forms.VisualStyles; /*
  • Base class for custom tooltips.
  • Office-2007-like tooltip class.
  • */ namespace PresentationControls { /// /// CodeProject.com "Simple pop-up control" "http://www.codeproject.com/cs/miscctrl/simplepopup.asp". /// Represents a pop-up window. /// [CLSCompliant(true), ToolboxItem(false)] public partial class Popup : ToolStripDropDown { #region " Fields & Properties " private Control content; /// /// Gets the content of the pop-up. /// public Control Content { get { return content; } } private bool fade; /// /// Gets a value indicating whether the uses the fade effect. /// /// true if pop-up uses the fade effect; otherwise, false. /// To use the fade effect, the FocusOnOpen property also has to be set to true. public bool UseFadeEffect { get { return fade; } set { if (fade == value) return; fade = value; } } private bool focusOnOpen = true; /// /// Gets or sets a value indicating whether to focus the content after the pop-up has been opened. /// /// true if the content should be focused after the pop-up has been opened; otherwise, false. /// If the FocusOnOpen property is set to false, then pop-up cannot use the fade effect. public bool FocusOnOpen { get { return focusOnOpen; } set { focusOnOpen = value; } } private bool acceptAlt = true; /// /// Gets or sets a value indicating whether presing the alt key should close the pop-up. /// /// true if presing the alt key does not close the pop-up; otherwise, false. public bool AcceptAlt { get { return acceptAlt; } set { acceptAlt = value; } } private Popup ownerPopup; private Popup childPopup; private bool _resizable; private bool resizable; /// /// Gets or sets a value indicating whether this is resizable. /// /// true if resizable; otherwise, false. public bool Resizable { get { return resizable && _resizable; } set { resizable = value; } } private ToolStripControlHost host; private Size minSize; /// /// Gets or sets the size that is the lower limit that can specify. /// /// An ordered pair of type representing the width and height of a rectangle. public new Size MinimumSize { get { return minSize; } set { minSize = value; } } private Size maxSize; /// /// Gets or sets the size that is the upper limit that can specify. /// /// An ordered pair of type representing the width and height of a rectangle. public new Size MaximumSize { get { return maxSize; } set { maxSize = value; } } /// /// Gets parameters of a new window. /// /// An object of type used when creating a new window. protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; cp.ExStyle |= NativeMethods.WS_EX_NOACTIVATE; return cp; } } #endregion #region " Constructors " /// /// Initializes a new instance of the class. /// /// The content of the pop-up. /// /// Pop-up will be disposed immediately after disposion of the content control. /// /// is null. public Popup(Control content) { if (content == null) { throw new ArgumentNullException("content"); } this.content = content; this.fade = SystemInformation.IsMenuAnimationEnabled && SystemInformation.IsMenuFadeEnabled; this._resizable = true; InitializeComponent(); AutoSize = false; DoubleBuffered = true; ResizeRedraw = true; host = new ToolStripControlHost(content); Padding = Margin = host.Padding = host.Margin = Padding.Empty; MinimumSize = content.MinimumSize; content.MinimumSize = content.Size; MaximumSize = content.MaximumSize; content.MaximumSize = content.Size; Size = content.Size; content.Location = Point.Empty; Items.Add(host); content.Disposed += delegate(object sender, EventArgs e) { content = null; Dispose(true); }; content.RegionChanged += delegate(object sender, EventArgs e) { UpdateRegion(); }; content.Paint += delegate(object sender, PaintEventArgs e) { PaintSizeGrip(e); }; UpdateRegion(); } #endregion #region " Methods " /// /// Processes a dialog box key. /// /// One of the values that represents the key to process. /// /// true if the key was processed by the control; otherwise, false. /// protected override bool ProcessDialogKey(Keys keyData) { if (acceptAlt && ((keyData & Keys.Alt) == Keys.Alt)) return false; return base.ProcessDialogKey(keyData); } /// /// Updates the pop-up region. /// protected void UpdateRegion() { if (this.Region != null) { this.Region.Dispose(); this.Region = null; } if (content.Region != null) { this.Region = content.Region.Clone(); } } /// /// Shows pop-up window below the specified control. /// /// The control below which the pop-up will be shown. /// /// When there is no space below the specified control, the pop-up control is shown above it. /// /// is null. public void Show(Control control) { if (control == null) { throw new ArgumentNullException("control"); } SetOwnerItem(control); Show(control, control.ClientRectangle); } /// /// Shows pop-up window below the specified area of specified control. /// /// The control used to compute screen location of specified area. /// The area of control below which the pop-up will be shown. /// /// When there is no space below specified area, the pop-up control is shown above it. /// /// is null. public void Show(Control control, Rectangle area) { if (control == null) { throw new ArgumentNullException("control"); } SetOwnerItem(control); resizableTop = resizableRight = false; Point location = control.PointToScreen(new Point(area.Left, area.Top + area.Height)); Rectangle screen = Screen.FromControl(control).WorkingArea; if (location.X + Size.Width > (screen.Left + screen.Width)) { resizableRight = true; location.X = (screen.Left + screen.Width) - Size.Width; } if (location.Y + Size.Height > (screen.Top + screen.Height)) { resizableTop = true; location.Y -= Size.Height + area.Height; } location = control.PointToClient(location); Show(control, location, ToolStripDropDownDirection.BelowRight); } private const int frames = 1; private const int totalduration = 0; // ML : 2007-11-05 : was 100 but caused a flicker. private const int frameduration = totalduration / frames; /// /// Adjusts the size of the owner to accommodate the if the owner is currently displayed, or clears and resets active child controls of the if the is not currently displayed. /// /// true if the owner is currently displayed; otherwise, false. protected override void SetVisibleCore(bool visible) { double opacity = Opacity; if (visible && fade && focusOnOpen) Opacity = 0; base.SetVisibleCore(visible); if (!visible || !fade || !focusOnOpen) return; for (int i = 1; i <= frames; i++) { if (i > 1) { System.Threading.Thread.Sleep(frameduration); } Opacity = opacity * (double)i / (double)frames; } Opacity = opacity; } private bool resizableTop; private bool resizableRight; private void SetOwnerItem(Control control) { if (control == null) { return; } if (control is Popup) { Popup popupControl = control as Popup; ownerPopup = popupControl; ownerPopup.childPopup = this; OwnerItem = popupControl.Items[0]; return; } if (control.Parent != null) { SetOwnerItem(control.Parent); } } /// /// Raises the event. /// /// An that contains the event data. protected override void OnSizeChanged(EventArgs e) { content.MinimumSize = Size; content.MaximumSize = Size; content.Size = Size; content.Location = Point.Empty; base.OnSizeChanged(e); } /// /// Raises the event. /// /// A that contains the event data. protected override void OnOpening(CancelEventArgs e) { if (content.IsDisposed || content.Disposing) { e.Cancel = true; return; } UpdateRegion(); base.OnOpening(e); } /// /// Raises the event. /// /// An that contains the event data. protected override void OnOpened(EventArgs e) { if (ownerPopup != null) { ownerPopup._resizable = false; } if (focusOnOpen) { content.Focus(); } base.OnOpened(e); } protected override void OnClosed(ToolStripDropDownClosedEventArgs e) { if (ownerPopup != null) { ownerPopup._resizable = true; } base.OnClosed(e); } public DateTime LastClosedTimeStamp = DateTime.Now; protected override void OnVisibleChanged(EventArgs e) { if (Visible == false) LastClosedTimeStamp = DateTime.Now; base.OnVisibleChanged(e); } #endregion #region " Resizing Support " /// /// Processes Windows messages. /// /// The Windows to process. [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { if (InternalProcessResizing(ref m, false)) { return; } base.WndProc(ref m); } /// /// Processes the resizing messages. /// /// The message. /// true, if the WndProc method from the base class shouldn't be invoked. [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public bool ProcessResizing(ref Message m) { return InternalProcessResizing(ref m, true); } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] private bool InternalProcessResizing(ref Message m, bool contentControl) { if (m.Msg == NativeMethods.WM_NCACTIVATE && m.WParam != IntPtr.Zero && childPopup != null && childPopup.Visible) { childPopup.Hide(); } if (!Resizable) { return false; } if (m.Msg == NativeMethods.WM_NCHITTEST) { return OnNcHitTest(ref m, contentControl); } else if (m.Msg == NativeMethods.WM_GETMINMAXINFO) { return OnGetMinMaxInfo(ref m); } return false; } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] private bool OnGetMinMaxInfo(ref Message m) { NativeMethods.MINMAXINFO minmax = (NativeMethods.MINMAXINFO)Marshal.PtrToStructure(m.LParam, typeof(NativeMethods.MINMAXINFO)); minmax.maxTrackSize = this.MaximumSize; minmax.minTrackSize = this.MinimumSize; Marshal.StructureToPtr(minmax, m.LParam, false); return true; } private bool OnNcHitTest(ref Message m, bool contentControl) { int x = NativeMethods.LOWORD(m.LParam); int y = NativeMethods.HIWORD(m.LParam); Point clientLocation = PointToClient(new Point(x, y)); GripBounds gripBouns = new GripBounds(contentControl ? content.ClientRectangle : ClientRectangle); IntPtr transparent = new IntPtr(NativeMethods.HTTRANSPARENT); if (resizableTop) { if (resizableRight && gripBouns.TopLeft.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTTOPLEFT; return true; } if (!resizableRight && gripBouns.TopRight.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTTOPRIGHT; return true; } if (gripBouns.Top.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTTOP; return true; } } else { if (resizableRight && gripBouns.BottomLeft.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTBOTTOMLEFT; return true; } if (!resizableRight && gripBouns.BottomRight.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTBOTTOMRIGHT; return true; } if (gripBouns.Bottom.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTBOTTOM; return true; } } if (resizableRight && gripBouns.Left.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTLEFT; return true; } if (!resizableRight && gripBouns.Right.Contains(clientLocation)) { m.Result = contentControl ? transparent : (IntPtr)NativeMethods.HTRIGHT; return true; } return false; } private VS.VisualStyleRenderer sizeGripRenderer; /// /// Paints the size grip. /// /// The instance containing the event data. public void PaintSizeGrip(PaintEventArgs e) { if (e == null || e.Graphics == null || !resizable) { return; } Size clientSize = content.ClientSize; if (Application.RenderWithVisualStyles) { if (this.sizeGripRenderer == null) { this.sizeGripRenderer = new VS.VisualStyleRenderer(VS.VisualStyleElement.Status.Gripper.Normal); } this.sizeGripRenderer.DrawBackground(e.Graphics, new Rectangle(clientSize.Width - 0x10, clientSize.Height - 0x10, 0x10, 0x10)); } else { ControlPaint.DrawSizeGrip(e.Graphics, content.BackColor, clientSize.Width - 0x10, clientSize.Height - 0x10, 0x10, 0x10); } } #endregion } } ================================================ FILE: CheckBoxComboBox/PopupComboBox.Designer.cs ================================================ namespace PresentationControls { partial class PopupComboBox { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } if (dropDown != null) { dropDown.Dispose(); } } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.SuspendLayout(); // // PopupComboBox // this.ResumeLayout(false); } #endregion } } ================================================ FILE: CheckBoxComboBox/PopupComboBox.cs ================================================ using System; using System.ComponentModel; using System.Drawing; using System.Security.Permissions; using System.Windows.Forms; namespace PresentationControls { /// /// CodeProject.com "Simple pop-up control" "http://www.codeproject.com/cs/miscctrl/simplepopup.asp". /// Represents a Windows combo box control with a custom popup control attached. /// [ToolboxBitmap(typeof(System.Windows.Forms.ComboBox)), ToolboxItem(true), ToolboxItemFilter("System.Windows.Forms"), Description("Displays an editable text box with a drop-down list of permitted values.")] public partial class PopupComboBox : ComboBox { /// /// Initializes a new instance of the class. /// public PopupComboBox() { InitializeComponent(); base.DropDownHeight = base.DropDownWidth = 1; base.IntegralHeight = false; } /// /// The pop-up wrapper for the dropDownControl. /// Made PROTECTED instead of PRIVATE so descendent classes can set its Resizable property. /// Note however the pop-up properties must be set after the dropDownControl is assigned, since this /// popup wrapper is recreated when the dropDownControl is assigned. /// protected Popup dropDown; private Control dropDownControl; /// /// Gets or sets the drop down control. /// /// The drop down control. public Control DropDownControl { get { return dropDownControl; } set { if (dropDownControl == value) return; dropDownControl = value; dropDown = new Popup(value); } } /// /// Shows the drop down. /// public void ShowDropDown() { if (dropDown != null) { dropDown.Show(this); } } /// /// Hides the drop down. /// public void HideDropDown() { if (dropDown != null) { dropDown.Hide(); } } /// /// Processes Windows messages. /// /// The Windows to process. [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { if (m.Msg == (NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND)) { if (NativeMethods.HIWORD(m.WParam) == NativeMethods.CBN_DROPDOWN) { // Blocks a redisplay when the user closes the control by clicking // on the combobox. TimeSpan TimeSpan = DateTime.Now.Subtract(dropDown.LastClosedTimeStamp); if (TimeSpan.TotalMilliseconds > 500) ShowDropDown(); return; } } base.WndProc(ref m); } #region " Unused Properties " /// This property is not relevant for this class. /// This property is not relevant for this class. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public new int DropDownWidth { get { return base.DropDownWidth; } set { base.DropDownWidth = value; } } /// This property is not relevant for this class. /// This property is not relevant for this class. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public new int DropDownHeight { get { return base.DropDownHeight; } set { dropDown.Height = value; base.DropDownHeight = value; } } /// This property is not relevant for this class. /// This property is not relevant for this class. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public new bool IntegralHeight { get { return base.IntegralHeight; } set { base.IntegralHeight = value; } } /// This property is not relevant for this class. /// This property is not relevant for this class. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public new ObjectCollection Items { get { return base.Items; } } /// This property is not relevant for this class. /// This property is not relevant for this class. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] public new int ItemHeight { get { return base.ItemHeight; } set { base.ItemHeight = value; } } #endregion } } ================================================ FILE: CheckBoxComboBox/PopupComboBox.resx ================================================ text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 False ================================================ FILE: CheckBoxComboBox/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.InteropServices; // 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("CheckBoxComboBox")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("IT")] [assembly: AssemblyProduct("CheckBoxComboBox")] [assembly: AssemblyCopyright("Copyright © IT 2007")] [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("0b11e2be-4211-4d2a-9102-6847a56d6fd8")] // 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("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: CheckBoxComboBox/Selection Wrappers/ListSelectionWrapper.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; namespace PresentationControls { /// /// Maintains an additional "Selected" & "Count" value for each item in a List. /// Useful in the CheckBoxComboBox. It holds a reference to the List[Index] Item and /// whether it is selected or not. /// It also caters for a Count, if needed. /// /// public class ListSelectionWrapper : List> { #region CONSTRUCTOR /// /// No property on the object is specified for display purposes, so simple ToString() operation /// will be performed. And no Counts will be displayed /// public ListSelectionWrapper(IEnumerable source) : this(source, false) { } /// /// No property on the object is specified for display purposes, so simple ToString() operation /// will be performed. /// public ListSelectionWrapper(IEnumerable source, bool showCounts) : base() { _Source = source; _ShowCounts = showCounts; if (_Source is IBindingList) ((IBindingList)_Source).ListChanged += new ListChangedEventHandler(ListSelectionWrapper_ListChanged); Populate(); } /// /// A Display "Name" property is specified. ToString() will not be performed on items. /// This is specifically useful on DataTable implementations, or where PropertyDescriptors are used to read the values. /// If a PropertyDescriptor is not found, a Property will be used. /// public ListSelectionWrapper(IEnumerable source, string usePropertyAsDisplayName) : this(source, false, usePropertyAsDisplayName) { } /// /// A Display "Name" property is specified. ToString() will not be performed on items. /// This is specifically useful on DataTable implementations, or where PropertyDescriptors are used to read the values. /// If a PropertyDescriptor is not found, a Property will be used. /// public ListSelectionWrapper(IEnumerable source, bool showCounts, string usePropertyAsDisplayName) : this(source, showCounts) { _DisplayNameProperty = usePropertyAsDisplayName; } #endregion #region PRIVATE PROPERTIES /// /// Is a Count indicator used. /// private bool _ShowCounts; /// /// The original List of values wrapped. A "Selected" and possibly "Count" functionality is added. /// private IEnumerable _Source; /// /// Used to indicate NOT to use ToString(), but read this property instead as a display value. /// private string _DisplayNameProperty = null; #endregion #region PUBLIC PROPERTIES /// /// When specified, indicates that ToString() should not be performed on the items. /// This property will be read instead. /// This is specifically useful on DataTable implementations, where PropertyDescriptors are used to read the values. /// public string DisplayNameProperty { get { return _DisplayNameProperty; } set { _DisplayNameProperty = value; } } /// /// Builds a concatenation list of selected items in the list. /// public string SelectedNames { get { string Text = ""; foreach (ObjectSelectionWrapper Item in this) if (Item.Selected) Text += ( string.IsNullOrEmpty(Text) ? String.Format("\"{0}\"", Item.Name) : String.Format(" & \"{0}\"", Item.Name)); return Text; } } /// /// Indicates whether the Item display value (Name) should include a count. /// public bool ShowCounts { get { return _ShowCounts; } set { _ShowCounts = value; } } #endregion #region HELPER MEMBERS /// /// Reset all counts to zero. /// public void ClearCounts() { foreach (ObjectSelectionWrapper Item in this) Item.Count = 0; } /// /// Creates a ObjectSelectionWrapper item. /// Note that the constructor signature of sub classes classes are important. /// /// /// private ObjectSelectionWrapper CreateSelectionWrapper(IEnumerator Object) { Type[] Types = new Type[] { typeof(T), this.GetType() }; ConstructorInfo CI = typeof(ObjectSelectionWrapper).GetConstructor(Types); if (CI == null) throw new Exception(String.Format( "The selection wrapper class {0} must have a constructor with ({1} Item, {2} Container) parameters.", typeof(ObjectSelectionWrapper), typeof(T), this.GetType())); object[] parameters = new object[] { Object.Current, this }; object result = CI.Invoke(parameters); return (ObjectSelectionWrapper)result; } public ObjectSelectionWrapper FindObjectWithItem(T Object) { return Find(new Predicate>( delegate(ObjectSelectionWrapper target) { return target.Item.Equals(Object); })); } /* public TSelectionWrapper FindObjectWithKey(object key) { return FindObjectWithKey(new object[] { key }); } public TSelectionWrapper FindObjectWithKey(object[] keys) { return Find(new Predicate( delegate(TSelectionWrapper target) { return ReflectionHelper.CompareKeyValues( ReflectionHelper.GetKeyValuesFromObject(target.Item, target.Item.TableInfo), keys); })); } public object[] GetArrayOfSelectedKeys() { List List = new List(); foreach (TSelectionWrapper Item in this) if (Item.Selected) { if (Item.Item.TableInfo.KeyProperties.Length == 1) List.Add(ReflectionHelper.GetKeyValueFromObject(Item.Item, Item.Item.TableInfo)); else List.Add(ReflectionHelper.GetKeyValuesFromObject(Item.Item, Item.Item.TableInfo)); } return List.ToArray(); } public T[] GetArrayOfSelectedKeys() { List List = new List(); foreach (TSelectionWrapper Item in this) if (Item.Selected) { if (Item.Item.TableInfo.KeyProperties.Length == 1) List.Add((T)ReflectionHelper.GetKeyValueFromObject(Item.Item, Item.Item.TableInfo)); else throw new LibraryException("This generator only supports single value keys."); // List.Add((T)ReflectionHelper.GetKeyValuesFromObject(Item.Item, Item.Item.TableInfo)); } return List.ToArray(); } */ private void Populate() { Clear(); /* for(int Index = 0; Index <= _Source.Count -1; Index++) Add(CreateSelectionWrapper(_Source[Index])); */ IEnumerator Enumerator = _Source.GetEnumerator(); if (Enumerator != null) while (Enumerator.MoveNext()) Add(CreateSelectionWrapper(Enumerator)); } #endregion #region EVENT HANDLERS private void ListSelectionWrapper_ListChanged(object sender, ListChangedEventArgs e) { switch (e.ListChangedType) { case ListChangedType.ItemAdded: Add(CreateSelectionWrapper((IEnumerator)((IBindingList)_Source)[e.NewIndex])); break; case ListChangedType.ItemDeleted: Remove(FindObjectWithItem((T)((IBindingList)_Source)[e.OldIndex])); break; case ListChangedType.Reset: Populate(); break; } } #endregion } } ================================================ FILE: CheckBoxComboBox/Selection Wrappers/ObjectSelectionWrapper.cs ================================================ using System; using System.ComponentModel; using System.Data; using System.Reflection; namespace PresentationControls { /// /// Used together with the ListSelectionWrapper in order to wrap data sources for a CheckBoxComboBox. /// It helps to ensure you don't add an extra "Selected" property to a class that don't really need or want that information. /// public class ObjectSelectionWrapper : INotifyPropertyChanged { public ObjectSelectionWrapper(T item, ListSelectionWrapper container) : base() { _Container = container; _Item = item; } #region PRIVATE PROPERTIES /// /// Used as a count indicator for the item. Not necessarily displayed. /// private int _Count = 0; /// /// Is this item selected. /// private bool _Selected = false; /// /// A reference to the wrapped item. /// private T _Item; /// /// The containing list for these selections. /// private ListSelectionWrapper _Container; #endregion #region PUBLIC PROPERTIES /// /// An indicator of how many items with the specified status is available for the current filter level. /// Thaught this would make the app a bit more user-friendly and help not to miss items in Statusses /// that are not often used. /// public int Count { get { return _Count; } set { _Count = value; } } /// /// A reference to the item wrapped. /// public T Item { get { return _Item; } set { _Item = value; } } /// /// The item display value. If ShowCount is true, it displays the "Name [Count]". /// public string Name { get { string Name = null; if (string.IsNullOrEmpty(_Container.DisplayNameProperty)) Name = Item.ToString(); else if (Item is DataRow) // A specific implementation for DataRow Name = ((DataRow)((Object)Item))[_Container.DisplayNameProperty].ToString(); else { PropertyDescriptorCollection PDs = TypeDescriptor.GetProperties(Item); foreach (PropertyDescriptor PD in PDs) if (PD.Name.CompareTo(_Container.DisplayNameProperty) == 0) { Name = (string)PD.GetValue(Item).ToString(); break; } if (string.IsNullOrEmpty(Name)) { PropertyInfo PI = Item.GetType().GetProperty(_Container.DisplayNameProperty); if (PI == null) throw new Exception(String.Format( "Property {0} cannot be found on {1}.", _Container.DisplayNameProperty, Item.GetType())); Name = PI.GetValue(Item, null).ToString(); } } return _Container.ShowCounts ? String.Format("{0} [{1}]", Name, Count) : Name; } } /// /// The textbox display value. The names concatenated. /// public string NameConcatenated { get { return _Container.SelectedNames; } } /// /// Indicates whether the item is selected. /// public bool Selected { get { return _Selected; } set { if (_Selected != value) { _Selected = value; OnPropertyChanged("Selected"); OnPropertyChanged("NameConcatenated"); } } } #endregion #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } #endregion } } ================================================ FILE: DoomLauncher/Adapters/DataAccess.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Data; using System.Data.Common; #pragma warning disable CA2100 //sql injection warning, these all use db parameters anyway namespace DoomLauncher { public class DataAccess { public DataAccess(IDatabaseAdapter dbAdapter, string connectionString) { DbAdapter = dbAdapter; ConnectionString = connectionString; } public DataSet ExecuteSelect(string sql) { DbConnection conn = DbAdapter.CreateConnection(ConnectionString); conn.Open(); DbCommand cmd = conn.CreateCommand(); cmd.CommandText = sql; DataSet ds = new DataSet(); DbDataAdapter adapter = DbAdapter.CreateAdapter(); adapter.SelectCommand = cmd; adapter.Fill(ds); conn.Close(); return ds; } public DataSet ExecuteSelect(string sql, IEnumerable parameters) { DbConnection conn = DbAdapter.CreateConnection(ConnectionString); conn.Open(); DbCommand cmd = conn.CreateCommand(); cmd.CommandText = sql; foreach (DbParameter dbParam in parameters) { cmd.Parameters.Add(dbParam); } DataSet ds = new DataSet(); DbDataAdapter adapter = DbAdapter.CreateAdapter(); adapter.SelectCommand = cmd; adapter.Fill(ds); conn.Close(); return ds; } public void ExecuteNonQuery(string sql) { DbConnection conn = DbAdapter.CreateConnection(ConnectionString); conn.Open(); DbCommand cmd = conn.CreateCommand(); cmd.CommandText = sql; cmd.ExecuteNonQuery(); conn.Close(); } public void ExecuteNonQuery(string sql, IEnumerable parameters) { DbConnection conn = DbAdapter.CreateConnection(ConnectionString); conn.Open(); DbCommand cmd = conn.CreateCommand(); cmd.CommandText = sql; foreach(DbParameter dbParam in parameters) { cmd.Parameters.Add(dbParam); } cmd.ExecuteNonQuery(); conn.Close(); } public IDatabaseAdapter DbAdapter { get; private set; } public string ConnectionString { get; private set; } } } ================================================ FILE: DoomLauncher/Adapters/DbDataSourceAdapter.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; namespace DoomLauncher { public class DbDataSourceAdapter : IDataSourceAdapter { public static readonly string DatabaseFileName = "DoomLauncher.sqlite"; public static readonly string InitDatabaseFileName = "DoomLauncher_.sqlite"; private static string[] s_opLookup = new string[] { "= ", "<>", "<", ">", "like" }; public DbDataSourceAdapter(IDatabaseAdapter dbAdapter, string connectionString) : this(dbAdapter, connectionString, false) { } public DbDataSourceAdapter(IDatabaseAdapter dbAdapter, string connectionString, bool outOfDateDatabase) { m_outOfDateDatabase = outOfDateDatabase; DataAccess = new DataAccess(dbAdapter, connectionString); } public static IDataSourceAdapter CreateAdapter() => CreateAdapter(false); public static IDataSourceAdapter CreateAdapter(bool outOfDateDatabase) { string databaseFile = Path.Combine(LauncherPath.GetDataDirectory(), DatabaseFileName); return new DbDataSourceAdapter(new SqliteDatabaseAdapter(), CreateConnectionString(databaseFile), outOfDateDatabase); } public static string CreateConnectionString(string dataSource) { return string.Format(@"Data Source={0}", dataSource); } public int GetGameFilesCount() { DataTable dt = DataAccess.ExecuteSelect("select count(*) from GameFiles").Tables[0]; return Convert.ToInt32(dt.Rows[0][0]); } public IEnumerable GetGameFiles() { DataTable dt = DataAccess.ExecuteSelect("select * from GameFiles").Tables[0]; return Util.TableToStructure(dt, typeof(GameFile)).Cast(); } public IEnumerable GetGameFiles(ITagData tag) { DataTable dt = DataAccess.ExecuteSelect(string.Format("select GameFiles.* from GameFiles join TagMapping on TagMapping.FileID = GameFiles.GameFileID where TagID = {0}", tag.TagID)).Tables[0]; return Util.TableToStructure(dt, typeof(GameFile)).Cast(); } public IEnumerable GetGameFiles(IGameFileGetOptions options) { return GetGameFiles(options, null); } public IEnumerable GetGameFiles(IGameFileGetOptions options, ITagData tag) { DataTable dt; string selectColumns = "GameFiles.*"; string join = string.Empty; string where = string.Empty; if (tag != null) { join = "join TagMapping on TagMapping.FileID = GameFiles.GameFileID"; where = string.Format("TagMapping.TagID = {0}", tag.TagID); } if (options.SelectFields != null) selectColumns = GetSelectFieldString(options.SelectFields); if (options.SearchField != null) { string op = s_opLookup[(int)options.SearchField.SearchOp]; if (op == "like") options.SearchField.SearchText = string.Format("{0}{1}{0}", "%", options.SearchField.SearchText); string searchCol = options.SearchField.SearchFieldType.ToString("g"); string searchParam = "@search"; if (DataAccess.DbAdapter is SqliteDatabaseAdapter && GameFileSearchField.IsDateTimeField(options.SearchField.SearchFieldType)) //sqlite datetime comparison hack { searchParam = string.Format("Datetime('{0}')", DateTime.Parse(options.SearchField.SearchText).ToString("yyyy-MM-dd")); } if (where != string.Empty) where = string.Format("and {0}", where); string query = string.Format("select {2} from GameFiles {5} where {0} {1} {3} {4} {6}", searchCol, op, selectColumns, searchParam, GetLimitOrderString(options), join, where); dt = DataAccess.ExecuteSelect(query, new DbParameter[] { DataAccess.DbAdapter.CreateParameter("search", options.SearchField.SearchText) }).Tables[0]; } else { if (where != string.Empty) where = string.Format("where {0}", where); string query = string.Format("select {0} from GameFiles {2} {3} {1}", selectColumns, GetLimitOrderString(options), join, where); dt = DataAccess.ExecuteSelect(query).Tables[0]; } return Util.TableToStructure(dt, typeof(GameFile)).Cast(); } public IEnumerable GetUntaggedGameFiles() { DataTable dt = DataAccess.ExecuteSelect("select GameFiles.* from GameFiles left join TagMapping on GameFiles.GameFileID = TagMapping.FileID where FileID is null").Tables[0]; return Util.TableToStructure(dt, typeof(GameFile)).Cast(); } private static string GetLimitOrderString(IGameFileGetOptions options) { string ret = string.Empty; if (options.OrderBy.HasValue && options.OrderField.HasValue) ret += string.Format("order by {0} {1}", options.OrderField.Value.ToString("g"), options.OrderBy.Value.ToString("g")); if (options.Limit.HasValue) ret += string.Format(" limit {0}", options.Limit.Value); return ret; } private string GetSelectFieldString(GameFileFieldType[] selectFields) { StringBuilder sb = new StringBuilder(); foreach (GameFileFieldType field in selectFields) { sb.Append(field.ToString("g")); sb.Append(','); } sb.Remove(sb.Length - 1, 1); return sb.ToString(); } public IEnumerable GetGameFileNames() { DataTable dt = DataAccess.ExecuteSelect("select FileName from GameFiles").Tables[0]; List ret = new List(dt.Rows.Count); foreach (DataRow dr in dt.Rows) ret.Add((string)dr[0]); return ret; } public IGameFile GetGameFile(string fileName) { List parameters = new List { DataAccess.DbAdapter.CreateParameter("FileName", fileName), DataAccess.DbAdapter.CreateParameter("FileNamePath", '%' + Path.DirectorySeparatorChar + fileName), }; DataTable dt = DataAccess.ExecuteSelect("select * from GameFiles where Filename = @FileName COLLATE NOCASE or Filename like @FileNamePath COLLATE NOCASE", parameters).Tables[0]; if (dt.Rows.Count > 0) return Util.TableToStructure(dt, typeof(GameFile)).Cast().ToList()[0]; else return null; } public IEnumerable GetGameFileIWads() { DataTable dt = DataAccess.ExecuteSelect("select GameFiles.* from GameFiles join IWads on IWads.GameFileID = GameFiles.GameFileID").Tables[0]; return Util.TableToStructure(dt, typeof(GameFile)).Cast(); } public void InsertGameFile(IGameFile gameFile) { string insert = InsertStatement("GameFiles", gameFile, new string[] { "GameFileID", "FileSizeBytes", "GameProfileID", "Name", "FullFileName" }, out List parameters); DataAccess.ExecuteNonQuery(insert, parameters); } public void UpdateGameFile(IGameFile gameFile) { UpdateGameFile(gameFile, null); } public void UpdateGameFile(IGameFile gameFile, GameFileFieldType[] updateFields) { StringBuilder query = new StringBuilder("update GameFiles set "); if (updateFields != null && updateFields.Length > 0) { foreach(GameFileFieldType field in updateFields) { if (field != GameFileFieldType.MD5 && field != GameFileFieldType.GameFileID) { query.Append(field.ToString()); query.Append(" = @"); query.Append(field.ToString()); query.Append(","); } } query.Remove(query.Length - 1, 1); query.Append(" where GameFileID = @gameFileID"); } else { query = new StringBuilder(@"update GameFiles set Title = @Title, Author = @Author, ReleaseDate = @ReleaseDate, Description = @Description, Map = @Map, SourcePortID = @SourcePortID, Thumbnail = @Thumbnail, Comments = @Comments, Rating = @Rating, IWadID = @IWadID, LastPlayed = @LastPlayed, Downloaded = @Downloaded, SettingsMap = @SettingsMap, SettingsSkill = @SettingsSkill, SettingsExtraParams = @SettingsExtraParams, SettingsFiles = @SettingsFiles, SettingsFilesSourcePort = @SettingsFilesSourcePort, SettingsFilesIWAD = @SettingsFilesIWAD, SettingsSpecificFiles = @SettingsSpecificFiles, SettingsStat = @SettingsStat, SettingsLoadLatestSave = @SettingsLoadLatestSave, FileName = @FileName, MapCount = @MapCount, MinutesPlayed = @MinutesPlayed, SettingsGameProfileID = @SettingsGameProfileID, SettingsSaved = @SettingsSaved, SettingsExtraParamsOnly = @SettingsExtraParamsOnly where GameFileID = @gameFileID"); } List parameters = new List { DataAccess.DbAdapter.CreateParameter("Title", gameFile.Title ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("Author", gameFile.Author ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("ReleaseDate", gameFile.ReleaseDate ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("Description", gameFile.Description ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("Map", gameFile.Map ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SourcePortID", gameFile.SourcePortID ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("Thumbnail", gameFile.Thumbnail ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("Comments", gameFile.Comments ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("Rating", gameFile.Rating.HasValue ? gameFile.Rating : (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("IWadID", gameFile.IWadID.HasValue ? gameFile.IWadID : (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("GameFileID", gameFile.GameFileID.Value), DataAccess.DbAdapter.CreateParameter("LastPlayed", gameFile.LastPlayed.HasValue ? gameFile.LastPlayed : (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("Downloaded", gameFile.Downloaded.HasValue ? gameFile.Downloaded : (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsMap", gameFile.SettingsMap ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsSkill", gameFile.SettingsSkill ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsExtraParams", gameFile.SettingsExtraParams ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsFiles", gameFile.SettingsFiles ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsFilesSourcePort", gameFile.SettingsFilesSourcePort ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsFilesIWAD", gameFile.SettingsFilesIWAD ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsSpecificFiles", gameFile.SettingsSpecificFiles ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsStat", gameFile.SettingsStat), DataAccess.DbAdapter.CreateParameter("SettingsLoadLatestSave", gameFile.SettingsLoadLatestSave), DataAccess.DbAdapter.CreateParameter("SettingsSaved", gameFile.SettingsSaved), DataAccess.DbAdapter.CreateParameter("SettingsExtraParamsOnly", gameFile.SettingsExtraParamsOnly), DataAccess.DbAdapter.CreateParameter("SettingsGameProfileID", gameFile.SettingsGameProfileID ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("MapCount", !gameFile.MapCount.HasValue ? (object)DBNull.Value : gameFile.MapCount), DataAccess.DbAdapter.CreateParameter("MinutesPlayed", gameFile.MinutesPlayed), DataAccess.DbAdapter.CreateParameter("FileName", gameFile.FileName) }; if (m_outOfDateDatabase) { DataTable dt = GetTableColumns("GameFiles"); query = RemoveUnknownColumnsFromQuery(dt, query); parameters = RemoveUnknownColumnsFromParameters(dt, parameters); } DataAccess.ExecuteNonQuery(query.ToString(), parameters); } public void UpdateGameFiles(GameFileFieldType ftWhere, GameFileFieldType ftSet, object fWhere, object fSet) { List parameters = new List { DataAccess.DbAdapter.CreateParameter("set1", fSet ?? DBNull.Value ), DataAccess.DbAdapter.CreateParameter("where1", fWhere ?? DBNull.Value ) }; DataAccess.ExecuteNonQuery(string.Format(@"update GameFiles set {0} = @set1 where {1} = @where1", ftSet.ToString("g"), ftWhere.ToString("g")), parameters); } public void DeleteGameFile(IGameFile gameFile) { if (gameFile.GameFileID.HasValue) { DataAccess.ExecuteNonQuery(string.Format("delete from GameFiles where GameFileID = {0}", gameFile.GameFileID)); } } public IEnumerable GetSourcePorts(bool loadArchived = false) => GetSourcePorts(SourcePortLaunchType.SourcePort, loadArchived); public IEnumerable GetUtilities(bool loadArchived = false) => GetSourcePorts(SourcePortLaunchType.Utility, loadArchived); public IEnumerable GetDoom64(bool loadArchived = false) => GetSourcePorts(SourcePortLaunchType.Doom64, loadArchived); private IEnumerable GetSourcePorts(SourcePortLaunchType type, bool loadArchived) { int sqlArchive = loadArchived ? 1 : 0; DataTable dt; try { dt = DataAccess.ExecuteSelect($"select * from SourcePorts where LaunchType = {(int)type} and Archived = {sqlArchive} order by Name collate nocase").Tables[0]; } catch { // This is for updates before Archived column existed... dt = DataAccess.ExecuteSelect($"select * from SourcePorts where LaunchType = {(int)type}").Tables[0]; } List sourcePorts = new List(); foreach (DataRow dr in dt.Rows) sourcePorts.Add(CreateSourcePortDataSource(dt, dr)); return sourcePorts; } private static ISourcePortData CreateSourcePortDataSource(DataTable dt, DataRow dr) { SourcePortData sourcePort = new SourcePortData { Directory = new LauncherPath((string)dr["Directory"]), Executable = (string)dr["Executable"], Name = (string)dr["Name"], SourcePortID = Convert.ToInt32(dr["SourcePortID"]), SupportedExtensions = (string)CheckDBNull(dr["SupportedExtensions"], string.Empty), LaunchType = (SourcePortLaunchType)Convert.ToInt32(dr["LaunchType"]), FileOption = (string)CheckDBNull(dr["FileOption"], string.Empty), ExtraParameters = (string)CheckDBNull(dr["ExtraParameters"], string.Empty), AltSaveDirectory = new LauncherPath((string)CheckDBNull(dr["AltSaveDirectory"], string.Empty)), }; if (dt.Columns.Contains("SettingsFiles")) sourcePort.SettingsFiles = (string)CheckDBNull(dr["SettingsFiles"], string.Empty); if (dt.Columns.Contains("Archived")) sourcePort.Archived = Convert.ToInt32(dr["Archived"]) != 0; return sourcePort; } private static object CheckDBNull(object obj, object defaultValue) { if (obj == DBNull.Value) return defaultValue; else return obj; } public ISourcePortData GetSourcePort(int sourcePortID) { DataTable dt = DataAccess.ExecuteSelect(string.Format("select * from SourcePorts where SourcePortID = {0}", sourcePortID)).Tables[0]; if (dt.Rows.Count > 0) return CreateSourcePortDataSource(dt, dt.Rows[0]); return null; } public void InsertSourcePort(ISourcePortData sourcePort) { string insert = @"insert into SourcePorts (Name,Executable,SupportedExtensions,Directory,SettingsFiles,LaunchType,FileOption,ExtraParameters,AltSaveDirectory,Archived) values(@Name,@Executable,@SupportedExtensions,@Directory,@SettingsFiles,@LaunchType,@FileOption,@ExtraParameters,@AltSaveDirectory,@Archived)"; DataAccess.ExecuteNonQuery(insert, GetSourcePortParams(sourcePort)); } public void UpdateSourcePort(ISourcePortData sourcePort) { string query = @"update SourcePorts set Name = @Name, Executable = @Executable, SupportedExtensions = @SupportedExtensions, Directory = @Directory, SettingsFiles = @SettingsFiles, LaunchType = @LaunchType, FileOption = @FileOption, ExtraParameters = @ExtraParameters, AltSaveDirectory = @AltSaveDirectory, Archived = @Archived where SourcePortID = @sourcePortID"; DataAccess.ExecuteNonQuery(query, GetSourcePortParams(sourcePort)); } private List GetSourcePortParams(ISourcePortData sourcePort) { List parameters = new List { DataAccess.DbAdapter.CreateParameter("Name", sourcePort.Name ?? string.Empty), DataAccess.DbAdapter.CreateParameter("Executable", sourcePort.Executable ?? string.Empty), DataAccess.DbAdapter.CreateParameter("SupportedExtensions", sourcePort.SupportedExtensions ?? string.Empty), DataAccess.DbAdapter.CreateParameter("Directory", sourcePort.Directory == null ? string.Empty : sourcePort.Directory.GetPossiblyRelativePath()), DataAccess.DbAdapter.CreateParameter("SettingsFiles", sourcePort.SettingsFiles ?? string.Empty), DataAccess.DbAdapter.CreateParameter("SourcePortID", sourcePort.SourcePortID), DataAccess.DbAdapter.CreateParameter("LaunchType", sourcePort.LaunchType), DataAccess.DbAdapter.CreateParameter("FileOption", sourcePort.FileOption ?? string.Empty), DataAccess.DbAdapter.CreateParameter("ExtraParameters", sourcePort.ExtraParameters ?? string.Empty), DataAccess.DbAdapter.CreateParameter("AltSaveDirectory", sourcePort.AltSaveDirectory == null ? string.Empty : sourcePort.AltSaveDirectory.GetPossiblyRelativePath()), DataAccess.DbAdapter.CreateParameter("Archived", sourcePort.Archived) }; return parameters; } public void DeleteSourcePort(ISourcePortData sourcePort) { DataAccess.ExecuteNonQuery(string.Format("delete from SourcePorts where SourcePortID = {0}", sourcePort.SourcePortID)); } public IEnumerable GetIWads() { DataTable dt = DataAccess.ExecuteSelect("select * from IWads order by Name collate nocase").Tables[0]; return Util.TableToStructure(dt, typeof(IWadData)).Cast().ToList(); } public IIWadData GetIWad(int gameFileID) { DataTable dt = DataAccess.ExecuteSelect(string.Format("select * from IWads where GameFileID = {0} order by Name collate nocase", gameFileID)).Tables[0]; return Util.TableToStructure(dt, typeof(IWadData)).Cast().FirstOrDefault(); } public void InsertIWad(IIWadData iwad) { string insert = InsertStatement("IWads", iwad, new string[] { "IWadID" }, out List parameters); DataAccess.ExecuteNonQuery(insert, parameters); } public void UpdateIWad(IIWadData iwad) { string update = "update IWads set FileName = @FileName, Name = @Name, GameFileID = @GameFileID where IWadID = @IWadID"; List parameters = new List { DataAccess.DbAdapter.CreateParameter("IWadID", iwad.IWadID), DataAccess.DbAdapter.CreateParameter("FileName", iwad.FileName), DataAccess.DbAdapter.CreateParameter("Name", iwad.Name), DataAccess.DbAdapter.CreateParameter("GameFileID", iwad.GameFileID.HasValue ? iwad.GameFileID : (object)DBNull.Value) }; DataAccess.ExecuteNonQuery(update, parameters); } public void DeleteIWad(IIWadData iwad) { DataAccess.ExecuteNonQuery(string.Format("delete from IWads where IWadID = {0}", iwad.IWadID)); } public IEnumerable GetFiles() { DataTable dt = DataAccess.ExecuteSelect("select * from Files").Tables[0]; return Util.TableToStructure(dt, typeof(FileData)).Cast().ToList(); } public IEnumerable GetFiles(IGameFile gameFile) { DataTable dt = DataAccess.ExecuteSelect(string.Format("select * from Files where GameFileID = {0} order by FileOrder, FileID", gameFile.GameFileID.Value)).Tables[0]; return Util.TableToStructure(dt, typeof(FileData)).Cast().ToList(); } public IEnumerable GetFiles(IGameFile gameFile, FileType fileTypeID) { DataTable dt = DataAccess.ExecuteSelect(string.Format("select * from Files where GameFileID = {0} and FileTypeID = {1} order by FileOrder, FileID", gameFile.GameFileID.Value, (int)fileTypeID)).Tables[0]; return Util.TableToStructure(dt, typeof(FileData)).Cast().ToList(); } public IEnumerable GetFiles(FileType fileTypeID) { DataTable dt = DataAccess.ExecuteSelect(string.Format("select * from Files where FileTypeID = {0} order by GameFileID, FileOrder desc", (int)fileTypeID)).Tables[0]; return Util.TableToStructure(dt, typeof(FileData)).Cast().ToList(); } public void UpdateFile(IFileData file) { string query = @"update Files set SourcePortID = @SourcePortID, Description = @Description, FileOrder = @FileOrder, DateCreated = @DateCreated, UserTitle = @UserTitle, UserDescription = @UserDescription, Map = @Map where FileID = @FileID"; List parameters = new List { DataAccess.DbAdapter.CreateParameter("SourcePortID", file.SourcePortID), DataAccess.DbAdapter.CreateParameter("Description", file.Description), DataAccess.DbAdapter.CreateParameter("FileID", file.FileID), DataAccess.DbAdapter.CreateParameter("FileOrder", file.FileOrder), DataAccess.DbAdapter.CreateParameter("DateCreated", file.DateCreated), DataAccess.DbAdapter.CreateParameter("UserTitle", file.UserTitle), DataAccess.DbAdapter.CreateParameter("Map", file.Map), DataAccess.DbAdapter.CreateParameter("UserDescription", file.UserDescription), }; DataAccess.ExecuteNonQuery(query, parameters); } public void UpdateFiles(int sourcePortID_Where, int? sourcePortID_Set) { DataAccess.ExecuteNonQuery(string.Format("update Files set SourcePortID = @id where SourcePortID = {0}", sourcePortID_Where), new DbParameter[] { DataAccess.DbAdapter.CreateParameter("id", sourcePortID_Set ?? (object)DBNull.Value) }); } public void InsertFile(IFileData file) { string insert = InsertStatement("Files", file, new string[] { "FileID" }, out List parameters); DataAccess.ExecuteNonQuery(insert, parameters); } public void DeleteFile(IFileData file) { DataAccess.ExecuteNonQuery(string.Format("delete from Files where FileID = {0}", file.FileID)); } public void DeleteFile(IGameFile file) { DataAccess.ExecuteNonQuery(string.Format("delete from Files where GameFileID = {0}", file.GameFileID.Value)); } public void DeleteFiles(ISourcePortData sourcePort, FileType fileTypeID) { DataAccess.ExecuteNonQuery(string.Format("delete from Files where SourcePortID = {0} and FileTypeID = {1}", sourcePort.SourcePortID, (int)fileTypeID)); } public IEnumerable GetConfiguration() { DataTable dt = DataAccess.ExecuteSelect("select * from Configuration").Tables[0]; return Util.TableToStructure(dt, typeof(ConfigurationData)).Cast().ToList(); } public void InsertConfiguration(IConfigurationData config) { string insert = InsertStatement("Configuration", config, new string[] { "ConfigID" }, out List parameters); DataAccess.ExecuteNonQuery(insert, parameters); } public void UpdateConfiguration(IConfigurationData config) { string query = @"update Configuration set Name = @Name, Value = @Value, AvailableValues = @AvailableValues where ConfigID = @ConfigID"; List parameters = new List { DataAccess.DbAdapter.CreateParameter("Name", config.Name ?? string.Empty ), DataAccess.DbAdapter.CreateParameter("Value", config.Value ?? string.Empty ), DataAccess.DbAdapter.CreateParameter("AvailableValues", config.AvailableValues == null ? string.Empty : config.AvailableValues), DataAccess.DbAdapter.CreateParameter("ConfigID", config.ConfigID) }; DataAccess.ExecuteNonQuery(query, parameters); } public IEnumerable GetTags() { DataTable dt = DataAccess.ExecuteSelect("select * from Tags").Tables[0]; return Util.TableToStructure(dt, typeof(TagData)).Cast().ToList(); } public void InsertTag(ITagData tag) { string insert = InsertStatement("Tags", tag, new string[] { "TagID" }, out List parameters); DataAccess.ExecuteNonQuery(insert, parameters); } public void UpdateTag(ITagData tag) { string query = @"update Tags set Name = @Name, HasTab = @HasTab, HasColor = @HasColor, Color = @Color, ExcludeFromOtherTabs = @ExcludeFromOtherTabs, Favorite = @Favorite where TagID = @TagID"; List parameters = new List { DataAccess.DbAdapter.CreateParameter("Name", tag.Name ?? string.Empty), DataAccess.DbAdapter.CreateParameter("HasTab", tag.HasTab), DataAccess.DbAdapter.CreateParameter("HasColor", tag.HasColor), DataAccess.DbAdapter.CreateParameter("Color", tag.Color.HasValue ? tag.Color : (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("TagID", tag.TagID), DataAccess.DbAdapter.CreateParameter("ExcludeFromOtherTabs", tag.ExcludeFromOtherTabs), DataAccess.DbAdapter.CreateParameter("Favorite", tag.Favorite) }; DataAccess.ExecuteNonQuery(query, parameters); } public void DeleteTag(ITagData tag) { DataAccess.ExecuteNonQuery(string.Format("delete from Tags where TagID = {0}", tag.TagID)); } public IEnumerable GetTagMappings() { DataTable dt = DataAccess.ExecuteSelect("select * from TagMapping").Tables[0]; return Util.TableToStructure(dt, typeof(TagMapping)).Cast().ToList(); } public IEnumerable GetTagMappings(int gameFileID) { DataTable dt = DataAccess.ExecuteSelect(string.Format("select * from TagMapping where FileID = {0}", gameFileID)).Tables[0]; return Util.TableToStructure(dt, typeof(TagMapping)).Cast().ToList(); } public void InsertTagMapping(ITagMapping tag) { string insert = InsertStatement("TagMapping", tag, out List parameters); DataAccess.ExecuteNonQuery(insert, parameters); } public void DeleteTagMapping(ITagMapping tag) { DataAccess.ExecuteNonQuery(string.Format("delete from TagMapping where TagID = {0} and FileID = {1}", tag.TagID, tag.FileID)); } public void DeleteTagMapping(int tagID) { DataAccess.ExecuteNonQuery(string.Format("delete from TagMapping where TagID = {0}", tagID)); } public IEnumerable GetStats() { DataTable dt = DataAccess.ExecuteSelect("select * from Stats").Tables[0]; return Util.TableToStructure(dt, typeof(StatsData)).Cast().ToList(); } public IEnumerable GetStats(int gameFileID) { DataTable dt = DataAccess.ExecuteSelect(string.Format("select * from Stats where GameFileID = {0}", gameFileID)).Tables[0]; return Util.TableToStructure(dt, typeof(StatsData)).Cast().ToList(); } public IEnumerable GetStats(IEnumerable gameFiles) { if (!gameFiles.Any()) return Array.Empty(); var gameFileIds = gameFiles.Select(x => x.GameFileID); DataTable dt = DataAccess.ExecuteSelect($"select * from Stats where GameFileID in ({string.Join(",", gameFileIds)})").Tables[0]; return Util.TableToStructure(dt, typeof(StatsData)).Cast().ToList(); } public void InsertStats(IStatsData stats) { string insert = InsertStatement("Stats", stats, new string[] { "StatID", "SaveFile" }, out List parameters); DataAccess.ExecuteNonQuery(insert, parameters); } public void UpdateStats(IStatsData stats) { string query = @"update Stats set SourcePortID = @SourcePortID where StatID = @StatID"; List parameters = new List { DataAccess.DbAdapter.CreateParameter("SourcePortID", stats.SourcePortID), DataAccess.DbAdapter.CreateParameter("StatID", stats.StatID) }; DataAccess.ExecuteNonQuery(query, parameters); } public void DeleteStatsByFile(int gameFileID) { DataAccess.ExecuteNonQuery(string.Format("delete from Stats where GameFileID = {0}", gameFileID)); } public void DeleteStats(int statID) { DataAccess.ExecuteNonQuery(string.Format("delete from Stats where StatID = {0}", statID)); } public void DeleteStats(ISourcePortData sourcePort) { DataAccess.ExecuteNonQuery(string.Format("delete from Stats where SourcePortID = {0}", sourcePort.SourcePortID)); } public IEnumerable GetGameProfiles(int gameFileID) { DataTable dt = DataAccess.ExecuteSelect(string.Format("select * from GameProfiles where GameFileID = {0}", gameFileID)).Tables[0]; return Util.TableToStructure(dt, typeof(GameProfile)).Cast(); } public IEnumerable GetGlobalGameProfiles() { DataTable dt = DataAccess.ExecuteSelect("select * from GameProfiles where GameFileID = -1").Tables[0]; return Util.TableToStructure(dt, typeof(GameProfile)).Cast(); } public void InsertGameProfile(IGameProfile gameProfile) { string insert = InsertStatement("GameProfiles", gameProfile, new string[] { "GameProfileID" }, out List parameters); DataAccess.ExecuteNonQuery(insert, parameters); } public void UpdateGameProfile(IGameProfile gameProfile) { string query = @"update GameProfiles set Name = @Name, SourcePortID = @SourcePortID, IWadID = @IWadID, SettingsMap = @SettingsMap, SettingsSkill = @SettingsSkill, SettingsExtraParams = @SettingsExtraParams, SettingsFiles = @SettingsFiles, SettingsFilesSourcePort = @SettingsFilesSourcePort, SettingsFilesIWAD = @SettingsFilesIWAD, SettingsSpecificFiles = @SettingsSpecificFiles, SettingsStat = @SettingsStat, SettingsLoadLatestSave =@SettingsLoadLatestSave, SettingsSaved = @SettingsSaved, SettingsExtraParamsOnly = @SettingsExtraParamsOnly where GameProfileID = @gameProfileID"; List parameters = new List { DataAccess.DbAdapter.CreateParameter("Name", gameProfile.Name ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SourcePortID", gameProfile.SourcePortID ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("IWadID", gameProfile.IWadID ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsMap", gameProfile.SettingsMap ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsSkill", gameProfile.SettingsSkill ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsExtraParams", gameProfile.SettingsExtraParams ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsFiles", gameProfile.SettingsFiles ?? (object)DBNull.Value ), DataAccess.DbAdapter.CreateParameter("SettingsFilesSourcePort", gameProfile.SettingsFilesSourcePort ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsFilesIWAD", gameProfile.SettingsFilesIWAD ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsSpecificFiles", gameProfile.SettingsSpecificFiles ?? (object)DBNull.Value), DataAccess.DbAdapter.CreateParameter("SettingsLoadLatestSave", gameProfile.SettingsLoadLatestSave), DataAccess.DbAdapter.CreateParameter("SettingsStat", gameProfile.SettingsStat), DataAccess.DbAdapter.CreateParameter("SettingsSaved", gameProfile.SettingsSaved), DataAccess.DbAdapter.CreateParameter("SettingsExtraParamsOnly", gameProfile.SettingsExtraParamsOnly), DataAccess.DbAdapter.CreateParameter("GameProfileID", gameProfile.GameProfileID), }; DataAccess.ExecuteNonQuery(query, parameters); } public void DeleteGameProfile(int gameProfileID) { DataAccess.ExecuteNonQuery(string.Format("delete from GameProfiles where GameProfileID = {0}", gameProfileID)); } public IEnumerable GetCleanupFiles() { return Util.TableToStructure(DataAccess.ExecuteSelect("select * from CleanupFiles").Tables[0], typeof(CleanupFile)).Cast(); } public void InsertCleanupFile(CleanupFile file) { string insert = InsertStatement("CleanupFiles", file, new string[] { "CleanupFileID" }, out List parameters); DataAccess.ExecuteNonQuery(insert, parameters); } public void DeleteCleanupFile(CleanupFile file) { DataAccess.ExecuteNonQuery(string.Format("delete from CleanupFiles where CleanupFileID = {0}", file.CleanupFileID)); } private string InsertStatement(string tableName, object obj, out List parameters) { return InsertStatement(tableName, obj, new string[] { }, out parameters); } private string InsertStatement(string tableName, object obj, string[] exclude, out List parameters) { StringBuilder sb = new StringBuilder("insert into "); sb.Append(tableName); sb.Append(" ("); parameters = new List(); PropertyInfo[] properties = obj.GetType().GetProperties().Where(p => p.GetSetMethod() != null && p.GetGetMethod() != null && !exclude.Contains(p.Name)).ToArray(); foreach(PropertyInfo pi in properties) { sb.Append(pi.Name); sb.Append(','); } sb.Remove(sb.Length - 1, 1); sb.Append(") values("); foreach(PropertyInfo pi in properties) { sb.Append("@"); sb.Append(pi.Name); sb.Append(','); object value = pi.GetValue(obj); if (value == null) value = DBNull.Value; parameters.Add(DataAccess.DbAdapter.CreateParameter(pi.Name, value)); } sb.Remove(sb.Length - 1, 1); sb.Append(")"); return sb.ToString(); } private static List RemoveUnknownColumnsFromParameters(DataTable dt, List parameters) { List badParameters = new List(); foreach (var parameter in parameters) { if (!ColumnExists(dt, parameter.ParameterName)) badParameters.Add(parameter); } return parameters.Except(badParameters).ToList(); } private static StringBuilder RemoveUnknownColumnsFromQuery(DataTable dt, StringBuilder query) { Regex regex = new Regex(@"@\S+"); var matches = regex.Matches(query.ToString()); foreach (Match match in matches) { string columnName = match.Value.Replace("@", string.Empty).Replace(",", string.Empty); if (!ColumnExists(dt, columnName)) { string replace = $"{columnName} = @{columnName}"; if (match.Value.EndsWith(",")) replace += ","; query.Replace(replace, string.Empty); } } Regex whereFix = new Regex(@",\s+where"); var whereMatch = whereFix.Match(query.ToString()); if (whereMatch.Success) query.Replace(whereMatch.Value, " where"); return query; } private static bool ColumnExists(DataTable dt, string columnName) { return dt.Select($"name = '{columnName}'").Any(); } private DataTable GetTableColumns(string tableName) { return DataAccess.ExecuteSelect($"pragma table_info({tableName});").Tables[0]; } public DataAccess DataAccess { get; private set; } private readonly bool m_outOfDateDatabase; } } ================================================ FILE: DoomLauncher/Adapters/DirectoryDataSourceAdapter.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace DoomLauncher { public class DirectoryDataSourceAdapter : IGameFileDataSourceAdapter, IIWadDataSourceAdapter { public DirectoryDataSourceAdapter(LauncherPath gameFileDirectory) { GameFileDirectory = gameFileDirectory; } public int GetGameFilesCount() { DirectoryInfo dir = new DirectoryInfo(GameFileDirectory.GetFullPath()); return dir.GetFiles().Length; } public IEnumerable GetGameFileNames() { return Directory.GetFiles(GameFileDirectory.GetFullPath()).Select(x => Path.GetFileName(x)); } public IGameFile GetGameFile(string fileName) { if (Util.IsDirectory(fileName)) return CreateGameFileFromDirectory(fileName); FileInfo fi = new FileInfo(Path.Combine(GameFileDirectory.GetFullPath(), fileName)); if (fi.Exists) return CreateGameFile(fi); return null; } public IEnumerable GetGameFiles() { return GetGameFiles(null); } public IEnumerable GetUntaggedGameFiles() { throw new NotSupportedException(); } public IEnumerable GetGameFiles(IGameFileGetOptions options) { int limit = -1; if (options != null && options.Limit.HasValue) limit = options.Limit.Value; List ret = new List(); DirectoryInfo dir = new DirectoryInfo(GameFileDirectory.GetFullPath()); int counter = 0; foreach (FileInfo fi in dir.GetFiles()) { counter++; ret.Add(CreateGameFile(fi)); if (limit > -1 && counter == limit) break; } return ret; } public IEnumerable GetGameFileIWads() { throw new NotSupportedException(); } public void DeleteGameFile(IGameFile gameFile) { HandleDelete(GameFileDirectory.GetFullPath(), gameFile.FileName); } private static void HandleDelete(string directory, string filename) { if (!string.IsNullOrEmpty(filename)) { FileInfo fi = new FileInfo(Path.Combine(directory, filename)); if (fi.Exists) { fi.Delete(); } } } public IEnumerable GetIWads() { throw new NotSupportedException(); } public IIWadData GetIWad(int gameFileID) { throw new NotSupportedException(); } public void InsertIWad(IIWadData iwad) { throw new NotSupportedException(); } public void UpdateIWad(IIWadData iwad) { throw new NotSupportedException(); } public void DeleteIWad(IIWadData iwad) { throw new NotSupportedException(); } public void InsertGameFile(IGameFile gameFile) { throw new NotSupportedException(); } public void UpdateGameFile(IGameFile gameFile) { throw new NotSupportedException(); } public void UpdateGameFile(IGameFile gameFile, GameFileFieldType[] updateFields) { throw new NotSupportedException(); } public LauncherPath GameFileDirectory { get; set; } public string[] DateParseFormats { get; set; } private IGameFile CreateGameFile(FileInfo fi) { return new GameFile() { FileName = fi.Name }; } private IGameFile CreateGameFileFromDirectory(string dir) { return new GameFile() { FileName = dir }; } } } ================================================ FILE: DoomLauncher/Adapters/GameLauncher.cs ================================================ using DoomLauncher.Adapters.Launch; using DoomLauncher.Config; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; namespace DoomLauncher { public class GameLauncher { public delegate void GameLaunchExitHandler(GameLaunchInfo info); public event GameLaunchExitHandler ProcessExited; private readonly List _features; private readonly IDirectoriesConfiguration _directories; public GameLauncher(IDirectoriesConfiguration directories, List features) { _features = AddGameFileFeatureIfMissing(features); _directories = directories; } public GameLauncher(IDirectoriesConfiguration directories) : this(directories, new List()) { } private static List AddGameFileFeatureIfMissing(List proposedFeatures) { var updatedFeatures = new List(proposedFeatures); if (!updatedFeatures.Exists(f => f is GameFilesLaunchFeature)) { var iWadIndex = updatedFeatures.FindIndex(f => f is IWadLaunchFeature); var insertIndex = (iWadIndex == -1) ? 0 : iWadIndex + 1; updatedFeatures.Insert(insertIndex, new GameFilesLaunchFeature(null, null)); } return updatedFeatures; } public LaunchResult Launch(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad) { if (!Directory.Exists(sourcePort.Directory.GetFullPath())) { var errorMessage = string.Concat("The source port directory does not exist:", Environment.NewLine, Environment.NewLine, sourcePort.Directory.GetPossiblyRelativePath()); return LaunchResult.Failure(errorMessage); } LaunchParameters launchParameters = GetLaunchParameters(gameFile, addFiles, sourcePort, isGameFileIwad); if (launchParameters.Failed) { return LaunchResult.Failure($"Failed to create launch parameters: {launchParameters.ErrorMessage}"); } Directory.SetCurrentDirectory(sourcePort.Directory.GetFullPath()); var gameLaunchInfo = new GameLaunchInfo(this, gameFile, sourcePort, launchParameters.RecordedFileName); try { Process proc = Process.Start(sourcePort.GetFullExecutablePath(), launchParameters.LaunchString); proc.EnableRaisingEvents = true; proc.Exited += gameLaunchInfo.proc_Exited; } catch { return LaunchResult.Failure("Failed to execute the source port process."); } return LaunchResult.Success(gameLaunchInfo); } public LaunchParameters GetLaunchParameters(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePortData, bool isGameFileIwad) { var paramList = _features.Select(f => f.CreateParameter(gameFile, addFiles, sourcePortData, isGameFileIwad, _directories)); var combinedParams = paramList.Aggregate(LaunchParameters.EMPTY, (a, b) => a.Combine(b)); return combinedParams.WithVariableReplacement("filename", gameFile.FileNameNoPath); } public class GameLaunchInfo { public IGameFile GameFile { get; } public ISourcePortData SourcePort { get; } public string RecordedFileName { get; } private readonly GameLauncher _adapter; public GameLaunchInfo(GameLauncher adapter, IGameFile gameFile, ISourcePortData sourcePort, string recordedFileName) { _adapter = adapter; GameFile = gameFile; SourcePort = sourcePort; RecordedFileName = recordedFileName; } public void proc_Exited(object sender, EventArgs e) { _adapter.ProcessExited.Invoke(this); } } } } ================================================ FILE: DoomLauncher/Adapters/IdGamesDataAdapater.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; using System.Net; namespace DoomLauncher { public class IdGamesBadResponseException : Exception { public IdGamesBadResponseException() : base("Bad response from idgames") { } } class IdGamesDataAdapater : IGameFileDataSourceAdapter { static string[] QueryLookup = { "action=search&type=filename&query={0}", //Filename, "action=search&type=title&query={0}", //Title, "action=search&type=author&query={0}", //Author, "action=search&type=descrption&query={0}", //Description, string.Empty, //ReleaseDate, "action=get&id={0}" }; //GameFileID, public IdGamesDataAdapater(string url, string apiPage, string mirrorUrl) { Url = url; ApiPage = apiPage; MirrorUrl = mirrorUrl; } public int GetGameFilesCount() { return 0; } public IEnumerable GetGameFiles() { return GetGameFiles(null); } public IEnumerable GetUntaggedGameFiles() { throw new NotSupportedException(); } public IEnumerable GetGameFiles(IGameFileGetOptions options) { if (options == null) return GetFiles("action=search&field=filename&query=zip&sort=date&dir=desc", "file"); if ((int)options.SearchField.SearchFieldType >= QueryLookup.Length) return Array.Empty(); var query = QueryLookup[(int)options.SearchField.SearchFieldType]; if (string.IsNullOrEmpty(query)) return Array.Empty(); const int minLength = 3; var searchText = options.SearchField.SearchText; var fieldType = options.SearchField.SearchFieldType; if (fieldType != GameFileFieldType.Filename && fieldType != GameFileFieldType.GameFileID && searchText.Length < minLength) return Array.Empty(); if (fieldType == GameFileFieldType.Filename) { var fi = new FileInfo(searchText); if (!string.IsNullOrEmpty(fi.Extension)) searchText = searchText.Replace(fi.Extension, string.Empty); if (searchText.Length < minLength) searchText += ".zip"; } return GetFiles(string.Format(query, Uri.EscapeDataString(searchText)), options.SearchField.SearchFieldType == GameFileFieldType.GameFileID ? "content" : "file"); } public IEnumerable GetGameFileIWads() { throw new NotSupportedException(); } public IEnumerable GetGameFileNames() { throw new NotSupportedException(); } public IGameFile GetGameFile(string fileName) { GameFileSearchField sf = new GameFileSearchField(GameFileFieldType.Filename, fileName); return GetGameFiles(new GameFileGetOptions(sf)).FirstOrDefault(); } public IEnumerable GetGameFilesByName(string fileName) { GameFileSearchField sf = new GameFileSearchField(GameFileFieldType.Filename, fileName); return GetGameFiles(new GameFileGetOptions(sf)); } public void InsertGameFile(IGameFile gameFile) { throw new NotSupportedException(); } public void UpdateGameFile(IGameFile gameFile) { throw new NotSupportedException(); } public void UpdateGameFile(IGameFile gameFile, GameFileFieldType[] updateFields) { throw new NotSupportedException(); } public void DeleteGameFile(IGameFile gameFile) { throw new NotSupportedException(); } public string Url { get; set; } public string ApiPage { get; set; } public string MirrorUrl { get; set; } private IEnumerable GetFiles(string query, string itemName) { WebRequest request = WebRequest.Create(string.Format(Url + ApiPage + "?" + query)); request.Credentials = CredentialCache.DefaultCredentials; HttpWebResponse response = (HttpWebResponse)request.GetResponse(); StreamReader reader = new StreamReader(response.GetResponseStream()); string xmlResponse = reader.ReadToEnd(); reader.Close(); response.Close(); if (xmlResponse.Length == 0) throw new IdGamesBadResponseException(); StringReader xmlReader = new StringReader(xmlResponse); DataSet ds = new DataSet(); ds.ReadXml(xmlReader); xmlReader.Dispose(); if (ds.Tables.Contains("warning") && ds.Tables["warning"].Rows[0]["type"].ToString() == "No Results") return Array.Empty(); IEnumerable files = Util.TableToStructure(ds.Tables[itemName], typeof(IdGamesGameFile)).Cast().ToList(); foreach (IdGamesGameFile file in files) file.Description = file.Description.Replace("
    ", "\n"); return files; } } } ================================================ FILE: DoomLauncher/Adapters/Launch/ExtraParametersLaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using System.Collections.Generic; namespace DoomLauncher.Adapters.Launch { public class ExtraParametersLaunchFeature : ILaunchFeature { private readonly string _extraParameters; // Never null private readonly bool _extraParametersOnly; public ExtraParametersLaunchFeature(string extraParameters, bool extraParametersOnly) { _extraParameters = extraParameters ?? ""; _extraParametersOnly = extraParametersOnly; } public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { if (_extraParametersOnly) return LaunchParameters.ExclusiveParam(_extraParameters); else return LaunchParameters.Param(_extraParameters); } } } ================================================ FILE: DoomLauncher/Adapters/Launch/GameFilesLaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace DoomLauncher.Adapters.Launch { public class GameFilesLaunchFeature : ILaunchFeature { private readonly List m_gameFiles; private readonly List m_specificFiles; private readonly bool m_extractFiles; public GameFilesLaunchFeature(List gameFiles, List specificFiles, bool extractFiles = true) { m_gameFiles = (gameFiles != null) ? new List(gameFiles) : new List(); m_specificFiles = (specificFiles != null) ? new List(specificFiles) : new List(); m_extractFiles = extractFiles; } public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { var filesToUse = new List(m_gameFiles); if (isGameFileIwad) filesToUse.Remove(gameFile); else if (!filesToUse.Contains(gameFile)) filesToUse.Add(gameFile); List launchFiles = new List(); foreach (IGameFile file in filesToUse) { if (!file.ArchiveExists(directories.GameFileDirectory)) { return LaunchParameters.Failure($"Couldn't find additional file at {new LauncherPath(file.FileName).GetFullPath()}"); } launchFiles.AddRange(GetExtractedFileNames(sourcePort, file, directories)); } // Build launch string // -file "file1", "file2",.. -deh "deh1", "deh2",.. var launchString = BuildLaunchString(sourcePort.GetFlavor(), launchFiles); return LaunchParameters.Param(launchString); } private string BuildLaunchString(ISourcePortFlavor sourcePort, List files) { StringBuilder sb = new StringBuilder(); List dehFiles = new List(); if (files.Count > 0) { sb.Append(sourcePort.FileParameter(new SpData())); var dehExtensions = Util.GetDehackedExtensions(); foreach (string str in files) { FileInfo fi = new FileInfo(str); if (!dehExtensions.Contains(fi.Extension, StringComparer.OrdinalIgnoreCase)) sb.Append(string.Format("\"{0}\" ", str)); else dehFiles.Add(str); } } if (dehFiles.Count > 0) { sb.Append(" -deh "); foreach (string str in dehFiles) sb.Append(string.Format("\"{0}\" ", str)); } return sb.ToString(); } public List GetExtractedFileNames(ISourcePortData sourcePortData, IGameFile gameFile, IDirectoriesConfiguration directories) { List launchFiles = new List(); if (gameFile.IsDirectory()) { launchFiles.Add(gameFile.FileName); return launchFiles; } try { using (IArchiveReader reader = gameFile.OpenGameFile(directories.GameFileDirectory)) { IEnumerable relevantEntries = GetRelevantEntries(reader, sourcePortData, m_specificFiles); foreach (IArchiveEntry entry in relevantEntries) { if (entry.ExtractRequired) { string extractFile = Path.Combine(directories.TempDirectory.GetFullPath(), entry.Name); if (m_extractFiles) entry.ExtractToFileForceOverwrite(extractFile); launchFiles.Add(extractFile); } else { launchFiles.Add(entry.FullName); } } } } catch (FileNotFoundException) { string.Format("The game file was not found: {0}", gameFile.FileName); } catch (InvalidDataException) { string.Format("The game file does not appear to be a valid zip file: {0}", gameFile.FileName); } return launchFiles; } public static IEnumerable GetRelevantEntries(IArchiveReader reader, ISourcePortData sourcePortData, IList specificFiles) { // Go with the specific files if available, otherwise pick out the SourcePort's allowed extensions if (specificFiles != null && specificFiles.Count > 0) { // This could be a relative unmanaged file which means the partial path must convert to a full path for comparison if (reader is FileArchiveReader && reader.Entries.Any()) { var filePaths = specificFiles.Select(x => new LauncherPath(x)).ToArray(); return reader.Entries.Where(entry => filePaths.Any(filePath => entry.FullName == filePath.GetFullPath())); } return reader.Entries.Where(entry => specificFiles.Contains(entry.FullName)); } else { return reader.Entries.Where(x => EntryMatchesSourcePortExtensions(x, sourcePortData)); } } private static bool EntryMatchesSourcePortExtensions(IArchiveEntry entry, ISourcePortData sourcePortData) { string[] extensions = sourcePortData.SupportedExtensions.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); return !string.IsNullOrEmpty(entry.Name) && entry.Name.Contains('.') && extensions.Any(y => y.Equals(Path.GetExtension(entry.Name), StringComparison.OrdinalIgnoreCase)); } } } ================================================ FILE: DoomLauncher/Adapters/Launch/ILaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using System.Collections.Generic; namespace DoomLauncher.Adapters.Launch { public interface ILaunchFeature { LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories); } } ================================================ FILE: DoomLauncher/Adapters/Launch/IWadLaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace DoomLauncher.Adapters.Launch { public class IWadLaunchFeature : ILaunchFeature { private readonly IGameFile _iwad; private readonly bool _extractFiles; public IWadLaunchFeature(IGameFile iwad, bool extractFiles = true) { _iwad = iwad; _extractFiles = extractFiles; } public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { if (!gameFile.ArchiveExists(directories.GameFileDirectory)) { return LaunchParameters.Failure($"Couldn't find game file at {directories.GameFileDirectory.GetFullPath()}"); } string extractedFileName; try { extractedFileName = GetExtractedFileName(sourcePort, directories); } catch (FileNotFoundException) { return LaunchParameters.Failure($"File not found: {gameFile.FileName}"); } catch (IOException) { return LaunchParameters.Failure($"File in use: {gameFile.FileName}"); } catch (Exception e) { return LaunchParameters.Failure($"There was an issue with the IWad: {gameFile.FileName}." + $"{Environment.NewLine}{Environment.NewLine}{e.Message}"); } if (extractedFileName == null) { return LaunchParameters.Failure("Failed to find any IWAD files in the select IWAD archive.\n" + "View the IWAD and click 'Select Individual Files...' to ensure the IWAD file is selected."); } var paramString = sourcePort.GetFlavor().IwadParameter(new SpData(extractedFileName, gameFile, addFiles)); return LaunchParameters.Param(paramString).WithVariableReplacement("iwad", Path.GetFileNameWithoutExtension(_iwad.FileNameNoPath)); } private string GetExtractedFileName(ISourcePortData sourcePortData, IDirectoriesConfiguration directories) { using (IArchiveReader reader = _iwad.OpenGameFile(directories.GameFileDirectory)) { IArchiveEntry firstMatchingEntry = GetFirstIWadEntry(reader, sourcePortData); if (firstMatchingEntry != null) { if (firstMatchingEntry.ExtractRequired) { string extractFile = Path.Combine(directories.TempDirectory.GetFullPath(), firstMatchingEntry.Name); if (_extractFiles) firstMatchingEntry.ExtractToFileForceOverwrite(extractFile); return extractFile; } else { return firstMatchingEntry.FullName; } } else return null; } } private IArchiveEntry GetFirstIWadEntry(IArchiveReader reader, ISourcePortData sourcePortData) { var specificFiles = _iwad.SettingsSpecificFiles; return GameFilesLaunchFeature.GetRelevantEntries(reader, sourcePortData, Util.SplitString(specificFiles)).FirstOrDefault(); } } } ================================================ FILE: DoomLauncher/Adapters/Launch/LaunchParameters.cs ================================================  using System.Collections.Generic; using System.Text; namespace DoomLauncher.Adapters.Launch { public class LaunchParameters { public string LaunchString { get => _launchString; } private readonly string _launchString; // Never null public string ErrorMessage { get; } // Null unless failure public string RecordedFileName { get; } // Null unless supplied public bool Failed { get => ErrorMessage != null; } private readonly IDictionary _variableReplacements; // Never null private readonly bool _isExclusive; // false unless supplied public static readonly LaunchParameters EMPTY = new LaunchParameters("", null, null, false, null); private LaunchParameters(string launchString, string recordedFileName, string errorMessage, bool isExclusive, IDictionary variableReplacements) { ErrorMessage = errorMessage; RecordedFileName = recordedFileName; _variableReplacements = variableReplacements ?? new Dictionary(); _isExclusive = isExclusive; _launchString = ReplaceVariables(launchString ?? ""); } public LaunchParameters Combine(LaunchParameters other) { if (Failed || _isExclusive) { return this; } else if (other.Failed || other._isExclusive) { return other; } else { return new LaunchParameters( $"{LaunchString.Trim()} {other.LaunchString.Trim()}".Trim(), RecordedFileName ?? other.RecordedFileName, null, other._isExclusive, CombineDictionaries(_variableReplacements, other._variableReplacements)); } } public LaunchParameters WithRecordedFileName(string recordedFileName) { return Combine(new LaunchParameters("", recordedFileName, null, false, null)); } public LaunchParameters WithVariableReplacement(string variable, string value) { var dict = new Dictionary { { variable, value } }; return Combine(new LaunchParameters("", null, null, false, dict)); } private IDictionary CombineDictionaries(IDictionary ourDict, IDictionary otherDict) { var combinedDictionary = new Dictionary(ourDict); foreach (var key in otherDict.Keys) { if (!combinedDictionary.ContainsKey(key)) combinedDictionary[key] = otherDict[key]; } return combinedDictionary; } private string ReplaceVariables(string paramString) { var sb = new StringBuilder(paramString); foreach (var key in _variableReplacements.Keys) { sb.Replace($"${key}", _variableReplacements[key]); } return sb.ToString(); } public static LaunchParameters ExclusiveParam(string paramString) { return new LaunchParameters(paramString, null, null, true, null); } public static LaunchParameters Param(string paramString) { return new LaunchParameters(paramString, null, null, false, null); } public static LaunchParameters Failure(string errorMessage) { return new LaunchParameters("", null, errorMessage, false, null); } } } ================================================ FILE: DoomLauncher/Adapters/Launch/LaunchResult.cs ================================================  using static DoomLauncher.GameLauncher; namespace DoomLauncher.Adapters.Launch { public class LaunchResult { public GameLaunchInfo GameLaunchInfo { get; } public string ErrorMessage { get; } public bool Failed { get => !string.IsNullOrEmpty(ErrorMessage); } private LaunchResult(GameLaunchInfo gameLaunchInfo, string errorMessage) { GameLaunchInfo = gameLaunchInfo; ErrorMessage = errorMessage; } public static LaunchResult Success(GameLaunchInfo gameLaunchInfo) => new LaunchResult(gameLaunchInfo, null); public static LaunchResult Failure(string errorMessage) => new LaunchResult(null, errorMessage); } } ================================================ FILE: DoomLauncher/Adapters/Launch/LoadSaveLaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System.Collections.Generic; namespace DoomLauncher.Adapters.Launch { public class LoadSaveLaunchFeature : ILaunchFeature { private readonly string _loadSaveFile; public LoadSaveLaunchFeature(string loadSaveFile) { _loadSaveFile = loadSaveFile; } public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { if (!string.IsNullOrEmpty(_loadSaveFile) && sourcePort.GetFlavor().LoadSaveGameSupported()) { var paramString = sourcePort.GetFlavor().LoadSaveParameter(new SpData(_loadSaveFile, gameFile, addFiles)); return LaunchParameters.Param(paramString); } else return LaunchParameters.EMPTY; } } } ================================================ FILE: DoomLauncher/Adapters/Launch/MapSkillLaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System.Collections.Generic; using System.Text; namespace DoomLauncher.Adapters.Launch { public class MapSkillLaunchFeature : ILaunchFeature { private readonly string _map; private readonly string _skill; public MapSkillLaunchFeature(string map, string skill) { _map = map; _skill = skill; } public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { LaunchParameters result = LaunchParameters.EMPTY; if (_map != null) { var warpParam = LaunchParameters.Param(sourcePort.GetFlavor().WarpParameter(new SpData(_map, gameFile, addFiles))); result = warpParam; if (_skill != null) { var skillParam = LaunchParameters.Param(sourcePort.GetFlavor().SkillParameter(new SpData(_skill, gameFile, addFiles))); result = warpParam.Combine(skillParam); } } return result; } } } ================================================ FILE: DoomLauncher/Adapters/Launch/PlayDemoLaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System; using System.Collections.Generic; using System.IO; namespace DoomLauncher.Adapters.Launch { public class PlayDemoLaunchFeature : ILaunchFeature { private readonly string _playDemoFile; public PlayDemoLaunchFeature(string playDemoFile) { _playDemoFile = playDemoFile; } public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { FileInfo fi = new FileInfo(_playDemoFile); if (!fi.Exists) { return LaunchParameters.Failure($"Failed to find demo file {_playDemoFile}"); } else { var paramString = sourcePort.GetFlavor().PlayDemoParameter(new SpData(_playDemoFile, gameFile, addFiles)); return LaunchParameters.Param(paramString); } } } } ================================================ FILE: DoomLauncher/Adapters/Launch/RecordLaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System; using System.Collections.Generic; using System.IO; namespace DoomLauncher.Adapters.Launch { public class RecordLaunchFeature : ILaunchFeature { public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { var recordedFileName = Path.Combine(directories.TempDirectory.GetFullPath(), Guid.NewGuid().ToString()); var paramString = sourcePort.GetFlavor().RecordParameter(new SpData(recordedFileName, gameFile, addFiles)); return LaunchParameters.Param(paramString).WithRecordedFileName(recordedFileName); } } } ================================================ FILE: DoomLauncher/Adapters/Launch/SourcePortExtraParametersLaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using System.Collections.Generic; namespace DoomLauncher.Adapters.Launch { public class SourcePortExtraParametersLaunchFeature : ILaunchFeature { public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { return LaunchParameters.Param(sourcePort.ExtraParameters); } } } ================================================ FILE: DoomLauncher/Adapters/Launch/StatisticsReaderLaunchFeature.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; namespace DoomLauncher.Adapters.Launch { public class StatisticsReaderLaunchFeature : ILaunchFeature { public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { IStatisticsReader statsReader = sourcePort.GetFlavor().CreateStatisticsReader(gameFile, Array.Empty()); return LaunchParameters.Param(statsReader?.LaunchParameter); } } } ================================================ FILE: DoomLauncher/Adapters/Launch/UtilityFilesLaunchFeature.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System; using DoomLauncher.Config; namespace DoomLauncher.Adapters.Launch { public class UtilityFilesLaunchFeature : ILaunchFeature { private readonly List _pathFiles; public UtilityFilesLaunchFeature(List pathFiles) { _pathFiles = new List(pathFiles); } public LaunchParameters CreateParameter(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool isGameFileIwad, IDirectoriesConfiguration directories) { ISourcePortFlavor sourcePortFlavor = new GenericSourcePortFlavor(sourcePort); StringBuilder sb = new StringBuilder(); try { List files = new List(); foreach (var pathFile in _pathFiles) { if (gameFile.IsUnmanaged()) { files.Add(pathFile.ExtractedFile); continue; } if (!File.Exists(pathFile.ExtractedFile)) continue; using (IArchiveReader reader = ArchiveReader.Create(pathFile.ExtractedFile)) { var entry = reader.Entries.FirstOrDefault(x => x.FullName == pathFile.InternalFilePath); if (entry != null) files.Add(Util.ExtractTempFile(directories.TempDirectory.GetFullPath(), entry)); } } var launchString = BuildLaunchString(sourcePortFlavor, files); return LaunchParameters.Param(launchString); } catch (FileNotFoundException) { var errorMessage = string.Format("The game file was not found: {0}", gameFile.FileName); return LaunchParameters.Failure(errorMessage); } catch (InvalidDataException) { var errorMessage = string.Format("The game file does not appear to be a valid zip file: {0}", gameFile.FileName); return LaunchParameters.Failure(errorMessage); } } private string BuildLaunchString(ISourcePortFlavor sourcePort, List files) { var sb = new StringBuilder(); List dehFiles = new List(); if (files.Count > 0) { sb.Append(sourcePort.FileParameter(new SpData())); var dehExtensions = Util.GetDehackedExtensions(); foreach (string str in files) { FileInfo fi = new FileInfo(str); if (!dehExtensions.Contains(fi.Extension, StringComparer.OrdinalIgnoreCase)) sb.Append(string.Format("\"{0}\" ", str)); else dehFiles.Add(str); } } if (dehFiles.Count > 0) { sb.Append(" -deh "); foreach (string str in dehFiles) sb.Append(string.Format("\"{0}\" ", str)); } return sb.ToString(); } } } ================================================ FILE: DoomLauncher/Adapters/MSSQLDataAdapter.cs ================================================ using DoomLauncher.Interfaces; using System.Data.Common; using System.Data.SqlClient; namespace DoomLauncher { class MSSQLDataAdapter : IDatabaseAdapter { public DbConnection CreateConnection(string connectionString) { return new SqlConnection(connectionString); } public DbDataAdapter CreateAdapter() { return new SqlDataAdapter(); } public DbParameter CreateParameter(string name, object value) { return new SqlParameter(name, value); } } } ================================================ FILE: DoomLauncher/Adapters/PlaySession.cs ================================================ using System; using static DoomLauncher.GameLauncher; namespace DoomLauncher.Adapters { public class PlaySession { public PlaySession(GameLaunchInfo launchInfo, IStatisticsReader statisticsReader, DateTime start) { GameLaunchInfo = launchInfo; StatisticsReader = statisticsReader; Start = start; } public GameLaunchInfo GameLaunchInfo { get; } public IStatisticsReader StatisticsReader { get; } public DateTime Start { get; set; } } } ================================================ FILE: DoomLauncher/Adapters/SqliteDatabaseAdapter.cs ================================================ using DoomLauncher.Interfaces; using System.Data.Common; using System.Data.SQLite; namespace DoomLauncher { public class SqliteDatabaseAdapter : IDatabaseAdapter { public DbConnection CreateConnection(string connectionString) { return new SQLiteConnection(connectionString); } public DbDataAdapter CreateAdapter() { return new SQLiteDataAdapter(); } public DbParameter CreateParameter(string name, object value) { return new SQLiteParameter(name, value); } } } ================================================ FILE: DoomLauncher/Adapters/WadArchiveDataAdapter.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Security.Cryptography; namespace DoomLauncher { class WadArchiveDataAdapter : IGameFileDataSourceAdapter { private string m_urlMD5 = "http://www.wad-archive.com/api/latest/"; private string m_urlFilename = "http://www.wad-archive.com/wadseeker/"; public WadArchiveGameFile Test(string file) { return GetGameFileFromMD5(file); } private WadArchiveGameFile GetGameFileFromMD5(string filePath) { var md5 = MD5.Create(); byte[] hash = null; using (var stream = File.OpenRead(filePath)) { hash = md5.ComputeHash(stream); } string md5_string = BitConverter.ToString(hash).Replace("-", string.Empty).ToLower(); return GetGameFileRequest(m_urlMD5 + md5_string); } private WadArchiveGameFile GetGameFileByName(string name) { return GetGameFileRequest(m_urlFilename + name); } private WadArchiveGameFile GetGameFileRequest(string url) { WebRequest request = WebRequest.Create(string.Format(url)); request.Credentials = CredentialCache.DefaultCredentials; HttpWebResponse response = (HttpWebResponse)request.GetResponse(); StreamReader reader = new StreamReader(response.GetResponseStream()); string data = reader.ReadToEnd(); if (data != "[]") { if (data.EndsWith(",\"screenshots\":[]}")) data = data.Replace(",\"screenshots\":[]", string.Empty); var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore }; return JsonConvert.DeserializeObject(data, settings); } return null; } public int GetGameFilesCount() { return 0; } public IEnumerable GetGameFiles() { throw new NotImplementedException(); } public IEnumerable GetUntaggedGameFiles() { throw new NotSupportedException(); } public IEnumerable GetGameFiles(IGameFileGetOptions options) { if (options.SearchField.SearchFieldType == GameFileFieldType.MD5) { return new WadArchiveGameFile[] { GetGameFileFromMD5(options.SearchField.SearchText) }; } else { throw new NotSupportedException("Only GamefileFieldType.MD5 is supported."); } } public IEnumerable GetGameFileIWads() { throw new NotSupportedException(); } public IEnumerable GetGameFileNames() { throw new NotImplementedException(); } public IGameFile GetGameFile(string fileName) { throw new NotImplementedException(); } public void InsertGameFile(IGameFile gameFile) { throw new NotImplementedException(); } public void UpdateGameFile(IGameFile gameFile) { throw new NotImplementedException(); } public void UpdateGameFile(IGameFile gameFile, GameFileFieldType[] updateFields) { throw new NotImplementedException(); } public void DeleteGameFile(IGameFile gameFile) { throw new NotImplementedException(); } public void UpdateGameFiles(GameFileFieldType ftWhere, GameFileFieldType ftSet, object fWhere, object fSet) { throw new NotImplementedException(); } public IEnumerable GetSourcePorts() { throw new NotImplementedException(); } public IEnumerable GetUtilities() { throw new NotImplementedException(); } public ISourcePortData GetSourcePort(int sourcePortID) { throw new NotImplementedException(); } public void InsertSourcePort(ISourcePortData sourcePort) { throw new NotImplementedException(); } public void UpdateSourcePort(ISourcePortData sourcePort) { throw new NotImplementedException(); } public void DeleteSourcePort(ISourcePortData sourcePort) { throw new NotImplementedException(); } public IEnumerable GetFiles() { throw new NotImplementedException(); } public IEnumerable GetFiles(IGameFile gameFile) { throw new NotImplementedException(); } public IEnumerable GetFiles(IGameFile gameFile, FileType fileTypeID) { WadArchiveGameFile archiveGameFile = gameFile as WadArchiveGameFile; if (archiveGameFile != null) { List ret = new List(); if (archiveGameFile.screenshots != null) { foreach (var item in archiveGameFile.screenshots) { WadArchiveFile file = new WadArchiveFile(); file.FileName = item.Value; ret.Add(file); } } return ret; } else { throw new ArgumentException("Parameter gameFile must be of type WadArchiveGameFile"); } } public void UpdateFiles(int sourcePortID_Where, int? sourcePortID_Set) { throw new NotImplementedException(); } public void InsertFile(IFileData file) { throw new NotImplementedException(); } public void UpdateFile(IFileData file) { throw new NotImplementedException(); } public void DeleteFile(IFileData file) { throw new NotImplementedException(); } public void DeleteFile(IGameFile file) { throw new NotImplementedException(); } public void DeleteFiles(ISourcePortData sourcePort, FileType fileTypeID) { throw new NotImplementedException(); } public IEnumerable GetGameFiles(ITagData tag) { throw new NotImplementedException(); } public IEnumerable GetGameFiles(IGameFileGetOptions options, ITagData tag) { throw new NotImplementedException(); } public IEnumerable GetIWads() { throw new NotImplementedException(); } public IIWadData GetIWad(int gameFileID) { throw new NotImplementedException(); } public void InsertIWad(IIWadData iwad) { throw new NotImplementedException(); } public void DeleteIWad(IIWadData iwad) { throw new NotImplementedException(); } public void UpdateIWad(IIWadData iwad) { throw new NotImplementedException(); } public IEnumerable GetConfiguration() { throw new NotImplementedException(); } public void InsertConfiguration(IConfigurationData config) { throw new NotImplementedException(); } public void UpdateConfiguration(IConfigurationData config) { throw new NotImplementedException(); } public IEnumerable GetTags() { throw new NotImplementedException(); } public void InsertTag(ITagData tag) { throw new NotImplementedException(); } public void UpdateTag(ITagData tag) { throw new NotImplementedException(); } public void DeleteTag(ITagData tag) { throw new NotImplementedException(); } public IEnumerable GetTagMappings() { throw new NotImplementedException(); } public IEnumerable GetTagMappings(int gameFileID) { throw new NotImplementedException(); } public void InsertTagMapping(ITagMapping tag) { throw new NotImplementedException(); } public void DeleteTagMapping(ITagMapping tag) { throw new NotImplementedException(); } public void DeleteTagMapping(int tagID) { throw new NotImplementedException(); } public IEnumerable GetStats() { throw new NotImplementedException(); } public IEnumerable GetStats(int gameFileID) { throw new NotImplementedException(); } public void InsertStats(IStatsData stats) { throw new NotImplementedException(); } public void UpdateStats(IStatsData stats) { throw new NotImplementedException(); } public void DeleteStatsByFile(int gameFileID) { throw new NotImplementedException(); } public void DeleteStats(int statID) { throw new NotImplementedException(); } public void DeleteStats(ISourcePortData sourcePort) { throw new NotImplementedException(); } } } ================================================ FILE: DoomLauncher/App.config ================================================ 
    ================================================ FILE: DoomLauncher/Archive/ArchiveReader.cs ================================================ using DoomLauncher.Archive.Rar; using DoomLauncher.Archive.SevenZip; using System; using System.IO; namespace DoomLauncher { public static class ArchiveReader { public static IArchiveReader EmptyArchiveReader = new EmptyArchiveReader(); public static string SevenZipInteropLibrary { get; private set; } = Path.Combine(Util.GetInteropDirectory(), "7z.dll"); public static void SetSevenZipInteropLibrary(string file) => SevenZipInteropLibrary = file; public static IArchiveReader Create(string path) { if (!File.Exists(path) && !Directory.Exists(path)) return EmptyArchiveReader; if (Util.IsDirectory(path)) return new DirectoryArchiveReader(path); string ext = Path.GetExtension(path); if (IsPk(ext)) return new ZipArchiveReader(path); if (IsWad(ext)) return new WadArchiveReader(path); if (IsSevenZip(ext)) return new SevenZipArchiveReader(path, SevenZipInteropLibrary); if (IsRar(ext)) return new RarArchiveReader(path); return new FileArchiveReader(path); } private static bool IsRar(string ext) => ext.Equals(".rar", StringComparison.OrdinalIgnoreCase); private static bool IsSevenZip(string ext) => ext.Equals(".7z", StringComparison.OrdinalIgnoreCase); public static bool IsWad(string ext) => ext.Equals(".wad", StringComparison.OrdinalIgnoreCase); public static bool IsPk(string fileExtension) { var extensions = Util.GetReadablePkExtensions(); foreach (string ext in extensions) { if (fileExtension.Equals(ext, StringComparison.OrdinalIgnoreCase)) return true; } return false; } } } ================================================ FILE: DoomLauncher/Archive/Directory/DirectoryArchiveEntry.cs ================================================ using System.IO; namespace DoomLauncher { internal class DirectoryArchiveEntry : AbstractArchiveEntry { private readonly FileInfo m_file; public DirectoryArchiveEntry(string file) { m_file = new FileInfo(file); } public override long Length => m_file.Length; public override string Name => m_file.Name; public override string FullName => m_file.FullName; public override bool ExtractRequired => false; public override bool IsDirectory => false; public override void ExtractToFile(string file, bool overwrite = false) { m_file.CopyTo(file, overwrite); } public override void Read(byte[] buffer, int offset, int length) { using (var fs = m_file.OpenRead()) fs.Read(buffer, offset, length); } public override string GetNameWithoutExtension() => Name; } } ================================================ FILE: DoomLauncher/Archive/Directory/DirectoryArchiveReader.cs ================================================ using System.Collections.Generic; using System.IO; namespace DoomLauncher { public class DirectoryArchiveReader : IArchiveReader { private readonly string m_directory; private readonly List m_entries = new List(); public IEnumerable Entries => m_entries; public bool EntriesHaveExtensions => true; public DirectoryArchiveReader(string directory) { m_directory = directory; RecursivelyIterateDirectory(m_directory); } void RecursivelyIterateDirectory(string directory) { try { foreach (string dir in Directory.GetDirectories(directory)) { foreach (string file in Directory.GetFiles(dir)) m_entries.Add(new DirectoryArchiveEntry(file)); RecursivelyIterateDirectory(dir); } } catch { } } public void Dispose() { } } } ================================================ FILE: DoomLauncher/Archive/EmptyArchiveReader.cs ================================================ using System; using System.Collections.Generic; namespace DoomLauncher { public class EmptyArchiveReader : IArchiveReader { public IEnumerable Entries => Array.Empty(); public bool EntriesHaveExtensions => false; public void Dispose() { } } } ================================================ FILE: DoomLauncher/Archive/FileArchiveReader.cs ================================================ using System; using System.Collections.Generic; using System.IO; namespace DoomLauncher { class FileArchiveReader : AbstractArchiveEntry, IArchiveReader { private readonly string m_file; private readonly List m_entries = new List(); public bool EntriesHaveExtensions => true; public FileArchiveReader(string file) { m_file = file; m_entries.Add(this); } public override long Length => new FileInfo(m_file).Length; public override void Read(byte[] buffer, int offset, int length) { using (var stream = File.OpenRead(m_file)) stream.Read(buffer, offset, length); } public IEnumerable Entries => m_entries; public override string Name => Path.GetFileName(m_file); public override string FullName => Path.GetFullPath(m_file); public override bool ExtractRequired => false; public override bool IsDirectory => false; public override void ExtractToFile(string file, bool overwrite = false) { File.Copy(m_file, file, overwrite); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { } public override bool Equals(object obj) { IArchiveEntry entry = obj as IArchiveEntry; if (entry == null) return false; return entry.FullName == FullName; } public override int GetHashCode() { return FullName.GetHashCode(); } public override string GetNameWithoutExtension() => Path.GetFileNameWithoutExtension(Name); } } ================================================ FILE: DoomLauncher/Archive/IArchiveEntry.cs ================================================ using SharpCompress.Common; using System; using System.IO; namespace DoomLauncher { public interface IArchiveEntry { long Length { get; } void Read(byte[] buffer, int offset, int length); string Name { get; } string FullName { get; } void ExtractToFile(string file, bool overwrite = false); void ExtractToFileForceOverwrite(string file); bool ExtractRequired { get; } bool IsDirectory { get; } string GetNameWithoutExtension(); } public abstract class AbstractArchiveEntry : IArchiveEntry { public abstract long Length { get; } public abstract string Name { get; } public abstract string FullName { get; } public abstract bool ExtractRequired { get; } public abstract bool IsDirectory { get; } public abstract void ExtractToFile(string file, bool overwrite = false); public void ExtractToFileForceOverwrite(string file) { try { ExtractToFile(file, true); } catch (Exception ex) { try { if (File.Exists(file)) { // Sometimes the read only flag can be set and the file can't be overwritten. This is our temp directory anyway so turn it off. File.SetAttributes(file, File.GetAttributes(file) & ~FileAttributes.ReadOnly); ExtractToFile(file, true); } } catch { throw ex; } } } public abstract string GetNameWithoutExtension(); public abstract void Read(byte[] buffer, int offset, int length); } } ================================================ FILE: DoomLauncher/Archive/IArchiveReader.cs ================================================ using System; using System.Collections.Generic; namespace DoomLauncher { public interface IArchiveReader : IDisposable { IEnumerable Entries { get; } bool EntriesHaveExtensions { get; } } } ================================================ FILE: DoomLauncher/Archive/Rar/RarArchiveEntry.cs ================================================ using SharpCompress.Archives; using System.IO; namespace DoomLauncher.Archive.Rar { public class RarArchiveEntry : AbstractArchiveEntry { private readonly SharpCompress.Archives.Rar.RarArchiveEntry m_entry; public RarArchiveEntry(SharpCompress.Archives.Rar.RarArchiveEntry entry) { m_entry = entry; } public override long Length => m_entry.Size; public override string Name => Path.GetFileName(m_entry.Key); public override string FullName => m_entry.Key; public override bool ExtractRequired => true; public override bool IsDirectory => m_entry.IsDirectory; public override void ExtractToFile(string file, bool overwrite = false) { if (!IsValid(m_entry)) return; if (!overwrite && File.Exists(file)) return; m_entry.WriteToFile(file); } public override void Read(byte[] buffer, int offset, int length) { using (MemoryStream ms = new MemoryStream(buffer, offset, length)) m_entry.WriteTo(ms); } public override bool Equals(object obj) { if (!(obj is IArchiveEntry entry)) return false; return entry.FullName == FullName; } public override int GetHashCode() { return FullName.GetHashCode(); } public override string GetNameWithoutExtension() => Path.GetFileNameWithoutExtension(Name); public static bool IsValid(SharpCompress.Archives.Rar.RarArchiveEntry entry) { // Addresses vulnerability https://github.com/nstlaurent/DoomLauncher/issues/369 return !entry.Key.Contains("../") && !entry.Key.Contains("..\\"); } } } ================================================ FILE: DoomLauncher/Archive/Rar/RarArchiveReader.cs ================================================ using SharpCompress.Archives.Rar; using System.Collections.Generic; using System.Linq; namespace DoomLauncher.Archive.Rar { public class RarArchiveReader : IArchiveReader { private readonly RarArchive m_archive; private readonly RarArchiveEntry[] m_entries; public RarArchiveReader(string file) { m_archive = RarArchive.Open(file); m_entries = m_archive.Entries.Where(x => RarArchiveEntry.IsValid(x)).Select(x => new RarArchiveEntry(x)).ToArray(); } public IEnumerable Entries => m_entries; public bool EntriesHaveExtensions => true; public void Dispose() { m_archive.Dispose(); } } } ================================================ FILE: DoomLauncher/Archive/RecursiveArchiveReader.cs ================================================ using System; using System.Collections.Generic; namespace DoomLauncher.Archive { public class RecursiveArchiveReader : IArchiveReader { public IArchiveReader Root { get; } private readonly Func m_getChildReader; // null return value means no children private delegate void DisposeMethod(); private event DisposeMethod Disposing; public RecursiveArchiveReader(IArchiveReader root, Func getChildReader) { Root = root; m_getChildReader = getChildReader; } public IEnumerable Entries => RecursiveSelect(); private IEnumerable RecursiveSelect() { // Recursive enumerable, adapted from https://stackoverflow.com/a/30441479 var stack = new Stack>(); IEnumerator enumerator = Root.Entries.GetEnumerator(); try { while (true) { if (enumerator.MoveNext()) { IArchiveEntry entry = enumerator.Current; yield return entry; var possibleReader = m_getChildReader(entry); if (possibleReader != null) { var recursiveReader = new RecursiveArchiveReader(possibleReader, m_getChildReader); Disposing += recursiveReader.Dispose; stack.Push(enumerator); enumerator = recursiveReader.Entries.GetEnumerator(); } } else if (stack.Count > 0) { enumerator.Dispose(); enumerator = stack.Pop(); } else { yield break; } } } finally { enumerator.Dispose(); while (stack.Count > 0) // Clean up in case of an exception. { enumerator = stack.Pop(); enumerator.Dispose(); } } } public bool EntriesHaveExtensions => Root.EntriesHaveExtensions; public void Dispose() { Root.Dispose(); Disposing?.Invoke(); } } } ================================================ FILE: DoomLauncher/Archive/SevenZip/SevenZipArchiveEntry.cs ================================================ using SevenZip; using System.IO; namespace DoomLauncher.Archive.SevenZip { public class SevenZipArchiveEntry : AbstractArchiveEntry { private readonly SevenZipExtractor m_extractor; private readonly ArchiveFileInfo m_archiveFileInfo; private readonly MemoryStreamManager m_streamManager; private MemoryStream m_ms; public SevenZipArchiveEntry(SevenZipExtractor extractor, ArchiveFileInfo archiveFileInfo, MemoryStreamManager streamManager) { m_extractor = extractor; m_archiveFileInfo = archiveFileInfo; m_streamManager = streamManager; } public override long Length => (long)m_archiveFileInfo.Size; public override string Name => Path.GetFileName(m_archiveFileInfo.FileName); public override string FullName => m_archiveFileInfo.FileName; public override bool ExtractRequired => true; public override bool IsDirectory => m_archiveFileInfo.IsDirectory; public override void ExtractToFile(string file, bool overwrite = false) { if (!overwrite && File.Exists(file)) return; using (FileStream fs = File.Create(file)) m_extractor.ExtractFile(m_archiveFileInfo.Index, fs); } public override void Read(byte[] buffer, int offset, int length) { if (m_ms == null) { m_ms = new MemoryStream(); m_streamManager.Streams.Add(m_ms); m_extractor.ExtractFile(m_archiveFileInfo.Index, m_ms); } m_ms.Position = 0; m_ms.Read(buffer, offset, length); } public override bool Equals(object obj) { if (!(obj is IArchiveEntry entry)) return false; return entry.FullName == FullName; } public override int GetHashCode() { return FullName.GetHashCode(); } public override string GetNameWithoutExtension() => Path.GetFileNameWithoutExtension(Name); } } ================================================ FILE: DoomLauncher/Archive/SevenZip/SevenZipArchiveReader.cs ================================================ using SevenZip; using System.Collections.Generic; using System.IO; namespace DoomLauncher.Archive.SevenZip { public class SevenZipArchiveReader : IArchiveReader { private readonly SevenZipExtractor m_extractor; private readonly MemoryStreamManager m_streamManager = new MemoryStreamManager(); public SevenZipArchiveReader(string file, string sevenZipInteropLibrary) { SevenZipBase.SetLibraryPath(sevenZipInteropLibrary); m_extractor = new SevenZipExtractor(file); } public IEnumerable Entries { get { foreach (var entry in m_extractor.ArchiveFileData) yield return new SevenZipArchiveEntry(m_extractor, entry, m_streamManager); } } public bool EntriesHaveExtensions => true; public void Dispose() { m_extractor.Dispose(); foreach (var stream in m_streamManager.Streams) stream.Dispose(); } } public class MemoryStreamManager { public List Streams = new List(); } } ================================================ FILE: DoomLauncher/Archive/Wad/WadArchiveReader.cs ================================================ using System.Collections.Generic; using System.IO; using WadReader; namespace DoomLauncher { internal class WadArchiveReader : IArchiveReader { private readonly FileStream m_fs; private readonly List m_entries = new List(); public IEnumerable Entries => m_entries; public bool EntriesHaveExtensions => false; public readonly bool IsValid; public readonly string Filename; public WadArchiveReader(string file) { Filename = file; m_fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); WadFileReader wadReader = new WadFileReader(m_fs); IsValid = wadReader.IsValid; var lumps = wadReader.ReadLumps(); foreach (var lump in lumps) m_entries.Add(new WadEntry(m_fs, lump)); } public void Dispose() { m_fs.Dispose(); } } } ================================================ FILE: DoomLauncher/Archive/Wad/WadEntry.cs ================================================ using System.IO; using WadReader; namespace DoomLauncher { public class WadEntry : AbstractArchiveEntry { private readonly FileStream m_fs; private readonly FileLump m_lump; public override long Length => m_lump.Length; public override string Name { get; } public override string FullName => Name; public override bool ExtractRequired => false; public override bool IsDirectory => false; public WadEntry(FileStream fs, FileLump lump) { m_fs = fs; m_lump = lump; Name = lump.Name; } public override void ExtractToFile(string file, bool overwrite = false) { File.WriteAllBytes(file, m_lump.ReadData(m_fs)); } public override void Read(byte[] buffer, int offset, int length) { m_lump.ReadData(m_fs, buffer, offset, length); } public override string GetNameWithoutExtension() => Name; } } ================================================ FILE: DoomLauncher/Archive/Zip/ZipArchiveReader.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; namespace DoomLauncher { public class ZipArchiveReader : IArchiveReader { private readonly ZipArchive m_archive; public bool EntriesHaveExtensions => true; public ZipArchiveReader(string file) { var stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); try { m_archive = new ZipArchive(stream); } catch (Exception ex) { stream.Dispose(); throw ex; } } public IEnumerable Entries { get { foreach (var entry in m_archive.Entries) yield return new ZipArchiveReaderEntry(entry); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { m_archive.Dispose(); } } } ================================================ FILE: DoomLauncher/Archive/Zip/ZipArchiveReaderEntry.cs ================================================ using System.IO; using System.IO.Compression; namespace DoomLauncher { class ZipArchiveReaderEntry : AbstractArchiveEntry { private readonly ZipArchiveEntry m_entry; public ZipArchiveReaderEntry(ZipArchiveEntry zipArchiveEntry) { m_entry = zipArchiveEntry; } public override long Length => m_entry.Length; public override void Read(byte[] buffer, int offset, int length) { m_entry.Open().Read(buffer, offset, length); } public override string Name => m_entry.Name; public override string FullName => m_entry.FullName; public override bool ExtractRequired => true; public override bool IsDirectory => m_entry.FullName.EndsWith("/"); public override void ExtractToFile(string file, bool overwrite = false) { m_entry.ExtractToFile(file, overwrite); } public override bool Equals(object obj) { if (!(obj is IArchiveEntry entry)) return false; return entry.FullName == FullName; } public override int GetHashCode() { return FullName.GetHashCode(); } public override string GetNameWithoutExtension() => Path.GetFileNameWithoutExtension(Name); } } ================================================ FILE: DoomLauncher/Config/AppConfiguration.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public class AppConfiguration : IDirectoriesConfiguration { public static readonly CultureInfo Culture = new CultureInfo("en-US"); public event EventHandler GameFileViewTypeChanged; public event EventHandler VisibleViewsChanged; public event EventHandler ColorThemeChanged; public static string SplitTopBottomName => "SplitTopBottom"; public static string SplitLeftRightName => "SplitLeftRight"; public static string SplitTagSelectName => "SplitTagSelect"; public static string AppWidthName => "AppWidth"; public static string AppHeightName => "AppHeight"; public static string AppXName => "AppX"; public static string AppYName => "AppY"; public static string WindowStateName => "WindowState"; public static string ColumnConfigName => "ColumnConfig"; public static string ScreenshotPreviewSizeName => "ScreenshotPreviewSize"; public static string ItemsPerPageName => "ItemsPerPage"; public static string LastSelectedTabIndexName => "LastSelectedTabIndex"; public static string TagSelectPinnedName => "TagSelectPinned"; public static string ShowTabHeadersName => "ShowTabHeaders"; public static string CopySaveFilesName => "CopySaveFiles"; public static string AllowMultiplePlaySessionsName => "AllowMultiplePlaySessions"; public static string AutomaticallyPullTitlpicName => "AutomaticallyPullTitlpic"; public static string VisibleViewsName => "VisibleViews"; public static string ShowPlayDialogName => "ShowPlayDialog"; public static string ImportScreenshotsName => "ImportScreenshots"; public static string ShowTooltipName => "ShowToolTip"; public static string TileImageSizeName => "TileImageSize"; public AppConfiguration(IDataSourceAdapter adapter) { DataSourceAdapter = adapter; Refresh(false); } public void Refresh() { Refresh(true); } public string GetConfigValue(ConfigType ct) { IConfigurationData config = DataSourceAdapter.GetConfiguration().FirstOrDefault(x => x.Name == ct.ToString("g")); if (config != null) return config.Value; return null; } public object GetTypedConfigValue(ConfigType ct, Type type) { return GetValueFromConfig(GetConfigValue(ct), type); } public LauncherPath PathForFileType(FileType type) { switch(type) { case FileType.Demo: return DemoDirectory; case FileType.SaveGame: return SaveGameDirectory; case FileType.Screenshot: return ScreenshotDirectory; case FileType.Thumbnail: return ThumbnailDirectory; default: throw new ArgumentException($"Invalid FileType {type}"); } } private static object GetValueFromConfig(string value, Type type) { if (type == typeof(string)) { return value; } else if (type == typeof(int)) { if (!int.TryParse(value, out int iValue)) return -1; return iValue; } else if (type == typeof(bool)) { if (!bool.TryParse(value, out bool bValue)) return false; return bValue; } else { throw new NotSupportedException(string.Format("Type {0} not supported ", type.ToString())); } } private void Refresh(bool throwErrors) { try { IEnumerable config = DataSourceAdapter.GetConfiguration(); int oldTileImageSize = TileImageSize; IdGamesUrl = GetValue(config, "IdGamesUrl", string.Empty); ApiPage = GetValue(config, "ApiPage", string.Empty); MirrorUrl = GetValue(config, "MirrorUrl", string.Empty); CleanTemp = Convert.ToBoolean(GetValue(config, "CleanTemp", "true")); SetChildDirectories(config); SplitTopBottom = TryGetDouble(config, SplitTopBottomName, 475); SplitLeftRight = TryGetDouble(config, SplitLeftRightName, 680); SplitTagSelect = TryGetDouble(config, SplitTagSelectName, 300); AppWidth = Convert.ToInt32(GetValue(config, AppWidthName, "1024")); AppHeight = Convert.ToInt32(GetValue(config, AppHeightName, "768")); AppX = Convert.ToInt32(GetValue(config, AppXName, "0")); AppY = Convert.ToInt32(GetValue(config, AppYName, "0")); WindowState = (FormWindowState)Enum.Parse(typeof(FormWindowState), GetValue(config, WindowStateName, FormWindowState.Normal.ToString("g"))); ColumnConfig = GetValue(config, ColumnConfigName, string.Empty); ScreenshotPreviewSize = Convert.ToInt32(GetValue(config, ScreenshotPreviewSizeName, "0")); FileManagement = (FileManagement)Enum.Parse(typeof(FileManagement), GetValue(config, "FileManagement", FileManagement.Managed.ToString("g"))); ItemsPerPage = Convert.ToInt32(GetValue(config, ItemsPerPageName, "30")); DeleteScreenshotsAfterImport = Convert.ToBoolean(GetValue(config, "DeleteScreenshotsAfterImport", "false")); LastSelectedTabIndex = Convert.ToInt32(GetValue(config, LastSelectedTabIndexName, "0")); TagSelectPinned = Convert.ToBoolean(GetValue(config, TagSelectPinnedName, "false")); ShowTabHeaders = Convert.ToBoolean(GetValue(config, ShowTabHeadersName, "true")); CopySaveFiles = Convert.ToBoolean(GetValue(config, CopySaveFilesName, "true")); AllowMultiplePlaySessions = Convert.ToBoolean(GetValue(config, AllowMultiplePlaySessionsName, "false")); AutomaticallyPullTitlpic = Convert.ToBoolean(GetValue(config, AutomaticallyPullTitlpicName, "true")); ShowPlayDialog = Convert.ToBoolean(GetValue(config, ShowPlayDialogName, "true")); ImportScreenshots = Convert.ToBoolean(GetValue(config, ImportScreenshotsName, "false")); ShowTooltip = Convert.ToBoolean(GetValue(config, ShowTooltipName, "true")); TileImageSize = Convert.ToInt32(GetValue(config, TileImageSizeName, "300")); List events = new List(); if (Enum.TryParse(GetValue(config, "ColorThemeType", "Default"), out ColorThemeType colorThemeType)) { if (colorThemeType != ConfigurationColorTheme) AddEvent(events, ColorThemeChanged); ConfigurationColorTheme = colorThemeType; if (ConfigurationColorTheme != ColorThemeType.System) ColorTheme = ConfigurationColorTheme; } var newType = (GameFileViewType)Enum.Parse(typeof(GameFileViewType), GetValue(config, "GameFileViewType", GameFileViewType.TileViewCondensed.ToString("g"))); if (newType != GameFileViewType || oldTileImageSize != TileImageSize) { GameFileViewType = newType; AddEvent(events, GameFileViewTypeChanged); } var newVisibleViews = Util.SplitString(GetValue(config, VisibleViewsName, "Recent;Untagged;IWads;Id Games")); if (VisibleViews == null || (newVisibleViews.Length != VisibleViews.Count)) { VisibleViews = newVisibleViews.ToHashSet(StringComparer.OrdinalIgnoreCase); AddEvent(events, VisibleViewsChanged); } DateParseFormats = Util.SplitString(GetValue(config, "DateParseFormats", "dd/M/yy;dd/MM/yyyy;dd MMMM yyyy;")); ScreenshotCaptureDirectories = Util.SplitString(GetValue(config, "ScreenshotCaptureDirectories", string.Empty)); foreach (var invokeEvent in events) invokeEvent?.Invoke(this, EventArgs.Empty); } catch { if (throwErrors) throw; } VerifyPaths(throwErrors); } private static double TryGetDouble(IEnumerable config, string name, double defaultValue) { string value = GetValue(config, name, defaultValue.ToString(Culture)); if (double.TryParse(value, NumberStyles.AllowDecimalPoint, Culture, out var doubleValue)) return doubleValue; return defaultValue; } private static void AddEvent(List events, EventHandler eventAdd) { if (eventAdd != null) events.Add(eventAdd); } private static string GetValue(IEnumerable config, string name, string defaultValue) { var item = config.FirstOrDefault(x => x.Name == name); if (item == null) return defaultValue; return item.Value; } private void SetChildDirectories(IEnumerable config) { string gameFileDir = GetValue(config, "GameFileDirectory", "GameFiles"); GameFileDirectory = GetGameFileDir(gameFileDir); gameFileDir = GameFileDirectory.GetFullPath(); ScreenshotDirectory = SetChildDirectory(gameFileDir, "Screenshots"); TempDirectory = SetChildDirectory(gameFileDir, "Temp"); DemoDirectory = SetChildDirectory(gameFileDir, "Demos"); SaveGameDirectory = SetChildDirectory(gameFileDir, "SaveGames"); ThumbnailDirectory = SetChildDirectory(gameFileDir, "Thumbnails"); } private static LauncherPath GetGameFileDir(string gameFileDir) { if (gameFileDir == "GameFiles\\") { string dataDir = LauncherPath.GetDataDirectory(); if (!Directory.Exists(dataDir)) { Directory.CreateDirectory(dataDir); Directory.CreateDirectory(Path.Combine(dataDir, gameFileDir)); } return new LauncherPath(Path.Combine(dataDir, gameFileDir)); } return new LauncherPath(gameFileDir); } private LauncherPath SetChildDirectory(string gameFileDirectory, string childDirectory) { if (!childDirectory.EndsWith(Path.DirectorySeparatorChar.ToString())) //might be some older code that is still not using Path.Combine... childDirectory = childDirectory + Path.DirectorySeparatorChar; return new LauncherPath(Path.Combine(gameFileDirectory, childDirectory)); } public void RefreshColumnConfig() { IEnumerable config = DataSourceAdapter.GetConfiguration(); ColumnConfig = GetValue(config, ColumnConfigName, string.Empty); } public void EnableCopySaveFiles() { CopySaveFiles = true; IConfigurationData config = DataSourceAdapter.GetConfiguration().FirstOrDefault(x => x.Name == CopySaveFilesName); if (config != null) { config.Value = "true"; DataSourceAdapter.UpdateConfiguration(config); } } private void VerifyPaths(bool throwErrors) { VerifyPath(GameFileDirectory, throwErrors); VerifyPath(ScreenshotDirectory, throwErrors); VerifyPath(TempDirectory, throwErrors); VerifyPath(DemoDirectory, throwErrors); VerifyPath(SaveGameDirectory, throwErrors); VerifyPath(ThumbnailDirectory, throwErrors); } private void VerifyPath(LauncherPath path, bool throwErrors) { bool exists = Directory.Exists(path.GetFullPath()); if (throwErrors && !exists) throw new DirectoryNotFoundException(path.GetPossiblyRelativePath()); if (!exists) Directory.CreateDirectory(path.GetFullPath()); } public IDataSourceAdapter DataSourceAdapter { get; set; } public LauncherPath GameFileDirectory { get; private set; } public LauncherPath ScreenshotDirectory { get; private set; } public LauncherPath SaveGameDirectory { get; private set; } public LauncherPath TempDirectory { get; private set; } public LauncherPath DemoDirectory { get; private set; } public LauncherPath ThumbnailDirectory { get; private set; } public string IdGamesUrl { get; private set; } public string ApiPage { get; private set; } public string MirrorUrl { get; private set; } public bool CleanTemp { get; private set; } public double SplitTopBottom { get; private set; } public double SplitLeftRight { get; private set; } public double SplitTagSelect { get; set; } public int AppWidth { get; private set; } public int AppHeight { get; private set; } public int AppX { get; private set; } public int AppY { get; private set; } public FormWindowState WindowState { get; private set; } public string[] ScreenshotCaptureDirectories { get; private set; } public string[] DateParseFormats { get; private set; } public string ColumnConfig { get; private set; } public int ScreenshotPreviewSize { get; private set; } public FileManagement FileManagement { get; private set; } public GameFileViewType GameFileViewType { get; private set; } public int ItemsPerPage { get; set; } public bool DeleteScreenshotsAfterImport { get; set; } public int LastSelectedTabIndex { get; set; } public bool TagSelectPinned { get; set; } public bool ShowTabHeaders { get; set; } = true; public bool CopySaveFiles { get; set; } public bool AllowMultiplePlaySessions { get; set; } public bool AutomaticallyPullTitlpic { get; set; } public bool ShowPlayDialog { get; set; } public bool ImportScreenshots { get; set; } public bool ShowTooltip { get; set; } public int TileImageSize { get; set; } public HashSet VisibleViews { get; set; } = new HashSet(); // The theme for the application to use. Default or Dark. public ColorThemeType ColorTheme { get; set; } // The configuration setting. System will use the OS setting if found. public ColorThemeType ConfigurationColorTheme { get; set; } } } ================================================ FILE: DoomLauncher/Config/AppVersion.cs ================================================ namespace DoomLauncher { public enum AppVersion { Unknown, Version_1_0_0, Version_1_1_0, Version_1_2_0, Version_2_1_0, Version_2_2_0, Version_2_2_1, Version_2_3_0, Version_2_4_0, Version_2_4_1, Version_2_6_0, Version_2_6_3, Version_2_6_3_1, Version_2_6_3_2, Version_2_6_4_1, Version_2_6_4_1_Update1, Version_2_7_0_0, Version_2_8_0_0, Version_2_8_0_0_1, Version_3_1_0, Version_3_2_0, Version_3_2_0_Update1, Version_3_2_0_Update2, Version_3_2_0_Update3, Version_3_3_0, Version_3_3_1, Version_3_3_2, Version_3_4_0, Version_3_4_0_Update1, Version_3_5_2, Version_3_5_2_Update1, Version_3_5_2_Update2, Version_3_5_3_Update1, Version_3_5_3_Update2, Version_3_5_3_Update3, Version_3_7_0, Version_3_7_0_Update1, Version_3_7_4, Version_3_7_7, } } ================================================ FILE: DoomLauncher/Config/ColorTheme.cs ================================================ namespace DoomLauncher { public static class ColorTheme { public static IThemeColors Current { get; set; } = new DefaultTheme(); } } ================================================ FILE: DoomLauncher/Config/ColorThemeType.cs ================================================ namespace DoomLauncher.Config { public enum ColorThemeType { Default, Dark, System } } ================================================ FILE: DoomLauncher/Config/ColumnConfig.cs ================================================ using System.Windows.Forms; namespace DoomLauncher { public class ColumnConfig { public ColumnConfig() { } public ColumnConfig(string parent, string column, int width, SortOrder sort) { Parent = parent; Column = column; Width = width; Sort = sort; } public string Parent { get; set; } public string Column { get; set; } public int Width { get; set; } public SortOrder Sort { get; set; } } } ================================================ FILE: DoomLauncher/Config/ConfigType.cs ================================================ namespace DoomLauncher { public enum ConfigType { DefaultSourcePort, DefaultIWad, DefaultSkill, AutoSearch, FileManagement, GameFileViewType, VisibleViews, ColorThemeType } } ================================================ FILE: DoomLauncher/Config/DataCache.cs ================================================ using DoomLauncher.Config; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Xml.Serialization; using WadReader; namespace DoomLauncher { public class DataCache { public static readonly DataCache Instance = new DataCache(); public event EventHandler TagsChanged; public IDataSourceAdapter DataSourceAdapter { get; private set; } public AppConfiguration AppConfiguration { get; private set; } public ITagMapLookup TagMapLookup { get; private set; } public ITagData[] Tags { get; private set; } public ITagData[] PreviousTags { get; private set; } public Image DefaultImage { get; private set; } public Palette DefaultPalette { get; private set; } public Palette HexenPalette { get; private set; } public Palette HereticPalette { get; private set; } public void Init(IDataSourceAdapter adapter) { DataSourceAdapter = adapter; AppConfiguration = new AppConfiguration(adapter); TagMapLookup = new TagMapLookup(adapter); DefaultImage = ImageExtensions.FromFileOrDefault(Path.Combine(LauncherPath.GetDataDirectory(), "TileImages", "DoomLauncherTile.png")); DefaultPalette = Palette.From(Properties.Resources.DoomPalette); HexenPalette = Palette.From(Properties.Resources.HexenPalette); HereticPalette = Palette.From(Properties.Resources.HereticPalette); ThumbnailManager.SetIWads(DataSourceAdapter.GetGameFileIWads().ToList()); switch (AppConfiguration.ColorTheme) { case ColorThemeType.Dark: ColorTheme.Current = new DarkTheme(); break; default: ColorTheme.Current = new DefaultTheme(); break; } UpdateTags(); } public void UpdateTags() { if (Tags != null) PreviousTags = Tags.ToArray(); Tags = SortTags(DataSourceAdapter.GetTags()).ToArray(); if (PreviousTags == null) PreviousTags = Tags; TagsChanged?.Invoke(this, EventArgs.Empty); } // Sorts tags by Favorite, then by Name public IEnumerable SortTags(IEnumerable tags) => tags.OrderByDescending(x => x.Favorite).ThenBy(x => x.Name); public void UpdateGameFileTags(IEnumerable gameFiles, IEnumerable tags) { ITagData[] changedTagsAll = Array.Empty(); foreach (IGameFile gameFile in gameFiles) { ITagData[] existingTags = Instance.TagMapLookup.GetTags(gameFile); var addTags = tags.Except(existingTags); var removeTags = existingTags.Except(tags); var updateFiles = new IGameFile[] { gameFile }; foreach (var tag in addTags) AddGameFileTag(updateFiles, tag, out _); foreach (var tag in removeTags) RemoveGameFileTag(updateFiles, tag); changedTagsAll = changedTagsAll.Union(addTags.Union(removeTags)).ToArray(); } TagMapLookup.Refresh(changedTagsAll); } public void AddGameFileTag(IEnumerable gameFiles, ITagData tag, out List alreadyTagged) { alreadyTagged = new List(); foreach (IGameFile gameFile in gameFiles) { TagMapping tagMapping = new TagMapping { FileID = gameFile.GameFileID.Value, TagID = tag.TagID }; if (!DataSourceAdapter.GetTagMappings(tagMapping.FileID).Contains(tagMapping)) DataSourceAdapter.InsertTagMapping(tagMapping); else alreadyTagged.Add(gameFile); } } public void RemoveGameFileTag(IEnumerable gameFiles, ITagData tag) { TagMapping tagMapping = new TagMapping(); foreach (IGameFile gameFile in gameFiles) { tagMapping.TagID = tag.TagID; tagMapping.FileID = gameFile.GameFileID.Value; DataSourceAdapter.DeleteTagMapping(tagMapping); } } public ColumnConfig[] GetColumnConfig() { try { XmlSerializer xml = new XmlSerializer(typeof(ColumnConfig[])); StringReader text = new StringReader(AppConfiguration.ColumnConfig); ColumnConfig[] ret = xml.Deserialize(text) as ColumnConfig[]; // Previous revisions of Doom Launcher use filename, most recent version uses filenamenopath - fix it here var columnsToFix = ret.Where(x => x.Column.Equals("filename", StringComparison.OrdinalIgnoreCase)); foreach (var colFix in columnsToFix) colFix.Column = "filenamenopath"; var idGamesReleaseDate = ret.FirstOrDefault(x => x.Parent.Equals(TabKeys.IdGamesKey) && x.Column.Equals("releasedate", StringComparison.OrdinalIgnoreCase)); if (idGamesReleaseDate == null) return InsertIdGamesReleaseDate(ret); return ret; } catch { return Array.Empty(); } } private static ColumnConfig[] InsertIdGamesReleaseDate(ColumnConfig[] ret) { var columnList = ret.ToList(); var idGamesFirstCol = columnList.FirstOrDefault(x => x.Parent.Equals(TabKeys.IdGamesKey)); if (idGamesFirstCol != null) { columnList.Insert(columnList.IndexOf(idGamesFirstCol) + 2, new ColumnConfig() { Parent = TabKeys.IdGamesKey, Column = "releasedate", Width = 100 }); } return columnList.ToArray(); } public static string SerializeColumnConfig(List config) { StringWriter text = new StringWriter(); XmlSerializer xml = new XmlSerializer(typeof(ColumnConfig[])); xml.Serialize(text, config.ToArray()); return text.ToString(); } public void UpdateConfig(IEnumerable config, string name, string value) { IConfigurationData configFind = config.FirstOrDefault(x => x.Name == name); if (configFind == null) { DataSourceAdapter.InsertConfiguration(new ConfigurationData { Name = name, Value = value, UserCanModify = false }); } else { configFind.Value = value; DataSourceAdapter.UpdateConfiguration(configFind); } } } } ================================================ FILE: DoomLauncher/Config/FileManagement.cs ================================================ namespace DoomLauncher { public enum FileManagement { Managed, Unmanaged, Prompt, } } ================================================ FILE: DoomLauncher/Config/GameFileListEventArgs.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; namespace DoomLauncher { public class GameFileListEventArgs { public IEnumerable GameFiles { get; set; } public GameFileListEventArgs(IEnumerable gameFiles) { GameFiles = gameFiles; } } } ================================================ FILE: DoomLauncher/Config/IDirectoriesConfiguration.cs ================================================  namespace DoomLauncher.Config { public interface IDirectoriesConfiguration { LauncherPath GameFileDirectory { get; } LauncherPath ScreenshotDirectory { get; } LauncherPath SaveGameDirectory { get; } LauncherPath TempDirectory { get; } LauncherPath DemoDirectory { get; } LauncherPath ThumbnailDirectory { get; } } } ================================================ FILE: DoomLauncher/Config/Icons.cs ================================================ using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; namespace DoomLauncher { public enum IconImage { UpArrow, DownArrow, Bars, Delete, Download, Edit, Export, ExportAll, File, FolderOpen, Play, Save, Search, StepBack, Video, Tags, Pin } public static class Icons { private static readonly Dictionary<(IconImage, Color), Bitmap> IconLookup = new Dictionary<(IconImage, Color), Bitmap>(); public static DpiScale DpiScale { get; set; } = new DpiScale(2, 2); public static Image ArrowUp => GetIcon(IconImage.UpArrow); public static Image ArrowDown => GetIcon(IconImage.DownArrow); public static Image Bars => GetIcon(IconImage.Bars); public static Image Delete => GetIcon(IconImage.Delete); public static Image Download => GetIcon(IconImage.Download); public static Image Edit => GetIcon(IconImage.Edit); public static Image Export => GetIcon(IconImage.Export); public static Image ExportAll => GetIcon(IconImage.ExportAll); public static Image File => GetIcon(IconImage.File); public static Image FolderOpen => GetIcon(IconImage.FolderOpen); public static Image Play => GetIcon(IconImage.Play); public static Image Save => GetIcon(IconImage.Save); public static Image Search => GetIcon(IconImage.Search); public static Image StepBack => GetIcon(IconImage.StepBack); public static Image Video => GetIcon(IconImage.Video); public static Image Tags => GetIcon(IconImage.Tags); public static Image Pin => GetIcon(IconImage.Pin); public static Image GetIcon(IconImage iconImage, Color color) => ColorizeIcon(iconImage, color); private static Bitmap GetBitmap(IconImage iconImage) { switch (iconImage) { case IconImage.UpArrow: return Properties.Resources.UpArrow; case IconImage.DownArrow: return Properties.Resources.DownArrow; case IconImage.Bars: return Properties.Resources.Bars; case IconImage.Delete: return Properties.Resources.Delete; case IconImage.Download: return Properties.Resources.th; case IconImage.Edit: return Properties.Resources.Edit; case IconImage.Export: return Properties.Resources.Export; case IconImage.ExportAll: return Properties.Resources.ExportAll; case IconImage.File: return Properties.Resources.File; case IconImage.FolderOpen: return Properties.Resources.FolderOpen; case IconImage.Play: return Properties.Resources.Play; case IconImage.Save: return Properties.Resources.Save; case IconImage.Search: return Properties.Resources.Search; case IconImage.StepBack: return Properties.Resources.StepBack; case IconImage.Video: return Properties.Resources.Video; case IconImage.Tags: return Properties.Resources.Tags; case IconImage.Pin: return Properties.Resources.Pin; default: return new Bitmap(0, 0); } } private static Image GetIcon(IconImage iconImage) => ColorizeIcon(iconImage, ColorTheme.Current.Text); private static Bitmap ColorizeIcon(IconImage iconImage, Color color) { if (IconLookup.TryGetValue((iconImage, color), out var bitmap)) return bitmap; bitmap = GetBitmap(iconImage); Bitmap copy; if (DpiScale.DpiScaleX > 1 || DpiScale.DpiScaleY > 1) copy = ResizeBitmap(bitmap, (int)DpiScale.ScaleFloatX(bitmap.Width * 0.8f), (int)DpiScale.ScaleFloatY(bitmap.Height * 0.8f)); else copy = new Bitmap(bitmap, bitmap.Width, bitmap.Height); for (int x = 0; x < copy.Width; x++) { for (int y = 0; y < copy.Height; y++) { var pixel = copy.GetPixel(x, y); if (pixel.A != 0) copy.SetPixel(x, y, Color.FromArgb(pixel.A, color)); } } IconLookup[(iconImage, color)] = copy; return copy; } public static Bitmap ResizeBitmap(Bitmap bitmap, int width, int height) { var destRect = new Rectangle(0, 0, width, height); var destImage = new Bitmap(width, height); destImage.SetResolution(bitmap.HorizontalResolution, bitmap.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(bitmap, destRect, 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, wrapMode); } } return destImage; } } } ================================================ FILE: DoomLauncher/Config/LauncherPath.cs ================================================ using DoomLauncher.Handlers; using System; using System.IO; using System.Text; namespace DoomLauncher { public class LauncherPath { public static readonly LauncherPath NoPath = new LauncherPath(string.Empty); private static readonly char[] PathSplit = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; private readonly string m_path, m_fullPath; public LauncherPath(string path) { if (string.IsNullOrEmpty(path)) { m_path = string.Empty; m_fullPath = string.Empty; return; } m_path = path; m_fullPath = m_path; if (PathExtensions.IsPartiallyQualified(m_fullPath)) m_fullPath = Path.Combine(Directory.GetCurrentDirectory(), m_fullPath); else m_path = GetRelativePath(m_fullPath); } public static string GetRelativePath(string path) { string current = GetDataDirectory(); if (!path.Contains(current)) return path; string[] filePath = path.Split(PathSplit, StringSplitOptions.RemoveEmptyEntries); string[] currentPath = current.Split(PathSplit, StringSplitOptions.RemoveEmptyEntries); StringBuilder sb = new StringBuilder(255); for (int i = currentPath.Length; i < filePath.Length; i++) { sb.Append(filePath[i]); sb.Append(Path.DirectorySeparatorChar); } if (sb.Length > 1) sb.Remove(sb.Length - 1, 1); return sb.ToString(); } public static string GetDataDirectory() { if (IsInstalled()) return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "DoomLauncher"); return Directory.GetCurrentDirectory(); } public static bool IsInstalled() => !File.Exists(Path.Combine(Directory.GetCurrentDirectory(), DbDataSourceAdapter.DatabaseFileName)) && !File.Exists(Path.Combine(Directory.GetCurrentDirectory(), DbDataSourceAdapter.InitDatabaseFileName)); public string GetFullPath() { return m_fullPath; } public string GetPossiblyRelativePath() { return m_path; } public static implicit operator string(LauncherPath p) { Util.ThrowDebugException("implicit operator should not be assumed"); return p.GetFullPath(); } public override bool Equals(object obj) { if (obj is LauncherPath path) return GetFullPath() == path.GetFullPath(); return false; } public override int GetHashCode() { return GetFullPath().GetHashCode(); } } } ================================================ FILE: DoomLauncher/Config/ScreenFilter.cs ================================================ namespace DoomLauncher { public enum ScreenFilterType { Ellipse, Scanline } public class ScreenFilter { public ScreenFilterType Type { get; set; } public float Opacity { get; set; } public int LineThickness { get; set; } public int BlockSize { get; set; } public float SpacingX { get; set; } public float SpacingY { get; set; } public bool Stagger { get; set; } public int ScanlineSpacing { get; set; } public bool VerticalScanlines { get; set; } public bool HorizontalScanlines { get; set; } public bool Enabled { get; set; } } } ================================================ FILE: DoomLauncher/Config/TagMapLookup.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Linq; namespace DoomLauncher { public class TagMapLookup : ITagMapLookup { public event EventHandler TagMappingChanged; private readonly IDataSourceAdapter m_adapter; private Dictionary m_fileTagMapping; // GameFileID -> ITagMapping private Dictionary m_tags; // TagID -> ITagData public TagMapLookup(IDataSourceAdapter adapter) { m_adapter = adapter; Refresh(Array.Empty()); } private Dictionary BuildTagMappingDictionary(IEnumerable tagMapping) { Dictionary ret = new Dictionary(); int[] files = tagMapping.Select(x => x.FileID).Distinct().ToArray(); Array.ForEach(files, x => ret.Add(x, tagMapping.Where(y => y.FileID == x).ToArray())); return ret; } public void Refresh(ITagData[] tags) { IEnumerable tagMapping = m_adapter.GetTagMappings().OrderBy(x => x.FileID); m_fileTagMapping = BuildTagMappingDictionary(tagMapping); m_tags = m_adapter.GetTags().ToDictionary(x => x.TagID, x => x); TagMappingChanged?.Invoke(this, tags); } public void RemoveGameFile(IGameFile gameFile) { if (gameFile.GameFileID.HasValue) m_fileTagMapping.Remove(gameFile.GameFileID.Value); } public ITagData[] GetTags(IGameFile gameFile) { if (gameFile != null && gameFile.GameFileID.HasValue && m_fileTagMapping.TryGetValue(gameFile.GameFileID.Value, out var tagMapping)) { return tagMapping.Where(k => m_tags.ContainsKey(k.TagID)) .Select(k => m_tags[k.TagID]).OrderBy(x => x.Name).ToArray(); } return Array.Empty(); } public IEnumerable GetEnumerableTags(IGameFile gameFile) { if (gameFile != null && gameFile.GameFileID.HasValue && m_fileTagMapping.TryGetValue(gameFile.GameFileID.Value, out var tagMapping)) { return tagMapping.Where(k => m_tags.ContainsKey(k.TagID)) .Select(k => m_tags[k.TagID]).OrderBy(x => x.Name); } return Array.Empty(); } } } ================================================ FILE: DoomLauncher/Controls/BasicFileView.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.Stylize; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public abstract class BasicFileView : UserControl, IFileAssociationView { protected ContextMenuStrip m_menu; private IFileData[] m_files = Array.Empty(); public abstract void SetData(IGameFile gameFile); protected void SetData(DataGridView dgvMain, IGameFile gameFile) { if (GameFile != null && GameFile.GameFileID.HasValue) { IEnumerable files = DataSourceAdapter.GetFiles(gameFile, FileType); List sourcePorts = Util.GetSourcePortsData(DataSourceAdapter); var items = from file in files join sp in sourcePorts on file.SourcePortID equals sp.SourcePortID select new { file.OriginalFileName, file.Description, file.DateCreated, SourcePortName = sp.Name, FileData = file }; dgvMain.DataSource = items.ToList(); dgvMain.ContextMenuStrip = m_menu; m_files = files.ToArray(); } else { dgvMain.DataSource = null; m_files = new IFileData[] { }; } } public abstract void ClearData(); public virtual bool Delete() { List selectedFiles = GetSelectedFiles(); if (selectedFiles.Count > 0 && StyledMessageBox.Show(this, "Delete selected file(s)?", "Confirm", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.OK) { foreach (IFileData file in selectedFiles) { if (file.IsUrl) { //can't delete a remote file... } else { FileInfo fi = new FileInfo(Path.Combine(DataDirectory.GetFullPath(), file.FileName)); DataSourceAdapter.DeleteFile(file); try { fi.Delete(); } catch { } } } return true; } return false; } public IGameFile GameFile { get; set; } public virtual bool New() { GameFile = DataSourceAdapter.GetGameFile(GameFile.FileName); //todo: refactor may have broke this, or no longer needed return CreateFileAssociation(this, DataSourceAdapter, DataDirectory, FileType, GameFile, null, true).Count > 0; } public virtual bool ShowCreateNew(IWin32Window parent, IDataSourceAdapter adapter, ISourcePortData sourcePort, string filename, bool isMultiImport, out NewFileData newFileData) { newFileData = null; FileDetailsEditForm detailsForm = new FileDetailsEditForm(); detailsForm.Initialize(adapter); detailsForm.StartPosition = FormStartPosition.CenterParent; detailsForm.ShowDescription(!isMultiImport); if (sourcePort != null) detailsForm.SourcePort = sourcePort; if (!isMultiImport) detailsForm.Description = Path.GetFileName(filename); if (detailsForm.ShowDialog(parent) == DialogResult.OK && detailsForm.SourcePort != null) { newFileData = new NewFileData() { SourcePortID = detailsForm.SourcePort.SourcePortID, Description = detailsForm.Description, }; return true; } else if (detailsForm.SourcePort == null) StyledMessageBox.Show(parent, "A source port must be selected.", "Error", MessageBoxButtons.OK); return false; } public List CreateFileAssociation(IWin32Window parent, IDataSourceAdapter adapter, LauncherPath directory, FileType type, IGameFile gameFile, ISourcePortData sourcePort, bool multiSelect = false) { List fileDataList = new List(); OpenFileDialog dialog = new OpenFileDialog(); dialog.Multiselect = multiSelect; if (dialog.ShowDialog(parent) != DialogResult.OK) return fileDataList; bool isMultiImport = dialog.FileNames.Length > 1; if (ShowCreateNew(parent, adapter, sourcePort, dialog.FileNames[0], isMultiImport, out var newFileData)) { foreach (string file in dialog.FileNames) { FileInfo fi = new FileInfo(file); IFileData fileData = CreateNewFileDataSource(newFileData, fi, type, gameFile); if (isMultiImport) fileData.Description = Path.GetFileName(file); fi.CopyTo(Path.Combine(directory.GetFullPath(), fileData.FileName)); adapter.InsertFile(fileData); var fileSearch = adapter.GetFiles(gameFile, type).FirstOrDefault(x => x.FileName == fileData.FileName); if (fileSearch != null) fileData = fileSearch; fileDataList.Add(fileData); } } return fileDataList; } private static FileData CreateNewFileDataSource(NewFileData newFileData, FileInfo fi, FileType type, IGameFile gameFile) { FileData fileData = new FileData { FileName = string.Concat(Guid.NewGuid(), fi.Extension), FileTypeID = type, GameFileID = gameFile.GameFileID.Value, Description = newFileData.Description, SourcePortID = newFileData.SourcePortID, UserTitle = newFileData.UserTitle, UserDescription = newFileData.UserDescription, Map = newFileData.Map, }; return fileData; } public virtual bool Edit() { List selectedFiles = GetSelectedFiles(); if (selectedFiles.Count > 0) { IFileData file = selectedFiles.First(); FileDetailsEditForm form = new FileDetailsEditForm(); form.Initialize(DataSourceAdapter, file); form.StartPosition = FormStartPosition.CenterParent; if (form.ShowDialog(this) == DialogResult.OK && form.SourcePort != null && !file.IsUrl) { file.SourcePortID = form.SourcePort.SourcePortID; file.Description = form.Description; DataSourceAdapter.UpdateFile(file); return true; } else if (form.SourcePort == null) { StyledMessageBox.Show(this, "A source port must be selected.", "Error", MessageBoxButtons.OK); } } return false; } public void CopyToClipboard() { CopyFilesToClipboard(GetSelectedFiles()); } public void CopyAllToClipboard() { CopyFilesToClipboard(Files); } private void CopyFilesToClipboard(IEnumerable files) { StringCollection paths = new StringCollection(); string tempDirectory = DataCache.Instance.AppConfiguration.TempDirectory.GetFullPath(); List exportedFiles = ExportFilesTo(files, tempDirectory); exportedFiles.ForEach(x => paths.Add(Path.Combine(tempDirectory, x))); if (paths.Count > 0) Clipboard.SetFileDropList(paths); } public bool Export() { List selectedFiles = GetSelectedFiles(); if (selectedFiles.Count == 0) return false; var sourcePorts = Util.GetSourcePortsData(DataSourceAdapter); SaveFileDialog dialog = new SaveFileDialog(); dialog.FileName = GetNiceFileName(selectedFiles[0], sourcePorts); if (dialog.ShowDialog(this) == DialogResult.OK) { CopyFile(selectedFiles[0], dialog.FileName); return true; } return false; } public bool ExportAll() { if (Files.Length == 0) return false; FolderBrowserDialog dialog = new FolderBrowserDialog(); if (dialog.ShowDialog(this) == DialogResult.OK) { ExportFilesTo(Files, dialog.SelectedPath); return true; } return false; } private List ExportFilesTo(IEnumerable files, string directory) { var sourcePorts = Util.GetSourcePortsData(DataSourceAdapter); HashSet exportedNames = new HashSet(); int collision = 2; foreach (IFileData file in files) { if (!File.Exists(Path.Combine(DataDirectory.GetFullPath(), file.FileName))) continue; string fileName = GetNiceFileName(file, sourcePorts); while (exportedNames.Contains(fileName)) { fileName = $"{Path.GetFileNameWithoutExtension(fileName)}_{collision}{Path.GetExtension(fileName)}"; collision++; } exportedNames.Add(fileName); CopyFile(file, Path.Combine(directory, fileName)); } return exportedNames.ToList(); } private void CopyFile(IFileData file, string to) { FileInfo fi = new FileInfo(Path.Combine(DataDirectory.GetFullPath(), file.FileName)); if (fi.Exists) fi.CopyTo(to, true); } private string GetNiceFileName(IFileData file, List sourcePorts) { string prefix; if (file.FileTypeID == FileType.Demo && file.Description.Length > 0) prefix = file.Description; else prefix = Path.GetFileNameWithoutExtension(file.OriginalFileName); if (string.IsNullOrEmpty(prefix)) prefix = file.FileTypeID.ToString().ToLower(); string sourcePortName = "NA"; var sourcePort = sourcePorts.FirstOrDefault(x => x.SourcePortID == file.SourcePortID); if (sourcePort != null) sourcePortName = sourcePort.Name; string fileName = $"{prefix}_{sourcePortName}_{Path.GetFileNameWithoutExtension(GameFile.FileNameNoPath)}{Path.GetExtension(file.FileName)}"; Array.ForEach(Path.GetInvalidFileNameChars(), x => fileName = fileName.Replace(x, ' ')); return fileName; } public virtual void View() { List selectedFiles = GetSelectedFiles(); try { if (selectedFiles.Count > 0) { IFileData file = selectedFiles.First(); if (file.IsUrl) { Process.Start(file.FileName); } else { if (File.Exists(Path.Combine(DataDirectory.GetFullPath(), file.FileName))) Process.Start(Path.Combine(DataDirectory.GetFullPath(), file.FileName)); } } } catch (Exception ex) { // This happens when windows doesn't recognize the file extension StyledMessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } public virtual bool MoveFileOrderUp() { return SetFilePriority(true); } public virtual bool MoveFileOrderDown() { return SetFilePriority(false); } public virtual bool SetFileOrderFirst() { List selectedFiles = GetSelectedFiles(); if (selectedFiles.Count > 0) { IFileData file = selectedFiles.First(); if (!file.IsUrl) { List files = Files.ToList(); files.Remove(file); files.Insert(0, file); SetFilePriorities(files); foreach (IFileData fileUpdate in files) DataSourceAdapter.UpdateFile(fileUpdate); return true; } } return false; } private bool SetFilePriority(bool up) //todo fix for new file { List selectedFiles = GetSelectedFiles(); if (selectedFiles.Count > 0) { IFileData file = selectedFiles.First(); if (!file.IsUrl) { List files = Files.ToList(); SetFilePriorities(files); if (up && file.FileOrder > 0) file.FileOrder--; if (!up && file.FileOrder < files.Count - 1) file.FileOrder++; files.Remove(file); files.Insert(file.FileOrder, file); SetFilePriorities(files); foreach (IFileData fileUpdate in files) DataSourceAdapter.UpdateFile(fileUpdate); return true; } } return false; } private void SetFilePriorities(List files) { int priority = 0; foreach (IFileData file in files) { file.FileOrder = priority++; } } public IDataSourceAdapter DataSourceAdapter { get; set; } public LauncherPath DataDirectory { get; set; } protected virtual IFileData[] Files { get { return m_files; } } public void SetContextMenu(ContextMenuStrip menu) { m_menu = menu; Stylizer.StylizeControl(m_menu, DesignMode); } protected abstract List GetSelectedFiles(); public FileType FileType { get; set; } public virtual bool DeleteAllowed { get { return true; } } public virtual bool CopyOrExportAllowed { get { return true; } } public virtual bool NewAllowed { get { return true; } } public virtual bool EditAllowed { get { return true; } } public virtual bool ViewAllowed { get { return true; } } public virtual bool ChangeOrderAllowed { get { return true; } } public virtual IList MenuOptions { get { return Array.Empty(); } } } } ================================================ FILE: DoomLauncher/Controls/CCheckBox.cs ================================================ using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public enum DarkControlState { Normal, Hover, Pressed } public class CCheckBox : CheckBox { private DarkControlState _controlState = DarkControlState.Normal; private bool _spacePressed; [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new Appearance Appearance { get { return base.Appearance; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new bool AutoEllipsis { get { return base.AutoEllipsis; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new Image BackgroundImage { get { return base.BackgroundImage; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new bool FlatAppearance { get { return false; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new FlatStyle FlatStyle { get { return base.FlatStyle; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new Image Image { get { return base.Image; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new ContentAlignment ImageAlign { get { return base.ImageAlign; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new int ImageIndex { get { return base.ImageIndex; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new string ImageKey { get { return base.ImageKey; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new ImageList ImageList { get { return base.ImageList; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new ContentAlignment TextAlign { get { return base.TextAlign; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new TextImageRelation TextImageRelation { get { return base.TextImageRelation; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new bool ThreeState { get { return base.ThreeState; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new bool UseCompatibleTextRendering { get { return false; } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new bool UseVisualStyleBackColor { get { return false; } } public CCheckBox() { SetStyle(ControlStyles.SupportsTransparentBackColor | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true); } private void SetControlState(DarkControlState controlState) { if (_controlState != controlState) { _controlState = controlState; Invalidate(); } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (_spacePressed) return; if (e.Button == MouseButtons.Left) { if (ClientRectangle.Contains(e.Location)) SetControlState(DarkControlState.Pressed); else SetControlState(DarkControlState.Hover); } else { SetControlState(DarkControlState.Hover); } } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (!ClientRectangle.Contains(e.Location)) return; SetControlState(DarkControlState.Pressed); } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (_spacePressed) return; SetControlState(DarkControlState.Normal); } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); if (_spacePressed) return; SetControlState(DarkControlState.Normal); } protected override void OnMouseCaptureChanged(EventArgs e) { base.OnMouseCaptureChanged(e); if (_spacePressed) return; var location = Cursor.Position; if (!ClientRectangle.Contains(location)) SetControlState(DarkControlState.Normal); } protected override void OnGotFocus(EventArgs e) { base.OnGotFocus(e); Invalidate(); } protected override void OnLostFocus(EventArgs e) { base.OnLostFocus(e); _spacePressed = false; var location = Cursor.Position; if (!ClientRectangle.Contains(location)) SetControlState(DarkControlState.Normal); else SetControlState(DarkControlState.Hover); } protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); if (e.KeyCode == Keys.Space) { _spacePressed = true; SetControlState(DarkControlState.Pressed); } } protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); if (e.KeyCode == Keys.Space) { _spacePressed = false; var location = Cursor.Position; if (!ClientRectangle.Contains(location)) SetControlState(DarkControlState.Normal); else SetControlState(DarkControlState.Hover); } } protected override void OnPaint(PaintEventArgs e) { var g = e.Graphics; var rect = new Rectangle(0, 0, ClientSize.Width, ClientSize.Height); var textColor = ColorTheme.Current.Text; var borderColor = ColorTheme.Current.Border; var fillColor = ColorTheme.Current.Highlight; if (Enabled) { if (_controlState == DarkControlState.Hover) { borderColor = ColorTheme.Current.Highlight; fillColor = ColorTheme.Current.Highlight; } else if (_controlState == DarkControlState.Pressed) { borderColor = ColorTheme.Current.Border; fillColor = ColorTheme.Current.Border; } } else { textColor = ColorTheme.Current.DisabledText; borderColor = ColorTheme.Current.Border; fillColor = ColorTheme.Current.Window; } using (var b = new SolidBrush(ColorTheme.Current.Window)) g.FillRectangle(b, rect); DpiScale dpiScale = new DpiScale(g); var size = new Size(dpiScale.ScaleIntX(12), dpiScale.ScaleIntY(12)); var checkBoxRect = new Rectangle(0, 0, size.Width, size.Height); using (var b = new SolidBrush(ColorTheme.Current.CheckBoxBackground)) g.FillRectangle(b, checkBoxRect); using (var p = new Pen(borderColor)) g.DrawRectangle(p, checkBoxRect); if (Checked) { using (var b = new SolidBrush(fillColor)) { var offsetX = dpiScale.ScaleFloatX(1.8f); var offsetY = dpiScale.ScaleFloatY(1.8f); var selectWidth = size.Width - offsetX * 2; var selectHeight = size.Height - offsetY * 2; var selectRect = new RectangleF(offsetX, offsetY, selectWidth, selectHeight); g.FillRectangle(b, selectRect); } } using (var b = new SolidBrush(textColor)) { var stringFormat = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Near }; var modRect = new Rectangle(size.Width + dpiScale.ScaleIntX(4), dpiScale.ScaleIntY(1), rect.Width, checkBoxRect.Height); g.DrawString(Text, Font, b, modRect, stringFormat); } } } } ================================================ FILE: DoomLauncher/Controls/CComboBox.cs ================================================ using System; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher.Controls { public class ComboBoxItemStyle { public string ValueMember { get; set; } public string Text { get; set; } public Font Font { get; set; } public Color ForeColor { get; set; } public DrawItemEventArgs DrawItem { get; set; } } public class CComboBox : ComboBox { private static readonly IThemeColors CurrentTheme = ColorTheme.Current; public event EventHandler StyleItem; public CComboBox() { DrawItem += ComboBoxDrawItemInternal; DrawMode = DrawMode.OwnerDrawFixed; } public static void ComboBoxDrawItem(object sender, DrawItemEventArgs e) { ComboBoxDrawItem(sender, e, null); } private void ComboBoxDrawItemInternal(object sender, DrawItemEventArgs e) { ComboBoxDrawItem(sender, e, StyleItem); } private static void ComboBoxDrawItem(object sender, DrawItemEventArgs e, EventHandler styleItem = null) { if (!(sender is ComboBox comboBox)) return; bool selected = e.State.HasFlag(DrawItemState.Selected); if (selected) e.Graphics.FillRectangle(new SolidBrush(CurrentTheme.Highlight), e.Bounds); else e.Graphics.FillRectangle(new SolidBrush(CurrentTheme.DropDownBackground), e.Bounds); if (e.Index < 0 || e.Index >= comboBox.Items.Count) return; var item = comboBox.Items[e.Index]; string text; if (!string.IsNullOrEmpty(comboBox.DisplayMember)) text = GetPropertyString(item, comboBox.DisplayMember); else if (item is string str) text = str; else text = item.ToString(); Color color = selected ? CurrentTheme.HighlightText : CurrentTheme.Text; if (styleItem != null) { string value = string.Empty; if (!string.IsNullOrEmpty(comboBox.ValueMember)) value = GetPropertyString(item, comboBox.ValueMember); var style = new ComboBoxItemStyle() { ValueMember = value, Text = text, Font = e.Font, ForeColor = color, DrawItem = e }; styleItem.Invoke(sender, style); e.Graphics.DrawString(style.Text, style.Font, new SolidBrush(style.ForeColor), new PointF(0, e.Bounds.Y)); return; } e.Graphics.DrawString(text, e.Font, new SolidBrush(color), new PointF(0, e.Bounds.Y)); } private static string GetPropertyString(object item, string propertyName) { var pi = item.GetType().GetProperty(propertyName); if (pi != null) return pi.GetValue(item).ToString(); return string.Empty; } } } ================================================ FILE: DoomLauncher/Controls/CDataGridView.cs ================================================ using System.Windows.Forms; namespace DoomLauncher { class CDataGridView : DataGridView { public CDataGridView() { DoubleBuffered = true; } } } ================================================ FILE: DoomLauncher/Controls/CProgressBar.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public class CProgressBar : ProgressBar { private readonly Timer m_timer; private int m_blend; private bool m_direction = true; public CProgressBar() { SetStyle(ControlStyles.UserPaint, true); VisibleChanged += CProgressBar_VisibleChanged; m_timer = new Timer(); m_timer.Interval = 5; m_timer.Tick += Timer_Tick; } private void CProgressBar_VisibleChanged(object sender, System.EventArgs e) { if (Visible && !m_timer.Enabled) m_timer.Start(); if (!Visible && !m_timer.Enabled) m_timer.Stop(); } private void Timer_Tick(object sender, System.EventArgs e) { if (!Visible) { m_timer.Stop(); return; } const int Amount = 4; if (m_direction) m_blend += Amount; else m_blend -= Amount; if (m_blend <= 0 && !m_direction) { m_blend = 0; m_direction = true; } if (m_blend >= 255 && m_direction) { m_blend = 255; m_direction = false; } Invalidate(); } protected override void OnPaint(PaintEventArgs e) { int value = Value; var brush = new SolidBrush(ColorTheme.Current.Highlight); if (Style == ProgressBarStyle.Marquee) { Value = Maximum; brush = new SolidBrush(Color.FromArgb(m_blend, ColorTheme.Current.Highlight)); } Rectangle rect = e.ClipRectangle; DpiScale dpiScale = new DpiScale(e.Graphics); int padX = dpiScale.ScaleIntX(2); int padY = dpiScale.ScaleIntY(2); if (Maximum != 0) rect.Width = (int)(rect.Width * ((double)value / Maximum)); rect.Width -= (padX * 2); if (ProgressBarRenderer.IsSupported) ProgressBarRenderer.DrawHorizontalBar(e.Graphics, e.ClipRectangle); rect.Height -= padY * 2; e.Graphics.FillRectangle(brush, padX, padY, rect.Width, rect.Height); } } } ================================================ FILE: DoomLauncher/Controls/CRichTextBox.Designer.cs ================================================ namespace DoomLauncher.Controls { partial class CRichTextBox { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion } } ================================================ FILE: DoomLauncher/Controls/CRichTextBox.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; namespace DoomLauncher.Controls { public partial class CRichTextBox : RichTextBox { public CRichTextBox() { InitializeComponent(); WarnLinkClick = true; LinkClicked += CRichTextBox_LinkClicked; } public bool WarnLinkClick { get; set; } private void CRichTextBox_LinkClicked(object sender, LinkClickedEventArgs e) { if (ShouldNavigate(e)) Process.Start(e.LinkText); } private bool ShouldNavigate(LinkClickedEventArgs e) { if (WarnLinkClick) { return MessageBox.Show(this, string.Format("You are about to navigate to:{1}{0} {1}{1}Continue?", e.LinkText, Environment.NewLine), "Navigate", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK; } return true; } } } ================================================ FILE: DoomLauncher/Controls/CTabControl.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public class CTabControl : TabControl { private const int TabMargin = 3; private readonly Dictionary> m_tabRowLookup = new Dictionary>(); private readonly List m_tabRows = new List(); private bool m_setting; private bool m_showHeaders = true; private int m_lastTabCount; private float m_spaceWidth = -1; private Size m_originalItemSize = new Size(0, 0); public CTabControl() { if (!DesignMode) Multiline = true; DrawMode = TabDrawMode.OwnerDrawFixed; SizeMode = TabSizeMode.Normal; SetStyle(ControlStyles.UserPaint, true); } public void SetShowHeaders(bool set) { m_showHeaders = set; if (set) { Appearance = TabAppearance.Normal; SizeMode = TabSizeMode.Normal; ItemSize = m_originalItemSize; } else { Appearance = TabAppearance.FlatButtons; SizeMode = TabSizeMode.Fixed; ItemSize = new Size(0, 1); } } protected override void OnCreateControl() { int width = 0; if (SizeMode == TabSizeMode.Fixed) width = ItemSize.Width; var dpiScale = new DpiScale(CreateGraphics()); ItemSize = new Size(width, dpiScale.ScaleIntY(18)); m_originalItemSize = ItemSize; } protected override void OnPaint(PaintEventArgs e) { if (m_setting) return; if (m_lastTabCount != TabCount && SizeMode != TabSizeMode.Fixed) { m_setting = true; var dpiScale = new DpiScale(e.Graphics); for (int i = 0; i < TabPages.Count; i++) { string text = TabPages[i].Text; var stringSize = e.Graphics.MeasureString(text, Font); int originalWidth = GetTabRect(i).Width; int width = (int)Math.Ceiling(stringSize.Width) + dpiScale.ScaleIntX(16); // Can't guarantee the dpi is working. Have to measure by injecting a space in the tab text. if (m_spaceWidth < 0) { TabPages[i].Text += " "; m_spaceWidth = GetTabRect(i).Width - originalWidth; } int spaces = (int)Math.Ceiling((width - GetTabRect(i).Width) / m_spaceWidth) + 2; for (int j = 0; j < spaces; j++) text += " "; TabPages[i].Text = text; } m_setting = false; m_lastTabCount = TabCount; } Rectangle fillRect = new Rectangle(0, 0, Width, Height); using (Brush controlBrush = new SolidBrush(ColorTheme.Current.Window)) e.Graphics.FillRectangle(controlBrush, fillRect); if (!m_showHeaders) return; var renderOrder = GetTabPageRenderOrder(); if (renderOrder.Count == 0) return; int selectedRow = SelectedIndex == -1 || SelectedIndex >= m_tabRows.Count ? 0 : m_tabRows[SelectedIndex]; foreach (int tabIndex in renderOrder) DrawTab(tabIndex, e, m_tabRows[tabIndex] == selectedRow); base.OnPaint(e); } private List GetTabPageRenderOrder() { m_tabRowLookup.Clear(); m_tabRows.Clear(); for (int i = 0; i < TabPages.Count; i++) { Rectangle tabRect = GetTabRect(i); if (!m_tabRowLookup.TryGetValue(tabRect.Y, out var list)) { list = new List(); m_tabRowLookup[tabRect.Y] = list; } list.Add(i); } var keys = m_tabRowLookup.Keys.ToList(); keys.Sort(); m_tabRows.Clear(); foreach (var key in keys) m_tabRows.AddRange(m_tabRowLookup[key]); return m_tabRows; } private void DrawTab(int index, PaintEventArgs e, bool selectedRow) { Rectangle tabRect = GetTabRect(index); if (tabRect.Width == 0) return; tabRect.X += 2; if (selectedRow) tabRect.Height += 8; tabRect.Width -= 4; bool selected = SelectedIndex == index; if (selected) tabRect.Y -= 2; else tabRect.Height += 2; Brush controlBrush = new SolidBrush(ColorTheme.Current.WindowLight); Brush textBrush = new SolidBrush(ColorTheme.Current.Text); Pen borderPen = new Pen(ColorTheme.Current.Border); e.Graphics.FillRectangle(controlBrush, tabRect); RectangleF layoutRect = new RectangleF(tabRect.Left, tabRect.Y + TabMargin, tabRect.Width, tabRect.Height - 2 * TabMargin); using (StringFormat format = new StringFormat()) { using (Font tabFont = new Font(Font, selected ? FontStyle.Bold : FontStyle.Regular)) { format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Center; e.Graphics.DrawString(TabPages[index].Text, tabFont, textBrush, layoutRect, format); } } if (selected) { Rectangle selectRect = tabRect; selectRect.Height = 4; using (Brush selectBrush = new SolidBrush(ColorTheme.Current.Highlight)) e.Graphics.FillRectangle(selectBrush, selectRect); } e.Graphics.DrawLine(borderPen, new Point(tabRect.Left, tabRect.Top), new Point(tabRect.Right, tabRect.Top)); e.Graphics.DrawLine(borderPen, new Point(tabRect.Left, tabRect.Top), new Point(tabRect.Left, tabRect.Bottom)); e.Graphics.DrawLine(borderPen, new Point(tabRect.Right, tabRect.Top), new Point(tabRect.Right, tabRect.Bottom)); } } } ================================================ FILE: DoomLauncher/Controls/CheckBoxList.Designer.cs ================================================ namespace DoomLauncher.Controls { partial class CheckBoxList { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0, 4, 0, 4); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 1; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(120, 120); this.tblMain.TabIndex = 0; // // CheckBoxList // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Name = "CheckBoxList"; this.ResumeLayout(false); } #endregion private TableLayoutPanelDB tblMain; } } ================================================ FILE: DoomLauncher/Controls/CheckBoxList.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher.Controls { public class CheckListBoxEventArgs { public readonly bool Checked; public readonly string Text; public CheckListBoxEventArgs(bool check, string text) { Checked = check; Text = text; } } public partial class CheckBoxList : UserControl { public event EventHandler ItemCheck; public List Items { get; private set; } = new List(); private List m_checkBoxes = new List(); public CheckBoxList() { InitializeComponent(); } public void SetItems(IEnumerable items) { tblMain.RowStyles.Clear(); Items = items.ToList(); DpiScale dpiScale = new DpiScale(CreateGraphics()); int rowHeight = dpiScale.ScaleIntY(20); int padding = dpiScale.ScaleIntX(4); int row = 0; foreach (var item in items) { var checkBox = new CCheckBox() { Dock = DockStyle.Fill, Text = item, Margin = new Padding(padding, padding, 0, 0) }; checkBox.CheckedChanged += CheckBox_CheckedChanged; m_checkBoxes.Add(checkBox); tblMain.RowStyles.Add(new RowStyle(SizeType.Absolute, rowHeight)); tblMain.Controls.Add(checkBox, 0, row); row++; } } private void CheckBox_CheckedChanged(object sender, EventArgs e) { var checkBox = (CCheckBox)sender; ItemCheck?.Invoke(this, new CheckListBoxEventArgs(checkBox.Checked, checkBox.Text)); } public string[] GetCheckedItems() { List checkedItems = new List(); foreach (var checkBox in m_checkBoxes) { if (checkBox.Checked) checkedItems.Add(checkBox.Text); } return checkedItems.ToArray(); } public bool IsChecked(string text) { foreach (var checkBox in m_checkBoxes) { if (checkBox.Text == text) return checkBox.Checked; } return false; } public void SetChecked(string text, bool check) { foreach (var checkBox in m_checkBoxes) { if (checkBox.Text == text) { checkBox.Checked = check; break; } } } } } ================================================ FILE: DoomLauncher/Controls/CheckBoxList.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/ColumnField.cs ================================================ namespace DoomLauncher { public class ColumnField { public ColumnField(string datakey, string title) { DataKey = datakey; Title = title; } public string DataKey { get; set; } public string Title { get; set; } } } ================================================ FILE: DoomLauncher/Controls/DownloadView.Designer.cs ================================================ namespace DoomLauncher { partial class DownloadView { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.menuOptions = new System.Windows.Forms.ContextMenuStrip(this.components); this.removeFromHistoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.cancelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.playToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.menuOptions.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblMain.Size = new System.Drawing.Size(150, 150); this.tblMain.TabIndex = 0; // // menuOptions // this.menuOptions.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.removeFromHistoryToolStripMenuItem, this.cancelToolStripMenuItem, this.playToolStripMenuItem}); this.menuOptions.Name = "contextMenuStrip1"; this.menuOptions.Size = new System.Drawing.Size(188, 92); // // removeFromHistoryToolStripMenuItem // this.removeFromHistoryToolStripMenuItem.Name = "removeFromHistoryToolStripMenuItem"; this.removeFromHistoryToolStripMenuItem.Size = new System.Drawing.Size(187, 22); this.removeFromHistoryToolStripMenuItem.Text = "Remove from History"; this.removeFromHistoryToolStripMenuItem.Click += new System.EventHandler(this.removeFromHistoryToolStripMenuItem_Click); // // cancelToolStripMenuItem // this.cancelToolStripMenuItem.Name = "cancelToolStripMenuItem"; this.cancelToolStripMenuItem.Size = new System.Drawing.Size(187, 22); this.cancelToolStripMenuItem.Text = "Cancel"; this.cancelToolStripMenuItem.Click += new System.EventHandler(this.cancelToolStripMenuItem_Click); // // playToolStripMenuItem // this.playToolStripMenuItem.Name = "playToolStripMenuItem"; this.playToolStripMenuItem.Size = new System.Drawing.Size(187, 22); this.playToolStripMenuItem.Text = "Play"; this.playToolStripMenuItem.Click += new System.EventHandler(this.playToolStripMenuItem_Click); // // DownloadView // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Name = "DownloadView"; this.menuOptions.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.ContextMenuStrip menuOptions; private System.Windows.Forms.ToolStripMenuItem removeFromHistoryToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem cancelToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem playToolStripMenuItem; } } ================================================ FILE: DoomLauncher/Controls/DownloadView.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class DownloadView : UserControl { public event EventHandler DownloadCancelled; public event EventHandler UserPlay; private readonly Dictionary m_downloadLookup = new Dictionary(); private readonly HashSet m_cancelledDownloads = new HashSet(); static readonly int s_rowHeight = 64; public DownloadView() { InitializeComponent(); tblMain.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single; tblMain.RowStyles.Clear(); tblMain.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); UpdateSize(); Stylizer.StylizeControl(this, DesignMode); } public new int Height { get; set; } public void AddDownload(object key, string text) { if (m_cancelledDownloads.Contains(key)) m_cancelledDownloads.Remove(key); if (!m_downloadLookup.ContainsKey(key)) { DpiScale dpiScale = new DpiScale(CreateGraphics()); DownloadViewItem item = CreateDownloadViewItem(text); m_downloadLookup.Add(key, item); tblMain.RowCount++; tblMain.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); tblMain.RowStyles[tblMain.RowStyles.Count - 2].SizeType = SizeType.Absolute; tblMain.RowStyles[tblMain.RowStyles.Count - 2].Height = dpiScale.ScaleIntY(s_rowHeight); tblMain.Controls.Add(item, 0, tblMain.RowStyles.Count - 2); UpdateSize(); } else { DownloadViewItem itemOld = m_downloadLookup[key]; DownloadViewItem item = CreateDownloadViewItem(text); TableLayoutPanelCellPosition pos = tblMain.GetPositionFromControl(itemOld); tblMain.Controls.Remove(itemOld); tblMain.Controls.Add(item, pos.Column, pos.Row); m_downloadLookup.Remove(key); m_downloadLookup.Add(key, item); } } private DownloadViewItem CreateDownloadViewItem(string text) { DownloadViewItem item = new DownloadViewItem(); item.Dock = DockStyle.Fill; item.Key = text; item.DisplayText = text; item.Cancelled += item_Cancelled; item.ContextMenuStrip = menuOptions; return item; } private void UpdateSize() { DpiScale dpiScale = new DpiScale(CreateGraphics()); Height = tblMain.RowStyles.Count * dpiScale.ScaleIntY(s_rowHeight); } void item_Cancelled(object sender, EventArgs e) { DownloadViewItem item = sender as DownloadViewItem; if (item != null) HandleItemCancel(item); } private void HandleItemCancel(DownloadViewItem item) { if (item.ProgressValue != 100) item.ProgressValue = 0; if (DownloadCancelled != null) { object key = GetKey(item); if (key != null) { m_cancelledDownloads.Add(key); DownloadCancelled(this, new EventArgs()); } } } private object GetKey(DownloadViewItem item) { foreach(KeyValuePair kvp in m_downloadLookup) { if (kvp.Value == item) return kvp.Key; } return null; } private void RemoveDownloadRow(DownloadViewItem item) { int row = tblMain.GetRow(item); if (row > -1) { tblMain.Controls.Remove(item); tblMain.RowStyles.RemoveAt(row); for(int i = row + 1; i < tblMain.RowCount; i++) //have to shift the controls up (TableLayoutPanel is dumb) { DownloadViewItem itemMove = GetItem(i); if (itemMove != null) { tblMain.Controls.Remove(itemMove); tblMain.Controls.Add(itemMove, 0, i - 1); } } } } public void RemoveDownload(object key) { if (m_downloadLookup.ContainsKey(key)) { DownloadViewItem item = m_downloadLookup[key]; m_downloadLookup.Remove(key); RemoveDownloadRow(item); UpdateSize(); } } public void UpdateDownload(object key, int percent) { if (m_downloadLookup.ContainsKey(key)) { DownloadViewItem item = m_downloadLookup[key]; item.ProgressValue = percent; if (percent == 100) item.CancelVisible = false; } } public void UpdateDownload(object key, string text) { if (m_downloadLookup.ContainsKey(key)) { DownloadViewItem item = m_downloadLookup[key]; item.DisplayText = text; } } public object[] GetCancelledDownloads() { return m_cancelledDownloads.ToArray(); } public DownloadViewItem SelectedItem { get; private set; } private DownloadViewItem GetItem(int row) { return tblMain.GetControlFromPosition(0, row) as DownloadViewItem; } private void removeFromHistoryToolStripMenuItem_Click(object sender, EventArgs e) { DownloadViewItem item = menuOptions.SourceControl as DownloadViewItem; if (item != null) { RemoveDownloadRow(item); object key = GetKey(item); if (key != null) m_downloadLookup.Remove(key); } } private void cancelToolStripMenuItem_Click(object sender, EventArgs e) { DownloadViewItem item = menuOptions.SourceControl as DownloadViewItem; SelectedItem = item; if (item != null) HandleItemCancel(item); } private void playToolStripMenuItem_Click(object sender, EventArgs e) { DownloadViewItem item = menuOptions.SourceControl as DownloadViewItem; SelectedItem = item; if (item != null && UserPlay != null && item.ProgressValue == 100) UserPlay(this, new EventArgs()); } } } ================================================ FILE: DoomLauncher/Controls/DownloadView.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 ================================================ FILE: DoomLauncher/Controls/DownloadViewItem.Designer.cs ================================================ namespace DoomLauncher { partial class DownloadViewItem { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.progressBar = new DoomLauncher.CProgressBar(); this.flp = new DoomLauncher.FlowLayoutPanelDB(); this.btnCancel = new System.Windows.Forms.Button(); this.lblText = new System.Windows.Forms.Label(); this.tblMain.SuspendLayout(); this.flp.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.progressBar, 0, 1); this.tblMain.Controls.Add(this.flp, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(309, 116); this.tblMain.TabIndex = 0; // // progressBar // this.progressBar.Dock = System.Windows.Forms.DockStyle.Top; this.progressBar.Location = new System.Drawing.Point(3, 35); this.progressBar.Name = "progressBar"; this.progressBar.Size = new System.Drawing.Size(303, 23); this.progressBar.TabIndex = 2; // // flp // this.flp.Controls.Add(this.btnCancel); this.flp.Controls.Add(this.lblText); this.flp.Dock = System.Windows.Forms.DockStyle.Fill; this.flp.Location = new System.Drawing.Point(0, 0); this.flp.Margin = new System.Windows.Forms.Padding(0); this.flp.Name = "flp"; this.flp.Size = new System.Drawing.Size(309, 32); this.flp.TabIndex = 3; // // btnCancel // this.btnCancel.Location = new System.Drawing.Point(3, 3); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(75, 23); this.btnCancel.TabIndex = 3; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); // // lblText // this.lblText.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblText.AutoSize = true; this.lblText.Location = new System.Drawing.Point(84, 8); this.lblText.Name = "lblText"; this.lblText.Size = new System.Drawing.Size(28, 13); this.lblText.TabIndex = 2; this.lblText.Text = "Text"; // // DownloadViewItem // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Name = "DownloadViewItem"; this.Size = new System.Drawing.Size(309, 116); this.tblMain.ResumeLayout(false); this.flp.ResumeLayout(false); this.flp.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.ProgressBar progressBar; private System.Windows.Forms.FlowLayoutPanel flp; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Label lblText; } } ================================================ FILE: DoomLauncher/Controls/DownloadViewItem.cs ================================================ using System; using System.Windows.Forms; namespace DoomLauncher { public partial class DownloadViewItem : UserControl { public event EventHandler Cancelled; public DownloadViewItem() { InitializeComponent(); Stylizer.StylizeControl(this, DesignMode); } public object Key { get; set; } public string DisplayText { get { return lblText.Text; } set { lblText.Text = value; } } public int ProgressValue { get { return progressBar.Value; } set { progressBar.Value = value; } } public bool CancelVisible { get { return btnCancel.Visible; } set { btnCancel.Visible = value; } } private void btnCancel_Click(object sender, EventArgs e) { Cancelled?.Invoke(this, new EventArgs()); } } } ================================================ FILE: DoomLauncher/Controls/DownloadViewItem.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/FileType.cs ================================================ namespace DoomLauncher { public enum FileType { Unknown = 0, Screenshot = 1, Demo = 2, SaveGame = 3, Thumbnail = 4, TileImage = 5, } } ================================================ FILE: DoomLauncher/Controls/FilesCtrl.Designer.cs ================================================ namespace DoomLauncher { partial class FilesCtrl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.dgvAdditionalFiles = new System.Windows.Forms.DataGridView(); this.FileName = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.btnAdd = new DoomLauncher.FormButton(); this.btnDelete = new DoomLauncher.FormButton(); this.btnMoveUp = new DoomLauncher.FormButton(); this.btnMoveDown = new DoomLauncher.FormButton(); this.tblMain.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.dgvAdditionalFiles)).BeginInit(); this.flpButtons.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.dgvAdditionalFiles, 0, 1); this.tblMain.Controls.Add(this.flpButtons, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Size = new System.Drawing.Size(370, 482); this.tblMain.TabIndex = 23; // // dgvAdditionalFiles // this.dgvAdditionalFiles.AllowUserToAddRows = false; this.dgvAdditionalFiles.BackgroundColor = System.Drawing.SystemColors.ControlLightLight; this.dgvAdditionalFiles.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dgvAdditionalFiles.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.FileName}); this.dgvAdditionalFiles.Dock = System.Windows.Forms.DockStyle.Fill; this.dgvAdditionalFiles.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically; this.dgvAdditionalFiles.Location = new System.Drawing.Point(4, 45); this.dgvAdditionalFiles.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.dgvAdditionalFiles.Name = "dgvAdditionalFiles"; this.dgvAdditionalFiles.RowHeadersWidth = 62; this.dgvAdditionalFiles.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; this.dgvAdditionalFiles.Size = new System.Drawing.Size(362, 432); this.dgvAdditionalFiles.TabIndex = 2; this.dgvAdditionalFiles.KeyDown += new System.Windows.Forms.KeyEventHandler(this.dgvAdditionalFiles_KeyDown); // // FileName // this.FileName.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; this.FileName.DataPropertyName = "FileName"; this.FileName.HeaderText = "File"; this.FileName.MinimumWidth = 8; this.FileName.Name = "FileName"; // // flpButtons // this.flpButtons.Controls.Add(this.btnAdd); this.flpButtons.Controls.Add(this.btnDelete); this.flpButtons.Controls.Add(this.btnMoveUp); this.flpButtons.Controls.Add(this.btnMoveDown); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.Location = new System.Drawing.Point(0, 0); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(370, 40); this.flpButtons.TabIndex = 4; // // btnAdd // this.btnAdd.Image = global::DoomLauncher.Properties.Resources.File; this.btnAdd.Location = new System.Drawing.Point(0, 0); this.btnAdd.Margin = new System.Windows.Forms.Padding(0); this.btnAdd.Name = "btnAdd"; this.btnAdd.Size = new System.Drawing.Size(40, 37); this.btnAdd.TabIndex = 0; this.btnAdd.UseVisualStyleBackColor = true; this.btnAdd.Click += new System.EventHandler(this.btnAddFile_Click); // // btnDelete // this.btnDelete.Image = global::DoomLauncher.Properties.Resources.Delete; this.btnDelete.Location = new System.Drawing.Point(40, 0); this.btnDelete.Margin = new System.Windows.Forms.Padding(0); this.btnDelete.Name = "btnDelete"; this.btnDelete.Size = new System.Drawing.Size(40, 37); this.btnDelete.TabIndex = 1; this.btnDelete.UseVisualStyleBackColor = true; this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); // // btnMoveUp // this.btnMoveUp.Image = global::DoomLauncher.Properties.Resources.UpArrow; this.btnMoveUp.Location = new System.Drawing.Point(80, 0); this.btnMoveUp.Margin = new System.Windows.Forms.Padding(0); this.btnMoveUp.Name = "btnMoveUp"; this.btnMoveUp.Size = new System.Drawing.Size(40, 37); this.btnMoveUp.TabIndex = 2; this.btnMoveUp.UseVisualStyleBackColor = true; this.btnMoveUp.Click += new System.EventHandler(this.btnMoveUp_Click); // // btnMoveDown // this.btnMoveDown.Image = global::DoomLauncher.Properties.Resources.DownArrow; this.btnMoveDown.Location = new System.Drawing.Point(120, 0); this.btnMoveDown.Margin = new System.Windows.Forms.Padding(0); this.btnMoveDown.Name = "btnMoveDown"; this.btnMoveDown.Size = new System.Drawing.Size(40, 37); this.btnMoveDown.TabIndex = 3; this.btnMoveDown.UseVisualStyleBackColor = true; this.btnMoveDown.Click += new System.EventHandler(this.btnMoveDown_Click); // // FilesCtrl // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "FilesCtrl"; this.Size = new System.Drawing.Size(370, 482); this.tblMain.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.dgvAdditionalFiles)).EndInit(); this.flpButtons.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.DataGridView dgvAdditionalFiles; private System.Windows.Forms.DataGridViewTextBoxColumn FileName; private System.Windows.Forms.FlowLayoutPanel flpButtons; private FormButton btnDelete; private FormButton btnMoveUp; private FormButton btnMoveDown; private FormButton btnAdd; } } ================================================ FILE: DoomLauncher/Controls/FilesCtrl.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Reflection; using System.Text; using System.Windows.Forms; using DoomLauncher.Handlers; namespace DoomLauncher { public partial class FilesCtrl : UserControl { public event AdditionalFilesEventHanlder CellFormatting; public event AdditionalFilesEventHanlder NewItemNeeded; public event AdditionalFilesEventHanlder ItemAdded; public event AdditionalFilesEventHanlder ItemRemoved; public event AdditionalFilesEventHanlder ItemPriorityUp; public event AdditionalFilesEventHanlder ItemPriorityDown; public event AdditionalFilesEventHanlder ItemRemoving; private readonly ToolTipGroup m_toolTipGroup = new ToolTipGroup(); private List m_files = new List(); private string m_keyProperty, m_dataProperty; public FilesCtrl() { InitializeComponent(); dgvAdditionalFiles.BackgroundColor = SystemColors.Window; dgvAdditionalFiles.AutoGenerateColumns = false; dgvAdditionalFiles.RowHeadersVisible = false; dgvAdditionalFiles.MultiSelect = false; dgvAdditionalFiles.CellFormatting += dgvAdditionalFiles_CellFormatting; Icons.DpiScale = new DpiScale(CreateGraphics()); btnAdd.Image = Icons.File; btnDelete.Image = Icons.Delete; btnMoveUp.Image = Icons.ArrowUp; btnMoveDown.Image = Icons.ArrowDown; m_toolTipGroup.SetToolTip(btnAdd, "Add File"); m_toolTipGroup.SetToolTip(btnDelete, "Remove File"); m_toolTipGroup.SetToolTip(btnMoveUp, "Move Up"); m_toolTipGroup.SetToolTip(btnMoveDown, "Move Down"); Stylizer.StylizeControl(this, DesignMode); } public void Initialize(string keyProperty, string dataProperty) { dgvAdditionalFiles.Columns[0].DataPropertyName = dataProperty; dgvAdditionalFiles.Columns[0].Name = dataProperty; m_keyProperty = keyProperty; m_dataProperty = dataProperty; } public List GetFiles() { return m_files.ToList(); } public void SetDataSource(object dataSource) { dgvAdditionalFiles.DataSource = null; dgvAdditionalFiles.DataSource = dataSource; m_files.Clear(); foreach (DataGridViewRow dgvr in dgvAdditionalFiles.Rows) m_files.Add(dgvr.DataBoundItem); } public string GetAdditionalFilesString() { StringBuilder sb = new StringBuilder(); foreach (object file in m_files) { PropertyInfo pi = file.GetType().GetProperty(m_dataProperty); sb.Append(pi.GetValue(file).ToString()); sb.Append(';'); } return sb.ToString(); } private void dgvAdditionalFiles_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { if (CellFormatting != null) { object item = dgvAdditionalFiles.Rows[e.RowIndex].DataBoundItem; AdditionalFilesEventArgs args = new AdditionalFilesEventArgs(item, (string)e.Value); CellFormatting(this, args); e.Value = args.DisplayText; } } private object SelectedItem { get { if (dgvAdditionalFiles.SelectedRows.Count > 0) return dgvAdditionalFiles.SelectedRows[0].DataBoundItem; return null; } } private void btnAddFile_Click(object sender, EventArgs e) { AdditionalFilesEventArgs args = new AdditionalFilesEventArgs(null); NewItemNeeded?.Invoke(this, args); if (args.NewItems != null) { m_files.AddRange(args.NewItems); Rebind(); if (dgvAdditionalFiles.Rows.Count > 0) dgvAdditionalFiles.Rows[dgvAdditionalFiles.Rows.Count - 1].Selected = true; ItemAdded?.Invoke(this, args); } } private void btnDelete_Click(object sender, EventArgs e) { HandleDelete(); } private void dgvAdditionalFiles_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Delete) HandleDelete(); } private void btnMoveUp_Click(object sender, EventArgs e) { MoveFile(true); object item = SelectedItem; if (item != null && ItemPriorityUp != null) ItemPriorityUp(this, new AdditionalFilesEventArgs(item)); } private void btnMoveDown_Click(object sender, EventArgs e) { MoveFile(false); object item = SelectedItem; if (item != null && ItemPriorityDown != null) ItemPriorityDown(this, new AdditionalFilesEventArgs(item)); } private void MoveFile(bool up) { if (dgvAdditionalFiles.SelectedRows.Count > 0) { object file = dgvAdditionalFiles.SelectedRows[0].DataBoundItem; int priority = GetFilePriority(file); if (up && priority > 0) priority--; if (!up && priority < m_files.Count - 1) priority++; m_files.Remove(file); m_files.Insert(priority, file); Rebind(); dgvAdditionalFiles.ClearSelection(); dgvAdditionalFiles.Rows[priority].Selected = true; } } private void Rebind() { m_files = m_files.Distinct().ToList(); SetDataSource(m_files.ToList()); } private int GetFilePriority(object file) { int count = 0; PropertyInfo pi = file.GetType().GetProperty(m_keyProperty); foreach (object fileCheck in m_files) { if (pi.GetValue(fileCheck).Equals(pi.GetValue(file))) break; count++; } return count; } private void HandleDelete() { object item = SelectedItem; if (item != null) { int index = dgvAdditionalFiles.SelectedRows[0].Index; AdditionalFilesEventArgs cancelEvent = new AdditionalFilesEventArgs(item); ItemRemoving?.Invoke(this, cancelEvent); if (!cancelEvent.Cancel) { m_files.Remove(item); Rebind(); if (dgvAdditionalFiles.Rows.Count > 0) { if (index >= dgvAdditionalFiles.Rows.Count) index = dgvAdditionalFiles.Rows.Count - 1; dgvAdditionalFiles.Rows[index].Selected = true; } ItemRemoved?.Invoke(this, new AdditionalFilesEventArgs(item)); } } } } public delegate void AdditionalFilesEventHanlder(object sender, AdditionalFilesEventArgs e); public class AdditionalFilesEventArgs : EventArgs { public AdditionalFilesEventArgs(object item) { Item = item; Cancel = false; } public AdditionalFilesEventArgs(object item, string displayText) { Item = item; DisplayText = displayText; Cancel = false; } public object Item { get; set; } public string DisplayText { get; set; } public List NewItems { get; set; } public bool Cancel { get; set; } } } ================================================ FILE: DoomLauncher/Controls/FilesCtrl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/FlowLayoutPanelDB.Designer.cs ================================================ namespace DoomLauncher { partial class FlowLayoutPanelDB { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { components = new System.ComponentModel.Container(); //this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; } #endregion } } ================================================ FILE: DoomLauncher/Controls/FlowLayoutPanelDB.cs ================================================ using System.Windows.Forms; namespace DoomLauncher { public partial class FlowLayoutPanelDB : FlowLayoutPanel { public bool CaptureArrowKeys { get; set; } = false; public FlowLayoutPanelDB() { SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); } protected override bool IsInputKey(Keys keyData) { if (CaptureArrowKeys) { switch (keyData) { case Keys.Up: case Keys.Down: case Keys.Left: case Keys.Right: return true; case Keys.Shift | Keys.Up: case Keys.Shift | Keys.Down: case Keys.Shift | Keys.Left: case Keys.Shift | Keys.Right: return true; } } return base.IsInputKey(keyData); } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; // WS_CLIPCHILDREN return cp; } } } } ================================================ FILE: DoomLauncher/Controls/FormButton.cs ================================================ using System.Windows.Forms; namespace DoomLauncher { public class FormButton : Button { } } ================================================ FILE: DoomLauncher/Controls/GameFileAssociationView.Designer.cs ================================================ namespace DoomLauncher { partial class GameFileAssociationView { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.Windows.Forms.TabPage tabPageScreenshots; this.ctrlScreenshotView = new DoomLauncher.ScreenshotView(); this.mnuOptions = new System.Windows.Forms.ContextMenuStrip(this.components); this.copyFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.copyAllFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.deleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); this.addFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.editDetailsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); this.moveUpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.moveDownToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.setFirstToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.tabControl = new DoomLauncher.CTabControl(); this.tabPageDemos = new System.Windows.Forms.TabPage(); this.ctrlDemoView = new DoomLauncher.GenericFileView(); this.tabPageSaveGames = new System.Windows.Forms.TabPage(); this.ctrlSaveGameView = new DoomLauncher.GenericFileView(); this.tabPageStatistics = new System.Windows.Forms.TabPage(); this.ctrlViewStats = new DoomLauncher.StatisticsView(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.btnCopy = new DoomLauncher.FormButton(); this.btnCopyAll = new DoomLauncher.FormButton(); this.btnDelete = new DoomLauncher.FormButton(); this.btnAddFile = new DoomLauncher.FormButton(); this.btnOpenFile = new DoomLauncher.FormButton(); this.btnEdit = new DoomLauncher.FormButton(); this.btnMoveUp = new DoomLauncher.FormButton(); this.btnMoveDown = new DoomLauncher.FormButton(); this.btnSetFirst = new DoomLauncher.FormButton(); tabPageScreenshots = new System.Windows.Forms.TabPage(); tabPageScreenshots.SuspendLayout(); this.mnuOptions.SuspendLayout(); this.tblMain.SuspendLayout(); this.tabControl.SuspendLayout(); this.tabPageDemos.SuspendLayout(); this.tabPageSaveGames.SuspendLayout(); this.tabPageStatistics.SuspendLayout(); this.flpButtons.SuspendLayout(); this.SuspendLayout(); // // tabPageScreenshots // tabPageScreenshots.Controls.Add(this.ctrlScreenshotView); tabPageScreenshots.Location = new System.Drawing.Point(4, 29); tabPageScreenshots.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); tabPageScreenshots.Name = "tabPageScreenshots"; tabPageScreenshots.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); tabPageScreenshots.Size = new System.Drawing.Size(1028, 320); tabPageScreenshots.TabIndex = 0; tabPageScreenshots.Text = "Screenshots"; tabPageScreenshots.UseVisualStyleBackColor = true; // // ctrlScreenshotView // this.ctrlScreenshotView.DataDirectory = null; this.ctrlScreenshotView.DataSourceAdapter = null; this.ctrlScreenshotView.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlScreenshotView.FileType = DoomLauncher.FileType.Unknown; this.ctrlScreenshotView.GameFile = null; this.ctrlScreenshotView.Location = new System.Drawing.Point(4, 5); this.ctrlScreenshotView.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); this.ctrlScreenshotView.Name = "ctrlScreenshotView"; this.ctrlScreenshotView.Size = new System.Drawing.Size(1020, 310); this.ctrlScreenshotView.TabIndex = 0; // // mnuOptions // this.mnuOptions.ImageScalingSize = new System.Drawing.Size(24, 24); this.mnuOptions.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.copyFileToolStripMenuItem, this.copyAllFilesToolStripMenuItem, this.deleteToolStripMenuItem, this.toolStripSeparator1, this.addFileToolStripMenuItem, this.openFileToolStripMenuItem, this.toolStripSeparator2, this.editDetailsToolStripMenuItem, this.toolStripSeparator3, this.moveUpToolStripMenuItem, this.moveDownToolStripMenuItem, this.setFirstToolStripMenuItem}); this.mnuOptions.Name = "mnuOptions"; this.mnuOptions.Size = new System.Drawing.Size(191, 310); // // copyFileToolStripMenuItem // this.copyFileToolStripMenuItem.Name = "copyFileToolStripMenuItem"; this.copyFileToolStripMenuItem.Size = new System.Drawing.Size(190, 32); this.copyFileToolStripMenuItem.Text = "Copy File"; this.copyFileToolStripMenuItem.Click += new System.EventHandler(this.copyFileToolStripMenuItem_Click); // // copyAllFilesToolStripMenuItem // this.copyAllFilesToolStripMenuItem.Name = "copyAllFilesToolStripMenuItem"; this.copyAllFilesToolStripMenuItem.Size = new System.Drawing.Size(190, 32); this.copyAllFilesToolStripMenuItem.Text = "Copy All Files"; this.copyAllFilesToolStripMenuItem.Click += new System.EventHandler(this.copyAllFilesToolStripMenuItem_Click); // // deleteToolStripMenuItem // this.deleteToolStripMenuItem.Name = "deleteToolStripMenuItem"; this.deleteToolStripMenuItem.Size = new System.Drawing.Size(190, 32); this.deleteToolStripMenuItem.Text = "Delete"; this.deleteToolStripMenuItem.Click += new System.EventHandler(this.deleteToolStripMenuItem_Click); // // toolStripSeparator1 // this.toolStripSeparator1.Name = "toolStripSeparator1"; this.toolStripSeparator1.Size = new System.Drawing.Size(187, 6); // // addFileToolStripMenuItem // this.addFileToolStripMenuItem.Name = "addFileToolStripMenuItem"; this.addFileToolStripMenuItem.Size = new System.Drawing.Size(190, 32); this.addFileToolStripMenuItem.Text = "Add File..."; this.addFileToolStripMenuItem.Click += new System.EventHandler(this.addFileToolStripMenuItem_Click); // // openFileToolStripMenuItem // this.openFileToolStripMenuItem.Name = "openFileToolStripMenuItem"; this.openFileToolStripMenuItem.Size = new System.Drawing.Size(190, 32); this.openFileToolStripMenuItem.Text = "Open File..."; this.openFileToolStripMenuItem.Click += new System.EventHandler(this.openFileToolStripMenuItem_Click); // // toolStripSeparator2 // this.toolStripSeparator2.Name = "toolStripSeparator2"; this.toolStripSeparator2.Size = new System.Drawing.Size(187, 6); // // editDetailsToolStripMenuItem // this.editDetailsToolStripMenuItem.Name = "editDetailsToolStripMenuItem"; this.editDetailsToolStripMenuItem.Size = new System.Drawing.Size(190, 32); this.editDetailsToolStripMenuItem.Text = "Edit Details..."; this.editDetailsToolStripMenuItem.Click += new System.EventHandler(this.editDetailsToolStripMenuItem_Click); // // toolStripSeparator3 // this.toolStripSeparator3.Name = "toolStripSeparator3"; this.toolStripSeparator3.Size = new System.Drawing.Size(187, 6); // // moveUpToolStripMenuItem // this.moveUpToolStripMenuItem.Name = "moveUpToolStripMenuItem"; this.moveUpToolStripMenuItem.Size = new System.Drawing.Size(190, 32); this.moveUpToolStripMenuItem.Text = "Move Up"; this.moveUpToolStripMenuItem.Click += new System.EventHandler(this.moveUpToolStripMenuItem_Click); // // moveDownToolStripMenuItem // this.moveDownToolStripMenuItem.Name = "moveDownToolStripMenuItem"; this.moveDownToolStripMenuItem.Size = new System.Drawing.Size(190, 32); this.moveDownToolStripMenuItem.Text = "Move Down"; this.moveDownToolStripMenuItem.Click += new System.EventHandler(this.moveDownToolStripMenuItem_Click); // // setFirstToolStripMenuItem // this.setFirstToolStripMenuItem.Name = "setFirstToolStripMenuItem"; this.setFirstToolStripMenuItem.Size = new System.Drawing.Size(190, 32); this.setFirstToolStripMenuItem.Text = "Set First"; this.setFirstToolStripMenuItem.Click += new System.EventHandler(this.setFirstToolStripMenuItem_Click); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.tabControl, 0, 1); this.tblMain.Controls.Add(this.flpButtons, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Size = new System.Drawing.Size(1044, 403); this.tblMain.TabIndex = 1; // // tabControl // this.tabControl.Controls.Add(tabPageScreenshots); this.tabControl.Controls.Add(this.tabPageDemos); this.tabControl.Controls.Add(this.tabPageSaveGames); this.tabControl.Controls.Add(this.tabPageStatistics); this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControl.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed; this.tabControl.Location = new System.Drawing.Point(4, 45); this.tabControl.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabControl.Multiline = true; this.tabControl.Name = "tabControl"; this.tabControl.SelectedIndex = 0; this.tabControl.Size = new System.Drawing.Size(1036, 353); this.tabControl.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; this.tabControl.TabIndex = 0; // // tabPageDemos // this.tabPageDemos.Controls.Add(this.ctrlDemoView); this.tabPageDemos.Location = new System.Drawing.Point(4, 29); this.tabPageDemos.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabPageDemos.Name = "tabPageDemos"; this.tabPageDemos.Size = new System.Drawing.Size(1027, 324); this.tabPageDemos.TabIndex = 1; this.tabPageDemos.Text = "Demos"; this.tabPageDemos.UseVisualStyleBackColor = true; // // ctrlDemoView // this.ctrlDemoView.DataDirectory = null; this.ctrlDemoView.DataSourceAdapter = null; this.ctrlDemoView.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlDemoView.FileType = DoomLauncher.FileType.Unknown; this.ctrlDemoView.GameFile = null; this.ctrlDemoView.Location = new System.Drawing.Point(0, 0); this.ctrlDemoView.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); this.ctrlDemoView.Name = "ctrlDemoView"; this.ctrlDemoView.Size = new System.Drawing.Size(1027, 324); this.ctrlDemoView.TabIndex = 0; // // tabPageSaveGames // this.tabPageSaveGames.Controls.Add(this.ctrlSaveGameView); this.tabPageSaveGames.Location = new System.Drawing.Point(4, 29); this.tabPageSaveGames.Margin = new System.Windows.Forms.Padding(0); this.tabPageSaveGames.Name = "tabPageSaveGames"; this.tabPageSaveGames.Size = new System.Drawing.Size(1027, 324); this.tabPageSaveGames.TabIndex = 2; this.tabPageSaveGames.Text = "Save Games"; this.tabPageSaveGames.UseVisualStyleBackColor = true; // // ctrlSaveGameView // this.ctrlSaveGameView.DataDirectory = null; this.ctrlSaveGameView.DataSourceAdapter = null; this.ctrlSaveGameView.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlSaveGameView.FileType = DoomLauncher.FileType.Unknown; this.ctrlSaveGameView.GameFile = null; this.ctrlSaveGameView.Location = new System.Drawing.Point(0, 0); this.ctrlSaveGameView.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); this.ctrlSaveGameView.Name = "ctrlSaveGameView"; this.ctrlSaveGameView.Size = new System.Drawing.Size(1027, 324); this.ctrlSaveGameView.TabIndex = 1; // // tabPageStatistics // this.tabPageStatistics.Controls.Add(this.ctrlViewStats); this.tabPageStatistics.Location = new System.Drawing.Point(4, 29); this.tabPageStatistics.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabPageStatistics.Name = "tabPageStatistics"; this.tabPageStatistics.Size = new System.Drawing.Size(1027, 324); this.tabPageStatistics.TabIndex = 3; this.tabPageStatistics.Text = "Statistics"; this.tabPageStatistics.UseVisualStyleBackColor = true; // // ctrlViewStats // this.ctrlViewStats.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.ctrlViewStats.DataSourceAdapter = null; this.ctrlViewStats.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlViewStats.ForeColor = System.Drawing.Color.White; this.ctrlViewStats.GameFile = null; this.ctrlViewStats.Location = new System.Drawing.Point(0, 0); this.ctrlViewStats.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); this.ctrlViewStats.Name = "ctrlViewStats"; this.ctrlViewStats.Size = new System.Drawing.Size(1027, 324); this.ctrlViewStats.TabIndex = 0; // // flpButtons // this.flpButtons.Controls.Add(this.btnCopy); this.flpButtons.Controls.Add(this.btnCopyAll); this.flpButtons.Controls.Add(this.btnDelete); this.flpButtons.Controls.Add(this.btnAddFile); this.flpButtons.Controls.Add(this.btnOpenFile); this.flpButtons.Controls.Add(this.btnEdit); this.flpButtons.Controls.Add(this.btnMoveUp); this.flpButtons.Controls.Add(this.btnMoveDown); this.flpButtons.Controls.Add(this.btnSetFirst); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.Location = new System.Drawing.Point(0, 0); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(1044, 40); this.flpButtons.TabIndex = 2; // // btnCopy // this.btnCopy.Image = global::DoomLauncher.Properties.Resources.Export; this.btnCopy.Location = new System.Drawing.Point(0, 0); this.btnCopy.Margin = new System.Windows.Forms.Padding(0); this.btnCopy.Name = "btnCopy"; this.btnCopy.Size = new System.Drawing.Size(40, 40); this.btnCopy.TabIndex = 0; this.btnCopy.UseVisualStyleBackColor = true; this.btnCopy.Click += new System.EventHandler(this.btnCopy_Click); // // btnCopyAll // this.btnCopyAll.Image = global::DoomLauncher.Properties.Resources.ExportAll; this.btnCopyAll.Location = new System.Drawing.Point(40, 0); this.btnCopyAll.Margin = new System.Windows.Forms.Padding(0); this.btnCopyAll.Name = "btnCopyAll"; this.btnCopyAll.Size = new System.Drawing.Size(40, 40); this.btnCopyAll.TabIndex = 1; this.btnCopyAll.UseVisualStyleBackColor = true; this.btnCopyAll.Click += new System.EventHandler(this.btnCopyAll_Click); // // btnDelete // this.btnDelete.Image = global::DoomLauncher.Properties.Resources.Delete; this.btnDelete.Location = new System.Drawing.Point(80, 0); this.btnDelete.Margin = new System.Windows.Forms.Padding(0); this.btnDelete.Name = "btnDelete"; this.btnDelete.Size = new System.Drawing.Size(40, 40); this.btnDelete.TabIndex = 2; this.btnDelete.UseVisualStyleBackColor = true; this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); // // btnAddFile // this.btnAddFile.Image = global::DoomLauncher.Properties.Resources.File; this.btnAddFile.Location = new System.Drawing.Point(120, 0); this.btnAddFile.Margin = new System.Windows.Forms.Padding(0); this.btnAddFile.Name = "btnAddFile"; this.btnAddFile.Size = new System.Drawing.Size(40, 40); this.btnAddFile.TabIndex = 3; this.btnAddFile.UseVisualStyleBackColor = true; this.btnAddFile.Click += new System.EventHandler(this.btnAddFile_Click); // // btnOpenFile // this.btnOpenFile.Image = global::DoomLauncher.Properties.Resources.FolderOpen; this.btnOpenFile.Location = new System.Drawing.Point(160, 0); this.btnOpenFile.Margin = new System.Windows.Forms.Padding(0); this.btnOpenFile.Name = "btnOpenFile"; this.btnOpenFile.Size = new System.Drawing.Size(40, 40); this.btnOpenFile.TabIndex = 4; this.btnOpenFile.UseVisualStyleBackColor = true; this.btnOpenFile.Click += new System.EventHandler(this.btnOpenFile_Click); // // btnEdit // this.btnEdit.Image = global::DoomLauncher.Properties.Resources.Edit; this.btnEdit.Location = new System.Drawing.Point(200, 0); this.btnEdit.Margin = new System.Windows.Forms.Padding(0); this.btnEdit.Name = "btnEdit"; this.btnEdit.Size = new System.Drawing.Size(40, 40); this.btnEdit.TabIndex = 5; this.btnEdit.UseVisualStyleBackColor = true; this.btnEdit.Click += new System.EventHandler(this.btnEdit_Click); // // btnMoveUp // this.btnMoveUp.Image = global::DoomLauncher.Properties.Resources.UpArrow; this.btnMoveUp.Location = new System.Drawing.Point(240, 0); this.btnMoveUp.Margin = new System.Windows.Forms.Padding(0); this.btnMoveUp.Name = "btnMoveUp"; this.btnMoveUp.Size = new System.Drawing.Size(40, 40); this.btnMoveUp.TabIndex = 6; this.btnMoveUp.UseVisualStyleBackColor = true; this.btnMoveUp.Click += new System.EventHandler(this.btnMoveUp_Click); // // btnMoveDown // this.btnMoveDown.Image = global::DoomLauncher.Properties.Resources.DownArrow; this.btnMoveDown.Location = new System.Drawing.Point(280, 0); this.btnMoveDown.Margin = new System.Windows.Forms.Padding(0); this.btnMoveDown.Name = "btnMoveDown"; this.btnMoveDown.Size = new System.Drawing.Size(40, 40); this.btnMoveDown.TabIndex = 7; this.btnMoveDown.UseVisualStyleBackColor = true; this.btnMoveDown.Click += new System.EventHandler(this.btnMoveDown_Click); // // btnSetFirst // this.btnSetFirst.Image = global::DoomLauncher.Properties.Resources.StepBack; this.btnSetFirst.Location = new System.Drawing.Point(320, 0); this.btnSetFirst.Margin = new System.Windows.Forms.Padding(0); this.btnSetFirst.Name = "btnSetFirst"; this.btnSetFirst.Size = new System.Drawing.Size(40, 40); this.btnSetFirst.TabIndex = 8; this.btnSetFirst.UseVisualStyleBackColor = true; this.btnSetFirst.Click += new System.EventHandler(this.btnSetFirst_Click); // // GameFileAssociationView // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "GameFileAssociationView"; this.Size = new System.Drawing.Size(1044, 403); tabPageScreenshots.ResumeLayout(false); this.mnuOptions.ResumeLayout(false); this.tblMain.ResumeLayout(false); this.tabControl.ResumeLayout(false); this.tabPageDemos.ResumeLayout(false); this.tabPageSaveGames.ResumeLayout(false); this.tabPageStatistics.ResumeLayout(false); this.flpButtons.ResumeLayout(false); this.ResumeLayout(false); } #endregion private CTabControl tabControl; private ScreenshotView ctrlScreenshotView; private System.Windows.Forms.TabPage tabPageDemos; private GenericFileView ctrlDemoView; private System.Windows.Forms.ContextMenuStrip mnuOptions; private System.Windows.Forms.ToolStripMenuItem copyFileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem deleteToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem copyAllFilesToolStripMenuItem; private System.Windows.Forms.TabPage tabPageSaveGames; private GenericFileView ctrlSaveGameView; private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; private System.Windows.Forms.ToolStripMenuItem addFileToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.ToolStripMenuItem editDetailsToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; private System.Windows.Forms.ToolStripMenuItem moveUpToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem moveDownToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem setFirstToolStripMenuItem; private TableLayoutPanelDB tblMain; private System.Windows.Forms.ToolStripMenuItem openFileToolStripMenuItem; private System.Windows.Forms.TabPage tabPageStatistics; private StatisticsView ctrlViewStats; private System.Windows.Forms.FlowLayoutPanel flpButtons; private FormButton btnCopy; private FormButton btnCopyAll; private FormButton btnDelete; private FormButton btnAddFile; private FormButton btnOpenFile; private FormButton btnEdit; private FormButton btnMoveUp; private FormButton btnMoveDown; private FormButton btnSetFirst; } } ================================================ FILE: DoomLauncher/Controls/GameFileAssociationView.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Windows.Forms; using DoomLauncher.Handlers; namespace DoomLauncher { public partial class GameFileAssociationView : UserControl { public event EventHandler FileAdded; public event EventHandler FileDeleted; public event EventHandler FileOrderChanged; public event EventHandler FileDetailsChanged; public event EventHandler RequestScreenshots; private readonly ToolTipGroup m_toolTipGroup = new ToolTipGroup(); private IGameFile m_gameFile; public GameFileAssociationView() { InitializeComponent(); tabControl.SelectedIndexChanged += tabControl_SelectedIndexChanged; ctrlScreenshotView.FileType = FileType.Screenshot; ctrlSaveGameView.FileType = FileType.SaveGame; ctrlDemoView.FileType = FileType.Demo; ctrlScreenshotView.RequestScreenshots += CtrlScreenshotView_RequestScreenshots; SetButtonsAllButtonsEnabled(false); Icons.DpiScale = new DpiScale(CreateGraphics()); btnDelete.Image = Icons.Delete; btnAddFile.Image = Icons.File; btnCopy.Image = Icons.Export; btnCopyAll.Image = Icons.ExportAll; btnEdit.Image = Icons.Edit; btnMoveUp.Image = Icons.ArrowUp; btnMoveDown.Image = Icons.ArrowDown; btnSetFirst.Image = Icons.StepBack; btnOpenFile.Image = Icons.FolderOpen; m_toolTipGroup.SetToolTip(btnDelete, "Delete"); m_toolTipGroup.SetToolTip(btnAddFile, "Add File"); m_toolTipGroup.SetToolTip(btnCopy, "Export"); m_toolTipGroup.SetToolTip(btnCopyAll, "Export All"); m_toolTipGroup.SetToolTip(btnEdit, "Edit"); m_toolTipGroup.SetToolTip(btnMoveUp, "Move Up"); m_toolTipGroup.SetToolTip(btnMoveDown, "Move Down"); m_toolTipGroup.SetToolTip(btnSetFirst, "Set First"); m_toolTipGroup.SetToolTip(btnOpenFile, "Open File"); Stylizer.StylizeControl(this, DesignMode); Stylizer.StylizeControl(mnuOptions, DesignMode); } private void CtrlScreenshotView_RequestScreenshots(object sender, RequestScreenshotsEventArgs e) { RequestScreenshots?.Invoke(this, e); } public void SetScreenshots(List screenshots) { ctrlScreenshotView.SetScreenshots(screenshots); } public void Initialize(IDataSourceAdapter adapter, AppConfiguration config) { DataSourceAdapter = adapter; ScreenshotDirectory = config.ScreenshotDirectory; SaveGameDirectory = config.SaveGameDirectory; ctrlScreenshotView.DataSourceAdapter = DataSourceAdapter; ctrlScreenshotView.DataDirectory = ScreenshotDirectory; ctrlScreenshotView.FileType = FileType.Screenshot; ctrlScreenshotView.SetContextMenu(BuildContextMenuStrip(ctrlScreenshotView)); ctrlScreenshotView.SetPictureWidth(config.ScreenshotPreviewSize); ctrlSaveGameView.DataSourceAdapter = DataSourceAdapter; ctrlSaveGameView.DataDirectory = SaveGameDirectory; ctrlSaveGameView.FileType = FileType.SaveGame; ctrlSaveGameView.SetContextMenu(BuildContextMenuStrip(ctrlSaveGameView)); ctrlDemoView.DataSourceAdapter = DataSourceAdapter; ctrlDemoView.DataDirectory = config.DemoDirectory; ctrlDemoView.FileType = FileType.Demo; ctrlDemoView.SetContextMenu(BuildContextMenuStrip(ctrlDemoView)); ctrlViewStats.DataSourceAdapter = DataSourceAdapter; ctrlViewStats.SetContextMenu(BuildContextMenuStrip(ctrlViewStats)); SetButtonsEnabled(CurrentView); } private IFileAssociationView[] GetViews() { return new IFileAssociationView[] { ctrlScreenshotView, ctrlDemoView, ctrlSaveGameView, ctrlViewStats }; } public void SetData(IGameFile gameFile) { m_gameFile = gameFile; Array.ForEach(GetViews(), x => SetViewData(x, gameFile)); SetButtonsEnabled(CurrentView); } private void SetViewData(IFileAssociationView view, IGameFile gameFile) { view.GameFile = gameFile; view.SetData(gameFile); } public void ClearData() { m_gameFile = null; Array.ForEach(GetViews(), x => x.ClearData()); SetButtonsAllButtonsEnabled(false); } public IDataSourceAdapter DataSourceAdapter { get; set; } public LauncherPath ScreenshotDirectory { get; set; } public LauncherPath SaveGameDirectory { get; set; } private void tabControl_SelectedIndexChanged(object sender, EventArgs e) { if (CurrentView != null && m_gameFile != null) SetButtonsEnabled(CurrentView); } private void SetButtonsEnabled(IFileAssociationView view) { SetIconButtonEnabled(btnAddFile, IconImage.File, view.NewAllowed); SetIconButtonEnabled(btnCopy, IconImage.Export, view.CopyOrExportAllowed); SetIconButtonEnabled(btnCopyAll, IconImage.ExportAll, view.CopyOrExportAllowed); SetIconButtonEnabled(btnDelete, IconImage.Delete, view.DeleteAllowed); SetIconButtonEnabled(btnEdit, IconImage.Edit, view.EditAllowed); SetIconButtonEnabled(btnMoveDown, IconImage.DownArrow, view.EditAllowed); SetIconButtonEnabled(btnMoveUp, IconImage.UpArrow, view.EditAllowed); SetIconButtonEnabled(btnSetFirst, IconImage.StepBack, view.EditAllowed); SetIconButtonEnabled(btnOpenFile, IconImage.FolderOpen, view.ViewAllowed); } public void SetButtonsAllButtonsEnabled(bool enabled) { SetIconButtonEnabled(btnAddFile, IconImage.File, enabled); SetIconButtonEnabled(btnCopy, IconImage.Export, enabled); SetIconButtonEnabled(btnCopyAll, IconImage.ExportAll, enabled); SetIconButtonEnabled(btnDelete, IconImage.Delete, enabled); SetIconButtonEnabled(btnEdit, IconImage.Edit, enabled); SetIconButtonEnabled(btnMoveDown, IconImage.DownArrow, enabled); SetIconButtonEnabled(btnMoveUp, IconImage.UpArrow, enabled); SetIconButtonEnabled(btnSetFirst, IconImage.StepBack, enabled); SetIconButtonEnabled(btnOpenFile, IconImage.FolderOpen, enabled); } private void SetIconButtonEnabled(FormButton button, IconImage iconImage, bool enabled) { button.Enabled = enabled; button.Image = Icons.GetIcon(iconImage, enabled ? ColorTheme.Current.Text : ColorTheme.Current.DisabledText); } private IFileAssociationView CurrentView => tabControl.SelectedTab.Controls[0] as IFileAssociationView; private void copyFileToolStripMenuItem_Click(object sender, EventArgs e) { HandleCopy(); } private void HandleCopy() { if (CurrentView != null && CurrentView.CopyOrExportAllowed) CurrentView.CopyToClipboard(); } private void copyAllFilesToolStripMenuItem_Click(object sender, EventArgs e) { HandleCopyAll(); } private void HandleCopyAll() { if (CurrentView != null && CurrentView.CopyOrExportAllowed) CurrentView.CopyAllToClipboard(); } private void deleteToolStripMenuItem_Click(object sender, EventArgs e) { HandleDelete(); } private void HandleDelete() { if (CurrentView != null && CurrentView.DeleteAllowed && CurrentView.Delete()) FileDeleted?.Invoke(this, EventArgs.Empty); } private void addFileToolStripMenuItem_Click(object sender, EventArgs e) { HandleAdd(); } private void HandleAdd() { if (CurrentView != null && CurrentView.NewAllowed && CurrentView.New()) { FileAdded?.Invoke(this, EventArgs.Empty); SetData(m_gameFile); } } private void exportToolStripMenuItem_Click(object sender, EventArgs e) { HandleExport(); } private void exportAllToolStripMenuItem_Click(object sender, EventArgs e) { HandleExportAll(); } private void HandleExport() { if (CurrentView != null && CurrentView.CopyOrExportAllowed) CurrentView.Export(); } private void HandleExportAll() { if (CurrentView != null && CurrentView.CopyOrExportAllowed) CurrentView.ExportAll(); } private void editDetailsToolStripMenuItem_Click(object sender, EventArgs e) { HandleEdit(); } private void HandleEdit() { if (CurrentView != null && CurrentView.EditAllowed && CurrentView.Edit()) { SetData(m_gameFile); FileDetailsChanged?.Invoke(this, EventArgs.Empty); } } private void moveUpToolStripMenuItem_Click(object sender, EventArgs e) { SetFilePriority(true); } private void moveDownToolStripMenuItem_Click(object sender, EventArgs e) { SetFilePriority(false); } private void setFirstToolStripMenuItem_Click(object sender, EventArgs e) { HandleSetFirst(); } private void HandleSetFirst() { if (CurrentView != null && CurrentView.ChangeOrderAllowed && CurrentView.SetFileOrderFirst()) { SetData(m_gameFile); FileOrderChanged?.Invoke(this, EventArgs.Empty); } } private void SetFilePriority(bool up) { bool success = false; if (CurrentView != null && CurrentView.ChangeOrderAllowed) { if (up) success = CurrentView.MoveFileOrderUp(); else success = CurrentView.MoveFileOrderDown(); } if (success) { SetData(m_gameFile); FileOrderChanged?.Invoke(this, EventArgs.Empty); } } private void btnCopy_Click(object sender, EventArgs e) { HandleExport(); } private void btnCopyAll_Click(object sender, EventArgs e) { HandleExportAll(); } private void btnDelete_Click(object sender, EventArgs e) { HandleDelete(); } private void btnAddFile_Click(object sender, EventArgs e) { HandleAdd(); } private void btnEdit_Click(object sender, EventArgs e) { HandleEdit(); } private void btnMoveUp_Click(object sender, EventArgs e) { SetFilePriority(true); } private void btnMoveDown_Click(object sender, EventArgs e) { SetFilePriority(false); } private void btnSetFirst_Click(object sender, EventArgs e) { HandleSetFirst(); } private void btnOpenFile_Click(object sender, EventArgs e) { HandleView(); } private void HandleView() { if (CurrentView != null && CurrentView.ViewAllowed) { CurrentView.View(); } } private void openFileToolStripMenuItem_Click(object sender, EventArgs e) { HandleView(); } private ContextMenuStrip BuildContextMenuStrip(IFileAssociationView view) { ContextMenuStrip menu = new ContextMenuStrip(); if (view.CopyOrExportAllowed) { CreateMenuItem(menu, "Export...", exportToolStripMenuItem_Click); CreateMenuItem(menu, "Export All...", exportAllToolStripMenuItem_Click); AddSeperator(menu); CreateMenuItem(menu, "Copy", copyFileToolStripMenuItem_Click); CreateMenuItem(menu, "Copy All", copyAllFilesToolStripMenuItem_Click); } if (view.DeleteAllowed) CreateMenuItem(menu, "Delete", deleteToolStripMenuItem_Click); AddSeperator(menu); if (view.NewAllowed) CreateMenuItem(menu, "Add File...", addFileToolStripMenuItem_Click); if (view.ViewAllowed) CreateMenuItem(menu, "Open File...", openFileToolStripMenuItem_Click); AddSeperator(menu); if (view.EditAllowed) CreateMenuItem(menu, "Edit Details...", editDetailsToolStripMenuItem_Click); AddSeperator(menu); if (view.ChangeOrderAllowed) { CreateMenuItem(menu, "Move Up", moveUpToolStripMenuItem_Click); CreateMenuItem(menu, "Move Down", moveDownToolStripMenuItem_Click); CreateMenuItem(menu, "Set First", setFirstToolStripMenuItem_Click); } var customMenuOptions = view.MenuOptions; if (customMenuOptions.Count > 0) { AddSeperator(menu); foreach (var option in customMenuOptions) { CreateMenuItem(menu, option.Title, (sender, eventArgs) => { if (!option.Action()) return; // Update data and assume the file order changed SetData(m_gameFile); FileOrderChanged?.Invoke(this, EventArgs.Empty); }); } } FinalizeMenu(menu); return menu; } private void FinalizeMenu(System.Windows.Forms.ContextMenuStrip menu) { if (menu.Items.Count > 0 && menu.Items[menu.Items.Count - 1] is ToolStripSeparator) menu.Items.Remove(menu.Items[menu.Items.Count - 1]); } private static void AddSeperator(ContextMenuStrip menu) { if (menu.Items.Count > 0 && !(menu.Items[menu.Items.Count - 1] is ToolStripSeparator)) menu.Items.Add(new ToolStripSeparator()); } private static void CreateMenuItem(ContextMenuStrip menu, string text, EventHandler handler) { ToolStripMenuItem item = new ToolStripMenuItem(text); item.Click += handler; menu.Items.Add(item); } } } ================================================ FILE: DoomLauncher/Controls/GameFileAssociationView.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 False 17, 17 ================================================ FILE: DoomLauncher/Controls/GameFileEdit.Designer.cs ================================================ namespace DoomLauncher { partial class GameFileEdit { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.label9 = new System.Windows.Forms.Label(); this.lblTags = new System.Windows.Forms.LinkLabel(); this.txtAuthor = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.txtTitle = new System.Windows.Forms.TextBox(); this.dtRelease = new System.Windows.Forms.DateTimePicker(); this.ctrlStarRating = new DoomLauncher.RatingControl(); this.label7 = new System.Windows.Forms.Label(); this.chkTitle = new DoomLauncher.CCheckBox(); this.chkAuthor = new DoomLauncher.CCheckBox(); this.chkReleaseDate = new DoomLauncher.CCheckBox(); this.chkRating = new DoomLauncher.CCheckBox(); this.chkDescription = new DoomLauncher.CCheckBox(); this.chkComments = new DoomLauncher.CCheckBox(); this.txtDescription = new DoomLauncher.Controls.CRichTextBox(); this.txtComments = new DoomLauncher.Controls.CRichTextBox(); this.chkTags = new DoomLauncher.CCheckBox(); this.label8 = new System.Windows.Forms.Label(); this.lblFile = new DoomLauncher.GrowLabel(); this.chkMaps = new DoomLauncher.CCheckBox(); this.lnkMapsEdit = new System.Windows.Forms.LinkLabel(); this.flpRating = new System.Windows.Forms.FlowLayoutPanel(); this.lnkClearRating = new System.Windows.Forms.LinkLabel(); this.tblMain.SuspendLayout(); this.flpRating.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 3; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.label9, 0, 8); this.tblMain.Controls.Add(this.lblTags, 2, 5); this.tblMain.Controls.Add(this.txtAuthor, 2, 2); this.tblMain.Controls.Add(this.label1, 0, 1); this.tblMain.Controls.Add(this.label2, 0, 2); this.tblMain.Controls.Add(this.label3, 0, 3); this.tblMain.Controls.Add(this.label4, 0, 4); this.tblMain.Controls.Add(this.label5, 0, 6); this.tblMain.Controls.Add(this.label6, 0, 7); this.tblMain.Controls.Add(this.txtTitle, 2, 1); this.tblMain.Controls.Add(this.dtRelease, 2, 3); this.tblMain.Controls.Add(this.label7, 0, 5); this.tblMain.Controls.Add(this.chkTitle, 1, 1); this.tblMain.Controls.Add(this.chkAuthor, 1, 2); this.tblMain.Controls.Add(this.chkReleaseDate, 1, 3); this.tblMain.Controls.Add(this.chkRating, 1, 4); this.tblMain.Controls.Add(this.chkDescription, 1, 6); this.tblMain.Controls.Add(this.chkComments, 1, 7); this.tblMain.Controls.Add(this.txtDescription, 2, 6); this.tblMain.Controls.Add(this.txtComments, 2, 7); this.tblMain.Controls.Add(this.chkTags, 1, 5); this.tblMain.Controls.Add(this.label8, 0, 0); this.tblMain.Controls.Add(this.lblFile, 2, 0); this.tblMain.Controls.Add(this.chkMaps, 1, 8); this.tblMain.Controls.Add(this.lnkMapsEdit, 2, 8); this.tblMain.Controls.Add(this.flpRating, 2, 4); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 10; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 36F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 160F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 160F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F)); this.tblMain.Size = new System.Drawing.Size(349, 553); this.tblMain.TabIndex = 0; // // label9 // this.label9.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label9.AutoSize = true; this.label9.Location = new System.Drawing.Point(3, 497); this.label9.Name = "label9"; this.label9.Size = new System.Drawing.Size(33, 13); this.label9.TabIndex = 25; this.label9.Text = "Maps"; // // lblTags // this.lblTags.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblTags.AutoSize = true; this.lblTags.Location = new System.Drawing.Point(103, 153); this.lblTags.Name = "lblTags"; this.lblTags.Size = new System.Drawing.Size(31, 13); this.lblTags.TabIndex = 13; this.lblTags.TabStop = true; this.lblTags.Text = "Tags"; this.lblTags.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lblTags_LinkClicked); // // txtAuthor // this.txtAuthor.Dock = System.Windows.Forms.DockStyle.Fill; this.txtAuthor.Location = new System.Drawing.Point(103, 67); this.txtAuthor.Name = "txtAuthor"; this.txtAuthor.Size = new System.Drawing.Size(243, 20); this.txtAuthor.TabIndex = 7; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(3, 45); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(27, 13); this.label1.TabIndex = 0; this.label1.Text = "Title"; // // label2 // this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(3, 69); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(38, 13); this.label2.TabIndex = 1; this.label2.Text = "Author"; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(3, 93); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(72, 13); this.label3.TabIndex = 2; this.label3.Text = "Release Date"; // // label4 // this.label4.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(3, 123); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(38, 13); this.label4.TabIndex = 3; this.label4.Text = "Rating"; // // label5 // this.label5.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label5.AutoSize = true; this.label5.Location = new System.Drawing.Point(3, 245); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(60, 13); this.label5.TabIndex = 4; this.label5.Text = "Description"; // // label6 // this.label6.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label6.AutoSize = true; this.label6.Location = new System.Drawing.Point(3, 405); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(56, 13); this.label6.TabIndex = 5; this.label6.Text = "Comments"; // // txtTitle // this.txtTitle.Dock = System.Windows.Forms.DockStyle.Fill; this.txtTitle.Location = new System.Drawing.Point(103, 43); this.txtTitle.Name = "txtTitle"; this.txtTitle.Size = new System.Drawing.Size(243, 20); this.txtTitle.TabIndex = 6; // // dtRelease // this.dtRelease.Format = System.Windows.Forms.DateTimePickerFormat.Custom; this.dtRelease.Location = new System.Drawing.Point(103, 91); this.dtRelease.Name = "dtRelease"; this.dtRelease.ShowCheckBox = true; this.dtRelease.Size = new System.Drawing.Size(200, 20); this.dtRelease.TabIndex = 8; // // label7 // this.label7.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label7.AutoSize = true; this.label7.Location = new System.Drawing.Point(3, 153); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(31, 13); this.label7.TabIndex = 12; this.label7.Text = "Tags"; // // chkTitle // this.chkTitle.Anchor = System.Windows.Forms.AnchorStyles.Top; this.chkTitle.AutoSize = true; this.chkTitle.Checked = true; this.chkTitle.CheckState = System.Windows.Forms.CheckState.Checked; this.chkTitle.Location = new System.Drawing.Point(83, 46); this.chkTitle.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.chkTitle.Name = "chkTitle"; this.chkTitle.Size = new System.Drawing.Size(14, 15); this.chkTitle.TabIndex = 14; this.chkTitle.Text = "checkBox1"; this.chkTitle.UseVisualStyleBackColor = true; // // chkAuthor // this.chkAuthor.Anchor = System.Windows.Forms.AnchorStyles.Top; this.chkAuthor.AutoSize = true; this.chkAuthor.Checked = true; this.chkAuthor.CheckState = System.Windows.Forms.CheckState.Checked; this.chkAuthor.Location = new System.Drawing.Point(83, 70); this.chkAuthor.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.chkAuthor.Name = "chkAuthor"; this.chkAuthor.Size = new System.Drawing.Size(14, 15); this.chkAuthor.TabIndex = 15; this.chkAuthor.Text = "checkBox2"; this.chkAuthor.UseVisualStyleBackColor = true; // // chkReleaseDate // this.chkReleaseDate.Anchor = System.Windows.Forms.AnchorStyles.Top; this.chkReleaseDate.AutoSize = true; this.chkReleaseDate.Checked = true; this.chkReleaseDate.CheckState = System.Windows.Forms.CheckState.Checked; this.chkReleaseDate.Location = new System.Drawing.Point(83, 94); this.chkReleaseDate.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.chkReleaseDate.Name = "chkReleaseDate"; this.chkReleaseDate.Size = new System.Drawing.Size(14, 15); this.chkReleaseDate.TabIndex = 16; this.chkReleaseDate.Text = "checkBox3"; this.chkReleaseDate.UseVisualStyleBackColor = true; // // chkRating // this.chkRating.Anchor = System.Windows.Forms.AnchorStyles.Top; this.chkRating.AutoSize = true; this.chkRating.Checked = true; this.chkRating.CheckState = System.Windows.Forms.CheckState.Checked; this.chkRating.Location = new System.Drawing.Point(83, 118); this.chkRating.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.chkRating.Name = "chkRating"; this.chkRating.Size = new System.Drawing.Size(14, 17); this.chkRating.TabIndex = 17; this.chkRating.Text = "checkBox4"; this.chkRating.UseVisualStyleBackColor = true; // // chkDescription // this.chkDescription.Anchor = System.Windows.Forms.AnchorStyles.Top; this.chkDescription.AutoSize = true; this.chkDescription.Checked = true; this.chkDescription.CheckState = System.Windows.Forms.CheckState.Checked; this.chkDescription.Location = new System.Drawing.Point(83, 178); this.chkDescription.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.chkDescription.Name = "chkDescription"; this.chkDescription.Size = new System.Drawing.Size(14, 17); this.chkDescription.TabIndex = 18; this.chkDescription.Text = "checkBox5"; this.chkDescription.UseVisualStyleBackColor = true; // // chkComments // this.chkComments.Anchor = System.Windows.Forms.AnchorStyles.Top; this.chkComments.AutoSize = true; this.chkComments.Checked = true; this.chkComments.CheckState = System.Windows.Forms.CheckState.Checked; this.chkComments.Location = new System.Drawing.Point(83, 338); this.chkComments.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.chkComments.Name = "chkComments"; this.chkComments.Size = new System.Drawing.Size(14, 17); this.chkComments.TabIndex = 19; this.chkComments.Text = "chkComments"; this.chkComments.UseVisualStyleBackColor = true; // // txtDescription // this.txtDescription.Dock = System.Windows.Forms.DockStyle.Fill; this.txtDescription.Location = new System.Drawing.Point(103, 175); this.txtDescription.Name = "txtDescription"; this.txtDescription.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; this.txtDescription.Size = new System.Drawing.Size(243, 154); this.txtDescription.TabIndex = 20; this.txtDescription.Text = ""; this.txtDescription.WarnLinkClick = true; // // txtComments // this.txtComments.Dock = System.Windows.Forms.DockStyle.Fill; this.txtComments.Location = new System.Drawing.Point(103, 335); this.txtComments.Name = "txtComments"; this.txtComments.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; this.txtComments.Size = new System.Drawing.Size(243, 154); this.txtComments.TabIndex = 21; this.txtComments.Text = ""; this.txtComments.WarnLinkClick = true; // // chkTags // this.chkTags.AutoSize = true; this.chkTags.Checked = true; this.chkTags.CheckState = System.Windows.Forms.CheckState.Checked; this.chkTags.Location = new System.Drawing.Point(83, 151); this.chkTags.Name = "chkTags"; this.chkTags.Size = new System.Drawing.Size(14, 14); this.chkTags.TabIndex = 22; this.chkTags.UseVisualStyleBackColor = true; // // label8 // this.label8.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label8.AutoSize = true; this.label8.Location = new System.Drawing.Point(3, 13); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(23, 13); this.label8.TabIndex = 23; this.label8.Text = "File"; // // lblFile // this.lblFile.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblFile.AutoSize = true; this.lblFile.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); this.lblFile.IsPath = false; this.lblFile.Location = new System.Drawing.Point(103, 13); this.lblFile.Name = "lblFile"; this.lblFile.Size = new System.Drawing.Size(23, 13); this.lblFile.TabIndex = 24; this.lblFile.Text = "File"; // // chkMaps // this.chkMaps.Anchor = System.Windows.Forms.AnchorStyles.Top; this.chkMaps.AutoSize = true; this.chkMaps.Checked = true; this.chkMaps.CheckState = System.Windows.Forms.CheckState.Checked; this.chkMaps.Location = new System.Drawing.Point(83, 498); this.chkMaps.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.chkMaps.Name = "chkMaps"; this.chkMaps.Size = new System.Drawing.Size(14, 14); this.chkMaps.TabIndex = 27; this.chkMaps.UseVisualStyleBackColor = true; // // lnkMapsEdit // this.lnkMapsEdit.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lnkMapsEdit.AutoSize = true; this.lnkMapsEdit.Location = new System.Drawing.Point(103, 497); this.lnkMapsEdit.Name = "lnkMapsEdit"; this.lnkMapsEdit.Size = new System.Drawing.Size(34, 13); this.lnkMapsEdit.TabIndex = 26; this.lnkMapsEdit.TabStop = true; this.lnkMapsEdit.Text = "Edit..."; this.lnkMapsEdit.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkMapsEdit_LinkClicked); // // flpRating // this.flpRating.Controls.Add(this.ctrlStarRating); this.flpRating.Controls.Add(this.lnkClearRating); this.flpRating.Dock = System.Windows.Forms.DockStyle.Fill; this.flpRating.Location = new System.Drawing.Point(150, 173); this.flpRating.Margin = new System.Windows.Forms.Padding(0); this.flpRating.Name = "flpRating"; this.flpRating.Size = new System.Drawing.Size(374, 55); this.flpRating.TabIndex = 28; // // ctrlStarRating // this.ctrlStarRating.Anchor = System.Windows.Forms.AnchorStyles.Left; this.ctrlStarRating.Location = new System.Drawing.Point(0, 13); this.ctrlStarRating.Name = "ctrlStarRating"; this.ctrlStarRating.SelectedRating = 0; this.ctrlStarRating.Size = new System.Drawing.Size(105, 20); this.ctrlStarRating.TabIndex = 11; // // lnkClearRating // this.lnkClearRating.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lnkClearRating.AutoSize = true; this.lnkClearRating.Location = new System.Drawing.Point(110, 16); this.lnkClearRating.Name = "lnkClearRating"; this.lnkClearRating.Size = new System.Drawing.Size(46, 20); this.lnkClearRating.TabIndex = 12; this.lnkClearRating.TabStop = true; this.lnkClearRating.Text = "Clear"; this.lnkClearRating.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkClearRating_LinkClicked); // // GameFileEdit // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Name = "GameFileEdit"; this.Size = new System.Drawing.Size(349, 553); this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label label5; private System.Windows.Forms.TextBox txtAuthor; private System.Windows.Forms.Label label6; private System.Windows.Forms.TextBox txtTitle; private System.Windows.Forms.DateTimePicker dtRelease; private RatingControl ctrlStarRating; private System.Windows.Forms.LinkLabel lblTags; private System.Windows.Forms.Label label7; private System.Windows.Forms.CheckBox chkTitle; private System.Windows.Forms.CheckBox chkAuthor; private System.Windows.Forms.CheckBox chkReleaseDate; private System.Windows.Forms.CheckBox chkRating; private System.Windows.Forms.CheckBox chkDescription; private System.Windows.Forms.CheckBox chkComments; private Controls.CRichTextBox txtDescription; private Controls.CRichTextBox txtComments; private System.Windows.Forms.CheckBox chkTags; private System.Windows.Forms.Label label8; private GrowLabel lblFile; private System.Windows.Forms.Label label9; private System.Windows.Forms.LinkLabel lnkMapsEdit; private System.Windows.Forms.CheckBox chkMaps; private System.Windows.Forms.FlowLayoutPanel flpRating; private System.Windows.Forms.LinkLabel lnkClearRating; } } ================================================ FILE: DoomLauncher/Controls/GameFileEdit.cs ================================================ using DoomLauncher.Controls; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class GameFileEdit : UserControl { public IGameFile DataSource { get; private set; } public ITagData[] TagData { get; private set; } public bool TagsChanged { get; private set; } private bool m_showCheckBoxes; private bool m_mapsEdited; private string m_maps; public GameFileEdit() { InitializeComponent(); chkTags.Visible = false; txtComments.WarnLinkClick = false; lblFile.IsPath = true; Stylizer.StylizeControl(this, DesignMode); } public void SetShowCheckBoxes(bool set) { chkAuthor.Visible = chkDescription.Visible = chkRating.Visible = chkReleaseDate.Visible = chkTitle.Visible = chkComments.Visible = chkMaps.Visible = set; m_showCheckBoxes = set; } public void SetShowTagCheckBox(bool set) { chkTags.Visible = set; } public void SetShowMaps(bool set) { lnkMapsEdit.Visible = set; chkMaps.Visible = set; } public void SetCheckBoxesChecked(bool set) { chkAuthor.Checked = chkDescription.Checked = chkRating.Checked = chkReleaseDate.Checked = chkTitle.Checked = chkComments.Checked = chkMaps.Checked = set; } public bool AuthorChecked { get { return chkAuthor.Checked; } set { chkAuthor.Checked = value; } } public bool DescriptionChecked { get { return chkDescription.Checked; } set { chkDescription.Checked = value; } } public bool RatingChecked { get { return chkRating.Checked; } set { chkRating.Checked = value; } } public bool ReleaseDateChecked { get { return chkReleaseDate.Checked; } set { chkReleaseDate.Checked = value; } } public bool TitleChecked { get { return chkTitle.Checked; } set { chkTitle.Checked = value; } } public bool CommentsChecked { get { return chkComments.Checked; } set { chkComments.Checked = value; } } public bool TagsChecked { get { return chkTags.Checked; } set { chkTags.Checked = value; } } public bool MapsChecked { get { return chkMaps.Checked; } set { chkMaps.Checked = value; } } public void SetDataSource(IGameFile gameFile, IEnumerable tags) { DataSource = gameFile; TagData = tags.ToArray(); lblFile.Text = gameFile.FileName; if (!string.IsNullOrEmpty(gameFile.Title)) txtTitle.Text = gameFile.Title; else txtTitle.Text = string.Empty; if (!string.IsNullOrEmpty(gameFile.Author)) txtAuthor.Text = gameFile.Author; else txtAuthor.Text = string.Empty; if (!string.IsNullOrEmpty(gameFile.Description)) txtDescription.Text = Util.CleanDescription(gameFile.Description); else txtDescription.Text = string.Empty; if (!string.IsNullOrEmpty(gameFile.Comments)) txtComments.Text = gameFile.Comments; else txtComments.Text = string.Empty; if (gameFile.ReleaseDate.HasValue) dtRelease.Value = gameFile.ReleaseDate.Value; else dtRelease.Value = DateTime.Now; if (gameFile.Rating.HasValue) ctrlStarRating.SelectedRating = Convert.ToInt32(gameFile.Rating.Value); else ctrlStarRating.SelectedRating = 0; dtRelease.Checked = gameFile.ReleaseDate.HasValue; SetTagsLabel(); m_maps = DataSource.Map; } private void SetTagsLabel() { if (TagData.Length > 0) lblTags.Text = string.Join(", ", TagData.Select(x => x.Name).ToArray()); else lblTags.Text = "Select..."; } public List UpdateDataSource(IGameFile gameFile) { List fields = new List(); if (AssertSet(chkTitle, fields, GameFileFieldType.Title)) gameFile.Title = txtTitle.Text; if (AssertSet(chkAuthor, fields, GameFileFieldType.Author)) gameFile.Author = txtAuthor.Text; if (AssertSet(chkDescription, fields, GameFileFieldType.Description)) gameFile.Description = txtDescription.Text; if (AssertSet(chkComments, fields, GameFileFieldType.Comments)) gameFile.Comments = txtComments.Text; if (AssertSet(chkReleaseDate, fields, GameFileFieldType.ReleaseDate)) { if (dtRelease.Checked) gameFile.ReleaseDate = new DateTime(dtRelease.Value.Year, dtRelease.Value.Month, dtRelease.Value.Day); else gameFile.ReleaseDate = null; } if (AssertSet(chkRating, fields, GameFileFieldType.Rating)) { if (ctrlStarRating.SelectedRating == 0) gameFile.Rating = null; else gameFile.Rating = ctrlStarRating.SelectedRating; } if (m_mapsEdited && AssertSet(chkMaps, fields, GameFileFieldType.Map)) { fields.Add(GameFileFieldType.MapCount); gameFile.Map = m_maps; gameFile.MapCount = DataSources.GameFile.GetMaps(gameFile).Length; } return fields; } private bool AssertSet(CheckBox chk, List fields, GameFileFieldType field) { if ((m_showCheckBoxes && chk.Checked) || !m_showCheckBoxes) { fields.Add(field); return true; } return false; } private void lnkMapsEdit_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { TextBoxForm textBoxForm = new TextBoxForm(true, MessageBoxButtons.OKCancel) { Title = "Maps", HeaderText = "Enter map names, separated by commas.", DisplayText = m_maps, StartPosition = FormStartPosition.CenterScreen }; if (textBoxForm.ShowDialog(this) == DialogResult.OK) { m_mapsEdited = true; m_maps = textBoxForm.DisplayText; } } private void lblTags_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { TagSelectForm form = new TagSelectForm(); form.StartPosition = FormStartPosition.CenterParent; form.TagSelectControl.Init(new TagSelectOptions() { ShowCheckBoxes = true }); form.TagSelectControl.SetCheckedTags(TagData); if (form.ShowDialog() == DialogResult.OK) { TagsChanged = true; TagData = form.TagSelectControl.GetCheckedTags().ToArray(); SetTagsLabel(); } } private void lnkClearRating_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { ctrlStarRating.SelectedRating = 0; } } } ================================================ FILE: DoomLauncher/Controls/GameFileEdit.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/GameFileSummary.Designer.cs ================================================ namespace DoomLauncher { partial class GameFileSummary { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.tblTags = new DoomLauncher.TableLayoutPanelDB(); this.lblTagsText = new DoomLauncher.GrowLabel(); this.lblTags = new System.Windows.Forms.Label(); this.tblLastMap = new DoomLauncher.TableLayoutPanelDB(); this.lblLastMap = new System.Windows.Forms.Label(); this.lblLastMapText = new System.Windows.Forms.Label(); this.lblTitle = new DoomLauncher.GrowLabel(); this.ctrlStats = new DoomLauncher.StatsControl(); this.txtDescription = new DoomLauncher.Controls.CRichTextBox(); this.txtComments = new DoomLauncher.Controls.CRichTextBox(); this.tblTimePlayed = new DoomLauncher.TableLayoutPanelDB(); this.lblTimePlayed = new System.Windows.Forms.Label(); this.lblTimePlayedText = new System.Windows.Forms.Label(); this.tblMain.SuspendLayout(); this.tblTags.SuspendLayout(); this.tblLastMap.SuspendLayout(); this.tblTimePlayed.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.Single; this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.tblTags, 0, 4); this.tblMain.Controls.Add(this.tblLastMap, 0, 3); this.tblMain.Controls.Add(this.lblTitle, 0, 0); this.tblMain.Controls.Add(this.ctrlStats, 0, 5); this.tblMain.Controls.Add(this.txtDescription, 0, 6); this.tblMain.Controls.Add(this.txtComments, 0, 7); this.tblMain.Controls.Add(this.tblTimePlayed, 0, 2); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 8; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 62F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 308F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 31F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 31F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 31F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 120F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 80F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 31F)); this.tblMain.Size = new System.Drawing.Size(306, 1006); this.tblMain.TabIndex = 0; // // tblTags // this.tblTags.ColumnCount = 3; this.tblTags.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 12F)); this.tblTags.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 135F)); this.tblTags.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblTags.Controls.Add(this.lblTagsText, 1, 0); this.tblTags.Controls.Add(this.lblTags, 2, 0); this.tblTags.Dock = System.Windows.Forms.DockStyle.Fill; this.tblTags.Location = new System.Drawing.Point(1, 437); this.tblTags.Margin = new System.Windows.Forms.Padding(0); this.tblTags.Name = "tblTags"; this.tblTags.RowCount = 1; this.tblTags.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblTags.Size = new System.Drawing.Size(304, 31); this.tblTags.TabIndex = 15; // // lblTagsText // this.lblTagsText.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblTagsText.AutoSize = true; this.lblTagsText.IsPath = false; this.lblTagsText.Location = new System.Drawing.Point(16, 5); this.lblTagsText.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblTagsText.Name = "lblTagsText"; this.lblTagsText.Size = new System.Drawing.Size(48, 20); this.lblTagsText.TabIndex = 5; this.lblTagsText.Text = "Tags:"; // // lblTags // this.lblTags.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblTags.AutoSize = true; this.lblTags.Location = new System.Drawing.Point(151, 5); this.lblTags.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblTags.Name = "lblTags"; this.lblTags.Size = new System.Drawing.Size(40, 20); this.lblTags.TabIndex = 13; this.lblTags.Text = "tags"; // // tblLastMap // this.tblLastMap.ColumnCount = 3; this.tblLastMap.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 12F)); this.tblLastMap.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 135F)); this.tblLastMap.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblLastMap.Controls.Add(this.lblLastMap, 2, 0); this.tblLastMap.Controls.Add(this.lblLastMapText, 1, 0); this.tblLastMap.Dock = System.Windows.Forms.DockStyle.Fill; this.tblLastMap.Location = new System.Drawing.Point(1, 405); this.tblLastMap.Margin = new System.Windows.Forms.Padding(0); this.tblLastMap.Name = "tblLastMap"; this.tblLastMap.RowCount = 1; this.tblLastMap.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblLastMap.Size = new System.Drawing.Size(304, 31); this.tblLastMap.TabIndex = 14; // // lblLastMap // this.lblLastMap.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblLastMap.AutoSize = true; this.lblLastMap.Location = new System.Drawing.Point(151, 5); this.lblLastMap.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblLastMap.Name = "lblLastMap"; this.lblLastMap.Size = new System.Drawing.Size(69, 20); this.lblLastMap.TabIndex = 12; this.lblLastMap.Text = "last map"; // // lblLastMapText // this.lblLastMapText.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblLastMapText.AutoSize = true; this.lblLastMapText.Location = new System.Drawing.Point(16, 5); this.lblLastMapText.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblLastMapText.Name = "lblLastMapText"; this.lblLastMapText.Size = new System.Drawing.Size(79, 20); this.lblLastMapText.TabIndex = 11; this.lblLastMapText.Text = "Last Map:"; // // lblTitle // this.lblTitle.Anchor = System.Windows.Forms.AnchorStyles.None; this.lblTitle.AutoSize = true; this.lblTitle.Font = new System.Drawing.Font("Arial", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lblTitle.IsPath = false; this.lblTitle.Location = new System.Drawing.Point(121, 17); this.lblTitle.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblTitle.Name = "lblTitle"; this.lblTitle.Size = new System.Drawing.Size(63, 29); this.lblTitle.TabIndex = 4; this.lblTitle.Text = "Title"; this.lblTitle.UseMnemonic = false; // // ctrlStats // this.ctrlStats.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlStats.Location = new System.Drawing.Point(1, 469); this.ctrlStats.Margin = new System.Windows.Forms.Padding(0); this.ctrlStats.Name = "ctrlStats"; this.ctrlStats.Size = new System.Drawing.Size(304, 120); this.ctrlStats.TabIndex = 8; // // txtDescription // this.txtDescription.Dock = System.Windows.Forms.DockStyle.Fill; this.txtDescription.Location = new System.Drawing.Point(5, 595); this.txtDescription.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtDescription.Name = "txtDescription"; this.txtDescription.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; this.txtDescription.Size = new System.Drawing.Size(296, 321); this.txtDescription.TabIndex = 9; this.txtDescription.Text = ""; this.txtDescription.WarnLinkClick = true; // // txtComments // this.txtComments.Dock = System.Windows.Forms.DockStyle.Fill; this.txtComments.Location = new System.Drawing.Point(5, 927); this.txtComments.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtComments.Name = "txtComments"; this.txtComments.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; this.txtComments.Size = new System.Drawing.Size(296, 73); this.txtComments.TabIndex = 10; this.txtComments.Text = ""; this.txtComments.WarnLinkClick = true; // // tblTimePlayed // this.tblTimePlayed.ColumnCount = 3; this.tblTimePlayed.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 12F)); this.tblTimePlayed.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 135F)); this.tblTimePlayed.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblTimePlayed.Controls.Add(this.lblTimePlayed, 2, 0); this.tblTimePlayed.Controls.Add(this.lblTimePlayedText, 1, 0); this.tblTimePlayed.Dock = System.Windows.Forms.DockStyle.Fill; this.tblTimePlayed.Location = new System.Drawing.Point(1, 373); this.tblTimePlayed.Margin = new System.Windows.Forms.Padding(0); this.tblTimePlayed.Name = "tblTimePlayed"; this.tblTimePlayed.RowCount = 1; this.tblTimePlayed.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblTimePlayed.Size = new System.Drawing.Size(304, 31); this.tblTimePlayed.TabIndex = 13; // // lblTimePlayed // this.lblTimePlayed.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblTimePlayed.AutoSize = true; this.lblTimePlayed.Location = new System.Drawing.Point(151, 5); this.lblTimePlayed.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblTimePlayed.Name = "lblTimePlayed"; this.lblTimePlayed.Size = new System.Drawing.Size(89, 20); this.lblTimePlayed.TabIndex = 8; this.lblTimePlayed.Text = "time played"; // // lblTimePlayedText // this.lblTimePlayedText.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblTimePlayedText.AutoSize = true; this.lblTimePlayedText.Location = new System.Drawing.Point(16, 5); this.lblTimePlayedText.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblTimePlayedText.Name = "lblTimePlayedText"; this.lblTimePlayedText.Size = new System.Drawing.Size(98, 20); this.lblTimePlayedText.TabIndex = 9; this.lblTimePlayedText.Text = "Time Played:"; // // GameFileSummary // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "GameFileSummary"; this.Size = new System.Drawing.Size(306, 1006); this.ClientSizeChanged += new System.EventHandler(this.GameFileSummary_ClientSizeChanged); this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.tblTags.ResumeLayout(false); this.tblTags.PerformLayout(); this.tblLastMap.ResumeLayout(false); this.tblLastMap.PerformLayout(); this.tblTimePlayed.ResumeLayout(false); this.tblTimePlayed.PerformLayout(); this.ResumeLayout(false); } #endregion private DoomLauncher.TableLayoutPanelDB tblMain; private GrowLabel lblTitle; private GrowLabel lblTagsText; private StatsControl ctrlStats; private Controls.CRichTextBox txtDescription; private Controls.CRichTextBox txtComments; private System.Windows.Forms.Label lblLastMapText; private System.Windows.Forms.Label lblLastMap; private System.Windows.Forms.Label lblTimePlayed; private System.Windows.Forms.Label lblTimePlayedText; private System.Windows.Forms.Label lblTags; private TableLayoutPanelDB tblTimePlayed; private TableLayoutPanelDB tblTags; private TableLayoutPanelDB tblLastMap; } } ================================================ FILE: DoomLauncher/Controls/GameFileSummary.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class GameFileSummary : UserControl { private readonly float m_labelHeight; private float m_imageHeight; private readonly SlideShowPictureBox pbImage = new SlideShowPictureBox(); private List m_previewImages = new List(); public GameFileSummary() { InitializeComponent(); tblMain.Controls.Add(pbImage, 0, 1); pbImage.Dock = DockStyle.Fill; pbImage.ImagePaint += PbImage_Paint; m_labelHeight = GetRowStyle(lblTitle).Height; m_imageHeight = GetRowStyle(pbImage).Height; ShowCommentsSection(false); txtComments.WarnLinkClick = false; tblMain.CellBorderStyle = TableLayoutPanelCellBorderStyle.None; tblMain.CellPaint += TblMain_CellPaint; tblLastMap.Margin = new Padding(1); tblTimePlayed.Margin = new Padding(1); tblTags.Margin = new Padding(1); ctrlStats.Margin = new Padding(1); txtDescription.BorderStyle = BorderStyle.None; txtComments.BorderStyle = BorderStyle.None; txtDescription.ReadOnly = true; txtComments.ReadOnly = true; SetStatistics(null, Array.Empty()); } private void TblMain_CellPaint(object sender, TableLayoutCellPaintEventArgs e) { Point pt = e.CellBounds.Location; Pen pen = new Pen(ColorTheme.Current.GridBorder); if (e.Row == 0) { Rectangle rect = new Rectangle(e.CellBounds.Location, new Size(e.CellBounds.Width - 1, e.CellBounds.Height - 1)); e.Graphics.DrawRectangle(pen, rect); return; } if (e.Row == 1) { Rectangle rect = new Rectangle(e.CellBounds.Location, new Size(e.CellBounds.Width - 1, e.CellBounds.Height - 1)); e.Graphics.FillRectangle(new SolidBrush(ColorTheme.Current.ImageBackground), rect); } e.Graphics.DrawLine(pen, pt.X, pt.Y, pt.X, pt.Y + e.CellBounds.Height); e.Graphics.DrawLine(pen, pt.X + e.CellBounds.Width - 1, pt.Y, e.CellBounds.Width - 1, pt.Y + e.CellBounds.Height); e.Graphics.DrawLine(pen, new Point(pt.X, pt.Y + e.CellBounds.Height - 1), new Point(pt.X + e.CellBounds.Width, pt.Y + e.CellBounds.Height - 1)); } private void PbImage_Paint(object sender, PaintEventArgs e) { if (pbImage.ImageIndex < 0 || pbImage.ImageIndex >= m_previewImages.Count) return; int alpha = (int)(pbImage.ImageAlpha * 255); Brush brush = new SolidBrush(Color.FromArgb(alpha, Color.White)); string title = m_previewImages[pbImage.ImageIndex].Title; Util.DrawImageTitleBar(title, pbImage.ClientRectangle, e, brush, Font); } public void SetTitle(string text) { if (lblTitle.Text == text) return; DpiScale dpiScale = new DpiScale(CreateGraphics()); lblTitle.Text = text; float height = lblTitle.Height + dpiScale.ScaleFloatY(6); if (height < m_labelHeight) height = m_labelHeight; GetRowStyle(lblTitle).Height = height; } public void SetDescription(string text) { txtDescription.Clear(); txtDescription.Visible = false; txtDescription.Text = Util.CleanDescription(text); txtDescription.Visible = true; } public void PauseSlideshow() { pbImage.Stop(); } public void ResumeSlideshow() { pbImage.Resume(); } public bool SetPreviewImages(List imagePaths) { m_previewImages = imagePaths; bool success = pbImage.SetImages(imagePaths.Select(x => x.Path).ToList()); ShowImageSection(true); return success; } public void SetPreviewImage(Image image) { m_previewImages.Clear(); pbImage.SetImage(image); ShowImageSection(true); } public void SetComments(string comments) { ShowCommentsSection(true); txtComments.Text = comments; } public void ClearPreviewImage() { pbImage.ClearImage(); ShowImageSection(false); } public void ClearComments() { txtComments.Text = string.Empty; ShowCommentsSection(false); } private RowStyle GetRowStyle(Control ctrl) { return tblMain.RowStyles[tblMain.GetRow(ctrl)]; } private void ShowImageSection(bool bShow) { if (bShow) GetRowStyle(pbImage).Height = m_imageHeight; else GetRowStyle(pbImage).Height = 0; } private void ShowCommentsSection(bool bShow) { if (bShow) { DpiScale dpiScale = new DpiScale(CreateGraphics()); GetRowStyle(txtComments).Height = dpiScale.ScaleFloatY(20); } else { GetRowStyle(txtComments).Height = 0; } } public string TagText { get { return lblTags.Text; } set { lblTags.Text = value; } } public void SetTimePlayed(int minutes) { lblTimePlayed.Text = Util.GetTimePlayedString(minutes); } public void SetStatistics(IGameFile gameFile, IEnumerable stats) { if (gameFile != null && stats.Any()) { DpiScale dpiScale = new DpiScale(CreateGraphics()); ctrlStats.Visible = true; GetRowStyle(ctrlStats).Height = dpiScale.ScaleFloatY(120); ctrlStats.SetStatistics(gameFile, stats); // Many maps can be saved at the same time, ordering by record time is not guaranteed to preserve the original order // Order by StatID to ensure the latest one is used lblLastMap.Text = stats.OrderByDescending(x => x.RecordTime).ThenByDescending(x => x.StatID).First().MapName; } else { ctrlStats.Visible = false; GetRowStyle(ctrlStats).Height = 0; lblLastMap.Text = "N/A"; } } private double m_aspectWidth = 16, m_aspectHeight = 9; private void GameFileSummary_ClientSizeChanged(object sender, EventArgs e) { int width = Width; double height = width / (m_aspectWidth / m_aspectHeight); m_imageHeight = Convert.ToSingle(height); if (pbImage.ImageCount > 0) ShowImageSection(true); } } } ================================================ FILE: DoomLauncher/Controls/GameFileSummary.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/GameFileTile.Designer.cs ================================================ namespace DoomLauncher { partial class GameFileTile { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.pb = new System.Windows.Forms.PictureBox(); ((System.ComponentModel.ISupportInitialize)(this.pb)).BeginInit(); this.SuspendLayout(); // // pb // this.pb.Location = new System.Drawing.Point(0, 0); this.pb.Name = "pb"; this.pb.Size = new System.Drawing.Size(100, 50); this.pb.TabIndex = 2; this.pb.TabStop = false; // // GameFileTile // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.pb); this.Name = "GameFileTile"; ((System.ComponentModel.ISupportInitialize)(this.pb)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.PictureBox pb; } } ================================================ FILE: DoomLauncher/Controls/GameFileTile.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class GameFileTile : GameFileTileBase { private static readonly int LabelHeight = 32; private static readonly string NewString = "New!"; private static readonly int NewPadX = 6; private static readonly int NewPadY = 4; private static readonly Font DisplayFont = new Font("Arial", 10, FontStyle.Bold); private static readonly Pen SeparatorPen = new Pen(Color.LightGray, 1.0f); public override event MouseEventHandler TileClick; public override event EventHandler TileDoubleClick; public override int ImageWidth { get; protected set; } public readonly int ImageHeight; public bool DrawBorder { get; set; } = true; public override IGameFile GameFile { get; protected set; } public override bool Selected { get; protected set; } private Color m_titleColor = ColorTheme.Current.Text; private bool m_new; private bool m_loadingImage; private Image m_setImage; public GameFileTile() { InitializeComponent(); DpiScale dpiScale = new DpiScale(CreateGraphics()); ImageWidth = dpiScale.ScaleIntX(Math.Max(DataCache.Instance.AppConfiguration.TileImageSize, 100)); ImageHeight = GetImageHeight(ImageWidth); int labelHeight = dpiScale.ScaleIntY(LabelHeight); Width = ImageWidth; Height = GetStandardHeight(dpiScale); pb.Width = Width; pb.Height = Height - labelHeight; pb.BackColor = Color.Black; pb.SizeMode = PictureBoxSizeMode.Zoom; pb.WaitOnLoad = false; pb.LoadCompleted += Pb_LoadCompleted; MouseClick += CtrlMouseClick; pb.MouseClick += CtrlMouseClick; DoubleClick += CtrlDoubleClick; pb.DoubleClick += CtrlDoubleClick; pb.Paint += Screenshot_Paint; Paint += GameFileTile_Paint; } public static int GetImageHeight(int imageWidth) => (int)(imageWidth / (16.0 / 9.0)); public int GetStandardHeight(DpiScale dpiScale) { return ImageHeight + dpiScale.ScaleIntY(LabelHeight); } private void Pb_LoadCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) { m_loadingImage = false; if (m_setImage != null) { pb.Image = m_setImage; m_setImage = null; } } private void GameFileTile_Paint(object sender, PaintEventArgs e) { if (GameFile == null) return; DpiScale dpiScale = new DpiScale(e.Graphics); int labelHeight = dpiScale.ScaleIntY(LabelHeight); int pad = dpiScale.ScaleIntX(1); SizeF layout = new SizeF(Width, 16); string text; if (!string.IsNullOrEmpty(GameFile.Title)) text = Util.GetClippedEllipsesText(e.Graphics, DisplayFont, GameFile.Title, layout); else text = GameFile.FileNameNoPath; SizeF size = e.Graphics.MeasureDisplayString(text, DisplayFont); float x = Width - size.Width - (Width - size.Width) / 2; float y = Height - size.Height - (labelHeight - size.Height) / 2; if (Selected) e.Graphics.DrawString(text, DisplayFont, new SolidBrush(ColorTheme.Current.HighlightText), x, y); else e.Graphics.DrawString(text, DisplayFont, new SolidBrush(m_titleColor), x, y); SeparatorPen.Color = ColorTheme.Current.Border; if (DrawBorder && !Selected) e.Graphics.DrawRectangle(SeparatorPen, 0, 0, Width - pad, Height - pad); } private void Screenshot_Paint(object sender, PaintEventArgs e) { if (m_new) { DpiScale dpiScale = new DpiScale(e.Graphics); int newPadX = dpiScale.ScaleIntX(NewPadX); int newPadY = dpiScale.ScaleIntY(NewPadY); int pad1 = dpiScale.ScaleIntX(1); SizeF size = e.Graphics.MeasureString(NewString, DisplayFont); RectangleF rect = new RectangleF(pb.ClientRectangle.Right - size.Width - newPadX - pad1, pb.ClientRectangle.Height - size.Height - newPadY - pad1, size.Width + newPadX, size.Height + newPadY); e.Graphics.FillRectangle(Brushes.Red, rect); e.Graphics.DrawRectangle(Pens.Gray, rect.Left, rect.Top, rect.Width, rect.Height); e.Graphics.DrawString(NewString, DisplayFont, Brushes.White, new PointF(rect.Left + newPadX / 2 + pad1 + pad1, rect.Top + newPadY / 2 + pad1)); } } public override void SetSelected(bool set) { Selected = set; if (set) { BorderStyle = BorderStyle.FixedSingle; BackColor = ColorTheme.Current.Highlight; } else { BorderStyle = BorderStyle.None; BackColor = ColorTheme.Current.WindowDark; } } public override void SetData(IGameFile gameFile, IEnumerable tags) { m_new = gameFile.Downloaded.HasValue && (DateTime.Now - gameFile.Downloaded.Value).TotalHours < 24; var colorTag = tags.FirstOrDefault(x => x.HasColor); if (colorTag != null && colorTag.Color != null) m_titleColor = Color.FromArgb(colorTag.Color.Value); else m_titleColor = ColorTheme.Current.Text; GameFile = gameFile; Invalidate(); } public override void ClearData() { GameFile = null; ClearImage(); } private void ClearImage() { m_setImage = null; pb.CancelAsync(); if (pb.Image != null) pb.Image = null; if (!string.IsNullOrEmpty(pb.ImageLocation)) pb.ImageLocation = string.Empty; } public override void SetImageLocation(string file) { if (file.Equals(pb.ImageLocation)) return; ClearImage(); if (!string.IsNullOrEmpty(file)) { m_loadingImage = true; pb.LoadAsync(file); } } public override void SetImage(Image image) { ClearImage(); // CancelAsync doesn't really work, to get around this set m_setImage set to pb.Image when Pb_LoadCompleted fires if (m_loadingImage) m_setImage = image; m_loadingImage = true; pb.Image = image; } private void CtrlDoubleClick(object sender, EventArgs e) { TileDoubleClick?.Invoke(this, e); } private void CtrlMouseClick(object sender, MouseEventArgs e) { TileClick?.Invoke(this, e); } } } ================================================ FILE: DoomLauncher/Controls/GameFileTile.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/GameFileTileBase.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public abstract class GameFileTileBase : UserControl, IGameFileTile { public abstract event MouseEventHandler TileClick; public abstract event EventHandler TileDoubleClick; public abstract int ImageWidth { get; protected set; } public abstract IGameFile GameFile { get; protected set; } public abstract bool Selected { get; protected set; } public abstract void ClearData(); public abstract void SetData(IGameFile gameFile, IEnumerable tags); public abstract void SetImageLocation(string file); public abstract void SetImage(Image image); public abstract void SetSelected(bool set); } } ================================================ FILE: DoomLauncher/Controls/GameFileTileExpanded.Designer.cs ================================================ namespace DoomLauncher { partial class GameFileTileExpanded { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.gameTile = new DoomLauncher.GameFileTile(); this.flpMain = new DoomLauncher.FlowLayoutPanelDB(); this.pnlData = new System.Windows.Forms.Panel(); this.flpMain.SuspendLayout(); this.SuspendLayout(); // // gameTile // this.gameTile.DrawBorder = true; this.gameTile.Location = new System.Drawing.Point(0, 0); this.gameTile.Margin = new System.Windows.Forms.Padding(0); this.gameTile.Name = "gameTile"; this.gameTile.Size = new System.Drawing.Size(300, 200); this.gameTile.TabIndex = 0; // // flpMain // this.flpMain.Controls.Add(this.gameTile); this.flpMain.Controls.Add(this.pnlData); this.flpMain.Dock = System.Windows.Forms.DockStyle.Fill; this.flpMain.Location = new System.Drawing.Point(0, 0); this.flpMain.Margin = new System.Windows.Forms.Padding(0); this.flpMain.Name = "flpMain"; this.flpMain.Size = new System.Drawing.Size(660, 204); this.flpMain.TabIndex = 1; // // pnlData // this.pnlData.Location = new System.Drawing.Point(302, 2); this.pnlData.Margin = new System.Windows.Forms.Padding(2, 2, 0, 0); this.pnlData.Name = "pnlData"; this.pnlData.Size = new System.Drawing.Size(352, 182); this.pnlData.TabIndex = 1; // // GameFileTileExpanded // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.flpMain); this.Name = "GameFileTileExpanded"; this.Size = new System.Drawing.Size(660, 204); this.flpMain.ResumeLayout(false); this.ResumeLayout(false); } #endregion private GameFileTile gameTile; private FlowLayoutPanelDB flpMain; private System.Windows.Forms.Panel pnlData; } } ================================================ FILE: DoomLauncher/Controls/GameFileTileExpanded.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class GameFileTileExpanded : GameFileTileBase { public override event MouseEventHandler TileClick; public override event EventHandler TileDoubleClick; public override IGameFile GameFile { get { return gameTile.GameFile; } protected set { } } public override bool Selected { get { return gameTile.Selected; } protected set { } } public override int ImageWidth { get { return gameTile.ImageWidth; } protected set { } } private static readonly Font DisplayFont = new Font("Microsof Sans Serif", 10); private static readonly Font DisplayBoldFont = new Font("Microsof Sans Serif", 10, FontStyle.Bold); private static readonly Pen SeparatorPen = new Pen(Color.LightGray, 1.0f); private static readonly Pen HighlightPen = new Pen(ColorTheme.Current.Highlight, 1.0f); private static readonly Brush TextBrush = new SolidBrush(ColorTheme.Current.Text); private string m_tags; private string m_maps; private string m_release; private string m_played; public GameFileTileExpanded() { InitializeComponent(); BackColor = ColorTheme.Current.WindowDark; DpiScale dpiScale = new DpiScale(CreateGraphics()); gameTile.Width = dpiScale.ScaleIntX(DataCache.Instance.AppConfiguration.TileImageSize); // Something in the designer is messing up the height, forcing it here gameTile.Height = gameTile.GetStandardHeight(dpiScale); Height = gameTile.Height + dpiScale.ScaleIntX(2); int pad = dpiScale.ScaleIntX(1); gameTile.DrawBorder = false; gameTile.Margin = new Padding(pad, pad, 0, 0); gameTile.TileClick += GameTile_TileClick; gameTile.TileDoubleClick += GameTile_TileDoubleClick; pnlData.Paint += PnlData_Paint; flpMain.Paint += FlpMain_Paint; pnlData.MouseClick += PnlData_Click; pnlData.DoubleClick += PnlData_DoubleClick; flpMain.MouseClick += FlpMain_MouseClick; flpMain.DoubleClick += FlpMain_DoubleClick; } private void FlpMain_DoubleClick(object sender, EventArgs e) { TileDoubleClick?.Invoke(this, e); } private void FlpMain_MouseClick(object sender, MouseEventArgs e) { TileClick?.Invoke(this, e); } private void PnlData_DoubleClick(object sender, EventArgs e) { TileDoubleClick?.Invoke(this, e); } private void PnlData_Click(object sender, MouseEventArgs e) { TileClick?.Invoke(this, new MouseEventArgs(e.Button, e.Clicks, e.X + gameTile.Width, e.Y, e.Delta)); } private void FlpMain_Paint(object sender, PaintEventArgs e) { if (gameTile.Selected) e.Graphics.DrawRectangle(HighlightPen, 0, 0, Width - 1, Height - 1); else e.Graphics.DrawRectangle(SeparatorPen, 0, 0, Width - 1, Height - 1); } private int m_selectPad = 1; private void PnlData_Paint(object sender, PaintEventArgs e) { if (GameFile == null) return; DpiScale dpiScale = new DpiScale(e.Graphics); m_selectPad = (int)Math.Ceiling(dpiScale.ScaleFloatY(1)); float xPos = gameTile.Location.X + dpiScale.ScaleIntX(4); int yPos = dpiScale.ScaleIntY(8); int offset = dpiScale.ScaleIntY(22); var graphics = e.Graphics; SafeDrawString(graphics, "Filename", DisplayBoldFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, "Title", DisplayBoldFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, "Author", DisplayBoldFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, "Release", DisplayBoldFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, "Played", DisplayBoldFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, "Maps", DisplayBoldFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, "Tags", DisplayBoldFont, TextBrush, xPos, yPos, offset); xPos = gameTile.Location.X + dpiScale.ScaleFloatX(82); yPos = dpiScale.ScaleIntY(8); SizeF maxLabelSize = new SizeF(pnlData.ClientRectangle.Width - xPos + dpiScale.ScaleIntX(8), 16); SafeDrawString(graphics, GameFile.FileNameNoPath, DisplayFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, Util.GetClippedEllipsesText(e.Graphics, DisplayFont, GameFile.Title, maxLabelSize), DisplayFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, Util.GetClippedEllipsesText(e.Graphics, DisplayFont, GameFile.Author, maxLabelSize), DisplayFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, m_release, DisplayFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, m_played, DisplayFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, m_maps, DisplayFont, TextBrush, xPos, yPos, offset); yPos += offset; SafeDrawString(graphics, m_tags, DisplayFont, TextBrush, xPos, yPos, offset); } private void SafeDrawString(Graphics g, string s, Font font, Brush brush, float x, float y, int sizeY) { if (y + sizeY >= Height - m_selectPad) return; g.DrawString(s, font, brush, x, y); } private void GameTile_TileDoubleClick(object sender, EventArgs e) { TileDoubleClick?.Invoke(this, e); } private void GameTile_TileClick(object sender, MouseEventArgs e) { TileClick?.Invoke(this, e); } public override void ClearData() { gameTile.ClearData(); } public override void SetData(IGameFile gameFile, IEnumerable tags) { gameTile.SetData(gameFile, tags); m_tags = string.Join(", ", tags.Select(x => x.Name)); if (gameFile.MapCount.HasValue) m_maps = gameFile.MapCount.ToString(); else m_maps = "0"; if (gameFile.ReleaseDate.HasValue) m_release = gameFile.ReleaseDate.Value.ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); else m_release = string.Empty; if (gameFile.LastPlayed.HasValue) m_played = gameFile.LastPlayed.Value.ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); else m_played = string.Empty; pnlData.Invalidate(); } public override void SetImageLocation(string file) { gameTile.SetImageLocation(file); } public override void SetImage(Image image) { gameTile.SetImage(image); } public override void SetSelected(bool set) { gameTile.SetSelected(set); flpMain.Invalidate(); } } } ================================================ FILE: DoomLauncher/Controls/GameFileTileExpanded.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/GameFileTileViewControl.Designer.cs ================================================ namespace DoomLauncher { partial class GameFileTileViewControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.flpPaging = new System.Windows.Forms.FlowLayoutPanel(); this.pagingControl = new DoomLauncher.PagingControl(); this.label1 = new System.Windows.Forms.Label(); this.cmbMaxItemsPerPage = new System.Windows.Forms.ComboBox(); this.tblMain.SuspendLayout(); this.flpPaging.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.flpPaging, 0, 1); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 46F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 31F)); this.tblMain.Size = new System.Drawing.Size(717, 482); this.tblMain.TabIndex = 1; // // flpPaging // this.flpPaging.Anchor = System.Windows.Forms.AnchorStyles.None; this.flpPaging.AutoSize = true; this.flpPaging.Controls.Add(this.pagingControl); this.flpPaging.Controls.Add(this.label1); this.flpPaging.Controls.Add(this.cmbMaxItemsPerPage); this.flpPaging.Location = new System.Drawing.Point(83, 436); this.flpPaging.Margin = new System.Windows.Forms.Padding(0); this.flpPaging.Name = "flpPaging"; this.flpPaging.Size = new System.Drawing.Size(550, 46); this.flpPaging.TabIndex = 1; this.flpPaging.WrapContents = false; // // pagingControl // this.pagingControl.BackColor = System.Drawing.SystemColors.Control; this.pagingControl.ForeColor = System.Drawing.SystemColors.WindowText; this.pagingControl.Location = new System.Drawing.Point(6, 8); this.pagingControl.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); this.pagingControl.Name = "pagingControl"; this.pagingControl.Size = new System.Drawing.Size(334, 32); this.pagingControl.TabIndex = 2; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(350, 14); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(118, 20); this.label1.TabIndex = 0; this.label1.Text = "Items Per Page"; // // cmbMaxItemsPerPage // this.cmbMaxItemsPerPage.Anchor = System.Windows.Forms.AnchorStyles.Left; this.cmbMaxItemsPerPage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbMaxItemsPerPage.FormattingEnabled = true; this.cmbMaxItemsPerPage.Items.AddRange(new object[] { "30", "60", "120", "240", "500", "1000"}); this.cmbMaxItemsPerPage.Location = new System.Drawing.Point(476, 10); this.cmbMaxItemsPerPage.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbMaxItemsPerPage.Name = "cmbMaxItemsPerPage"; this.cmbMaxItemsPerPage.Size = new System.Drawing.Size(70, 28); this.cmbMaxItemsPerPage.TabIndex = 1; // // GameFileTileViewControl // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "GameFileTileViewControl"; this.Size = new System.Drawing.Size(717, 482); this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.flpPaging.ResumeLayout(false); this.flpPaging.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ToolTip toolTip1; private TableLayoutPanelDB tblMain; private System.Windows.Forms.Label label1; private System.Windows.Forms.ComboBox cmbMaxItemsPerPage; private System.Windows.Forms.FlowLayoutPanel flpPaging; private PagingControl pagingControl; } } ================================================ FILE: DoomLauncher/Controls/GameFileTileViewControl.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class GameFileTileViewControl : UserControl, IGameFileView, IGameFileSortableView { public event EventHandler ItemClick; public event EventHandler ItemDoubleClick; public event EventHandler SelectionChange; public event KeyPressEventHandler ViewKeyPress; public event KeyEventHandler ViewKeyDown; public event GameFileEventHandler GameFileEnter; public event GameFileEventHandler GameFileLeave; private readonly int LayoutRow = 0; private List m_gameFiles = new List(); private readonly List m_selectedTiles = new List(); private readonly Label m_label = new Label(); private ContextMenuStrip m_menu; private bool m_visible; private bool m_menuShowing; private bool m_tilesRecreated; private readonly System.Timers.Timer m_mouseTimer; private int m_lastScrollPos; private GameFileTileBase m_lastHover; private string m_sortedColumn; private SortOrder m_sortedColumnOrder; private FlowLayoutPanelDB flpMain = new FlowLayoutPanelDB(); public GameFileTileViewControl() { InitializeComponent(); SetItemsPerPage(GameFileTileManager.Instance.MaxItems); pagingControl.PageIndexChanged += M_pagingControl_PageIndexChanged; m_mouseTimer = new System.Timers.Timer(); m_mouseTimer.Interval = 100; m_mouseTimer.Elapsed += MouseTimer_Elapsed; m_mouseTimer.Start(); m_label.Visible = false; GameFileViewControl.StyleDisplayLabel(m_label); cmbMaxItemsPerPage.KeyDown += CmbMaxItemsPerPage_KeyDown; GameFileTileManager.Instance.TilesRecreated += Instance_TilesRecreated; InitTiles(); Stylizer.StylizeControl(this, DesignMode); BackColor = ColorTheme.Current.WindowLight; } private void SetItemsPerPage(int maxItems) { cmbMaxItemsPerPage.SelectedIndexChanged -= CmbMaxItemsPerPage_SelectedIndexChanged; cmbMaxItemsPerPage.SelectedItem = maxItems.ToString(); if (cmbMaxItemsPerPage.SelectedIndex == -1) cmbMaxItemsPerPage.SelectedIndex = 0; cmbMaxItemsPerPage.SelectedIndexChanged += CmbMaxItemsPerPage_SelectedIndexChanged; } public IEnumerable DataSource { get => m_gameFiles; set => SetData(value); } public IGameFile SelectedItem { get { if (m_selectedTiles.Count > 0) return m_selectedTiles[0].GameFile; return null; } set { SelectGameFile(value); } } public IGameFile[] SelectedItems => m_selectedTiles.Select(x => x.GameFile).ToArray(); public bool MultiSelect { get; set; } public object DoomLauncherParent { get; set; } public void SetVisible(bool set) { m_visible = set; if (set) { flpMain = GameFileTileManager.Instance.TileLayout; flpMain.TabIndex = 1; flpMain.Anchor = AnchorStyles.Top | AnchorStyles.Left; flpMain.Dock = DockStyle.Fill; if (m_label.Visible) { // Data in the view has changed since last displayed if (m_gameFiles.Count > 0) ClearDisplayLabel(); } else { tblMain.Controls.Add(flpMain, 0, LayoutRow); } flpMain.BackColor = ColorTheme.Current.WindowLight; flpMain.KeyPress += GameFileTileViewControl_KeyPress; flpMain.KeyDown += GameFileTileViewControl_KeyDown; SetPageData(pagingControl.PageIndex, false); SetSavedScrollValue(); flpMain.PerformLayout(); SetDefaultSelection(); } else { flpMain.KeyPress -= GameFileTileViewControl_KeyPress; flpMain.KeyDown -= GameFileTileViewControl_KeyDown; m_selectedTiles.ForEach(x => x.SetSelected(false)); m_lastScrollPos = flpMain.VerticalScroll.Value; Controls.Remove(flpMain); } } private void SetSavedScrollValue() { if (m_lastScrollPos < flpMain.VerticalScroll.Minimum) flpMain.VerticalScroll.Value = flpMain.VerticalScroll.Minimum; else if (m_lastScrollPos > flpMain.VerticalScroll.Maximum) flpMain.VerticalScroll.Value = flpMain.VerticalScroll.Maximum; else flpMain.VerticalScroll.Value = m_lastScrollPos; } public string GetSortedColumnKey() { return m_sortedColumn; } public SortOrder GetColumnSort(string key) { if (m_sortedColumn == key) return m_sortedColumnOrder; return SortOrder.None; } public void SetSortedColumn(string key, SortOrder sort) { m_sortedColumn = key; m_sortedColumnOrder = sort; } private void SetDefaultSelection() { if (SelectedItem == null && m_gameFiles.Count > 0) { int index = pagingControl.PageIndex * GameFileTileManager.Instance.MaxItems; if (index < m_gameFiles.Count) { SelectGameFile(m_gameFiles[index]); return; } } SelectionChange?.Invoke(this, EventArgs.Empty); } private void InitTiles() { m_lastScrollPos = 0; foreach (var tile in GameFileTileManager.Instance.OldTiles) { tile.TileClick -= Tile_TileClick; tile.TileDoubleClick -= Tile_TileDoubleClick; } foreach (var tile in GameFileTileManager.Instance.Tiles) { tile.TileClick += Tile_TileClick; tile.TileDoubleClick += Tile_TileDoubleClick; } } private void CmbMaxItemsPerPage_KeyDown(object sender, KeyEventArgs e) { e.Handled = true; } private void Instance_TilesRecreated(object sender, EventArgs e) { m_tilesRecreated = true; pagingControl.Init(m_gameFiles.Count, GameFileTileManager.Instance.MaxItems, 0); SetItemsPerPage(GameFileTileManager.Instance.MaxItems); InitTiles(); RefreshData(); SetDefaultSelection(); } private void M_pagingControl_PageIndexChanged(object sender, EventArgs e) { SetPageData(pagingControl.PageIndex, true); SetDefaultSelection(); } private void MouseTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { try { if (!IsDisposed && InvokeRequired) Invoke(new Action(MouseMoveTimer)); } catch (ObjectDisposedException) { m_mouseTimer.Stop(); } } private void MouseMoveTimer() { if (!m_visible || m_menuShowing) return; // PointToClient will return null if the view is obscured at the current position Point pt = Cursor.Position; Control test = tblMain.GetChildAtPoint(tblMain.PointToClient(pt)); if (test != null && test == flpMain) { flpMain.Focus(); foreach (var tile in GameFileTileManager.Instance.Tiles) { if (!tile.Visible) continue; var rect = tile.RectangleToScreen(tile.DisplayRectangle); if (rect.Contains(pt)) { if (m_lastHover != tile && tile.IsVisibleAtPoint(pt)) { m_lastHover = tile; GameFileEnter?.Invoke(this, new GameFileEventArgs(tile.GameFile)); } return; } } } ClearHover(); } private void GameFileTileViewControl_KeyDown(object sender, KeyEventArgs e) { if (m_selectedTiles.Count > 0 && (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up || e.KeyCode == Keys.Left || e.KeyCode == Keys.Right)) { int baseIndex = pagingControl.PageIndex * GameFileTileManager.Instance.MaxItems; int originalIndex = GameFileTileIndex(m_selectedTiles[0]) + baseIndex; int newIndex = GetNextGameFileIndex(originalIndex, e.KeyCode); if (newIndex == originalIndex) return; if (newIndex > 0 && newIndex < m_gameFiles.Count) { ClearSelection(); SelectGameFile(m_gameFiles[newIndex]); } } ViewKeyDown?.Invoke(this, e); } private int GetNextGameFileIndex(int gameFileIndex, Keys keycode) { if (gameFileIndex < 0 || gameFileIndex >= m_gameFiles.Count) return gameFileIndex; try { GameFileTileBase tile = GameFileTileManager.Instance.Tiles[gameFileIndex % GameFileTileManager.Instance.MaxItems]; int baseIndex = pagingControl.PageIndex * GameFileTileManager.Instance.MaxItems; int startIndex; int endIndex; if (keycode == Keys.Down || keycode == Keys.Right) { startIndex = gameFileIndex; endIndex = baseIndex + GameFileTileManager.Instance.MaxItems; } else if (keycode == Keys.Up || keycode == Keys.Left) { startIndex = gameFileIndex; endIndex = baseIndex - 1; } else { return gameFileIndex; } while (startIndex != endIndex) { GameFileTileBase nextTile = GameFileTileManager.Instance.Tiles[startIndex % GameFileTileManager.Instance.MaxItems]; if (keycode == Keys.Down && nextTile.Location.Y > tile.Location.Y && nextTile.Location.X == tile.Location.X) return startIndex; else if (keycode == Keys.Up && nextTile.Location.Y < tile.Location.Y && nextTile.Location.X == tile.Location.X) return startIndex; else if (keycode == Keys.Right && nextTile.Location.X > tile.Location.X) return startIndex; else if (keycode == Keys.Left && nextTile.Location.X < tile.Location.X) return startIndex; if (keycode == Keys.Down || keycode == Keys.Right) startIndex++; else startIndex--; } if (keycode == Keys.Left && gameFileIndex - 1 > startIndex) return gameFileIndex - 1; else if (keycode == Keys.Right && gameFileIndex + 1 < endIndex) return gameFileIndex + 1; return gameFileIndex; } catch { return gameFileIndex; } } private void GameFileTileViewControl_KeyPress(object sender, KeyPressEventArgs e) { ViewKeyPress?.Invoke(this, e); } public IGameFile GameFileForIndex(int index) { if (index >= m_gameFiles.Count) return null; return m_gameFiles[index]; } public void UpdateGameFile(IGameFile gameFile) { foreach (var tile in GameFileTileManager.Instance.Tiles) { if (ShouldSkipTile(tile)) break; if (tile.GameFile.Equals(gameFile)) { var screenshots = DataCache.Instance.DataSourceAdapter.GetFiles(FileType.Screenshot); var thumbnails = DataCache.Instance.DataSourceAdapter.GetFiles(FileType.Thumbnail); SetTileData(tile, gameFile, screenshots, thumbnails, DataCache.Instance.TagMapLookup.GetTags(tile.GameFile), true); } } } public void RefreshData() { if (!m_visible) return; if (m_tilesRecreated) { m_tilesRecreated = false; SetPageData(pagingControl.PageIndex, true); return; } var screenshots = DataCache.Instance.DataSourceAdapter.GetFiles(FileType.Screenshot); var thumbnails = DataCache.Instance.DataSourceAdapter.GetFiles(FileType.Thumbnail); foreach (var tile in GameFileTileManager.Instance.Tiles) { if (ShouldSkipTile(tile)) break; SetTileData(tile, tile.GameFile, screenshots, thumbnails, DataCache.Instance.TagMapLookup.GetTags(tile.GameFile), false); } } private static bool ShouldSkipTile(GameFileTileBase tile) => !tile.Visible || tile.GameFile == null; public void SetContextMenuStrip(ContextMenuStrip menu) { m_menu = menu; m_menu.Opened += M_menu_Opened; m_menu.Closed += M_menu_Closed; } public void SetDisplayText(string text) { tblMain.Controls.Remove(flpMain); tblMain.Controls.Add(m_label, 0, LayoutRow); m_label.Text = text; m_label.Visible = true; } private void SetData(IEnumerable gameFiles) { ClearHover(); ClearDisplayLabel(); int saveIndex = pagingControl.PageIndex; if (gameFiles == null) { m_gameFiles.Clear(); } else { m_gameFiles = gameFiles.ToList(); } pagingControl.Init(m_gameFiles.Count, GameFileTileManager.Instance.MaxItems, saveIndex); if (!m_visible) return; SetPageData(saveIndex, false); SetDefaultSelection(); } private void ClearDisplayLabel() { if (m_label.Visible) { m_label.Visible = false; tblMain.Controls.Remove(m_label); tblMain.Controls.Add(GameFileTileManager.Instance.TileLayout, 0, LayoutRow); } } private void ClearHover() { if (m_lastHover != null) GameFileLeave?.Invoke(this, new GameFileEventArgs(m_lastHover.GameFile)); m_lastHover = null; } private void SetPageData(int pageIndex, bool dataChange) { GameFileTileManager.Instance.Tiles.ForEach(x => x.SetSelected(false)); var screenshots = DataCache.Instance.DataSourceAdapter.GetFiles(FileType.Screenshot); var thumbnails = DataCache.Instance.DataSourceAdapter.GetFiles(FileType.Thumbnail); var gameFiles = m_gameFiles.Skip(pageIndex * GameFileTileManager.Instance.MaxItems).Take(GameFileTileManager.Instance.MaxItems).ToList(); SetLayout(gameFiles, screenshots, thumbnails); if (dataChange) { ClearSelection(false); flpMain.VerticalScroll.Value = flpMain.VerticalScroll.Minimum; flpMain.PerformLayout(); } else { m_selectedTiles.ForEach(x => x.SetSelected(true)); } } private void SetLayout(List gameFiles, IEnumerable screenshots, IEnumerable thumbnails) { flpMain.SuspendLayout(); int tileIndex = 0; foreach (var gameFile in gameFiles) { SetTileData(GameFileTileManager.Instance.Tiles[tileIndex], gameFile, screenshots, thumbnails, DataCache.Instance.TagMapLookup.GetEnumerableTags(gameFile), false); GameFileTileManager.Instance.Tiles[tileIndex].Visible = true; tileIndex++; } for (int i = gameFiles.Count; i < GameFileTileManager.Instance.Tiles.Count; i++) { GameFileTileManager.Instance.Tiles[i].ClearData(); GameFileTileManager.Instance.Tiles[i].Visible = false; } flpMain.ResumeLayout(); } private static void SetTileData(GameFileTileBase tile, IGameFile gameFile, IEnumerable screenshots, IEnumerable thumbnails, IEnumerable tags, bool forceRefresh) { if (gameFile == null || (!forceRefresh && gameFile.Equals(tile.GameFile))) return; tile.SetData(gameFile, tags); if (!gameFile.GameFileID.HasValue) { tile.SetImage(DataCache.Instance.DefaultImage); return; } IFileData thumbnail = ThumbnailManager.GetOrCreateThumbnail(gameFile, screenshots, thumbnails); if (thumbnail != null) { if (thumbnail.FileTypeID == FileType.TileImage) tile.SetImageLocation(thumbnail.FileName); else tile.SetImageLocation(Path.Combine(DataCache.Instance.AppConfiguration.ThumbnailDirectory.GetFullPath(), thumbnail.FileName)); return; } tile.SetImage(DataCache.Instance.DefaultImage); } private void M_menu_Opened(object sender, EventArgs e) { m_menuShowing = true; } private void M_menu_Closed(object sender, ToolStripDropDownClosedEventArgs e) { m_menuShowing = false; } private void CmbMaxItemsPerPage_SelectedIndexChanged(object sender, EventArgs e) { GameFileTileManager.Instance.SetMaxItems(Convert.ToInt32(cmbMaxItemsPerPage.SelectedItem)); } private void Tile_TileDoubleClick(object sender, EventArgs e) { if (!m_visible) return; ItemDoubleClick?.Invoke(this, e); } private void Tile_TileClick(object sender, MouseEventArgs e) { if (!(sender is GameFileTileBase tile) || !m_visible) return; ItemClick?.Invoke(this, EventArgs.Empty); if (e.Button == MouseButtons.Left) { if ((ModifierKeys & Keys.Control) == Keys.Control) ToggleSelection(tile); else if ((ModifierKeys & Keys.Shift) == Keys.Shift && m_selectedTiles.Count > 0) SelectRange(tile); else SelectGameFile(tile.GameFile); } else if (e.Button == MouseButtons.Right) { if (!m_selectedTiles.Contains(tile)) SelectGameFile(tile.GameFile); Point pt = PointToScreen(new Point(Location.X + tile.Location.X + e.X, Location.Y + tile.Location.Y + e.Y)); m_menu.Show(pt); } } private void SelectRange(GameFileTileBase tile) { int start = GameFileTileIndex(m_selectedTiles.First()); int end = GameFileTileIndex(m_selectedTiles.Last()); if (start == -1 || end == -1) return; if (start > end) { int temp = start; end = start; start = temp; } int index = GameFileTileIndex(tile); if (index == -1) return; if (index < start) start = index; if (index > end) end = index; ClearSelection(false); for (int i = start; i <= end; i++) { m_selectedTiles.Add(GameFileTileManager.Instance.Tiles[i]); GameFileTileManager.Instance.Tiles[i].SetSelected(true); } SelectionChange?.Invoke(this, EventArgs.Empty); } private int GameFileTileIndex(GameFileTileBase tile) { for (int i = 0; i < GameFileTileManager.Instance.Tiles.Count; i++) { if (tile == GameFileTileManager.Instance.Tiles[i]) return i; } return -1; } private void ToggleSelection(GameFileTileBase tile) { tile.SetSelected(!tile.Selected); if (tile.Selected) { m_selectedTiles.Add(tile); } else { m_selectedTiles.Remove(tile); } } private void SelectGameFile(IGameFile gameFile) { ClearSelection(false); if (gameFile == null) return; foreach (GameFileTileBase tile in GameFileTileManager.Instance.Tiles) { if (ShouldSkipTile(tile)) break; if (tile.GameFile.Equals(gameFile)) { tile.SetSelected(true); m_selectedTiles.Add(tile); break; } } if (m_selectedTiles.Count > 0) { flpMain.ScrollControlIntoView(m_selectedTiles[0]); SelectionChange?.Invoke(this, EventArgs.Empty); } } private void ClearSelection(bool fireEvent = true) { if (m_selectedTiles.Count == 0) return; m_selectedTiles.ForEach(x => x.SetSelected(false)); m_selectedTiles.Clear(); if (fireEvent) SelectionChange?.Invoke(this, EventArgs.Empty); } } } ================================================ FILE: DoomLauncher/Controls/GameFileTileViewControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 ================================================ FILE: DoomLauncher/Controls/GameFileViewControl.Designer.cs ================================================ namespace DoomLauncher { partial class GameFileViewControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.dgvMain = new DoomLauncher.CDataGridView(); ((System.ComponentModel.ISupportInitialize)(this.dgvMain)).BeginInit(); this.SuspendLayout(); // // dgvMain // this.dgvMain.AllowUserToAddRows = false; this.dgvMain.AllowUserToDeleteRows = false; this.dgvMain.AllowUserToOrderColumns = true; dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this.dgvMain.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1; this.dgvMain.BackgroundColor = System.Drawing.SystemColors.ControlLightLight; this.dgvMain.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal; this.dgvMain.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dgvMain.Dock = System.Windows.Forms.DockStyle.Fill; this.dgvMain.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically; this.dgvMain.GridColor = System.Drawing.SystemColors.ActiveBorder; this.dgvMain.Location = new System.Drawing.Point(0, 0); this.dgvMain.Name = "dgvMain"; this.dgvMain.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; this.dgvMain.Size = new System.Drawing.Size(150, 150); this.dgvMain.TabIndex = 1; this.dgvMain.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvMain_CellDoubleClick); this.dgvMain.CellMouseDown += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dgvMain_CellMouseDown); this.dgvMain.CellMouseEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvMain_CellMouseEnter); this.dgvMain.CellMouseLeave += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvMain_CellMouseLeave); this.dgvMain.RowPostPaint += new System.Windows.Forms.DataGridViewRowPostPaintEventHandler(this.dgvMain_RowPostPaint); this.dgvMain.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.dgvMain_KeyPress); // // GameFileViewControl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.dgvMain); this.Name = "GameFileViewControl"; ((System.ComponentModel.ISupportInitialize)(this.dgvMain)).EndInit(); this.ResumeLayout(false); } #endregion private CDataGridView dgvMain; private System.Windows.Forms.ToolTip toolTip1; } } ================================================ FILE: DoomLauncher/Controls/GameFileViewControl.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Equin.ApplicationFramework; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Reflection; using System.Windows.Forms; namespace DoomLauncher { public partial class GameFileViewControl : UserControl, IGameFileColumnView { public event EventHandler ItemClick; public event EventHandler ItemDoubleClick; public event EventHandler SelectionChange; public event CancelEventHandler CustomRowPaint; public event KeyPressEventHandler ViewKeyPress; public event KeyEventHandler ViewKeyDown; public event GameFileEventHandler GameFileEnter; public event GameFileEventHandler GameFileLeave; private readonly Label m_label = new Label(); private readonly Dictionary m_properties = new Dictionary(); private BindingListView m_datasource; private bool m_binding = false; public GameFileViewControl() { InitializeComponent(); SetupGridView(); StyleDisplayLabel(m_label); Controls.Add(m_label); dgvMain.KeyDown += dgvMain_KeyDown; Stylizer.StylizeControl(this, DesignMode); } public static void StyleDisplayLabel(Label label) { label.AutoSize = true; label.Visible = false; label.Anchor = AnchorStyles.Top | AnchorStyles.Left; label.Margin = new Padding(12); label.Font = new Font(label.Font.FontFamily, 12.0f, FontStyle.Bold); } public void SetVisible(bool set) { // Not required } public bool MultiSelect { get { return dgvMain.MultiSelect; } set { dgvMain.MultiSelect = value; } } public void SetDisplayText(string text) { m_label.Text = text; m_label.Visible = true; BorderStyle = BorderStyle.FixedSingle; Controls.Remove(dgvMain); } public void SetContextMenuStrip(ContextMenuStrip menu) { dgvMain.ContextMenuStrip = menu; } public void SetColumnFields(IEnumerable columnFields) { m_setting = true; bool resetDataSource = dgvMain.Columns.Count > 0; m_orderLookup.Clear(); ColumnFields = columnFields.ToArray(); dgvMain.Columns.Clear(); if (columnFields.Any()) { foreach (var item in columnFields) { DataGridViewColumn col = new DataGridViewTextBoxColumn { HeaderText = item.Title, Name = item.DataKey, DataPropertyName = item.DataKey }; dgvMain.Columns.Add(col); m_orderLookup.Add(item.DataKey, col); } dgvMain.Columns[dgvMain.Columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; if (resetDataSource) //need to reset datasource so columns show in correct order SetDataSource(m_datasource); } m_setting = false; } public void SetColumnFormat(string colName, string format) { if (dgvMain.Columns[colName] != null) dgvMain.Columns[colName].DefaultCellStyle.Format = format; } public List> GetColumnFormats() { List> formats = new List>(); foreach (DataGridViewColumn col in dgvMain.Columns) { if (!string.IsNullOrEmpty(col.DefaultCellStyle.Format)) formats.Add(new Tuple(col.Name, col.DefaultCellStyle.Format)); } return formats; } public IEnumerable DataSource { get { if (m_datasource != null) return m_datasource; else return new IGameFile[] { }; } set { if (value != null) SetDataSource(new BindingListView(value.ToList())); else SetDataSource(new BindingListView(new GameFile[] { })); } } private void SetDataSource(object datasource) { m_binding = true; m_datasource = (BindingListView)datasource; if (m_datasource == null) { dgvMain.RowCount = 0; } else { dgvMain.RowCount = m_datasource.Count; DataGridViewColumn col = dgvMain.Columns.Cast() .FirstOrDefault(x => x.HeaderCell.SortGlyphDirection != SortOrder.None); if (col != null) HandleSort(col.Index, false); } if (!Controls.Contains(dgvMain)) { Controls.Add(dgvMain); m_label.Visible = false; BorderStyle = BorderStyle.None; } Refresh(); m_binding = false; } public void UpdateGameFile(IGameFile gameFile) { // Grid view inherently supports this } public IGameFile SelectedItem { get { if (dgvMain.SelectedRows.Count > 0) { DataGridViewRow dgvr = dgvMain.SelectedRows[0]; return m_datasource[dgvr.Index].Object; } return null; } set { dgvMain.ClearSelection(); int rowIndex = 0; foreach (DataGridViewRow dgvr in dgvMain.Rows) { if (m_datasource[dgvr.Index].Equals(value)) { dgvr.Selected = true; dgvr.Cells[0].Selected = true; dgvMain.FirstDisplayedScrollingRowIndex = rowIndex; break; } rowIndex++; } } } public IGameFile[] SelectedItems { get { if (m_datasource != null && dgvMain.SelectedRows.Count > 0) { List ret = new List(dgvMain.Rows.Count); foreach (DataGridViewRow dgvr in dgvMain.SelectedRows) { if (m_datasource.Count > dgvr.Index) ret.Add(m_datasource[dgvr.Index].Object); } return ret.ToArray(); } return new IGameFile[] { }; } } public IGameFile GameFileForIndex(int index) { if (index > -1 && index < m_datasource.Count) return m_datasource[index].Object; return null; } public void RefreshData() { Refresh(); } private void SetupGridView() { dgvMain.SelectionChanged += dgvMain_SelectionChanged; dgvMain.CellClick += dgvMain_CellClick; dgvMain.ColumnDisplayIndexChanged += dgvMain_ColumnDisplayIndexChanged; dgvMain.VirtualMode = true; dgvMain.CellValueNeeded += dgvMain_CellValueNeeded; dgvMain.ColumnHeaderMouseClick += dgvMain_ColumnHeaderMouseClick; dgvMain.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None; dgvMain.AllowUserToResizeRows = false; } private void dgvMain_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) { HandleSort(e.ColumnIndex, true); } private void HandleSort(int columnIndex, bool flip) { if (m_datasource != null) { DataGridViewColumn dgvcSet = dgvMain.Columns[columnIndex]; foreach (DataGridViewColumn dgvc in dgvMain.Columns) { if (dgvc != dgvcSet) dgvc.HeaderCell.SortGlyphDirection = SortOrder.None; } if (flip) { if (dgvcSet.HeaderCell.SortGlyphDirection == SortOrder.Ascending) dgvcSet.HeaderCell.SortGlyphDirection = SortOrder.Descending; else dgvcSet.HeaderCell.SortGlyphDirection = SortOrder.Ascending; } string sortOrder; if (dgvcSet.HeaderCell.SortGlyphDirection == SortOrder.Ascending) sortOrder = "ASC"; else sortOrder = "DESC"; m_datasource.ApplySort(string.Concat(dgvcSet.Name, " ", sortOrder)); dgvMain.Invalidate(); } } void dgvMain_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) { if (m_datasource != null && m_datasource.Count > e.RowIndex) { IGameFile gameFile = m_datasource[e.RowIndex].Object; if (!m_properties.ContainsKey(e.ColumnIndex)) m_properties.Add(e.ColumnIndex, gameFile.GetType().GetProperty(dgvMain.Columns[e.ColumnIndex].DataPropertyName)); e.Value = m_properties[e.ColumnIndex].GetValue(gameFile); } } void dgvMain_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e) { ColumnReorderIndex = e.Column.DisplayIndex; } void dgvMain_CellClick(object sender, DataGridViewCellEventArgs e) { ItemClick?.Invoke(this, EventArgs.Empty); HandleSelection(); } void dgvMain_SelectionChanged(object sender, EventArgs e) { HandleSelection(); } private void HandleSelection() { if (m_setting) return; SelectionChange?.Invoke(this, new EventArgs()); } private bool m_setting = false; private void dgvMain_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e) { if (e.RowIndex > -1 && e.Button == MouseButtons.Right && dgvMain.SelectedRows.Count < 2) { m_setting = true; dgvMain.ClearSelection(); dgvMain.Rows[e.RowIndex].Selected = true; dgvMain.Rows[e.RowIndex].Cells[0].Selected = true; SelectionChange?.Invoke(this, new EventArgs()); m_setting = false; } } private IGameFile m_lastEnter; private void dgvMain_CellMouseEnter(object sender, DataGridViewCellEventArgs e) { if (m_binding) return; var gameFile = GameFileForIndex(e.RowIndex); if (gameFile != m_lastEnter) { m_lastEnter = gameFile; GameFileEnter?.Invoke(this, new GameFileEventArgs(gameFile)); } } private void dgvMain_CellMouseLeave(object sender, DataGridViewCellEventArgs e) { GameFileLeave?.Invoke(this, new GameFileEventArgs(m_lastEnter)); m_lastEnter = null; } private void dgvMain_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { ItemDoubleClick?.Invoke(this, new EventArgs()); } public void SetColumnWidth(string key, int width) { if (width <= 0) return; if (!m_orderLookup.ContainsKey(key)) return; m_orderLookup[key].Width = width; } public int GetColumnWidth(string key) { if (!m_orderLookup.ContainsKey(key)) return 64; return m_orderLookup[key].Width; } public SortOrder GetColumnSort(string key) { foreach(DataGridViewColumn column in dgvMain.Columns) { if (column.Name.Equals(key, StringComparison.CurrentCultureIgnoreCase)) return column.HeaderCell.SortGlyphDirection; } return SortOrder.None; } public string GetSortedColumnKey() { foreach (DataGridViewColumn column in dgvMain.Columns) { if (column.HeaderCell.SortGlyphDirection != SortOrder.None) return column.Name; } return string.Empty; } public string[] GetColumnKeyOrder() { string[] items = new string[m_orderLookup.Count]; int count = 0; foreach (KeyValuePair item in m_orderLookup) { items[count] = m_orderLookup.First(x => x.Value.DisplayIndex == count).Key; count++; } return items; } public void SetSortedColumn(string column, SortOrder sort) { DataGridViewColumn col = dgvMain.Columns.Cast() .FirstOrDefault(x => x.Name.Equals(column, StringComparison.CurrentCultureIgnoreCase)); if (col != null) { col.HeaderCell.SortGlyphDirection = sort; if (m_datasource != null) HandleSort(col.Index, false); } } private void dgvMain_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e) { if (CustomRowColorPaint && CustomRowPaint != null && !m_binding) { CustomRowPaintDataBoundItem = GameFileForIndex(e.RowIndex); CancelEventArgs args = new CancelEventArgs(); CustomRowPaint(this, args); if (!args.Cancel) dgvMain.Rows[e.RowIndex].DefaultCellStyle.ForeColor = CustomRowPaintForeColor; } } public ColumnField[] ColumnFields { get; private set; } public int ToolTipItemIndex { get; private set; } public int ColumnReorderIndex { get; private set; } public bool CustomRowColorPaint { get; set; } public IGameFile CustomRowPaintDataBoundItem { get; private set; } public Color CustomRowPaintForeColor { get; set; } public object DoomLauncherParent { get; set; } private readonly Dictionary m_orderLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); private void dgvMain_KeyPress(object sender, KeyPressEventArgs e) { ViewKeyPress?.Invoke(this, e); } private void dgvMain_KeyDown(object sender, KeyEventArgs e) { ViewKeyDown?.Invoke(this, e); } } } ================================================ FILE: DoomLauncher/Controls/GameFileViewControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 ================================================ FILE: DoomLauncher/Controls/GenericFileView.Designer.cs ================================================ namespace DoomLauncher { partial class GenericFileView { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); this.dgvMain = new DoomLauncher.CDataGridView(); ((System.ComponentModel.ISupportInitialize)(this.dgvMain)).BeginInit(); this.SuspendLayout(); // // dgvMain // this.dgvMain.AllowUserToAddRows = false; this.dgvMain.AllowUserToDeleteRows = false; this.dgvMain.AllowUserToOrderColumns = true; dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this.dgvMain.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1; this.dgvMain.BackgroundColor = System.Drawing.SystemColors.ControlLightLight; this.dgvMain.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal; this.dgvMain.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dgvMain.Dock = System.Windows.Forms.DockStyle.Fill; this.dgvMain.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically; this.dgvMain.GridColor = System.Drawing.SystemColors.ActiveBorder; this.dgvMain.Location = new System.Drawing.Point(0, 0); this.dgvMain.Name = "dgvMain"; this.dgvMain.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; this.dgvMain.Size = new System.Drawing.Size(150, 150); this.dgvMain.TabIndex = 2; this.dgvMain.CellMouseDown += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.dgvMain_CellMouseDown); // // DemoView // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.dgvMain); this.Name = "DemoView"; ((System.ComponentModel.ISupportInitialize)(this.dgvMain)).EndInit(); this.ResumeLayout(false); } #endregion private CDataGridView dgvMain; } } ================================================ FILE: DoomLauncher/Controls/GenericFileView.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using System.Windows.Forms; namespace DoomLauncher { public partial class GenericFileView : BasicFileView { private static readonly string s_dateColumn = "DateCreated"; public GenericFileView() { InitializeComponent(); SetColumnFields(new Tuple[] { new Tuple("Description", "Description"), new Tuple("OriginalFileName", "File"), new Tuple(s_dateColumn, "Created"), new Tuple("SourcePortName", "SourcePort") }); } public override void SetData(IGameFile gameFile) { SetData(dgvMain, gameFile); } public override void ClearData() { dgvMain.DataSource = null; } private void SetColumnFields(IEnumerable> columnFields) { if (columnFields.Any()) { foreach (Tuple item in columnFields) { DataGridViewColumn col = new DataGridViewTextBoxColumn(); col.HeaderText = item.Item2; col.Name = item.Item1; col.DataPropertyName = item.Item1; dgvMain.Columns.Add(col); } dgvMain.Columns[s_dateColumn].DefaultCellStyle.Format = string.Format("{0} {1}", CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern, CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern); dgvMain.Columns[dgvMain.Columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; } } private void dgvMain_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e) { if (e.RowIndex > -1 && e.Button == MouseButtons.Right && dgvMain.SelectedRows.Count < 2) { dgvMain.ClearSelection(); dgvMain.Rows[e.RowIndex].Selected = true; dgvMain.Rows[e.RowIndex].Cells[0].Selected = true; } } protected override List GetSelectedFiles() { List files = new List(); if (dgvMain.SelectedRows.Count > 0) { PropertyInfo pi = dgvMain.SelectedRows[0].DataBoundItem.GetType().GetProperty("FileData"); foreach (DataGridViewRow row in dgvMain.SelectedRows) files.Add(pi.GetValue(row.DataBoundItem) as IFileData); } return files; } } } ================================================ FILE: DoomLauncher/Controls/GenericFileView.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/GlowButton.cs ================================================ using System; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public class GlowButton : Button { private readonly Timer m_timer; private readonly Color m_baseColor; private readonly Color m_baseTextColor; private int m_alpha = 0; private int m_increment = 25; public Color BackGroundGlowColor => ColorTheme.Current.Highlight; public Color TextGlowColor => ColorTheme.Current.WindowLight; public GlowButton() { DoubleBuffered = true; m_timer = new Timer() { Interval = 50 }; m_timer.Tick += timer_Tick; FlatStyle = FlatStyle.Flat; m_baseColor = ColorTheme.Current.Window; m_baseTextColor = ColorTheme.Current.Text; } public void GlowOnce() { m_timer.Start(); BackColor = CreateBlendColor(m_alpha, m_baseColor, BackGroundGlowColor); } void timer_Tick(object sender, EventArgs e) { int testValue = m_alpha + m_increment; if (testValue > 255 || testValue < 0) { if (testValue < 0) { m_timer.Stop(); BackColor = m_baseColor; ForeColor = m_baseTextColor; return; } m_increment = -m_increment; } m_alpha += m_increment; BackColor = CreateBlendColor(m_alpha, m_baseColor, BackGroundGlowColor); ForeColor = CreateBlendColor(m_alpha, m_baseTextColor, TextGlowColor); } protected override void Dispose(bool disposing) { if (disposing) m_timer.Dispose(); base.Dispose(disposing); } private Color CreateBlendColor(int alpha, Color baseColor, Color glowColor) { return AlphaBlend(Color.FromArgb(alpha, glowColor), baseColor); } public Color AlphaBlend(Color A, Color B) { var r = (A.R * A.A / 255) + (B.R * B.A * (255 - A.A) / (255 * 255)); var g = (A.G * A.A / 255) + (B.G * B.A * (255 - A.A) / (255 * 255)); var b = (A.B * A.A / 255) + (B.B * B.A * (255 - A.A) / (255 * 255)); var a = A.A + (B.A * (255 - A.A) / 255); return Color.FromArgb(a, r, g, b); } } } ================================================ FILE: DoomLauncher/Controls/GrowLabel.Designer.cs ================================================ namespace DoomLauncher { partial class GrowLabel { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion } } ================================================ FILE: DoomLauncher/Controls/GrowLabel.cs ================================================ using System; using System.Drawing; using System.IO; using System.Windows.Forms; namespace DoomLauncher { public partial class GrowLabel : Label { private bool mGrowing; public GrowLabel() { AutoSize = false; IsPath = false; } public bool IsPath { get; set; } private void ResizeLabel() { if (mGrowing) return; try { mGrowing = true; Size sz = new Size(Width, int.MaxValue); string text = Text; if (IsPath) text = Text.Replace(Path.DirectorySeparatorChar, ' ').Replace(Path.AltDirectorySeparatorChar, ' '); sz = TextRenderer.MeasureText(text, Font, sz, TextFormatFlags.WordBreak); Height = sz.Height; } finally { mGrowing = false; } } protected override void OnTextChanged(EventArgs e) { base.OnTextChanged(e); ResizeLabel(); } protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); ResizeLabel(); } protected override void OnSizeChanged(EventArgs e) { base.OnSizeChanged(e); ResizeLabel(); } } } ================================================ FILE: DoomLauncher/Controls/PagingControl.Designer.cs ================================================ namespace DoomLauncher { partial class PagingControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.btnPrev = new System.Windows.Forms.Button(); this.btnNext = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); this.lblPage = new System.Windows.Forms.Label(); this.lblPageTotal = new System.Windows.Forms.Label(); this.btnFirst = new System.Windows.Forms.Button(); this.btnLast = new System.Windows.Forms.Button(); this.tblMain.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 9; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 36F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 36F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 12F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 36F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 36F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblMain.Controls.Add(this.btnPrev, 2, 0); this.tblMain.Controls.Add(this.btnNext, 6, 0); this.tblMain.Controls.Add(this.label1, 4, 0); this.tblMain.Controls.Add(this.lblPage, 3, 0); this.tblMain.Controls.Add(this.lblPageTotal, 5, 0); this.tblMain.Controls.Add(this.btnFirst, 1, 0); this.tblMain.Controls.Add(this.btnLast, 7, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 1; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(240, 30); this.tblMain.TabIndex = 2; // // btnPrev // this.btnPrev.Anchor = System.Windows.Forms.AnchorStyles.None; this.btnPrev.Location = new System.Drawing.Point(59, 3); this.btnPrev.Margin = new System.Windows.Forms.Padding(0); this.btnPrev.Name = "btnPrev"; this.btnPrev.Size = new System.Drawing.Size(34, 23); this.btnPrev.TabIndex = 0; this.btnPrev.Text = "<"; this.btnPrev.UseVisualStyleBackColor = true; this.btnPrev.Click += new System.EventHandler(this.BtnPrev_Click); // // btnNext // this.btnNext.Anchor = System.Windows.Forms.AnchorStyles.None; this.btnNext.Location = new System.Drawing.Point(147, 3); this.btnNext.Margin = new System.Windows.Forms.Padding(0); this.btnNext.Name = "btnNext"; this.btnNext.Size = new System.Drawing.Size(34, 23); this.btnNext.TabIndex = 1; this.btnNext.Text = ">"; this.btnNext.UseVisualStyleBackColor = true; this.btnNext.Click += new System.EventHandler(this.BtnNext_Click); // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.None; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(114, 8); this.label1.Margin = new System.Windows.Forms.Padding(0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(12, 13); this.label1.TabIndex = 2; this.label1.Text = "/"; // // lblPage // this.lblPage.Anchor = System.Windows.Forms.AnchorStyles.None; this.lblPage.AutoSize = true; this.lblPage.Location = new System.Drawing.Point(97, 8); this.lblPage.Margin = new System.Windows.Forms.Padding(0); this.lblPage.Name = "lblPage"; this.lblPage.Size = new System.Drawing.Size(13, 13); this.lblPage.TabIndex = 3; this.lblPage.Text = "0"; // // lblPageTotal // this.lblPageTotal.Anchor = System.Windows.Forms.AnchorStyles.None; this.lblPageTotal.AutoSize = true; this.lblPageTotal.Location = new System.Drawing.Point(129, 8); this.lblPageTotal.Margin = new System.Windows.Forms.Padding(0); this.lblPageTotal.Name = "lblPageTotal"; this.lblPageTotal.Size = new System.Drawing.Size(13, 13); this.lblPageTotal.TabIndex = 4; this.lblPageTotal.Text = "0"; // // btnFirst // this.btnFirst.Anchor = System.Windows.Forms.AnchorStyles.None; this.btnFirst.Location = new System.Drawing.Point(23, 3); this.btnFirst.Margin = new System.Windows.Forms.Padding(0); this.btnFirst.Name = "btnFirst"; this.btnFirst.Size = new System.Drawing.Size(34, 23); this.btnFirst.TabIndex = 5; this.btnFirst.Text = "<<"; this.btnFirst.UseVisualStyleBackColor = true; this.btnFirst.Click += new System.EventHandler(this.BtnFirst_Click); // // btnLast // this.btnLast.Anchor = System.Windows.Forms.AnchorStyles.None; this.btnLast.Location = new System.Drawing.Point(183, 3); this.btnLast.Margin = new System.Windows.Forms.Padding(0); this.btnLast.Name = "btnLast"; this.btnLast.Size = new System.Drawing.Size(34, 23); this.btnLast.TabIndex = 6; this.btnLast.Text = ">>"; this.btnLast.UseVisualStyleBackColor = true; this.btnLast.Click += new System.EventHandler(this.BtnLast_Click); // // PagingControl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Name = "PagingControl"; this.Size = new System.Drawing.Size(240, 30); this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Button btnPrev; private System.Windows.Forms.Button btnNext; private TableLayoutPanelDB tblMain; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label lblPage; private System.Windows.Forms.Label lblPageTotal; private System.Windows.Forms.Button btnFirst; private System.Windows.Forms.Button btnLast; } } ================================================ FILE: DoomLauncher/Controls/PagingControl.cs ================================================ using System; using System.Windows.Forms; namespace DoomLauncher { public partial class PagingControl : UserControl { public event EventHandler PageIndexChanged; public int PageIndex { get; private set; } public int Pages { get; private set; } public PagingControl() { InitializeComponent(); DoubleBuffered = true; Stylizer.StylizeControl(this, DesignMode); } public void Init(int records, int recordsPerPage, int initialPage) { Pages = records / recordsPerPage; if (records % recordsPerPage != 0) Pages++; lblPageTotal.Text = Pages.ToString(); var size = TextRenderer.MeasureText(lblPageTotal.Text, lblPageTotal.Font); tblMain.ColumnStyles[3].Width = size.Width; tblMain.ColumnStyles[5].Width = size.Width; if (Pages > 0) SetPageIndex(initialPage, false); else PageIndex = 0; } private void SetPageIndex(int index, bool publishEvent) { if (index < 0) index = 0; if (index >= Pages) index = Pages - 1; lblPage.Text = (index + 1).ToString(); if (PageIndex == index) return; PageIndex = index; if (publishEvent) PageIndexChanged?.Invoke(this, EventArgs.Empty); } private void BtnNext_Click(object sender, EventArgs e) { SetPageIndex(PageIndex + 1, true); } private void BtnPrev_Click(object sender, EventArgs e) { SetPageIndex(PageIndex - 1, true); } private void BtnFirst_Click(object sender, EventArgs e) { SetPageIndex(0, true); } private void BtnLast_Click(object sender, EventArgs e) { SetPageIndex(Pages - 1, true); } } } ================================================ FILE: DoomLauncher/Controls/PagingControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/RatingControl.Designer.cs ================================================ namespace DoomLauncher { partial class RatingControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 5; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20F)); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 1; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(137, 29); this.tblMain.TabIndex = 0; // // RatingControl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Name = "RatingControl"; this.Size = new System.Drawing.Size(137, 29); this.ResumeLayout(false); } #endregion private TableLayoutPanelDB tblMain; } } ================================================ FILE: DoomLauncher/Controls/RatingControl.cs ================================================ using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public partial class RatingControl : UserControl { private readonly int ColumnCount = 5; private int m_selectedRating; private List m_pictures = new List(); public RatingControl() { InitializeComponent(); InitPictures(); } private void InitPictures() { foreach(PictureBox pb in m_pictures) { tblMain.Controls.Remove(pb); pb.Dispose(); } m_pictures.Clear(); for (int i = 0; i < ColumnCount; i++) { PictureBox pb; if (i <= m_selectedRating) pb = CreatePictureBox(DoomLauncher.Properties.Resources.bon2b); else pb = CreatePictureBox(DoomLauncher.Properties.Resources.bon2a); m_pictures.Add(pb); tblMain.Controls.Add(pb, i, 0); } } private PictureBox CreatePictureBox(Image img) { PictureBox pb = new PictureBox { Image = img, SizeMode = PictureBoxSizeMode.StretchImage, Margin = new Padding(2, 2, 2, 2), Dock = DockStyle.Fill }; pb.MouseDown += pb_MouseDown; return pb; } void pb_MouseDown(object sender, MouseEventArgs e) { PictureBox pb = sender as PictureBox; for (int i = 0; i < m_pictures.Count; i++) { if (m_pictures[i].Equals(pb)) { m_selectedRating = i; break; } } InitPictures(); } public int RatingCount { get { return ColumnCount; } } public int SelectedRating { get { return m_selectedRating + 1; } set { m_selectedRating = value - 1; InitPictures(); } } } } ================================================ FILE: DoomLauncher/Controls/RatingControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/ScreenshotView.Designer.cs ================================================ namespace DoomLauncher { partial class ScreenshotView { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.flpScreenshots = new DoomLauncher.FlowLayoutPanelDB(); this.SuspendLayout(); // // flpScreenshots // this.flpScreenshots.AutoScroll = true; this.flpScreenshots.Dock = System.Windows.Forms.DockStyle.Fill; this.flpScreenshots.Location = new System.Drawing.Point(0, 0); this.flpScreenshots.Name = "flpScreenshots"; this.flpScreenshots.Size = new System.Drawing.Size(150, 150); this.flpScreenshots.TabIndex = 0; // // ScreenshotView // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.flpScreenshots); this.Name = "ScreenshotView"; this.ResumeLayout(false); } #endregion private DoomLauncher.FlowLayoutPanelDB flpScreenshots; } } ================================================ FILE: DoomLauncher/Controls/ScreenshotView.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Forms; using DoomLauncher.Handlers; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Threading; using System.Windows.Forms; namespace DoomLauncher { public class RequestScreenshotsEventArgs : EventArgs { public RequestScreenshotsEventArgs(IGameFile gameFile) { GameFile = gameFile; } public IGameFile GameFile { get; private set; } } class PictureItem { public PictureItem(PictureBox pb) { PictureBox = pb; } public PictureBox PictureBox { get; private set; } public IFileData CurrentFile { get; set; } public bool Skip { get; set; } } public partial class ScreenshotView : BasicFileView { private const double AspectWidth = 16; private const double AspectHeight = 9; private readonly Dictionary m_lookup = new Dictionary(); private readonly List m_pictureBoxes = new List(); private List m_screenshots = new List(); private int m_pictureWidth; private CancellationTokenSource m_ct = new CancellationTokenSource(); private bool m_imageWorkerComplete = true; private readonly Stopwatch m_clickStopwatch = new Stopwatch(); private readonly ToolTipGroup m_toolTipGroup = new ToolTipGroup(); public event EventHandler RequestScreenshots; public ScreenshotView() { InitializeComponent(); flpScreenshots.Click += FlpScreenshots_Click; } public void SetPictureWidth(int pictureWidth) { m_pictureWidth = pictureWidth; InitPictureBoxes(); } private void FlpScreenshots_Click(object sender, EventArgs e) { SelectedFile = null; foreach (PictureBox pbSet in m_lookup.Keys) SetSelectedStyle(pbSet, false); } public override void SetData(IGameFile gameFile) { flpScreenshots.BackColor = ColorTheme.Current.GridBackground; SelectedFile = null; foreach (PictureBox pbSet in m_lookup.Keys) SetSelectedStyle(pbSet, false); flpScreenshots.SuspendLayout(); flpScreenshots.Controls.Clear(); flpScreenshots.ResumeLayout(); m_lookup.Clear(); if (gameFile != null && gameFile.GameFileID.HasValue) RequestScreenshots?.Invoke(this, new RequestScreenshotsEventArgs(gameFile)); } public override void ClearData() { SelectedFile = null; flpScreenshots.SuspendLayout(); flpScreenshots.Controls.Clear(); flpScreenshots.ResumeLayout(); m_lookup.Clear(); } public override bool ShowCreateNew(IWin32Window parent, IDataSourceAdapter adapter, ISourcePortData sourcePort, string filename, bool isMultiImport, out NewFileData newFileData) { newFileData = null; ScreenshotEditForm screenshotEditForm = new ScreenshotEditForm(); screenshotEditForm.StartPosition = FormStartPosition.CenterParent; screenshotEditForm.SetData(adapter.GetGameFile(GameFile.FileName), null); if (screenshotEditForm.ShowDialog() != DialogResult.OK) return false; newFileData = new NewFileData() { UserTitle = screenshotEditForm.Title, UserDescription = screenshotEditForm.Description, Map = screenshotEditForm.Map, SourcePortID = -1, }; return true; } public override bool New() { if (base.New()) { ThumbnailManager.UpdateThumbnail(GameFile); return true; } return false; } public override bool Edit() { if (SelectedFile == null) return false; return ScreenshotEditForm.ShowDialogAndUpdate(this, DataSourceAdapter, DataSourceAdapter.GetGameFile(GameFile.FileName), SelectedFile); } public override bool Delete() { if (base.Delete()) { ThumbnailManager.UpdateThumbnail(GameFile); return true; } return false; } public override bool MoveFileOrderUp() { if (base.MoveFileOrderUp()) { ThumbnailManager.UpdateThumbnail(GameFile); return true; } return false; } public override bool MoveFileOrderDown() { if (base.MoveFileOrderDown()) { ThumbnailManager.UpdateThumbnail(GameFile); return true; } return false; } public override bool SetFileOrderFirst() { if (base.SetFileOrderFirst()) { ThumbnailManager.UpdateThumbnail(GameFile); return true; } return false; } private void InitPictureBoxes() { lock (m_pictureBoxes) { foreach (var item in m_pictureBoxes) { PictureBox pb = item.PictureBox; if (flpScreenshots.Controls.Contains(pb)) flpScreenshots.Controls.Remove(pb); if (pb.Image != null) pb.Image.Dispose(); } m_pictureBoxes.Clear(); ExpandPictureBoxes(50); } } private void ExpandPictureBoxes(int count) { for (int i = 0; i < count; i++) m_pictureBoxes.Add(new PictureItem(CreatePictureBox())); foreach (var pb in m_pictureBoxes) m_toolTipGroup.SetToolTip(pb.PictureBox, "Double-click to view"); } public void SetScreenshots(List screenshots) { m_ct.Cancel(); while (!m_imageWorkerComplete) Thread.Sleep(10); m_imageWorkerComplete = false; m_ct = new CancellationTokenSource(); flpScreenshots.SuspendLayout(); m_screenshots = screenshots.ToList(); m_lookup.Clear(); lock (m_pictureBoxes) { if (m_screenshots.Count > m_pictureBoxes.Count) ExpandPictureBoxes(m_screenshots.Count - m_pictureBoxes.Count); List.Enumerator enumerator = m_pictureBoxes.GetEnumerator(); foreach (IFileData screen in screenshots) { enumerator.MoveNext(); if (enumerator.Current == null) break; PictureBox pbScreen = enumerator.Current.PictureBox; enumerator.Current.Skip = true; flpScreenshots.Controls.Add(pbScreen); m_lookup.Add(pbScreen, screen); try { if (screen.IsUrl) { pbScreen.CancelAsync(); pbScreen.Image = null; pbScreen.LoadAsync(screen.FileName); } else { if (screen.Equals(enumerator.Current.CurrentFile)) continue; enumerator.Current.Skip = false; Image image = pbScreen.Image; pbScreen.Image = null; if (image != null) image.Dispose(); } } catch { pbScreen.ImageLocation = string.Empty; } } } var thread = new Thread(new ThreadStart(SetImages)); thread.Start(); flpScreenshots.ResumeLayout(); } private void SetImages() { lock (m_pictureBoxes) { List.Enumerator enumerator = m_pictureBoxes.GetEnumerator(); foreach (var screen in m_screenshots) { enumerator.MoveNext(); if (m_ct.IsCancellationRequested || enumerator.Current == null) break; if (enumerator.Current.Skip) continue; PictureBox pbScreen = enumerator.Current.PictureBox; string file = Path.Combine(DataDirectory.GetFullPath(), screen.FileName); try { using (var image = Image.FromFile(file)) pbScreen.Image = image.FixedSize(pbScreen.Width, pbScreen.Height, Color.Black); } catch { // Most likely file doesn't exist... // Can also be out of memory exception } enumerator.Current.CurrentFile = screen; } enumerator.MoveNext(); while (enumerator.Current != null) { enumerator.Current.CurrentFile = null; enumerator.MoveNext(); } m_imageWorkerComplete = true; } } private PictureBox CreatePictureBox() { PictureBox pbScreen = new PictureBox { WaitOnLoad = false, BackColor = Color.Black, Width = m_pictureWidth }; pbScreen.Height = Convert.ToInt32(pbScreen.Width / (AspectWidth / AspectHeight)); pbScreen.SizeMode = PictureBoxSizeMode.Zoom; pbScreen.Margin = new Padding(7); pbScreen.MouseDown += pbScreen_MouseDown; pbScreen.DoubleClick += PbScreen_DoubleClick; pbScreen.Paint += PbScreen_Paint; return pbScreen; } private void PbScreen_Paint(object sender, PaintEventArgs e) { PictureBox pb = sender as PictureBox; if (pb == null || !m_lookup.TryGetValue(pb, out var fileData)) return; string title = FileData.GetTitle(fileData); if (string.IsNullOrEmpty(title)) return; Util.DrawImageTitleBar(title, pb.ClientRectangle, e, Brushes.White, Font); } private void PbScreen_DoubleClick(object sender, EventArgs e) { HandleDoubleClick(sender); } void pbScreen_MouseDown(object sender, MouseEventArgs e) { HandleClick(sender, e); } private void HandleDoubleClick(object sender) { if (sender is PictureBox pb && m_lookup.ContainsKey(pb)) { HandleClick(pb, null); View(); } } public override void View() { if (m_screenshots.Count > 0) { ScreenshotViewerForm screenshotForm = new ScreenshotViewerForm(); screenshotForm.StartPosition = FormStartPosition.CenterParent; var imagePaths = m_screenshots.Select(x => Path.Combine(DataDirectory.GetFullPath(), x.FileName)).ToArray(); screenshotForm.SetImageFileData(DataSourceAdapter, DataSourceAdapter.GetGameFile(GameFile.FileName), imagePaths, m_screenshots); screenshotForm.WindowState = FormWindowState.Maximized; screenshotForm.Shown += ScreenshotForm_Shown; screenshotForm.ShowDialog(this); SetData(GameFile); } } private void ScreenshotForm_Shown(object sender, EventArgs e) { if (!(sender is ScreenshotViewerForm screenshotForm)) return; if (SelectedFile != null) screenshotForm.SetImage(Path.Combine(DataDirectory.GetFullPath(), SelectedFile.FileName)); } private void HandleClick(object sender, MouseEventArgs e) { if (!(sender is PictureBox pb) || !m_lookup.TryGetValue(pb, out var fileData)) return; if (SelectedFile != null && SelectedFile.FileID == fileData.FileID) { if (e != null && e.Button == MouseButtons.Left && m_clickStopwatch.ElapsedMilliseconds < SystemInformation.DoubleClickTime) HandleDoubleClick(sender); m_clickStopwatch.Restart(); } else { m_clickStopwatch.Restart(); foreach (var pbSet in m_lookup.Keys) SetSelectedStyle(pbSet, false); SelectedFile = fileData; SetSelectedStyle(pb, true); } if (e != null && e.Button == MouseButtons.Right) m_menu.Show(pb.PointToScreen(e.Location)); } private void SetSelectedStyle(PictureBox pb, bool selected) { if (InvokeRequired) { Invoke(new Action(SetSelectedStyle), pb, selected); return; } if (selected) { pb.BackColor = ColorTheme.Current.Highlight; pb.Padding = new Padding(2); pb.BorderStyle = BorderStyle.Fixed3D; } else { pb.BackColor = Color.Black; pb.Padding = new Padding(0); pb.BorderStyle = BorderStyle.None; } } protected override IFileData[] Files => m_lookup.Values.ToArray(); protected override List GetSelectedFiles() { var files = new List(); if (SelectedFile != null) files.Add(SelectedFile); return files; } private IFileData SelectedFile { get; set; } public override bool EditAllowed => true; public override IList MenuOptions { get { return new MenuOptions[] { new MenuOptions() { Title = "Sort by Map", Action = SortByMap } }; } } private bool SortByMap() { if (Files.Count() <= 1) return false; var files = Files.ToList(); var firstFile = files[0]; files.Sort(new FileMapComparer()); int order = 0; foreach (var file in files) file.FileOrder = order++; foreach (IFileData fileUpdate in files) DataSourceAdapter.UpdateFile(fileUpdate); if (firstFile.FileOrder != 0) ThumbnailManager.UpdateThumbnail(GameFile); return true; } } } ================================================ FILE: DoomLauncher/Controls/ScreenshotView.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/SearchControl.Designer.cs ================================================ namespace DoomLauncher { partial class SearchControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.txtSearch = new System.Windows.Forms.TextBox(); this.btnFilters = new DoomLauncher.FormButton(); this.pbSearch = new System.Windows.Forms.PictureBox(); this.tblMain.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbSearch)).BeginInit(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 3; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.tblMain.Controls.Add(this.txtSearch, 1, 0); this.tblMain.Controls.Add(this.btnFilters, 2, 0); this.tblMain.Controls.Add(this.pbSearch, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 1; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(415, 23); this.tblMain.TabIndex = 2; // // txtSearch // this.txtSearch.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.txtSearch.Dock = System.Windows.Forms.DockStyle.Fill; this.txtSearch.Location = new System.Drawing.Point(26, 0); this.txtSearch.Margin = new System.Windows.Forms.Padding(0); this.txtSearch.Name = "txtSearch"; this.txtSearch.Size = new System.Drawing.Size(359, 22); this.txtSearch.TabIndex = 1; // // btnFilters // this.btnFilters.Dock = System.Windows.Forms.DockStyle.Left; this.btnFilters.Location = new System.Drawing.Point(385, 0); this.btnFilters.Margin = new System.Windows.Forms.Padding(0); this.btnFilters.Name = "btnFilters"; this.btnFilters.Size = new System.Drawing.Size(30, 23); this.btnFilters.TabIndex = 2; this.btnFilters.Text = "▼"; this.btnFilters.TextAlign = System.Drawing.ContentAlignment.TopCenter; this.btnFilters.UseVisualStyleBackColor = true; this.btnFilters.Click += new System.EventHandler(this.btnFilters_Click); // // pbSearch // this.pbSearch.Location = new System.Drawing.Point(3, 5); this.pbSearch.Margin = new System.Windows.Forms.Padding(3, 5, 0, 3); this.pbSearch.Name = "pbSearch"; this.pbSearch.Size = new System.Drawing.Size(20, 15); this.pbSearch.TabIndex = 3; this.pbSearch.TabStop = false; // // SearchControl // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "SearchControl"; this.Size = new System.Drawing.Size(415, 23); this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbSearch)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TextBox txtSearch; private TableLayoutPanelDB tblMain; private FormButton btnFilters; private System.Windows.Forms.PictureBox pbSearch; } } ================================================ FILE: DoomLauncher/Controls/SearchControl.cs ================================================ using PresentationControls; using System; using System.Collections.Generic; using System.Windows.Forms; namespace DoomLauncher { public partial class SearchControl : UserControl { public event EventHandler SearchTextChanged; public event EventHandler SearchTextChangedNoDebounce; public event PreviewKeyDownEventHandler SearchTextKeyPreviewDown; private readonly Controls.CheckBoxList m_checkBoxList = new Controls.CheckBoxList(); private readonly System.Timers.Timer m_textTimer = new System.Timers.Timer(); private bool m_fireEvent = true; public SearchControl() { InitializeComponent(); Stylizer.StylizeControl(m_checkBoxList, DesignMode); pbSearch.Image = Icons.Search; txtSearch.TextChanged += txtSearch_TextChanged; txtSearch.PreviewKeyDown += TxtSearch_PreviewKeyDown; m_textTimer.AutoReset = false; m_textTimer.Interval = 400; m_textTimer.Elapsed += TextTimer_Elapsed; } public void SetSearchText(string text, bool fireEvent = false) { m_fireEvent = fireEvent; txtSearch.Text = text; m_fireEvent = true; } public void SetShowDropDown(bool set) { DpiScale dpiScale = new DpiScale(CreateGraphics()); tblMain.ColumnStyles[2].Width = set ? dpiScale.ScaleIntX(30) : 0; } private void TextTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (InvokeRequired) Invoke(new Action(InvokeSearchTextChanged)); else InvokeSearchTextChanged(); } private void InvokeSearchTextChanged() { SearchTextChanged?.Invoke(this, EventArgs.Empty); } private void TxtSearch_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) { SearchTextKeyPreviewDown?.Invoke(this, e); } void txtSearch_TextChanged(object sender, EventArgs e) { if (!m_fireEvent) return; SearchTextChangedNoDebounce?.Invoke(this, EventArgs.Empty); m_textTimer.Stop(); m_textTimer.Start(); } public void SetSearchFilter(string item, bool check) { m_checkBoxList.SetChecked(item, check); } public bool GetSearchFilter(string item) { return m_checkBoxList.IsChecked(item); } public string[] GetSearchFilters() { return m_checkBoxList.Items.ToArray(); } public string[] GetSelectedSearchFilters() { return m_checkBoxList.GetCheckedItems(); } public void SetSearchFilters(IEnumerable items) { m_checkBoxList.SetItems(items); } public string SearchText { get => txtSearch.Text; set => txtSearch.Text = value; } private void btnFilters_Click(object sender, EventArgs e) { Popup popup = new Popup(m_checkBoxList) { Width = btnFilters.Location.X + btnFilters.Width, Height = m_checkBoxList.Height }; popup.Show(txtSearch); } } } ================================================ FILE: DoomLauncher/Controls/SearchControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/SlideShowPictureBox.Designer.cs ================================================ namespace DoomLauncher { partial class SlideShowPictureBox { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.pbImage = new System.Windows.Forms.PictureBox(); ((System.ComponentModel.ISupportInitialize)(this.pbImage)).BeginInit(); this.SuspendLayout(); // // pbImage // this.pbImage.Dock = System.Windows.Forms.DockStyle.Fill; this.pbImage.Location = new System.Drawing.Point(0, 0); this.pbImage.Name = "pbImage"; this.pbImage.Size = new System.Drawing.Size(150, 150); this.pbImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pbImage.TabIndex = 0; this.pbImage.TabStop = false; // // SlideShowPictureBox // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.pbImage); this.Name = "SlideShowPictureBox"; ((System.ComponentModel.ISupportInitialize)(this.pbImage)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.PictureBox pbImage; } } ================================================ FILE: DoomLauncher/Controls/SlideShowPictureBox.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Diagnostics; namespace DoomLauncher { public partial class SlideShowPictureBox : UserControl { private const int ImageMilliseconds = 4000; private const int FadeMilliseconds = 500; private const int FadeTimes = 10; private readonly Timer m_timer = new Timer(); private readonly Brush m_bgBrush = new SolidBrush(Color.Black); private readonly Stopwatch m_stopwatch = new Stopwatch(); private Stopwatch m_fadeOut = new Stopwatch(); private int m_index = 0; private int m_fadeCount = 0; private int m_lastFadeDiff = 0; private List m_images = new List(); private float m_alpha = 0.0F; private SlideshowState m_state = SlideshowState.SetImage; private Image m_currentImage; private Image m_drawImage; private Graphics m_currentGraphics; public float ImageAlpha => m_alpha; public int ImageIndex => m_index; private enum SlideshowState { SetImage, FadeIn, Wait, FadeOut } public int ImageCount => m_images.Count; public event EventHandler ImageChanged; public event EventHandler ImagePaint; public SlideShowPictureBox() { InitializeComponent(); pbImage.Paint += PbImage_Paint; m_timer.Interval = FadeMilliseconds / FadeTimes; m_timer.Tick += M_timer_Tick; Resize += SlideShowPictureBox_Resize; } private void PbImage_Paint(object sender, PaintEventArgs e) { pbImage.BackColor = ColorTheme.Current.ImageBackground; ImagePaint?.Invoke(this, e); } public void Stop() { if (m_images.Count > 0) { SetImages(m_images); m_timer.Stop(); } } public void Resume() { if (m_images.Count > 0) SetImages(m_images); } public void SetImage(Image image) { ClearImage(); pbImage.Image = image; ImageChanged?.Invoke(this, m_index); } public Image GetImage() => pbImage.Image; public bool SetImages(List imagePaths, int startIndex = 0) { imagePaths = imagePaths.Where(x => File.Exists(x)).ToList(); if (imagePaths.Count == 0) { ClearImage(); m_images.Clear(); m_timer.Stop(); return false; } if (startIndex < 0 || startIndex > imagePaths.Count) startIndex = imagePaths.Count - 1; m_alpha = 1.0F; m_index = startIndex; m_images = imagePaths; m_state = SlideshowState.Wait; m_timer.Stop(); pbImage.CancelAsync(); // Don't cycle with one image if (imagePaths.Count == 1) { pbImage.ImageLocation = m_images[m_index]; return true; } m_timer.Start(); m_fadeOut.Restart(); SetImage(); return true; } public void ClearImage() { pbImage.CancelAsync(); m_images = new List(); m_timer.Stop(); pbImage.ImageLocation = string.Empty; } private void SlideShowPictureBox_Resize(object sender, EventArgs e) { SetImage(); } private void HandleState() { switch (m_state) { case SlideshowState.SetImage: m_state = SlideshowState.FadeIn; m_alpha = 0.0F; m_fadeOut.Restart(); SetImage(); m_fadeCount = FadeTimes; break; case SlideshowState.FadeIn: Fade(1.0F / FadeTimes); if (m_fadeCount == 0) m_state = SlideshowState.Wait; break; case SlideshowState.Wait: if (m_fadeOut.ElapsedMilliseconds >= ImageMilliseconds - FadeMilliseconds) { m_fadeCount = FadeTimes; m_state = SlideshowState.FadeOut; } break; case SlideshowState.FadeOut: Fade(-1.0F / FadeTimes); if (m_fadeCount == 0) { m_index++; m_index %= m_images.Count; m_state = SlideshowState.SetImage; } break; } } private void M_timer_Tick(object sender, EventArgs e) { HandleState(); } private void Fade(float step) { m_stopwatch.Start(); m_fadeCount--; m_alpha += step; SetTransparency(); // SetTransparency uses DrawImage and becomes slower as the image becomes larger // If the blend takes longer than expected then skip the next one to keep the overall fade time roughly the same if (m_stopwatch.ElapsedMilliseconds + m_lastFadeDiff >= FadeMilliseconds / FadeTimes) { m_lastFadeDiff = (int)m_stopwatch.ElapsedMilliseconds - (FadeMilliseconds / FadeTimes); m_fadeCount--; m_alpha += step; if (m_fadeCount <= 0) { if (step < 0) m_alpha = 0.0F; else m_alpha = 1.0F; m_fadeCount = 0; } } else { m_lastFadeDiff = 0; } m_stopwatch.Reset(); } private void SetImage() { if (m_images.Count == 0) { pbImage.ImageLocation = string.Empty; return; } try { pbImage.Image = null; InitBlendCache(); pbImage.Image = m_drawImage; SetTransparency(); } catch { // File data is not accessible or out of memory, ignore for now } } private void InitBlendCache() { m_currentImage?.Dispose(); m_currentGraphics?.Dispose(); m_drawImage?.Dispose(); using (var image = ImageExtensions.FromFileOrDefault(m_images[m_index])) m_currentImage = image.FixedSize(pbImage.Width, pbImage.Height, Color.Black); m_drawImage = new Bitmap(m_currentImage.Width, m_currentImage.Height); m_currentGraphics = Graphics.FromImage(m_drawImage); } private void SetTransparency() { if (m_currentImage == null) return; try { if (m_alpha >= 1.0F) { pbImage.Image = m_currentImage; return; } ColorMatrix colorMatrix = new ColorMatrix(new float[][] { new [] { 1.0F, 0.0F, 0.0F, 0.0F, 0.0F }, new [] { 0.0F, 1.0F, 0.0F, 0.0F, 0.0F }, new [] { 0.0F, 0.0F, 1.0F, 0.0F, 0.0F }, new [] { 0.0F, 0.0F, 0.0F, 0.0F, 0.0F }, new [] { 0.0F, 0.0F, 0.0F, m_alpha, 1.0F }, }); Rectangle rect = new Rectangle(0, 0, m_currentImage.Width, m_currentImage.Height); m_currentGraphics.FillRectangle(m_bgBrush, rect); ImageAttributes imageAttrs = new ImageAttributes(); imageAttrs.SetColorMatrix(colorMatrix); m_currentGraphics.DrawImage(m_currentImage, rect, 0, 0, m_drawImage.Width, m_drawImage.Height, GraphicsUnit.Pixel, imageAttrs); pbImage.Image = m_drawImage; pbImage.Refresh(); } catch { // Sometimes this can randomly fail, can be timing or whatever... just ignore } } } } ================================================ FILE: DoomLauncher/Controls/SlideShowPictureBox.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/SourcePortEdit.Designer.cs ================================================ namespace DoomLauncher { partial class SourcePortEdit { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.label1 = new System.Windows.Forms.Label(); this.txtName = new System.Windows.Forms.TextBox(); this.tblExec = new System.Windows.Forms.TableLayoutPanel(); this.txtExec = new System.Windows.Forms.TextBox(); this.btnBrowse = new System.Windows.Forms.Button(); this.label3 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.txtExtensions = new System.Windows.Forms.TextBox(); this.lblParameters = new System.Windows.Forms.Label(); this.txtParameters = new System.Windows.Forms.TextBox(); this.label4 = new System.Windows.Forms.Label(); this.tblAltSave = new System.Windows.Forms.TableLayoutPanel(); this.txtAltSave = new System.Windows.Forms.TextBox(); this.btnAltSaveBrowse = new System.Windows.Forms.Button(); this.label5 = new System.Windows.Forms.Label(); this.lblFileOption = new System.Windows.Forms.Label(); this.txtFileOption = new System.Windows.Forms.TextBox(); this.chkArchive = new System.Windows.Forms.CheckBox(); this.tblMain.SuspendLayout(); this.tblExec.SuspendLayout(); this.tblAltSave.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 2; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 180F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.label2, 0, 2); this.tblMain.Controls.Add(this.txtExtensions, 1, 2); this.tblMain.Controls.Add(this.lblParameters, 0, 3); this.tblMain.Controls.Add(this.txtParameters, 1, 3); this.tblMain.Controls.Add(this.label4, 0, 4); this.tblMain.Controls.Add(this.tblAltSave, 1, 4); this.tblMain.Controls.Add(this.label5, 0, 5); this.tblMain.Controls.Add(this.lblFileOption, 0, 6); this.tblMain.Controls.Add(this.txtFileOption, 1, 6); this.tblMain.Controls.Add(this.chkArchive, 1, 5); this.tblMain.Controls.Add(this.label3, 0, 0); this.tblMain.Controls.Add(this.tblExec, 1, 0); this.tblMain.Controls.Add(this.label1, 0, 1); this.tblMain.Controls.Add(this.txtName, 1, 1); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 8; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblMain.Size = new System.Drawing.Size(465, 725); this.tblMain.TabIndex = 0; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(4, 47); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(51, 20); this.label1.TabIndex = 0; this.label1.Text = "Name"; // // txtName // this.txtName.Dock = System.Windows.Forms.DockStyle.Fill; this.txtName.Location = new System.Drawing.Point(184, 43); this.txtName.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtName.Name = "txtName"; this.txtName.Size = new System.Drawing.Size(277, 26); this.txtName.TabIndex = 4; // // tblExec // this.tblExec.ColumnCount = 2; this.tblExec.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblExec.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); this.tblExec.Controls.Add(this.txtExec, 0, 0); this.tblExec.Controls.Add(this.btnBrowse, 1, 0); this.tblExec.Dock = System.Windows.Forms.DockStyle.Fill; this.tblExec.Location = new System.Drawing.Point(180, 0); this.tblExec.Margin = new System.Windows.Forms.Padding(0); this.tblExec.Name = "tblExec"; this.tblExec.RowCount = 1; this.tblExec.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblExec.Size = new System.Drawing.Size(285, 38); this.tblExec.TabIndex = 5; // // txtExec // this.txtExec.Dock = System.Windows.Forms.DockStyle.Fill; this.txtExec.Location = new System.Drawing.Point(4, 5); this.txtExec.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtExec.Name = "txtExec"; this.txtExec.Size = new System.Drawing.Size(157, 26); this.txtExec.TabIndex = 0; // // btnBrowse // this.btnBrowse.Location = new System.Drawing.Point(165, 0); this.btnBrowse.Margin = new System.Windows.Forms.Padding(0); this.btnBrowse.Name = "btnBrowse"; this.btnBrowse.Size = new System.Drawing.Size(99, 38); this.btnBrowse.TabIndex = 1; this.btnBrowse.Text = "Browse"; this.btnBrowse.UseVisualStyleBackColor = true; this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click); // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(4, 9); this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(88, 20); this.label3.TabIndex = 1; this.label3.Text = "Executable"; // // label2 // this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(4, 85); this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(166, 20); this.label2.TabIndex = 2; this.label2.Text = "Supported Extensions"; // // txtExtensions // this.txtExtensions.Dock = System.Windows.Forms.DockStyle.Fill; this.txtExtensions.Location = new System.Drawing.Point(184, 81); this.txtExtensions.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtExtensions.Name = "txtExtensions"; this.txtExtensions.Size = new System.Drawing.Size(277, 26); this.txtExtensions.TabIndex = 6; // // lblParameters // this.lblParameters.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblParameters.AutoSize = true; this.lblParameters.Location = new System.Drawing.Point(4, 123); this.lblParameters.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblParameters.Name = "lblParameters"; this.lblParameters.Size = new System.Drawing.Size(91, 20); this.lblParameters.TabIndex = 8; this.lblParameters.Text = "Parameters"; // // txtParameters // this.txtParameters.Dock = System.Windows.Forms.DockStyle.Fill; this.txtParameters.Location = new System.Drawing.Point(184, 119); this.txtParameters.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtParameters.Name = "txtParameters"; this.txtParameters.Size = new System.Drawing.Size(277, 26); this.txtParameters.TabIndex = 9; // // label4 // this.label4.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(4, 161); this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(135, 20); this.label4.TabIndex = 10; this.label4.Text = "Alt Save Directory"; // // tblAltSave // this.tblAltSave.ColumnCount = 2; this.tblAltSave.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblAltSave.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); this.tblAltSave.Controls.Add(this.txtAltSave, 0, 0); this.tblAltSave.Controls.Add(this.btnAltSaveBrowse, 1, 0); this.tblAltSave.Dock = System.Windows.Forms.DockStyle.Fill; this.tblAltSave.Location = new System.Drawing.Point(180, 152); this.tblAltSave.Margin = new System.Windows.Forms.Padding(0); this.tblAltSave.Name = "tblAltSave"; this.tblAltSave.RowCount = 1; this.tblAltSave.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblAltSave.Size = new System.Drawing.Size(285, 38); this.tblAltSave.TabIndex = 11; // // txtAltSave // this.txtAltSave.Dock = System.Windows.Forms.DockStyle.Fill; this.txtAltSave.Location = new System.Drawing.Point(4, 5); this.txtAltSave.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtAltSave.Name = "txtAltSave"; this.txtAltSave.Size = new System.Drawing.Size(157, 26); this.txtAltSave.TabIndex = 0; // // btnAltSaveBrowse // this.btnAltSaveBrowse.Location = new System.Drawing.Point(165, 0); this.btnAltSaveBrowse.Margin = new System.Windows.Forms.Padding(0); this.btnAltSaveBrowse.Name = "btnAltSaveBrowse"; this.btnAltSaveBrowse.Size = new System.Drawing.Size(99, 38); this.btnAltSaveBrowse.TabIndex = 1; this.btnAltSaveBrowse.Text = "Browse"; this.btnAltSaveBrowse.UseVisualStyleBackColor = true; this.btnAltSaveBrowse.Click += new System.EventHandler(this.btnAltSaveBrowse_Click); // // label5 // this.label5.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label5.AutoSize = true; this.label5.Location = new System.Drawing.Point(4, 199); this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(61, 20); this.label5.TabIndex = 12; this.label5.Text = "Archive"; // // lblFileOption // this.lblFileOption.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblFileOption.AutoSize = true; this.lblFileOption.Location = new System.Drawing.Point(4, 237); this.lblFileOption.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblFileOption.Name = "lblFileOption"; this.lblFileOption.Size = new System.Drawing.Size(85, 20); this.lblFileOption.TabIndex = 3; this.lblFileOption.Text = "File Option"; // // txtFileOption // this.txtFileOption.Dock = System.Windows.Forms.DockStyle.Fill; this.txtFileOption.Location = new System.Drawing.Point(184, 233); this.txtFileOption.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtFileOption.Name = "txtFileOption"; this.txtFileOption.Size = new System.Drawing.Size(277, 26); this.txtFileOption.TabIndex = 7; // // chkArchive // this.chkArchive.Anchor = System.Windows.Forms.AnchorStyles.Left; this.chkArchive.AutoSize = true; this.chkArchive.Location = new System.Drawing.Point(183, 198); this.chkArchive.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.chkArchive.Name = "chkArchive"; this.chkArchive.Size = new System.Drawing.Size(22, 21); this.chkArchive.TabIndex = 13; this.chkArchive.UseVisualStyleBackColor = true; // // SourcePortEdit // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "SourcePortEdit"; this.Size = new System.Drawing.Size(465, 725); this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.tblExec.ResumeLayout(false); this.tblExec.PerformLayout(); this.tblAltSave.ResumeLayout(false); this.tblAltSave.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.TextBox txtExtensions; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.TextBox txtName; private System.Windows.Forms.TableLayoutPanel tblExec; private System.Windows.Forms.TextBox txtExec; private System.Windows.Forms.Button btnBrowse; private System.Windows.Forms.Label lblFileOption; private System.Windows.Forms.TextBox txtFileOption; private System.Windows.Forms.Label lblParameters; private System.Windows.Forms.TextBox txtParameters; private System.Windows.Forms.Label label4; private System.Windows.Forms.TableLayoutPanel tblAltSave; private System.Windows.Forms.TextBox txtAltSave; private System.Windows.Forms.Button btnAltSaveBrowse; private System.Windows.Forms.Label label5; private System.Windows.Forms.CheckBox chkArchive; } } ================================================ FILE: DoomLauncher/Controls/SourcePortEdit.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.Stylize; using System; using System.IO; using System.Windows.Forms; namespace DoomLauncher { public partial class SourcePortEdit : UserControl { private string m_directory, m_exec; private SourcePortLaunchType m_launchType; public SourcePortEdit() { InitializeComponent(); txtFileOption.Text = "-file"; //default for source port, currently user will not see this but it will be populated in the database Stylizer.StylizeControl(this, DesignMode); } public void SetSupportedExtensions(string text) { txtExtensions.Text = text; } public void ShowOptions(bool set) { var controls = new Control[] { lblFileOption, txtFileOption }; foreach (var ctrl in controls) ctrl.Visible = set; if (set) txtFileOption.Text = string.Empty; } public void SetDataSource(ISourcePortData sourcePort) { m_directory = sourcePort.Directory.GetPossiblyRelativePath(); m_exec = sourcePort.Executable; m_launchType = sourcePort.LaunchType; txtName.Text = txtExec.Text = txtExtensions.Text = txtFileOption.Text = txtParameters.Text = txtAltSave.Text = string.Empty; if (!string.IsNullOrEmpty(sourcePort.Name)) txtName.Text = sourcePort.Name; if (sourcePort.Directory != null && sourcePort.Executable != null) txtExec.Text = sourcePort.Executable; if (!string.IsNullOrEmpty(sourcePort.SupportedExtensions)) txtExtensions.Text = sourcePort.SupportedExtensions; if (!string.IsNullOrEmpty(sourcePort.FileOption)) txtFileOption.Text = sourcePort.FileOption; if (!string.IsNullOrEmpty(sourcePort.ExtraParameters)) txtParameters.Text = sourcePort.ExtraParameters; if (sourcePort.AltSaveDirectory != null) txtAltSave.Text = sourcePort.AltSaveDirectory.GetPossiblyRelativePath(); chkArchive.Checked = sourcePort.Archived; } public void UpdateDataSource(ISourcePortData sourcePort) { sourcePort.Name = txtName.Text; sourcePort.Directory = new LauncherPath(m_directory); sourcePort.Executable = m_exec; sourcePort.SupportedExtensions = txtExtensions.Text; sourcePort.FileOption = txtFileOption.Text; sourcePort.ExtraParameters = txtParameters.Text; sourcePort.AltSaveDirectory = new LauncherPath(txtAltSave.Text); sourcePort.Archived = chkArchive.Checked; } public string SourcePortName { get { return txtName.Text; } } public string SourcePortExec { get { return txtExec.Text; } } public LauncherPath GetSourcePortDirectory() => new LauncherPath(m_directory); private string GetFilter() { if (m_launchType == SourcePortLaunchType.Doom64) return "Doom 64 Executable (DOOM64_x64.exe)|DOOM64_x64.exe|All Files (*.*)|*.*"; else return "Executable (*.exe)|*.exe|All Files (*.*)|*.*"; } private void btnBrowse_Click(object sender, EventArgs e) { OpenFileDialog dialog = new OpenFileDialog(); dialog.Filter = GetFilter(); dialog.InitialDirectory = m_directory; if (dialog.ShowDialog(this) == DialogResult.OK) { string file = LauncherPath.GetRelativePath(dialog.FileName); string exec = Path.GetFileName(file); string directory = file.Replace(exec, string.Empty); if (string.IsNullOrEmpty(directory)) { StyledMessageBox.Show(this, "The source port can't be in the same directory as Doom Launcher.", "Invalid Path", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } m_exec = exec; m_directory = directory; txtExec.Text = m_exec; if (string.IsNullOrEmpty(txtName.Text)) txtName.Text = Path.GetFileNameWithoutExtension(file); } } private void btnAltSaveBrowse_Click(object sender, EventArgs e) { FolderBrowserDialog dialog = new FolderBrowserDialog(); if (dialog.ShowDialog(this) == DialogResult.OK) txtAltSave.Text = LauncherPath.GetRelativePath(dialog.SelectedPath); } } } ================================================ FILE: DoomLauncher/Controls/SourcePortEdit.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/StatBar.cs ================================================ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace DoomLauncher { class StatBar : UserControl { private static readonly Font DisplayFont = new Font(FontFamily.GenericSerif, 10.0f, FontStyle.Bold); private static readonly Brush FontBrush = new SolidBrush(ColorTheme.Current.StatText); private int m_count, m_total, m_maxHeight; private string m_text; private static bool UseNewStyling = true; public StatBar() { Paint += StatBar_Paint; SizeChanged += StatBar_SizeChanged; } public void SetMaxHeight(int maxHeight) => m_maxHeight = maxHeight; //this is to get around some sort of bug with the designer initilization blowing away the max size somewhere... private void StatBar_SizeChanged(object sender, EventArgs e) { if (m_maxHeight == 0) m_maxHeight = MaximumSize.Height; if (Height > m_maxHeight) Height = m_maxHeight; } public void SetStats(int count, int total, string text) { m_count = count; m_total = total; m_text = text; } private void StatBar_Paint(object sender, PaintEventArgs e) { if (m_text != null) DrawProgress(new Point(0, 0), m_count, m_total, m_text); } private void DrawProgress(Point pt, int count, int total, string text) { Graphics g = CreateGraphics(); DpiScale dpiScale = new DpiScale(g); int offsetX = dpiScale.ScaleIntX(1); int offsetY = dpiScale.ScaleIntY(1); int height = Height - offsetY; int width = Width - offsetX; Pen pen = new Pen(Color.Black, 1.0f); Rectangle rect = new Rectangle(pt, new Size(width, height)); g.DrawRectangle(pen, rect); double percent = 0; if (total > 0) percent = count / (double)total; width = Width - offsetX; if (total != 0) width = (int)(width * percent); pt.Offset(offsetX, offsetY); rect = new Rectangle(pt, new Size(rect.Width - offsetX, rect.Height - offsetY)); Brush bgBrush = new LinearGradientBrush(rect, ColorTheme.Current.StatBackgroundGradientFrom, ColorTheme.Current.StatBackgroundGradientTo, 90.0f); Rectangle percentRect = new Rectangle(rect.Location, new Size(width, rect.Height)); Brush brush = GetPercentBrush(rect, percent, total); g.FillRectangle(bgBrush, rect); if (width > 0) { g.FillRectangle(brush, percentRect); pt.Offset(-offsetX, -offsetY); g.DrawRectangle(GetPrecentPen(percent, total), new Rectangle(pt, new Size(percentRect.Width + offsetX, percentRect.Height + offsetY))); } // Not exactly sure what is going on here, have to fudge the value below 125% DPI float fudgeY = dpiScale.DpiScaleY > 1.25f ? 0 : dpiScale.ScaleFloatY(2.5f); SizeF size = g.MeasureDisplayString(text, DisplayFont); float stringOffsetY = dpiScale.ScaleFloatY((height - size.Height) / 2); PointF position = new PointF(pt.X + dpiScale.ScaleIntX(8), pt.Y + stringOffsetY + fudgeY); g.DrawString(text, DisplayFont, FontBrush, position); } private static Brush GetPercentBrush(Rectangle rect, double percent, int total) { if (percent >= 1.0 || total == 0) return GetCompleteBrush(rect); return GetUncompleteBrush(rect); } private static Brush GetUncompleteBrush(Rectangle rect) { if (UseNewStyling) return new SolidBrush(ColorTheme.Current.Highlight); return new LinearGradientBrush(rect, Color.LightBlue, Color.Blue, LinearGradientMode.ForwardDiagonal); } private static Brush GetCompleteBrush(Rectangle rect) { if (UseNewStyling) return new SolidBrush(ColorTheme.Current.Highlight); return new LinearGradientBrush(rect, Color.LightGreen, Color.Green, LinearGradientMode.ForwardDiagonal); } private static Pen GetPrecentPen(double percent, int total) { if (UseNewStyling) return new Pen(Color.Black); if (percent >= 1.0 || total == 0) return new Pen(Color.Green); else return new Pen(Color.Blue); } } } ================================================ FILE: DoomLauncher/Controls/StatisticsView.Designer.cs ================================================ namespace DoomLauncher { partial class StatisticsView { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); this.dgvMain = new DoomLauncher.CDataGridView(); ((System.ComponentModel.ISupportInitialize)(this.dgvMain)).BeginInit(); this.SuspendLayout(); // // dgvMain // this.dgvMain.AllowUserToAddRows = false; this.dgvMain.AllowUserToDeleteRows = false; this.dgvMain.AllowUserToOrderColumns = true; dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this.dgvMain.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1; this.dgvMain.BackgroundColor = System.Drawing.SystemColors.ControlLightLight; this.dgvMain.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal; this.dgvMain.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dgvMain.Dock = System.Windows.Forms.DockStyle.Fill; this.dgvMain.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically; this.dgvMain.GridColor = System.Drawing.SystemColors.ActiveBorder; this.dgvMain.Location = new System.Drawing.Point(0, 0); this.dgvMain.Name = "dgvMain"; this.dgvMain.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; this.dgvMain.Size = new System.Drawing.Size(150, 150); this.dgvMain.TabIndex = 2; // // StatisticsView // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.dgvMain); this.Name = "StatisticsView"; ((System.ComponentModel.ISupportInitialize)(this.dgvMain)).EndInit(); this.ResumeLayout(false); } #endregion private CDataGridView dgvMain; } } ================================================ FILE: DoomLauncher/Controls/StatisticsView.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.Stylize; using Equin.ApplicationFramework; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class StatisticsView : UserControl, IFileAssociationView { private ContextMenuStrip m_menu; private static readonly Tuple[] ColumnFields = new Tuple[] { new Tuple("MapName", "Map"), new Tuple("FormattedKills", "Kills"), new Tuple("FormattedSecrets", "Secrets"), new Tuple("FormattedItems", "Items"), new Tuple("FormattedTime", "Time"), new Tuple("RecordTime", "Date"), new Tuple("Skill", "Skill"), new Tuple("SourcePort", "SourcePort"), }; public StatisticsView() { InitializeComponent(); dgvMain.RowHeadersVisible = false; dgvMain.AutoGenerateColumns = false; dgvMain.ShowCellToolTips = false; dgvMain.DefaultCellStyle.SelectionBackColor = Color.Gray; dgvMain.DefaultCellStyle.NullValue = "N/A"; SetColumnFields(ColumnFields); dgvMain.Columns[dgvMain.Columns.Count - 3].DefaultCellStyle.Format = string.Format("{0} {1}", CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern, CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern); Stylizer.StylizeControl(this, DesignMode); } public void SetContextMenu(ContextMenuStrip menu) { m_menu = menu; Stylizer.StylizeControl(m_menu, DesignMode); } public void SetData(IGameFile gameFile) { SetDataByMap(gameFile, null); } public IEnumerable SetDataByMap(IGameFile gameFile, string map) { if (gameFile != null && gameFile.GameFileID.HasValue) { IEnumerable stats = DataSourceAdapter.GetStats(gameFile.GameFileID.Value); if (map != null) stats = stats.Where(x => x.MapName.Equals(map, StringComparison.OrdinalIgnoreCase)); dgvMain.DataSource = new BindingListView(GetStatsBind(stats)); dgvMain.ContextMenuStrip = m_menu; return stats; } else { dgvMain.DataSource = null; return Array.Empty(); } } public void ClearData() { dgvMain.DataSource = null; } public void SetMapsVisible(bool visible) { dgvMain.Columns.Clear(); if (visible) { SetColumnFields(ColumnFields); return; } SetColumnFields(ColumnFields.Skip(1)); } private List GetStatsBind(IEnumerable stats) { List sourcePorts = Util.GetSourcePortsData(DataSourceAdapter); IEnumerable statsBind = from stat in stats join sp in sourcePorts on stat.SourcePortID equals sp.SourcePortID select new StatsBind(stat.MapName, stat.FormattedKills, stat.FormattedSecrets, stat.FormattedItems, TimeSpan.FromSeconds(stat.LevelTime), stat.RecordTime, stat.Skill, sp.Name, stat); return statsBind.ToList(); } public bool Delete() { if (dgvMain.SelectedRows.Count > 0 && StyledMessageBox.Show(this, "Delete selected statistic(s)?", "Confirm", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.OK) { foreach (DataGridViewRow row in dgvMain.SelectedRows) { IStatsData stats = GetStatsFromGridRow(row); DataSourceAdapter.DeleteStats(stats.StatID); } return true; } return false; } public void CopyToClipboard() { Clipboard.SetDataObject(dgvMain.GetClipboardContent(), true); } public void CopyAllToClipboard() { dgvMain.SelectAll(); Clipboard.SetDataObject(dgvMain.GetClipboardContent(), true); } public bool Export() => false; public bool ExportAll() => false; private IStatsData GetStatsFromGridRow(DataGridViewRow row) { StatsBind bind = ((ObjectView)row.DataBoundItem).Object as StatsBind; return bind.StatsData; } public bool New() { throw new NotSupportedException(); } public bool Edit() { throw new NotSupportedException(); } public void View() { throw new NotSupportedException(); } public bool MoveFileOrderUp() { throw new NotSupportedException(); } public bool MoveFileOrderDown() { throw new NotSupportedException(); } public bool SetFileOrderFirst() { throw new NotSupportedException(); } public IDataSourceAdapter DataSourceAdapter { get; set; } private void SetColumnFields(IEnumerable> columnFields) { if (columnFields.Any()) { foreach (Tuple item in columnFields) { DataGridViewColumn col = new DataGridViewTextBoxColumn { HeaderText = item.Item2, Name = item.Item1, DataPropertyName = item.Item1 }; dgvMain.Columns.Add(col); } dgvMain.Columns[dgvMain.Columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; } } public IGameFile GameFile { get; set; } public bool DeleteAllowed { get { return true; } } public bool CopyOrExportAllowed { get { return false; } } public bool NewAllowed { get { return false; } } public bool EditAllowed { get { return false; } } public bool ViewAllowed { get { return false; } } public bool ChangeOrderAllowed { get { return false; } } public IList MenuOptions { get { return Array.Empty(); } } private class StatsBind { public StatsBind(string map, string kills, string secrets, string items, TimeSpan time, DateTime recordtime, int? skill, string sourceport, IStatsData statsdatasource) { MapName = map; FormattedKills = kills; FormattedSecrets = secrets; FormattedItems = items; FormattedTime = time; RecordTime = recordtime; SourcePort = sourceport; Skill = skill; StatsData = statsdatasource; } public string MapName { get; set; } public string FormattedKills { get; set; } public string FormattedSecrets { get; set; } public string FormattedItems { get; set; } public TimeSpan FormattedTime { get; set; } public DateTime RecordTime { get; set; } public string SourcePort { get; set; } public int? Skill { get; set; } public IStatsData StatsData { get; set; } } } } ================================================ FILE: DoomLauncher/Controls/StatisticsView.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/StatsControl.Designer.cs ================================================ namespace DoomLauncher { partial class StatsControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblStats = new System.Windows.Forms.TableLayoutPanel(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.pbKills = new System.Windows.Forms.PictureBox(); this.pbSecrets = new System.Windows.Forms.PictureBox(); this.pbItems = new System.Windows.Forms.PictureBox(); this.ctrlStatsSecrets = new DoomLauncher.StatBar(); this.ctrlStatsItems = new DoomLauncher.StatBar(); this.ctrlStatsKills = new DoomLauncher.StatBar(); this.lblMaps = new System.Windows.Forms.Label(); this.ctrlStatsMaps = new DoomLauncher.StatBar(); this.pbMaps = new System.Windows.Forms.PictureBox(); this.tblStats.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbKills)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pbSecrets)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pbItems)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pbMaps)).BeginInit(); this.SuspendLayout(); // // tblStats // this.tblStats.ColumnCount = 3; this.tblStats.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 75F)); this.tblStats.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 81F)); this.tblStats.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblStats.Controls.Add(this.label1, 1, 2); this.tblStats.Controls.Add(this.label2, 1, 3); this.tblStats.Controls.Add(this.label3, 1, 4); this.tblStats.Controls.Add(this.pbKills, 0, 2); this.tblStats.Controls.Add(this.pbSecrets, 0, 3); this.tblStats.Controls.Add(this.pbItems, 0, 4); this.tblStats.Controls.Add(this.ctrlStatsSecrets, 2, 3); this.tblStats.Controls.Add(this.ctrlStatsItems, 2, 4); this.tblStats.Controls.Add(this.ctrlStatsKills, 2, 2); this.tblStats.Controls.Add(this.lblMaps, 1, 1); this.tblStats.Controls.Add(this.ctrlStatsMaps, 2, 1); this.tblStats.Controls.Add(this.pbMaps, 0, 1); this.tblStats.Dock = System.Windows.Forms.DockStyle.Fill; this.tblStats.Location = new System.Drawing.Point(0, 0); this.tblStats.Margin = new System.Windows.Forms.Padding(0); this.tblStats.MinimumSize = new System.Drawing.Size(240, 0); this.tblStats.Name = "tblStats"; this.tblStats.RowCount = 6; this.tblStats.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblStats.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblStats.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 38F)); this.tblStats.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 35F)); this.tblStats.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblStats.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblStats.Size = new System.Drawing.Size(459, 231); this.tblStats.TabIndex = 7; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(79, 87); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(40, 20); this.label1.TabIndex = 0; this.label1.Text = "Kills:"; // // label2 // this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(79, 123); this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(68, 20); this.label2.TabIndex = 1; this.label2.Text = "Secrets:"; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(79, 160); this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(53, 20); this.label3.TabIndex = 2; this.label3.Text = "Items:"; // // pbKills // this.pbKills.Anchor = System.Windows.Forms.AnchorStyles.None; this.pbKills.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; this.pbKills.Location = new System.Drawing.Point(22, 81); this.pbKills.Margin = new System.Windows.Forms.Padding(0); this.pbKills.Name = "pbKills"; this.pbKills.Size = new System.Drawing.Size(30, 31); this.pbKills.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.pbKills.TabIndex = 6; this.pbKills.TabStop = false; // // pbSecrets // this.pbSecrets.Anchor = System.Windows.Forms.AnchorStyles.None; this.pbSecrets.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; this.pbSecrets.Location = new System.Drawing.Point(22, 118); this.pbSecrets.Margin = new System.Windows.Forms.Padding(0); this.pbSecrets.Name = "pbSecrets"; this.pbSecrets.Size = new System.Drawing.Size(30, 31); this.pbSecrets.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.pbSecrets.TabIndex = 7; this.pbSecrets.TabStop = false; // // pbItems // this.pbItems.Anchor = System.Windows.Forms.AnchorStyles.None; this.pbItems.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; this.pbItems.Location = new System.Drawing.Point(22, 155); this.pbItems.Margin = new System.Windows.Forms.Padding(0); this.pbItems.Name = "pbItems"; this.pbItems.Size = new System.Drawing.Size(30, 31); this.pbItems.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.pbItems.TabIndex = 8; this.pbItems.TabStop = false; // // ctrlStatsSecrets // this.ctrlStatsSecrets.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlStatsSecrets.Location = new System.Drawing.Point(156, 118); this.ctrlStatsSecrets.Margin = new System.Windows.Forms.Padding(0, 2, 6, 2); this.ctrlStatsSecrets.MaximumSize = new System.Drawing.Size(0, 32); this.ctrlStatsSecrets.Name = "ctrlStatsSecrets"; this.ctrlStatsSecrets.Size = new System.Drawing.Size(297, 26); this.ctrlStatsSecrets.TabIndex = 10; // // ctrlStatsItems // this.ctrlStatsItems.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlStatsItems.Location = new System.Drawing.Point(156, 153); this.ctrlStatsItems.Margin = new System.Windows.Forms.Padding(0, 2, 6, 2); this.ctrlStatsItems.MaximumSize = new System.Drawing.Size(0, 32); this.ctrlStatsItems.Name = "ctrlStatsItems"; this.ctrlStatsItems.Size = new System.Drawing.Size(297, 26); this.ctrlStatsItems.TabIndex = 11; // // ctrlStatsKills // this.ctrlStatsKills.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlStatsKills.Location = new System.Drawing.Point(156, 80); this.ctrlStatsKills.Margin = new System.Windows.Forms.Padding(0, 2, 6, 2); this.ctrlStatsKills.MaximumSize = new System.Drawing.Size(0, 32); this.ctrlStatsKills.Name = "ctrlStatsKills"; this.ctrlStatsKills.Size = new System.Drawing.Size(297, 26); this.ctrlStatsKills.TabIndex = 12; // // lblMaps // this.lblMaps.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblMaps.AutoSize = true; this.lblMaps.Location = new System.Drawing.Point(79, 49); this.lblMaps.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0); this.lblMaps.Name = "lblMaps"; this.lblMaps.Size = new System.Drawing.Size(52, 20); this.lblMaps.TabIndex = 13; this.lblMaps.Text = "Maps:"; // // ctrlStatsMaps // this.ctrlStatsMaps.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlStatsMaps.Location = new System.Drawing.Point(156, 42); this.ctrlStatsMaps.Margin = new System.Windows.Forms.Padding(0, 2, 6, 2); this.ctrlStatsMaps.MaximumSize = new System.Drawing.Size(0, 32); this.ctrlStatsMaps.Name = "ctrlStatsMaps"; this.ctrlStatsMaps.Size = new System.Drawing.Size(297, 26); this.ctrlStatsMaps.TabIndex = 14; // // pbMaps // this.pbMaps.Anchor = System.Windows.Forms.AnchorStyles.None; this.pbMaps.Location = new System.Drawing.Point(22, 43); this.pbMaps.Margin = new System.Windows.Forms.Padding(0); this.pbMaps.Name = "pbMaps"; this.pbMaps.Size = new System.Drawing.Size(30, 31); this.pbMaps.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.pbMaps.TabIndex = 15; this.pbMaps.TabStop = false; // // StatsControl // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblStats); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "StatsControl"; this.Size = new System.Drawing.Size(459, 231); this.tblStats.ResumeLayout(false); this.tblStats.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbKills)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.pbSecrets)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.pbItems)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.pbMaps)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblStats; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.PictureBox pbKills; private System.Windows.Forms.PictureBox pbSecrets; private System.Windows.Forms.PictureBox pbItems; private StatBar ctrlStatsSecrets; private StatBar ctrlStatsItems; private StatBar ctrlStatsKills; private System.Windows.Forms.Label lblMaps; private StatBar ctrlStatsMaps; private System.Windows.Forms.PictureBox pbMaps; } } ================================================ FILE: DoomLauncher/Controls/StatsControl.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class StatsControl : UserControl { public StatsControl() { InitializeComponent(); DpiScale dpiScale = new DpiScale(CreateGraphics()); SetImage(pbMaps, Properties.Resources.map, 0.7f * dpiScale.DpiScaleY); SetImage(pbKills, Properties.Resources.kill, 0.7f * dpiScale.DpiScaleY); SetImage(pbItems, Properties.Resources.bon2b, 0.8f * dpiScale.DpiScaleY); SetImage(pbSecrets, Properties.Resources.secret, 0.8f * dpiScale.DpiScaleY); SetStatBarDpi(ctrlStatsMaps, dpiScale); SetStatBarDpi(ctrlStatsKills, dpiScale); SetStatBarDpi(ctrlStatsSecrets, dpiScale); SetStatBarDpi(ctrlStatsItems, dpiScale); } private void SetStatBarDpi(StatBar statBar, DpiScale dpiScale) { statBar.MaximumSize = new Size(statBar.MaximumSize.Width, dpiScale.ScaleIntY(statBar.MaximumSize.Height)); statBar.SetMaxHeight(statBar.MaximumSize.Height); statBar.Size = MaximumSize; } private void SetImage(PictureBox pb, Bitmap bmp, float scale) { pb.Width = Convert.ToInt32(bmp.Width * scale); pb.Height = Convert.ToInt32(bmp.Height * scale); pb.Image = bmp; } public void SetStatistics(IGameFile gameFile, IEnumerable stats) { SetStatistics(new IGameFile[] { gameFile }, stats); } public void SetStatistics(IEnumerable gameFiles, IEnumerable stats) { StatsData statTotal = new StatsData(); int maps = 0, totalMaps = 0; var groupedStats = stats.OrderByDescending(x => x.RecordTime).GroupBy(x => x.GameFileID); foreach (var group in groupedStats) { var gameFile = gameFiles.FirstOrDefault(x => x.GameFileID.Value == group.Key); if (gameFile != null) { var mapStats = group.GroupBy(x => x.MapName).Select(x => x.First()); if (gameFile.MapCount.HasValue) { int mapCount = mapStats.Count(); if (mapCount > gameFile.MapCount.Value) mapCount = gameFile.MapCount.Value; maps += mapCount; totalMaps += gameFile.MapCount.Value; } foreach (var stat in mapStats) { statTotal.KillCount += stat.KillCount; statTotal.TotalKills += stat.TotalKills; statTotal.SecretCount += stat.SecretCount; statTotal.TotalSecrets += stat.TotalSecrets; statTotal.ItemCount += stat.ItemCount; statTotal.TotalItems += stat.TotalItems; statTotal.LevelTime += stat.LevelTime; } } } ctrlStatsMaps.SetStats(maps, totalMaps, string.Format("{0} / {1}", maps, totalMaps)); ctrlStatsKills.SetStats(statTotal.KillCount, statTotal.TotalKills, statTotal.FormattedKills); ctrlStatsSecrets.SetStats(statTotal.SecretCount, statTotal.TotalSecrets, statTotal.FormattedSecrets); ctrlStatsItems.SetStats(statTotal.ItemCount, statTotal.TotalItems, statTotal.FormattedItems); } public void SetMapsVisible(bool visible) { ctrlStatsMaps.Visible = visible; pbMaps.Visible = visible; if (visible) tblStats.RowStyles[1].Height = 38; else tblStats.RowStyles[1].Height = 0; } } } ================================================ FILE: DoomLauncher/Controls/StatsControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/TableLayoutPanelDB.Designer.cs ================================================ namespace DoomLauncher { partial class TableLayoutPanelDB { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { components = new System.ComponentModel.Container(); } #endregion } } ================================================ FILE: DoomLauncher/Controls/TableLayoutPanelDB.cs ================================================ using System.Windows.Forms; namespace DoomLauncher { public partial class TableLayoutPanelDB : TableLayoutPanel { public TableLayoutPanelDB() { SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= 0x02000000; // WS_CLIPCHILDREN return cp; } } } } ================================================ FILE: DoomLauncher/Controls/TagControl.Designer.cs ================================================ namespace DoomLauncher { partial class TagControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.btnAdd = new System.Windows.Forms.Button(); this.btnEdit = new System.Windows.Forms.Button(); this.btnDelete = new System.Windows.Forms.Button(); this.tagSelectCtrl = new DoomLauncher.Controls.TagSelectControl(); this.tblMain.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.flowLayoutPanel1, 0, 1); this.tblMain.Controls.Add(this.tagSelectCtrl, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.Size = new System.Drawing.Size(296, 150); this.tblMain.TabIndex = 0; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.btnAdd); this.flowLayoutPanel1.Controls.Add(this.btnEdit); this.flowLayoutPanel1.Controls.Add(this.btnDelete); this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 118); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(296, 32); this.flowLayoutPanel1.TabIndex = 1; // // btnAdd // this.btnAdd.Location = new System.Drawing.Point(3, 3); this.btnAdd.Name = "btnAdd"; this.btnAdd.Size = new System.Drawing.Size(75, 23); this.btnAdd.TabIndex = 0; this.btnAdd.Text = "Add"; this.btnAdd.UseVisualStyleBackColor = true; this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click); // // btnEdit // this.btnEdit.Location = new System.Drawing.Point(84, 3); this.btnEdit.Name = "btnEdit"; this.btnEdit.Size = new System.Drawing.Size(75, 23); this.btnEdit.TabIndex = 1; this.btnEdit.Text = "Edit"; this.btnEdit.UseVisualStyleBackColor = true; this.btnEdit.Click += new System.EventHandler(this.btnEdit_Click); // // btnDelete // this.btnDelete.Location = new System.Drawing.Point(165, 3); this.btnDelete.Name = "btnDelete"; this.btnDelete.Size = new System.Drawing.Size(75, 23); this.btnDelete.TabIndex = 2; this.btnDelete.Text = "Delete"; this.btnDelete.UseVisualStyleBackColor = true; this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); // // tagSelectCtrl // this.tagSelectCtrl.Dock = System.Windows.Forms.DockStyle.Fill; this.tagSelectCtrl.Location = new System.Drawing.Point(3, 3); this.tagSelectCtrl.Name = "tagSelectCtrl"; this.tagSelectCtrl.Size = new System.Drawing.Size(290, 112); this.tagSelectCtrl.TabIndex = 2; // // TagControl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Name = "TagControl"; this.Size = new System.Drawing.Size(296, 150); this.tblMain.ResumeLayout(false); this.flowLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button btnAdd; private System.Windows.Forms.Button btnEdit; private System.Windows.Forms.Button btnDelete; private Controls.TagSelectControl tagSelectCtrl; } } ================================================ FILE: DoomLauncher/Controls/TagControl.cs ================================================ using DoomLauncher.Controls; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using DoomLauncher.Stylize; using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class TagControl : UserControl { private IDataSourceAdapter m_adapter; private readonly List m_addTags = new List(); private readonly List m_editTags = new List(); private readonly List m_deleteTags = new List(); public TagControl() { InitializeComponent(); Stylizer.StylizeControl(this, DesignMode); } public void Init(IDataSourceAdapter adapter) { m_adapter = adapter; tagSelectCtrl.Init(new TagSelectOptions() { AllowRowSelect = true, ShowTagData = true, CustomSetData = true }); SetTagData(); } private void SetTagData() { tagSelectCtrl.SetDataSource(DataCache.Instance.SortTags(m_adapter.GetTags()).ToList()); } public ITagData[] AddedTags { get { return m_addTags.ToArray(); } } public ITagData[] EditedTags { get { return m_editTags.ToArray(); } } public ITagData[] DeletedTags { get { return m_deleteTags.ToArray(); } } private void btnAdd_Click(object sender, EventArgs e) { TagEditForm form = new TagEditForm(); form.StartPosition = FormStartPosition.CenterParent; if (form.ShowDialog(this) == DialogResult.OK) { TagData tag = new TagData(); form.TagEditControl.GetDataSource(tag); if (!IsTagNameUnique(tag)) { StyledMessageBox.Show(this, "Tag name must be unique and not empty.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { m_adapter.InsertTag(tag); SetTagData(); IEnumerable check = m_adapter.GetTags().Where(x => x.Name == tag.Name); if (check.Any()) { if (m_addTags.Contains(check.First())) m_addTags.Remove(check.First()); m_addTags.Add(check.First()); tagSelectCtrl.SetSelectedItem(check.First()); } } } } private void btnEdit_Click(object sender, EventArgs e) { if (tagSelectCtrl.SelectedItem != null) { ITagData tag = tagSelectCtrl.SelectedItem; TagEditForm form = new TagEditForm(); form.TagEditControl.SetDataSource(tag); form.StartPosition = FormStartPosition.CenterParent; if (form.ShowDialog(this) == DialogResult.OK) { form.TagEditControl.GetDataSource(tag); if (!IsTagNameUnique(tag)) { StyledMessageBox.Show(this, "Tag name must be unique and not empty.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); SetTagData(); } else { m_adapter.UpdateTag(tag); SetTagData(); if (m_editTags.Contains(tag)) m_editTags.Remove(tag); m_editTags.Add(tag); } tagSelectCtrl.SetSelectedItem(tag); } } } private bool IsTagNameUnique(ITagData tag) { IEnumerable check = m_adapter.GetTags().Where(x => x.Name.Equals(tag.Name, StringComparison.CurrentCultureIgnoreCase) && !x.Equals(tag)); return !(string.IsNullOrEmpty(tag.Name) || check.Any() || TabKeys.KeyNames.ToList().FindAll(x=> tag.Name.Equals(x, StringComparison.CurrentCultureIgnoreCase)).Any()); } private void btnDelete_Click(object sender, EventArgs e) { if (tagSelectCtrl.SelectedItem != null && StyledMessageBox.Show(this, "Are you sure you want to delete this tag?", "Delete", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK) { ITagData tag = tagSelectCtrl.SelectedItem; m_adapter.DeleteTag(tag); m_adapter.DeleteTagMapping(tag.TagID); SetTagData(); if (m_deleteTags.Contains(tag)) m_deleteTags.Remove(tag); m_deleteTags.Add(tag); } } } } ================================================ FILE: DoomLauncher/Controls/TagControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/TagEdit.Designer.cs ================================================ namespace DoomLauncher { partial class TagEdit { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.txtName = new System.Windows.Forms.TextBox(); this.cmbTab = new System.Windows.Forms.ComboBox(); this.label3 = new System.Windows.Forms.Label(); this.flp = new System.Windows.Forms.FlowLayoutPanel(); this.pnlColor = new System.Windows.Forms.Panel(); this.btnSelect = new System.Windows.Forms.Button(); this.cmbExclude = new System.Windows.Forms.ComboBox(); this.label4 = new System.Windows.Forms.Label(); this.cmbColor = new System.Windows.Forms.ComboBox(); this.lblFavorite = new System.Windows.Forms.Label(); this.cmbFavorite = new System.Windows.Forms.ComboBox(); this.tblMain.SuspendLayout(); this.flp.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 2; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 136F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.label1, 0, 0); this.tblMain.Controls.Add(this.label2, 0, 1); this.tblMain.Controls.Add(this.txtName, 1, 0); this.tblMain.Controls.Add(this.cmbTab, 1, 1); this.tblMain.Controls.Add(this.label3, 0, 2); this.tblMain.Controls.Add(this.flp, 1, 5); this.tblMain.Controls.Add(this.cmbExclude, 1, 2); this.tblMain.Controls.Add(this.label4, 0, 4); this.tblMain.Controls.Add(this.cmbColor, 1, 4); this.tblMain.Controls.Add(this.lblFavorite, 0, 3); this.tblMain.Controls.Add(this.cmbFavorite, 1, 3); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 7; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Size = new System.Drawing.Size(294, 178); this.tblMain.TabIndex = 0; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(3, 6); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(35, 13); this.label1.TabIndex = 0; this.label1.Text = "Name"; // // label2 // this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(3, 32); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(56, 13); this.label2.TabIndex = 1; this.label2.Text = "Show Tab"; // // txtName // this.txtName.Dock = System.Windows.Forms.DockStyle.Fill; this.txtName.Location = new System.Drawing.Point(139, 3); this.txtName.Name = "txtName"; this.txtName.Size = new System.Drawing.Size(152, 20); this.txtName.TabIndex = 2; // // cmbTab // this.cmbTab.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbTab.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbTab.FormattingEnabled = true; this.cmbTab.Items.AddRange(new object[] { "Yes", "No"}); this.cmbTab.Location = new System.Drawing.Point(139, 29); this.cmbTab.Name = "cmbTab"; this.cmbTab.Size = new System.Drawing.Size(152, 21); this.cmbTab.TabIndex = 3; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(3, 58); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(127, 13); this.label3.TabIndex = 4; this.label3.Text = "Exclude From Other Tabs"; // // flp // this.flp.Controls.Add(this.pnlColor); this.flp.Controls.Add(this.btnSelect); this.flp.Dock = System.Windows.Forms.DockStyle.Fill; this.flp.Location = new System.Drawing.Point(136, 130); this.flp.Margin = new System.Windows.Forms.Padding(0); this.flp.Name = "flp"; this.flp.Size = new System.Drawing.Size(158, 32); this.flp.TabIndex = 7; // // pnlColor // this.pnlColor.Anchor = System.Windows.Forms.AnchorStyles.Left; this.pnlColor.BackColor = System.Drawing.SystemColors.ActiveCaptionText; this.pnlColor.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.pnlColor.Location = new System.Drawing.Point(3, 4); this.pnlColor.Margin = new System.Windows.Forms.Padding(3, 4, 0, 0); this.pnlColor.Name = "pnlColor"; this.pnlColor.Size = new System.Drawing.Size(24, 24); this.pnlColor.TabIndex = 0; // // btnSelect // this.btnSelect.Anchor = System.Windows.Forms.AnchorStyles.Left; this.btnSelect.Location = new System.Drawing.Point(30, 4); this.btnSelect.Margin = new System.Windows.Forms.Padding(3, 4, 0, 0); this.btnSelect.Name = "btnSelect"; this.btnSelect.Size = new System.Drawing.Size(75, 23); this.btnSelect.TabIndex = 5; this.btnSelect.Text = "Select"; this.btnSelect.UseVisualStyleBackColor = true; this.btnSelect.Click += new System.EventHandler(this.btnSelect_Click); // // cmbExclude // this.cmbExclude.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbExclude.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbExclude.FormattingEnabled = true; this.cmbExclude.Items.AddRange(new object[] { "Yes", "No"}); this.cmbExclude.Location = new System.Drawing.Point(139, 55); this.cmbExclude.Name = "cmbExclude"; this.cmbExclude.Size = new System.Drawing.Size(152, 21); this.cmbExclude.TabIndex = 8; // // label4 // this.label4.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(3, 110); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(75, 13); this.label4.TabIndex = 9; this.label4.Text = "Highlight Color"; // // cmbColor // this.cmbColor.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbColor.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbColor.FormattingEnabled = true; this.cmbColor.Items.AddRange(new object[] { "Yes", "No"}); this.cmbColor.Location = new System.Drawing.Point(139, 107); this.cmbColor.Name = "cmbColor"; this.cmbColor.Size = new System.Drawing.Size(152, 21); this.cmbColor.TabIndex = 6; this.cmbColor.SelectedIndexChanged += new System.EventHandler(this.cmbColor_SelectedIndexChanged); // // lblFavorite // this.lblFavorite.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblFavorite.AutoSize = true; this.lblFavorite.Location = new System.Drawing.Point(3, 84); this.lblFavorite.Name = "lblFavorite"; this.lblFavorite.Size = new System.Drawing.Size(45, 13); this.lblFavorite.TabIndex = 10; this.lblFavorite.Text = "Favorite"; // // cmbFavorite // this.cmbFavorite.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbFavorite.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbFavorite.FormattingEnabled = true; this.cmbFavorite.Items.AddRange(new object[] { "Yes", "No"}); this.cmbFavorite.Location = new System.Drawing.Point(139, 81); this.cmbFavorite.Name = "cmbFavorite"; this.cmbFavorite.Size = new System.Drawing.Size(152, 21); this.cmbFavorite.TabIndex = 11; // // TagEdit // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Name = "TagEdit"; this.Size = new System.Drawing.Size(294, 178); this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.flp.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.TextBox txtName; private System.Windows.Forms.ComboBox cmbTab; private System.Windows.Forms.Label label3; private System.Windows.Forms.Button btnSelect; private System.Windows.Forms.ComboBox cmbColor; private System.Windows.Forms.FlowLayoutPanel flp; private System.Windows.Forms.Panel pnlColor; private System.Windows.Forms.ComboBox cmbExclude; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label lblFavorite; private System.Windows.Forms.ComboBox cmbFavorite; } } ================================================ FILE: DoomLauncher/Controls/TagEdit.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public partial class TagEdit : UserControl { private Color? m_color; public TagEdit() { InitializeComponent(); lblFavorite.Text = string.Concat("Favorite ", TagData.FavoriteChar); cmbTab.SelectedIndex = 0; cmbColor.SelectedIndex = 1; cmbExclude.SelectedIndex = 1; cmbFavorite.SelectedIndex = 1; m_color = Color.Black; Load += TagEdit_Load; Stylizer.StylizeControl(this, DesignMode); } private void TagEdit_Load(object sender, EventArgs e) { txtName.Select(); } public void SetDataSource(ITagData tag) { txtName.Text = tag.Name; cmbTab.SelectedIndex = tag.HasTab ? 0 : 1; cmbColor.SelectedIndex = tag.HasColor ? 0 : 1; cmbExclude.SelectedIndex = tag.ExcludeFromOtherTabs ? 0 : 1; cmbFavorite.SelectedIndex = tag.Favorite ? 0 : 1; if (tag.HasColor && tag.Color.HasValue) m_color = pnlColor.BackColor = Color.FromArgb(tag.Color.Value); } public void GetDataSource(ITagData tag) { tag.Name = txtName.Text; tag.HasTab = cmbTab.SelectedIndex == 0; tag.HasColor = cmbColor.SelectedIndex == 0; tag.ExcludeFromOtherTabs = cmbExclude.SelectedIndex == 0; tag.Favorite = cmbFavorite.SelectedIndex == 0; if (m_color.HasValue) tag.Color = m_color.Value.ToArgb(); else tag.Color = null; } private void btnSelect_Click(object sender, EventArgs e) { ColorDialog dialog = new ColorDialog(); if (dialog.ShowDialog(this) == DialogResult.OK) { pnlColor.BackColor = dialog.Color; m_color = dialog.Color; } } private void cmbColor_SelectedIndexChanged(object sender, EventArgs e) { btnSelect.Visible = pnlColor.Visible = cmbColor.SelectedIndex == 0; } } } ================================================ FILE: DoomLauncher/Controls/TagEdit.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/TagSelectControl.Designer.cs ================================================ namespace DoomLauncher.Controls { partial class TagSelectControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.dgvTags = new System.Windows.Forms.DataGridView(); this.tblTop = new System.Windows.Forms.TableLayoutPanel(); this.flpSearch = new System.Windows.Forms.FlowLayoutPanel(); this.txtSearch = new System.Windows.Forms.TextBox(); this.btnPin = new System.Windows.Forms.Button(); this.menu = new System.Windows.Forms.ContextMenuStrip(this.components); this.manageTagsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.pbSearch = new System.Windows.Forms.PictureBox(); this.tblMain.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.dgvTags)).BeginInit(); this.tblTop.SuspendLayout(); this.flpSearch.SuspendLayout(); this.menu.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbSearch)).BeginInit(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.dgvTags, 0, 1); this.tblMain.Controls.Add(this.tblTop, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F)); this.tblMain.Size = new System.Drawing.Size(290, 516); this.tblMain.TabIndex = 0; // // dgvTags // this.dgvTags.AllowUserToAddRows = false; this.dgvTags.AllowUserToDeleteRows = false; this.dgvTags.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dgvTags.Dock = System.Windows.Forms.DockStyle.Fill; this.dgvTags.Location = new System.Drawing.Point(4, 43); this.dgvTags.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.dgvTags.Name = "dgvTags"; this.dgvTags.ReadOnly = true; this.dgvTags.RowHeadersWidth = 62; this.dgvTags.Size = new System.Drawing.Size(282, 469); this.dgvTags.TabIndex = 2; // // tblTop // this.tblTop.ColumnCount = 2; this.tblTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 27F)); this.tblTop.Controls.Add(this.flpSearch, 0, 0); this.tblTop.Controls.Add(this.btnPin, 1, 0); this.tblTop.Dock = System.Windows.Forms.DockStyle.Fill; this.tblTop.Location = new System.Drawing.Point(0, 0); this.tblTop.Margin = new System.Windows.Forms.Padding(0); this.tblTop.Name = "tblTop"; this.tblTop.RowCount = 1; this.tblTop.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblTop.Size = new System.Drawing.Size(290, 39); this.tblTop.TabIndex = 4; // // flpSearch // this.flpSearch.Controls.Add(this.pbSearch); this.flpSearch.Controls.Add(this.txtSearch); this.flpSearch.Dock = System.Windows.Forms.DockStyle.Fill; this.flpSearch.Location = new System.Drawing.Point(0, 0); this.flpSearch.Margin = new System.Windows.Forms.Padding(0); this.flpSearch.Name = "flpSearch"; this.flpSearch.Size = new System.Drawing.Size(263, 39); this.flpSearch.TabIndex = 0; // // txtSearch // this.txtSearch.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.txtSearch.Location = new System.Drawing.Point(26, 6); this.txtSearch.Margin = new System.Windows.Forms.Padding(0, 6, 0, 4); this.txtSearch.MaximumSize = new System.Drawing.Size(169, 26); this.txtSearch.Name = "txtSearch"; this.txtSearch.Size = new System.Drawing.Size(169, 22); this.txtSearch.TabIndex = 0; this.txtSearch.TextChanged += new System.EventHandler(this.txtSearch_TextChanged); // // btnPin // this.btnPin.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnPin.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.btnPin.FlatAppearance.BorderSize = 0; this.btnPin.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnPin.Image = global::DoomLauncher.Properties.Resources.Pin; this.btnPin.Location = new System.Drawing.Point(263, 0); this.btnPin.Margin = new System.Windows.Forms.Padding(0); this.btnPin.Name = "btnPin"; this.btnPin.Size = new System.Drawing.Size(27, 25); this.btnPin.TabIndex = 4; this.btnPin.UseVisualStyleBackColor = true; this.btnPin.Click += new System.EventHandler(this.btnPin_Click); // // menu // this.menu.ImageScalingSize = new System.Drawing.Size(24, 24); this.menu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.manageTagsToolStripMenuItem}); this.menu.Name = "contextMenuStrip1"; this.menu.Size = new System.Drawing.Size(175, 28); // // manageTagsToolStripMenuItem // this.manageTagsToolStripMenuItem.Name = "manageTagsToolStripMenuItem"; this.manageTagsToolStripMenuItem.Size = new System.Drawing.Size(174, 24); this.manageTagsToolStripMenuItem.Text = "Manage Tags..."; this.manageTagsToolStripMenuItem.Click += new System.EventHandler(this.manageTagsToolStripMenuItem_Click); // // pbSearch // this.pbSearch.Location = new System.Drawing.Point(6, 11); this.pbSearch.Margin = new System.Windows.Forms.Padding(6, 11, 0, 3); this.pbSearch.Name = "pbSearch"; this.pbSearch.Size = new System.Drawing.Size(20, 15); this.pbSearch.TabIndex = 4; this.pbSearch.TabStop = false; // // TagSelectControl // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.Name = "TagSelectControl"; this.Size = new System.Drawing.Size(290, 516); this.tblMain.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.dgvTags)).EndInit(); this.tblTop.ResumeLayout(false); this.flpSearch.ResumeLayout(false); this.flpSearch.PerformLayout(); this.menu.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.pbSearch)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.FlowLayoutPanel flpSearch; private System.Windows.Forms.TextBox txtSearch; private System.Windows.Forms.DataGridView dgvTags; private System.Windows.Forms.TableLayoutPanel tblTop; private System.Windows.Forms.Button btnPin; private System.Windows.Forms.ContextMenuStrip menu; private System.Windows.Forms.ToolStripMenuItem manageTagsToolStripMenuItem; private System.Windows.Forms.PictureBox pbSearch; } } ================================================ FILE: DoomLauncher/Controls/TagSelectControl.cs ================================================ using System; using System.Windows.Forms; using DoomLauncher.Interfaces; using System.Linq; using System.Collections.Generic; using System.Drawing; namespace DoomLauncher.Controls { public partial class TagSelectControl : UserControl { public event EventHandler TagSelectionChanged; public event EventHandler StaticSelectionChanged; public event EventHandler PinChanged; public event EventHandler ManageTags; public bool Pinned { get; private set; } private TagSelectOptions m_options = new TagSelectOptions(); private List m_checkedTags = new List(); private IEnumerable m_customSource; private bool m_loaded; public TagSelectControl() { InitializeComponent(); Icons.DpiScale = new DpiScale(CreateGraphics()); pbSearch.Image = Icons.Search; btnPin.Image = Icons.Pin; SetPinned(false); Resize += TagSelectControl_Resize; Stylizer.StylizeControl(this, DesignMode); Stylizer.StylizeControl(menu, DesignMode); } private void TagSelectControl_Resize(object sender, EventArgs e) { DpiScale dpiScale = new DpiScale(CreateGraphics()); int offset = btnPin.Width + dpiScale.ScaleIntX(16); if (flpSearch.Width < dpiScale.ScaleIntX(80)) { pbSearch.Visible = false; return; } pbSearch.Visible = true; if (flpSearch.Width < txtSearch.MaximumSize.Width + offset) txtSearch.Width = flpSearch.Width - offset; else txtSearch.Width = txtSearch.MaximumSize.Width; } public void Init(TagSelectOptions options) { m_loaded = true; m_options = options; btnPin.Visible = options.ShowPin; if (options.ShowMenu) dgvTags.ContextMenuStrip = menu; InitGrid(dgvTags); EnableSelection(); DataCache.Instance.TagsChanged += DataCache_TagsChanged; SetTags(); } public void SetCheckedTags(IEnumerable tags) { m_checkedTags = tags.ToList(); SetTags(); } public void SetCheckedTags() { int index = GetCheckBoxCellIndex(); if (index == -1) return; HashSet tagHash = new HashSet(m_checkedTags); foreach (DataGridViewRow row in dgvTags.Rows) { ITagData tag = row.DataBoundItem as ITagData; if (!(dgvTags.Rows[row.Index].Cells[index] is DataGridViewCheckBoxCell checkCell)) continue; checkCell.Value = tagHash.Contains(tag); } } private int GetCheckBoxCellIndex() { int index = 0; foreach(DataGridViewColumn column in dgvTags.Columns) { if (column is DataGridViewCheckBoxColumn) return index; index++; } return -1; } public List GetCheckedTags() => m_checkedTags; public ITagData SelectedItem => dgvTags.SelectedRows.Count == 0 ? null : dgvTags.SelectedRows[0].DataBoundItem as ITagData; public void SetSelectedItem(ITagData tag) { ClearSelections(); foreach (DataGridViewRow row in dgvTags.Rows) { if (row.DataBoundItem is ITagData tagData && tagData.Equals(tag)) { dgvTags.FirstDisplayedScrollingRowIndex = row.Index; row.Selected = true; break; } } } public void SetDataSource(IEnumerable tags) { if (!m_options.CustomSetData) throw new InvalidOperationException("Can only call SetDataSource with CustomSetData in TagSelectOptions."); m_customSource = tags; SetTags(); } private void SetTags() { DisableSelection(); IEnumerable tags; if (m_options.CustomSetData) { if (m_customSource == null) return; tags = m_customSource; } else { tags = DataCache.Instance.Tags; } if (m_options.HasTabOnly) tags = tags.Where(x => x.HasTab); if (!string.IsNullOrEmpty(txtSearch.Text)) tags = tags.Where(x => x.Name.IndexOf(txtSearch.Text, StringComparison.CurrentCultureIgnoreCase) >= 0); List allTags; if (m_options.ShowStatic) { allTags = GetStaticTags(); allTags.AddRange(tags); } else { allTags = tags.ToList(); } dgvTags.DataSource = allTags; SetCheckedTags(); ClearSelections(); EnableSelection(); } private List GetStaticTags() { List tags = new List(); HashSet visibleViews = DataCache.Instance.AppConfiguration.VisibleViews; foreach (var key in TabKeys.KeyNames) { if (key != TabKeys.LocalKey && !visibleViews.Contains(key)) continue; tags.Add(new StaticTagData() { Name = key, Favorite = true }); } return tags; } private void DataCache_TagsChanged(object sender, EventArgs e) { SetTags(); } private void InitGrid(DataGridView view) { DpiScale dpiScale = new DpiScale(CreateGraphics()); view.Columns.Clear(); view.ColumnHeadersVisible = m_options.ShowTagData; view.MultiSelect = false; view.AllowUserToResizeRows = false; view.SelectionMode = DataGridViewSelectionMode.FullRowSelect; if (m_options.ShowCheckBoxes) view.Columns.Add(new DataGridViewCheckBoxColumn() { ReadOnly = false, Width = dpiScale.ScaleIntX(32) }); view.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "Name", Name = nameof(ITagData.Name), DataPropertyName = nameof(ITagData.FavoriteName), Width = dpiScale.ScaleIntX(200) }); if (m_options.ShowTagData) { view.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "Display Tab", Name = nameof(ITagData.HasTab), DataPropertyName = nameof(ITagData.HasTab) }); view.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "Exclude", Name = nameof(ITagData.ExcludeFromOtherTabs), DataPropertyName = nameof(ITagData.ExcludeFromOtherTabs) }); view.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "Favorite", Name = nameof(ITagData.Favorite), DataPropertyName = nameof(ITagData.Favorite) }); view.Columns.Add(new DataGridViewTextBoxColumn { HeaderText = "Display Color", Name = nameof(ITagData.HasColor), DataPropertyName = nameof(ITagData.HasColor) }); } view.Columns[view.Columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; } private void View_SelectionChanged(object sender, EventArgs e) { DataGridView view = sender as DataGridView; if (view.SelectedRows.Count == 0) return; int checkIndex = GetCheckBoxCellIndex(); if (m_options.ShowCheckBoxes && checkIndex != -1 && view.SelectedRows[0].Cells[checkIndex] is DataGridViewCheckBoxCell checkBoxCell && checkBoxCell.Value != null) { bool set = !(bool)checkBoxCell.Value; checkBoxCell.Value = set; if (set) m_checkedTags.Add(view.SelectedRows[0].DataBoundItem as ITagData); else m_checkedTags.Remove(view.SelectedRows[0].DataBoundItem as ITagData); } if (dgvTags.SelectedRows[0].DataBoundItem is StaticTagData staticTag) StaticSelectionChanged?.Invoke(this, staticTag.Name); else TagSelectionChanged?.Invoke(this, view.SelectedRows[0].DataBoundItem as ITagData); if (!m_options.AllowRowSelect) view.ClearSelection(); } private void txtSearch_TextChanged(object sender, EventArgs e) { SetTags(); } public void ClearSelections() => dgvTags.ClearSelection(); private void DisableSelection() => dgvTags.SelectionChanged -= View_SelectionChanged; private void EnableSelection() { if (m_loaded) dgvTags.SelectionChanged += View_SelectionChanged; } private void btnPin_Click(object sender, EventArgs e) { SetPinned(!Pinned); PinChanged?.Invoke(this, EventArgs.Empty); } public void SetPinned(bool pinned) { Pinned = pinned; DpiScale dpiScale = new DpiScale(CreateGraphics()); Image img = Icons.Pin; if (!Pinned) img = img.Rotate(90); btnPin.Image = img; btnPin.Image = img.FixedSize((int)(img.Width * .8), (int)(img.Height * .8), Color.Transparent); btnPin.Width = img.Width + dpiScale.ScaleIntX(2); btnPin.Height = img.Height + dpiScale.ScaleIntY(4); } private void manageTagsToolStripMenuItem_Click(object sender, EventArgs e) { ManageTags?.Invoke(this, EventArgs.Empty); } } public class TagSelectOptions { public bool ShowStatic { get; set; } public bool HasTabOnly { get; set; } public bool ShowCheckBoxes { get; set; } public bool AllowRowSelect { get; set; } public bool ShowPin { get; set; } public bool ShowTagData { get; set; } public bool CustomSetData { get; set; } public bool ShowMenu { get; set; } } } ================================================ FILE: DoomLauncher/Controls/TagSelectControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 ================================================ FILE: DoomLauncher/Controls/TitleBarControl.Designer.cs ================================================ namespace DoomLauncher.Controls { partial class TitleBarControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.lblTitle = new System.Windows.Forms.Label(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.btnClose = new DoomLauncher.FormButton(); this.btnMaximize = new DoomLauncher.FormButton(); this.btnMinimize = new DoomLauncher.FormButton(); this.tblMain.SuspendLayout(); this.flpButtons.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 2; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 130F)); this.tblMain.Controls.Add(this.lblTitle, 0, 0); this.tblMain.Controls.Add(this.flpButtons, 1, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 1; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(572, 30); this.tblMain.TabIndex = 0; this.tblMain.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.tblMain_MouseDoubleClick); this.tblMain.MouseDown += new System.Windows.Forms.MouseEventHandler(this.tblMain_MouseDown); // // lblTitle // this.lblTitle.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblTitle.AutoSize = true; this.lblTitle.Location = new System.Drawing.Point(3, 7); this.lblTitle.Name = "lblTitle"; this.lblTitle.Size = new System.Drawing.Size(33, 16); this.lblTitle.TabIndex = 0; this.lblTitle.Text = "Title"; this.lblTitle.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.lblTitle_MouseDoubleClick); // // flpButtons // this.flpButtons.Controls.Add(this.btnClose); this.flpButtons.Controls.Add(this.btnMaximize); this.flpButtons.Controls.Add(this.btnMinimize); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flpButtons.Location = new System.Drawing.Point(442, 0); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(130, 30); this.flpButtons.TabIndex = 1; // // btnClose // this.btnClose.Location = new System.Drawing.Point(86, 0); this.btnClose.Margin = new System.Windows.Forms.Padding(0); this.btnClose.Name = "btnClose"; this.btnClose.Size = new System.Drawing.Size(44, 32); this.btnClose.TabIndex = 0; this.btnClose.Text = "❌"; this.btnClose.UseVisualStyleBackColor = true; this.btnClose.Click += new System.EventHandler(this.btnClose_Click); // // btnMaximize // this.btnMaximize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnMaximize.Location = new System.Drawing.Point(45, 0); this.btnMaximize.Margin = new System.Windows.Forms.Padding(0); this.btnMaximize.Name = "btnMaximize"; this.btnMaximize.Size = new System.Drawing.Size(41, 32); this.btnMaximize.TabIndex = 1; this.btnMaximize.Text = "▢"; this.btnMaximize.UseVisualStyleBackColor = true; this.btnMaximize.Click += new System.EventHandler(this.btnMaximize_Click); // // btnMinimize // this.btnMinimize.Location = new System.Drawing.Point(6, 0); this.btnMinimize.Margin = new System.Windows.Forms.Padding(0); this.btnMinimize.Name = "btnMinimize"; this.btnMinimize.Size = new System.Drawing.Size(39, 32); this.btnMinimize.TabIndex = 2; this.btnMinimize.Text = "−"; this.btnMinimize.UseVisualStyleBackColor = true; this.btnMinimize.Click += new System.EventHandler(this.btnMinimize_Click); // // TitleBarControl // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.tblMain); this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.Name = "TitleBarControl"; this.Size = new System.Drawing.Size(572, 30); this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.flpButtons.ResumeLayout(false); this.ResumeLayout(false); } #endregion private TableLayoutPanelDB tblMain; private System.Windows.Forms.Label lblTitle; private System.Windows.Forms.FlowLayoutPanel flpButtons; private DoomLauncher.FormButton btnClose; private DoomLauncher.FormButton btnMaximize; private DoomLauncher.FormButton btnMinimize; } } ================================================ FILE: DoomLauncher/Controls/TitleBarControl.cs ================================================ using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; namespace DoomLauncher.Controls { public partial class TitleBarControl : UserControl { public const int WM_NCLBUTTONDOWN = 0xA1; public const int HT_CAPTION = 0x2; [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); [DllImport("user32.dll")] public static extern bool ReleaseCapture(); public FormWindowState WindowState { get; private set; } private bool m_maximized; private DragState m_dragState; private bool m_settingState; private Size? m_size; private Point? m_location; private enum DragState { None, Start, Dragging } public string Title { get => lblTitle.Text; set => lblTitle.Text = value; } public bool CanClose { get; set; } = true; public bool RememberNormalSize { get; set; } = true; public bool ControlBox { get { return flpButtons.Visible; } set { flpButtons.Visible = value; } } public TitleBarControl() { InitializeComponent(); flpButtons.MouseDown += FlpButtons_MouseDown; flpButtons.DoubleClick += FlpButtons_DoubleClick; lblTitle.MouseDown += LblTitle_MouseDown; btnClose.MouseEnter += BtnClose_MouseEnter; btnClose.MouseLeave += BtnClose_MouseLeave; lblTitle.AutoEllipsis = true; lblTitle.AutoSize = false; Stylizer.StylizeControl(this, DesignMode, StylizerOptions.StylizeTitleBar); SetBackgroundColor(); Load += TitleBarControl_Load; Resize += TitleBarControl_Resize; } private void TitleBarControl_Resize(object sender, EventArgs e) { lblTitle.Width = lblTitle.Parent.Width; } private void BtnClose_MouseEnter(object sender, EventArgs e) { btnClose.ForeColor = ColorTheme.Current.CloseForeColorHighlight; btnClose.BackColor = ColorTheme.Current.CloseBackgroundHighlight; } private void BtnClose_MouseLeave(object sender, EventArgs e) { btnClose.ForeColor = ColorTheme.Current.Text; btnClose.BackColor = ThemeBackColor; } public void HandleWindowStateChange(FormWindowState state) { if (state == FormWindowState.Maximized) SetMaximized(); } public void SetNormal() => SetNormal(true); private void TitleBarControl_Load(object sender, EventArgs e) { if (ParentForm == null) return; ParentForm.LocationChanged += TitleBarControl_LocationChanged; WindowState = ParentForm.WindowState; if (WindowState == FormWindowState.Normal) { m_size = ParentForm.ClientSize; m_location = ParentForm.Location; } } private Color ThemeBackColor => ColorTheme.Current.TitlebarBackground; private void TitleBarControl_LocationChanged(object sender, EventArgs e) { if (ParentForm == null) return; if (WindowState == FormWindowState.Normal) m_location = ParentForm.Location; if (!m_settingState && m_dragState == DragState.Start && WindowState == FormWindowState.Maximized) { m_dragState = DragState.Dragging; m_settingState = true; SetNormal(false, false); m_settingState = false; } } private void SetBackgroundColor() { var backColor = ThemeBackColor; tblMain.BackColor = backColor; flpButtons.BackColor = backColor; lblTitle.BackColor = backColor; btnClose.BackColor = backColor; btnMinimize.BackColor = backColor; btnMaximize.BackColor = backColor; } private void SetNormal(bool centerY, bool setLocation = true) { if (ParentForm == null) return; m_maximized = false; WindowState = FormWindowState.Normal; ParentForm.WindowState = FormWindowState.Normal; var bounds = Screen.GetWorkingArea(ParentForm); if (m_size == null || ParentForm is MainForm) { if (setLocation) ParentForm.Location = new Point(bounds.X + (int)(bounds.Width * 0.12), centerY ? bounds.Y + (int)(bounds.Height * 0.12) : 0); ParentForm.Size = new Size((int)(bounds.Width * 0.75), (int)(bounds.Height * 0.75)); return; } if (setLocation && m_location.HasValue) ParentForm.Location = m_location.Value; ParentForm.Size = m_size.Value; } private void SetMaximized() { if (ParentForm == null) return; m_maximized = true; WindowState = FormWindowState.Maximized; ParentForm.WindowState = FormWindowState.Normal; var bounds = Screen.GetWorkingArea(ParentForm); ParentForm.StartPosition = FormStartPosition.Manual; ParentForm.Location = bounds.Location; ParentForm.Width = bounds.Width; ParentForm.Height = bounds.Height; } public void SetControlBox(bool set) { flpButtons.Visible = set; } private void FlpButtons_DoubleClick(object sender, EventArgs e) { SetMinMax(); } private void tblMain_MouseDoubleClick(object sender, MouseEventArgs e) { SetMinMax(); } private void lblTitle_MouseDoubleClick(object sender, MouseEventArgs e) { SetMinMax(); } private void FlpButtons_MouseDown(object sender, MouseEventArgs e) { HandleMousePress(e); } private void tblMain_MouseDown(object sender, MouseEventArgs e) { HandleMousePress(e); } private void LblTitle_MouseDown(object sender, MouseEventArgs e) { HandleMousePress(e); } private void HandleMousePress(MouseEventArgs e) { if (e.Button != MouseButtons.Left) return; if (e.Clicks != 1) return; m_dragState = DragState.Start; ReleaseCapture(); if (ParentForm != null) SendMessage(ParentForm.Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); } private void btnClose_Click(object sender, EventArgs e) { if (ParentForm != null && CanClose) ParentForm.Close(); } private void btnMaximize_Click(object sender, EventArgs e) { SetMinMax(); } private void SetMinMax() { m_settingState = true; if (m_maximized) SetNormal(true); else SetMaximized(); m_settingState = false; } private void btnMinimize_Click(object sender, EventArgs e) { if (ParentForm == null) return; WindowState = FormWindowState.Minimized; ParentForm.WindowState = FormWindowState.Minimized; } } } ================================================ FILE: DoomLauncher/Controls/TitleBarControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Controls/UpdateControl.Designer.cs ================================================ namespace DoomLauncher { partial class UpdateControl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.btnInstall = new System.Windows.Forms.Button(); this.lblVersion = new System.Windows.Forms.Label(); this.lnkPage = new System.Windows.Forms.LinkLabel(); this.SuspendLayout(); // // btnInstall // this.btnInstall.Location = new System.Drawing.Point(161, 76); this.btnInstall.Margin = new System.Windows.Forms.Padding(4); this.btnInstall.Name = "btnInstall"; this.btnInstall.Size = new System.Drawing.Size(208, 28); this.btnInstall.TabIndex = 0; this.btnInstall.Text = "Download and Install"; this.btnInstall.UseVisualStyleBackColor = true; this.btnInstall.Click += new System.EventHandler(this.btnInstall_Click); // // lblVersion // this.lblVersion.AutoSize = true; this.lblVersion.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lblVersion.Location = new System.Drawing.Point(4, 12); this.lblVersion.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblVersion.Name = "lblVersion"; this.lblVersion.Size = new System.Drawing.Size(46, 17); this.lblVersion.TabIndex = 1; this.lblVersion.Text = "label1"; // // lnkPage // this.lnkPage.AutoSize = true; this.lnkPage.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lnkPage.Location = new System.Drawing.Point(4, 42); this.lnkPage.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lnkPage.Name = "lnkPage"; this.lnkPage.Size = new System.Drawing.Size(134, 17); this.lnkPage.TabIndex = 2; this.lnkPage.TabStop = true; this.lnkPage.Text = "Release Information"; this.lnkPage.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkPage_LinkClicked); // // UpdateControl // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Controls.Add(this.lnkPage); this.Controls.Add(this.lblVersion); this.Controls.Add(this.btnInstall); this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.Name = "UpdateControl"; this.Size = new System.Drawing.Size(397, 110); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.Button btnInstall; private System.Windows.Forms.Label lblVersion; private System.Windows.Forms.LinkLabel lnkPage; } } ================================================ FILE: DoomLauncher/Controls/UpdateControl.cs ================================================ using System; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Net; using System.Windows.Forms; namespace DoomLauncher { public partial class UpdateControl : UserControl { public static readonly string AppUpdateFileName = "AppUpdate.zip"; private ApplicationUpdateInfo m_info; private AppConfiguration m_appConfig; private ProgressBarForm m_updateDownloadProgress; private WebClient m_updateWebClient; public UpdateControl() { InitializeComponent(); Stylizer.StylizeControl(this, DesignMode); } public void Initialize(AppConfiguration appConfig, ApplicationUpdateInfo info) { m_info = info; m_appConfig = appConfig; lblVersion.Text = string.Format("{0} - {1}", m_info.Version.ToString(), m_info.ReleaseDate.ToShortDateString()); } private void lnkPage_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { Process.Start(m_info.ReleasePageUrl); } private void btnInstall_Click(object sender, EventArgs e) { m_updateDownloadProgress = new ProgressBarForm { Title = "Downloading", DisplayText = "Downloading Update...", Minimum = 0, Maximum = 100, StartPosition = FormStartPosition.CenterScreen }; StartUpdateDownload(m_info); ShowUpdateDownloadProgress(); } private void StartUpdateDownload(ApplicationUpdateInfo info) { if (InvokeRequired) { Invoke(new Action(StartUpdateDownload), new object[] { info }); } else { m_updateWebClient = new WebClient(); m_updateWebClient.DownloadProgressChanged += WebClient_DownloadProgressChanged; m_updateWebClient.DownloadFileCompleted += WebClient_DownloadFileCompleted; m_updateWebClient.DownloadFileAsync(new Uri(info.DownloadUrl), GetUpdateArchive()); } } private string GetUpdateArchive() { return Path.Combine(m_appConfig.TempDirectory.GetFullPath(), AppUpdateFileName); } private void ShowUpdateDownloadProgress() { if (InvokeRequired) Invoke(new Action(ShowUpdateDownloadProgress)); else m_updateDownloadProgress.ShowDialog(this); } private void CloseUpdateDownloadProgress() { if (InvokeRequired) { Invoke(new Action(CloseUpdateDownloadProgress)); } else { m_updateWebClient.Dispose(); m_updateDownloadProgress.Hide(); m_updateDownloadProgress.Close(); ApplicationUpdater applicationUpdater = new ApplicationUpdater(GetUpdateArchive(), AppDomain.CurrentDomain.BaseDirectory); if (!applicationUpdater.Execute()) CreateUpdateFailureForm(applicationUpdater).ShowDialog(this); } } public static TextBoxForm CreateUpdateFailureForm(ApplicationUpdater applicationUpdater) { return new TextBoxForm(true, MessageBoxButtons.OK) { Title = "Update Error", HeaderText = "The application was unable to update. Please download the update manually.", DisplayText = applicationUpdater.LastError, StartPosition = FormStartPosition.CenterScreen }; } private void UpdateDownloadProgress(int value) { if (InvokeRequired) Invoke(new Action(UpdateDownloadProgress)); else m_updateDownloadProgress.Value = value; } private void WebClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { CloseUpdateDownloadProgress(); } private void WebClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { UpdateDownloadProgress(e.ProgressPercentage); } } } ================================================ FILE: DoomLauncher/Controls/UpdateControl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/DataSources/CleanupFile.cs ================================================ namespace DoomLauncher.DataSources { public class CleanupFile { public int CleanupFileID { get; set; } public string FileName { get; set; } } } ================================================ FILE: DoomLauncher/DataSources/ConfigurationData.cs ================================================ using DoomLauncher.Interfaces; namespace DoomLauncher.DataSources { class ConfigurationData : IConfigurationData { public ConfigurationData() { AvailableValues = string.Empty; Value = string.Empty; } public int ConfigID { get; set; } public string Name { get; set; } public string Value { get; set; } public string AvailableValues { get; set; } public bool UserCanModify { get; set; } } } ================================================ FILE: DoomLauncher/DataSources/CustomParam.cs ================================================ namespace DoomLauncher { public class CustomParamDataSource : ICustomParam { public int CustomParamID { get; set; } public string FileName { get; set; } public string Parameter { get; set; } public int GameFileID { get; set; } } } ================================================ FILE: DoomLauncher/DataSources/FileData.cs ================================================ using DoomLauncher.Interfaces; using System; namespace DoomLauncher { public class FileData : IFileData { public FileData() { FileOrder = int.MaxValue; DateCreated = DateTime.Now; } public int? FileID { get; set; } public int GameFileID { get; set; } public string FileName { get; set; } public DateTime DateCreated { get; set; } public FileType FileTypeID { get; set; } public int SourcePortID { get; set; } public string Description { get; set; } public string OriginalFileName { get; set; } public string OriginalFilePath { get; set; } public string UserTitle { get; set; } public string UserDescription { get; set; } public string Map { get; set; } public int FileOrder { get; set; } public virtual bool IsUrl { get { return false; } } public static string GetTitle(IFileData fileData) { if (!string.IsNullOrEmpty(fileData.Map)) { string title = fileData.Map; if (string.IsNullOrEmpty(fileData.UserTitle)) return title; return $"{title} {fileData.UserTitle}"; } if (!string.IsNullOrEmpty(fileData.UserTitle)) return fileData.UserTitle; return string.Empty; } public override bool Equals(object obj) { if (obj is IFileData file && file.IsUrl == IsUrl) { if (FileID.HasValue) return FileID.Value == file.FileID; else return FileName.Equals(file.FileName); } return false; } public override int GetHashCode() { if (FileID.HasValue) return FileID.Value; return FileName.GetHashCode(); } } } ================================================ FILE: DoomLauncher/DataSources/GameFile.cs ================================================ using DoomLauncher.Handlers; using DoomLauncher.Interfaces; using System; using System.IO; namespace DoomLauncher.DataSources { public class GameFile : IGameFile, IGameProfile, ICloneable { public const string DefaultProfileName = "Default Profile"; public static string[] GetMaps(IGameFile gameFile) => gameFile.Map.Split(new [] { ", ", "," }, StringSplitOptions.RemoveEmptyEntries); private string m_lastDirectory; public GameFile() { FileName = Title = Author = Description = Thumbnail = Comments = Map = SettingsMap = SettingsSkill = SettingsExtraParams = SettingsFiles = SettingsSpecificFiles = string.Empty; Name = DefaultProfileName; SettingsStat = true; GameProfileID = -1; } public int? GameFileID { get; set; } public string FullFileName { get; set; } public virtual string FileName { get; set; } public string FileNameBase => Path.GetFileNameWithoutExtension(FileName); public string FileNameNoPath => Path.GetFileName(FileName); public virtual string LastDirectory => GetLastDirectory(FileName); public virtual string Title { get; set; } public virtual string Author { get; set; } public virtual DateTime? ReleaseDate { get; set; } public virtual string Description { get; set; } public int? IWadID { get; set; } public int? SourcePortID { get; set; } public string Thumbnail { get; set; } public string Comments { get; set; } public string Map { get; set; } public int? MapCount { get; set; } public virtual double? Rating { get; set; } public DateTime? LastPlayed { get; set; } public DateTime? Downloaded { get; set; } public int GameProfileID { get; set; } public string Name { get; set; } public string SettingsMap { get; set; } public string SettingsSkill { get; set; } public string SettingsExtraParams { get; set; } public string SettingsFiles { get; set; } public string SettingsFilesSourcePort { get; set; } public string SettingsFilesIWAD { get; set; } public string SettingsSpecificFiles { get; set; } public bool SettingsStat { get; set; } public bool SettingsLoadLatestSave { get; set; } public bool SettingsSaved { get; set; } public bool SettingsExtraParamsOnly { get; set; } public int? SettingsGameProfileID { get; set; } public bool IsGlobal => false; public int MinutesPlayed { get; set; } public virtual int FileSizeBytes { get; set; } public bool IsDoom64 { get; set; } public bool IsUnmanaged() => IsUnmanaged(FileName); public static bool IsUnmanaged(string filename) => Path.IsPathRooted(filename) || (PathExtensions.IsPartiallyQualified(filename) && HasPathSeparator(filename)); private static bool HasPathSeparator(string filename) { for (int i = 0; i < filename.Length; i++) { if (filename[i] == Path.DirectorySeparatorChar || filename[i] == Path.AltDirectorySeparatorChar) return true; } return false; } public bool IsDirectory() { if (!IsUnmanaged()) return false; return Util.IsDirectory(FileName); } public IArchiveReader OpenGameFile(LauncherPath gameFileDirectory) { string file; if (IsUnmanaged()) file = new LauncherPath(FileName).GetFullPath(); else file = Path.Combine(gameFileDirectory.GetFullPath(), FileName); // If the unmanaged file is a pk3 then ArchiveReader.Create will read it as a zip and try to unpack // Return FileArchiveReader instead so the pk3 will be added as a file // Zip extensions are ignored in this case since Doom Launcher's base functionality revovles around reading zip contents // SpecificFilesForm will also read zip files explicitly to allow user to select files in the archive if (!IsDirectory() && IsUnmanaged() && !ArchiveUtil.ShouldReadPackagedArchive(FileName)) return new FileArchiveReader(file); return ArchiveReader.Create(file); } public bool ArchiveExists(LauncherPath gameFileDirectory) { if (IsDirectory()) { return Directory.Exists(FileName); } else if (IsUnmanaged()) { var launcherPath = new LauncherPath(FileName); FileInfo fi = new FileInfo(launcherPath.GetFullPath()); return fi.Exists; } else { FileInfo fi = new FileInfo(Path.Combine(gameFileDirectory.GetFullPath(), FileName)); return fi.Exists; } } public object Clone() { GameFile gameFile = new GameFile(); var properties = gameFile.GetType().GetProperties(); foreach (var prop in properties) { if (prop.GetSetMethod() != null) prop.SetValue(gameFile, prop.GetValue(this)); } return gameFile; } public override bool Equals(object obj) { if (obj is IGameFile gameFile) return gameFile.FileName == FileName; return false; } public override int GetHashCode() { if (FileName != null) return FileName.GetHashCode(); return 0; } public override string ToString() { if (FileName == null) return string.Empty; return FileName; } private string GetLastDirectory(string path) { if (m_lastDirectory != null) return m_lastDirectory; if (!IsUnmanaged()) { m_lastDirectory = string.Empty; return m_lastDirectory; } string dir = Path.GetDirectoryName(FileName); m_lastDirectory = new DirectoryInfo(dir).Name; return m_lastDirectory; } } } ================================================ FILE: DoomLauncher/DataSources/GameProfile.cs ================================================ using DoomLauncher.Interfaces; namespace DoomLauncher.DataSources { public class GameProfile : IGameProfile { public GameProfile() { SettingsMap = SettingsSkill = SettingsExtraParams = SettingsFiles = SettingsSpecificFiles = string.Empty; SettingsStat = SettingsSaved = true; } public GameProfile(int gameFileID, string name) : this() { GameFileID = gameFileID; Name = name; } public static GameProfile CreateGlobalProfile(string name) { return new GameProfile() { GameFileID = -1, Name = name }; } public static void ApplyDefaultsToProfile(IGameProfile gameProfile, AppConfiguration appConfig) { if (!gameProfile.SourcePortID.HasValue) gameProfile.SourcePortID = (int)appConfig.GetTypedConfigValue(ConfigType.DefaultSourcePort, typeof(int)); if (!gameProfile.IWadID.HasValue) gameProfile.IWadID = (int)appConfig.GetTypedConfigValue(ConfigType.DefaultIWad, typeof(int)); if (string.IsNullOrEmpty(gameProfile.SettingsSkill)) gameProfile.SettingsSkill = (string)appConfig.GetTypedConfigValue(ConfigType.DefaultSkill, typeof(string)); } public int GameProfileID { get; set; } public int? GameFileID { get; set; } public int? IWadID { get; set; } public int? SourcePortID { get; set; } public string Name { get; set; } public string SettingsMap { get; set; } public string SettingsSkill { get; set; } public string SettingsExtraParams { get; set; } public string SettingsFiles { get; set; } public string SettingsFilesSourcePort { get; set; } public string SettingsFilesIWAD { get; set; } public string SettingsSpecificFiles { get; set; } public bool SettingsStat { get; set; } public bool SettingsLoadLatestSave { get; set; } public bool SettingsSaved { get; set; } public bool SettingsExtraParamsOnly { get; set; } public bool IsGlobal => GameFileID == -1; } } ================================================ FILE: DoomLauncher/DataSources/ITagDataSource.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DoomLauncher.Interfaces { public interface ITagDataSource { int TagID { get; set; } string Name { get; set; } bool HasTab { get; set; } bool HasColor { get; set; } int? Color { get; set; } } } ================================================ FILE: DoomLauncher/DataSources/IWadData.cs ================================================ using DoomLauncher.Interfaces; using System.IO; namespace DoomLauncher { public class IWadData : IIWadData { public int IWadID { get; set; } public string Name { get; set; } public string FileName { get; set; } public string FileNameBase => Path.GetFileNameWithoutExtension(FileName); public int? GameFileID { get; set; } public override bool Equals(object obj) { IIWadData iwad = obj as IIWadData; if (iwad != null) { return iwad.FileName == FileName; } return false; } public override int GetHashCode() { return FileName.GetHashCode(); } } } ================================================ FILE: DoomLauncher/DataSources/IdGamesGameFile.cs ================================================ using DoomLauncher.Interfaces; using System; using System.ComponentModel; using System.Net; namespace DoomLauncher.DataSources { class IdGamesGameFile : GameFile, IGameFileDownloadable, IDisposable { public event DownloadProgressChangedEventHandler DownloadProgressChanged; public event AsyncCompletedEventHandler DownloadCompleted; private WebClient m_webClient; public override string Title { get { return title; } set { title = value; } } public override string Author { get { return author; } set { author = value; } } public override DateTime? ReleaseDate { get { return date; } set { if (value.HasValue) date = value.Value; } } public override string Description { get { return description; } set { description = value; } } public override double? Rating { get { return rating; } set { if (value.HasValue) rating = value.Value; } } public override int FileSizeBytes { get { return size; } set { size = value; } } public override string LastDirectory => string.Empty; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (m_webClient != null) m_webClient.Dispose(); } public int id { get; set; } public string title { get; set; } public string author { get; set; } public string description { get; set; } public DateTime? date { get; set; } public string filename { get; set; } public string dir { get; set; } public double rating { get; set; } public int size { get; set; } public void Download(IGameFileDataSourceAdapter adapter, string dlFilename) { IdGamesDataAdapater dataAdapter = adapter as IdGamesDataAdapater; if (dataAdapter != null) { m_webClient = new WebClient(); m_webClient.DownloadProgressChanged += client_DownloadProgressChanged; m_webClient.DownloadFileCompleted += client_DownloadFileCompleted; m_webClient.DownloadFileAsync(new Uri(dataAdapter.MirrorUrl + dir + filename), dlFilename, id); } } public void Cancel() { if (m_webClient != null) m_webClient.CancelAsync(); } void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { m_webClient.Dispose(); m_webClient = null; DownloadCompleted?.Invoke(this, e); } void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { DownloadProgressChanged?.Invoke(this, e); } public override bool Equals(object obj) { if (obj is IdGamesGameFile gameFile) return id == gameFile.id; return false; } public override int GetHashCode() { return id; } } } ================================================ FILE: DoomLauncher/DataSources/NewFileData.cs ================================================ namespace DoomLauncher { public class NewFileData { public int SourcePortID { get; set; } public string Description { get; set; } public string UserTitle { get; set; } public string UserDescription { get; set; } public string Map { get; set; } } } ================================================ FILE: DoomLauncher/DataSources/PreviewImage.cs ================================================ namespace DoomLauncher { public class PreviewImage { public PreviewImage(string path, string title) { Path = path; Title = title; } public string Path { get; set; } public string Title { get; set; } } } ================================================ FILE: DoomLauncher/DataSources/SettingsDataSource.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DoomLauncher.DataSources { class SettingsDataSource : ISettingsDataSource { public int GameFileID { get; set; } public int SettingsID { get; set; } public string SettingsName { get; set; } public string SettingsMap { get; set; } public string SettingsSkill { get; set; } public string SettingsExtraParams { get; set; } public string SettingsFiles { get; set; } public string SettingsSpecificFiles { get; set; } } } ================================================ FILE: DoomLauncher/DataSources/SourcePortData.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System; using System.IO; using System.Linq; namespace DoomLauncher.DataSources { public class SourcePortData : ISourcePortData { public int SourcePortID { get; set; } public string Name { get; set; } public string SupportedExtensions { get; set; } public string SettingsFiles { get; set; } public string Executable { get; set; } public SourcePortLaunchType LaunchType { get; set; } public string FileOption { get; set; } public string ExtraParameters { get; set; } public LauncherPath AltSaveDirectory { get; set; } public bool Archived { get; set; } public LauncherPath Directory { get; set; } public ISourcePortFlavor GetFlavor() { ISourcePortFlavor[] sourcePortFlavors = new ISourcePortFlavor[] { new ZDoomSourcePortFlavor(this), new StatdumpSourcePortFlavor(this), new LevelstatSourcePortFlavor(this), new DoomsdaySourcePortFlavor(this), new HelionSourcePortFlavor(this), new GenericSourcePortFlavor(this) }; return sourcePortFlavors.First(x => x.Supported()); } public string GetFullExecutablePath() { return Path.Combine(Directory.GetFullPath(), Executable); } public LauncherPath GetReadSavePath() { if (!string.IsNullOrEmpty(AltSaveDirectory.GetFullPath())) return AltSaveDirectory; return Directory; } public LauncherPath GetLoadSavePath() { if (!string.IsNullOrEmpty(AltSaveDirectory.GetFullPath())) return AltSaveDirectory; return LauncherPath.NoPath; } public override bool Equals(object obj) { ISourcePortData sourcePort = obj as ISourcePortData; if (sourcePort != null) { return sourcePort.SourcePortID == SourcePortID; } return false; } public override int GetHashCode() { return SourcePortID; } public static string[] GetSupportedExtensions(ISourcePortData sourcePort) { string[] supportedExtensions = new string[] { }; if (sourcePort != null) supportedExtensions = sourcePort.SupportedExtensions.Split(new string[] { ", ", "," }, StringSplitOptions.RemoveEmptyEntries); return supportedExtensions; } } } ================================================ FILE: DoomLauncher/DataSources/SourcePortLaunchType.cs ================================================ namespace DoomLauncher { public enum SourcePortLaunchType { SourcePort, Utility, Doom64 } } ================================================ FILE: DoomLauncher/DataSources/StaticTagData.cs ================================================ using DoomLauncher.DataSources; namespace DoomLauncher { class StaticTagData : TagData { public static string GetFavoriteName(string name) => string.Concat("● ", name); public override string FavoriteName => GetFavoriteName(Name); } } ================================================ FILE: DoomLauncher/DataSources/StatsData.cs ================================================ using System; using System.Globalization; namespace DoomLauncher { public class StatsData : IStatsData { public int KillCount { get; set; } public int TotalKills { get; set; } public int SecretCount { get; set; } public int TotalSecrets { get; set; } public float LevelTime { get; set; } public int ItemCount { get; set; } public int TotalItems { get; set; } public string MapName { get; set; } public DateTime RecordTime { get; set; } public int? Skill { get; set; } public string FormattedKills { get { return string.Format("{0} / {1}", KillCount.ToString("N0", CultureInfo.InvariantCulture), TotalKills.ToString("N0", CultureInfo.InvariantCulture)); } } public string FormattedSecrets { get { return string.Format("{0} / {1}", SecretCount.ToString("N0", CultureInfo.InvariantCulture), TotalSecrets.ToString("N0", CultureInfo.InvariantCulture)); } } public string FormattedItems { get { return string.Format("{0} / {1}", ItemCount.ToString("N0", CultureInfo.InvariantCulture), TotalItems.ToString("N0", CultureInfo.InvariantCulture)); } } public string FormattedTime { get { TimeSpan ts = TimeSpan.FromSeconds(LevelTime); return string.Format("{0:D2}:{1:D2}:{2:D2}:{3:D3}ms", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds); } } public int GameFileID { get; set; } public int SourcePortID { get; set; } public int StatID { get; set; } public string SaveFile { get; set; } public override bool Equals(object obj) { if (obj is IStatsData stats) { return GameFileID == stats.GameFileID && KillCount == stats.KillCount && TotalKills == stats.TotalKills && SecretCount == stats.SecretCount && TotalSecrets == stats.TotalSecrets && ItemCount == stats.ItemCount && TotalItems == stats.TotalItems && LevelTime == stats.LevelTime && Skill == Skill && MapName.Equals(stats.MapName, StringComparison.OrdinalIgnoreCase); } return false; } public override int GetHashCode() { return string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}", GameFileID, KillCount, TotalKills, SecretCount, TotalSecrets, ItemCount, TotalItems, LevelTime, Skill.HasValue ? Skill : 0, MapName.ToLower()).GetHashCode(); } } } ================================================ FILE: DoomLauncher/DataSources/TagData.cs ================================================ using DoomLauncher.Interfaces; namespace DoomLauncher.DataSources { public class TagData : ITagData { public static readonly string FavoriteChar = "★"; public int TagID { get; set; } public string Name { get; set; } public bool HasTab { get; set; } public bool HasColor { get; set; } public int? Color { get; set; } public bool ExcludeFromOtherTabs { get; set; } public bool Favorite { get; set; } public virtual string FavoriteName => Favorite ? string.Concat(FavoriteChar, " ", Name) : Name; public override bool Equals(object obj) { if (obj is ITagData tag) return tag.TagID == TagID; return false; } public override int GetHashCode() { return TagID; } } } ================================================ FILE: DoomLauncher/DataSources/TagMapping.cs ================================================ using DoomLauncher.Interfaces; namespace DoomLauncher.DataSources { public class TagMapping : ITagMapping { public int TagID { get; set; } public int FileID { get; set; } public override bool Equals(object obj) { ITagMapping mapping = obj as ITagMapping; if (mapping != null) { return mapping.FileID == this.FileID && mapping.TagID == this.TagID; } return false; } public override int GetHashCode() { return string.Format("{0},{1}", FileID, TagID).GetHashCode(); } } } ================================================ FILE: DoomLauncher/DataSources/WadArchiveFile.cs ================================================ namespace DoomLauncher.DataSources { class WadArchiveFile : FileData { public override bool IsUrl { get { return true; } } } } ================================================ FILE: DoomLauncher/DataSources/WadArchiveGameFile.cs ================================================ using Newtonsoft.Json; using System.Collections.Generic; namespace DoomLauncher.DataSources { class WadArchiveGameFile : GameFile { public override string FileName { get { if (filenames != null && filenames.Length > 0) return filenames[0]; return null; } set { if (filenames == null) filenames = new string[1]; filenames[0] = value; } } [JsonProperty("extra")] public override string Title { get; set; } [JsonProperty("size")] public override int FileSizeBytes { get; set; } [JsonProperty("type")] public string type { get; set; } [JsonProperty("port")] public string port { get; set; } [JsonProperty("links")] public string[] links { get; set; } [JsonProperty("filenames")] public string[] filenames { get; set; } [JsonProperty("screenshots")] public Dictionary screenshots { get; set; } public override string LastDirectory => string.Empty; } class ScreenshotItem { [JsonProperty("name")] public string name { get; set; } [JsonProperty("value")] public string value { get; set; } } } ================================================ FILE: DoomLauncher/Demo/CldDemoParser.cs ================================================ using System.Collections.Generic; using System.IO; using System.Linq; namespace DoomLauncher.Demo { public class CldDemoParser : IDemoParser { private enum DemoCmds { CLD_DEMOLENGTH = 242, CLD_DEMOVERSION, CLD_CVARS, CLD_USERINFO, CLD_BODYSTART, CLD_TICCMD, CLD_LOCALCOMMAND, CLD_DEMOEND, CLD_DEMOWADS, NUM_DEMO_COMMANDS }; private readonly string m_file; private const int m_hdr = 0x444C435A; public CldDemoParser(string demofile) { m_file = demofile; } public bool CanParse() { if (File.Exists(m_file)) { using (BinaryReader br = new BinaryReader(new FileStream(m_file, FileMode.Open))) { return br.BaseStream.Length > 16 && br.ReadInt32() == m_hdr && br.ReadByte() == (byte)DemoCmds.CLD_DEMOLENGTH; } } return false; } public string[] GetRequiredFiles() { string[] wads = new string[] { }; try { using (BinaryReader br = new BinaryReader(new FileStream(m_file, FileMode.Open))) { if (br.ReadInt32() == m_hdr && br.ReadByte() == (byte)DemoCmds.CLD_DEMOLENGTH) { bool bodyStart = false; while (!bodyStart) { byte cmd = br.ReadByte(); switch ((DemoCmds)cmd) { case DemoCmds.CLD_DEMOVERSION: ReadVersion(br); break; case DemoCmds.CLD_DEMOWADS: return ReadDemoWads(br); case DemoCmds.CLD_BODYSTART: bodyStart = false; break; default: break; } } } } } catch { //bad file, do nothing } return wads; } private void ReadVersion(BinaryReader br) { br.ReadInt16(); //demo version ReadString(br); //string app version br.ReadByte(); br.ReadInt32(); } private string[] ReadDemoWads(BinaryReader br) { List wads = new List(); short wadCount = br.ReadInt16(); for (short i = 0; i < wadCount; i++) wads.Add(ReadString(br)); return wads.ToArray(); } private string ReadString(BinaryReader br) { List data = new List(); do { data.Add((char)br.ReadByte()); } while (data.Last() != 0); return new string(data.Take(data.Count - 1).ToArray()); } } } ================================================ FILE: DoomLauncher/Demo/DemoUtil.cs ================================================ using System.Linq; namespace DoomLauncher.Demo { public static class DemoUtil { public static IDemoParser GetDemoParser(string demoFile) { IDemoParser[] parsers = new IDemoParser[] { new CldDemoParser(demoFile) }; return parsers.FirstOrDefault(x => x.CanParse()); } } } ================================================ FILE: DoomLauncher/Demo/IDemoParser.cs ================================================ namespace DoomLauncher.Demo { public interface IDemoParser { bool CanParse(); string[] GetRequiredFiles(); } } ================================================ FILE: DoomLauncher/DoomLauncher.VisualElementsManifest.xml ================================================ ================================================ FILE: DoomLauncher/DoomLauncher.csproj ================================================  Debug AnyCPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1} WinExe Properties DoomLauncher DoomLauncher v4.8 512 publish\ true Disk false Foreground 7 Days false false true 0 1.0.0.%2a false false true AnyCPU true full false bin\Debug\ TRACE;DEBUG prompt 4 false AnyCPU pdbonly true bin\Release\ TRACE prompt 4 false DoomLauncher.ico app.manifest ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll ..\packages\Gameloop.Vdf.0.6.2\lib\net45\Gameloop.Vdf.dll ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\Octokit.0.50.0\lib\net46\Octokit.dll ..\packages\SevenZipSharp.0.64\lib\SevenZipSharp.dll ..\packages\SharpCompress.0.31.0\lib\net461\SharpCompress.dll ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll ..\packages\System.Collections.Specialized.4.3.0\lib\net46\System.Collections.Specialized.dll True True ..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\lib\net46\System.Data.SQLite.dll ..\packages\System.Data.SQLite.EF6.1.0.115.5\lib\net46\System.Data.SQLite.EF6.dll ..\packages\System.Data.SQLite.Linq.1.0.115.5\lib\net46\System.Data.SQLite.Linq.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.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll ..\packages\System.Text.Encoding.CodePages.5.0.0\lib\net461\System.Text.Encoding.CodePages.dll Component CheckBoxList.cs Component Component Component UserControl GameFileTile.cs Component UserControl TagSelectControl.cs UserControl TitleBarControl.cs UserControl UpdateControl.cs Component FlowLayoutPanelDB.cs Form FileManagementSelect.cs UserControl GameFileTileViewControl.cs UserControl UserControl GameFileTileExpanded.cs Form PlayRandomForm.cs Form SaveInfo.cs Form ScreenshotEditForm.cs Form TextBoxForm.cs UserControl PagingControl.cs UserControl SlideShowPictureBox.cs Form TagSelectForm.cs Component CRichTextBox.cs Form FilterForm.cs Form FilterSettingsForm.cs Form ScreenshotViewerForm.cs Form Welcome.cs Form DirectoriesForm.cs UserControl UserControl FilesCtrl.cs Form CumulativeStats.cs UserControl StatisticsView.cs Form SimpleFileSelectForm.cs UserControl Form FileDetailsEditForm.cs Form MessageCheckBox.cs Form SplashScreen.cs UserControl RatingControl.cs Form Form Form Form UserControl TagEdit.cs Form TagForm.cs Form SpecificFilesForm.cs Form MetaDataForm.cs Form Form StatsInfo.cs UserControl StatsControl.cs Form SyncStatusForm.cs UserControl UserControl IdGamesTabViewCtrl.cs UserControl BasicTabViewCtrl.cs Form AboutBox.cs UserControl GameFileEdit.cs UserControl GenericFileView.cs UserControl DownloadView.cs UserControl DownloadViewItem.cs Form FileSelectForm.cs Form SettingsForm.cs UserControl OptionsTabViewCtrl.cs UserControl IWadTabViewCtrl.cs Form GameFileEditForm.cs UserControl GameFileAssociationView.cs ScreenshotView.cs Form ProgressBarForm.cs UserControl GameFileViewControl.cs UserControl GameFileSummary.cs Component GrowLabel.cs Form PlayForm.cs Component TableLayoutPanelDB.cs Component Form MainForm.cs UserControl SearchControl.cs Form SourcePortViewForm.cs Form SourcePortEditForm.cs UserControl SourcePortEdit.cs UserControl TagTabView.cs UserControl TagControl.cs Form TagEditForm.cs Form TxtGenerator.cs UserControl CheckBoxList.cs FilesCtrl.cs GameFileTile.cs GameFileTileViewControl.cs StatisticsView.cs StatsControl.cs TagSelectControl.cs TitleBarControl.cs FileManagementSelect.cs FilterForm.cs CumulativeStats.cs DirectoriesForm.cs FileDetailsEditForm.cs FilterSettingsForm.cs MessageCheckBox.cs MetaDataForm.cs PlayRandomForm.cs SaveInfo.cs ScreenshotEditForm.cs ScreenshotViewerForm.cs SplashScreen.cs RatingControl.cs SpecificFilesForm.cs StatsInfo.cs SyncStatusForm.cs SimpleFileSelectForm.cs TextBoxForm.cs Welcome.cs PagingControl.cs GameFileTileExpanded.cs SlideShowPictureBox.cs BasicTabViewCtrl.cs AboutBox.cs GenericFileView.cs DownloadView.cs DownloadViewItem.cs FileSelectForm.cs GameFileEditForm.cs MainForm.cs GameFileViewControl.cs GameFileSummary.cs GameFileEdit.cs GameFileAssociationView.cs ProgressBarForm.cs SettingsForm.cs SourcePortViewForm.cs ResXFileCodeGenerator Resources.Designer.cs Designer True Resources.resx True PlayForm.cs SearchControl.cs ScreenshotView.cs SourcePortEditForm.cs SourcePortEdit.cs TagControl.cs TagEdit.cs TagForm.cs TagEditForm.cs TxtGenerator.cs UpdateControl.cs TagSelectForm.cs PreserveNewest SettingsSingleFileGenerator Settings.Designer.cs True Settings.settings True Designer {75af36a8-7797-4023-b183-5b63d448420a} BindingListView {70a25201-8ea4-48f8-a4a6-ed13adf8823c} CheckBoxComboBox {15a9a8fc-096e-4458-ab7f-02386740cf4e} WadReader False Microsoft .NET Framework 4.5 %28x86 and x64%29 true False .NET Framework 3.5 SP1 Client Profile false False .NET Framework 3.5 SP1 false PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B} 1 0 0 tlbimp False True if $(ConfigurationName) == Release $(SolutionDir)DoomLauncherRelease\bin\$(ConfigurationName)\DoomLauncherRelease.exe 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: DoomLauncher/Dpi/DpiScale.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public class DpiScale { public DpiScale(Graphics g) { DpiScaleX = g.DpiX / 96.0f; DpiScaleY = g.DpiY / 96.0f; } public DpiScale(float scaleX, float scaleY) { DpiScaleX = scaleX; DpiScaleY = scaleY; } public int ScaleIntY(int height) => (int)(height * DpiScaleY); public int ScaleIntX(int width) => (int)(width * DpiScaleY); public float ScaleFloatY(float height) => height * DpiScaleY; public float ScaleFloatX(float width) => width * DpiScaleX; public readonly float DpiScaleX; public readonly float DpiScaleY; public void ScaleControl(Control control) { control.Width = ScaleIntX(control.Width); control.Height = ScaleIntY(control.Height); } } } ================================================ FILE: DoomLauncher/Forms/AboutBox.Designer.cs ================================================ namespace DoomLauncher { partial class AboutBox { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutBox)); this.tblInfo = new System.Windows.Forms.TableLayoutPanel(); this.logoPictureBox = new System.Windows.Forms.PictureBox(); this.labelProductName = new System.Windows.Forms.Label(); this.labelVersion = new System.Windows.Forms.Label(); this.okButton = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); this.lblAuthor = new System.Windows.Forms.Label(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.lnkRepository = new System.Windows.Forms.LinkLabel(); this.lnkThread = new System.Windows.Forms.LinkLabel(); this.lnkThread2 = new System.Windows.Forms.LinkLabel(); this.lblWindowsVersion = new System.Windows.Forms.Label(); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblInfo.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit(); this.flowLayoutPanel1.SuspendLayout(); this.tblMain.SuspendLayout(); this.SuspendLayout(); // // tblInfo // this.tblInfo.ColumnCount = 2; this.tblInfo.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33F)); this.tblInfo.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67F)); this.tblInfo.Controls.Add(this.logoPictureBox, 0, 0); this.tblInfo.Controls.Add(this.labelProductName, 1, 0); this.tblInfo.Controls.Add(this.labelVersion, 1, 1); this.tblInfo.Controls.Add(this.okButton, 1, 6); this.tblInfo.Controls.Add(this.label1, 1, 4); this.tblInfo.Controls.Add(this.lblAuthor, 1, 3); this.tblInfo.Controls.Add(this.flowLayoutPanel1, 1, 5); this.tblInfo.Controls.Add(this.lblWindowsVersion, 1, 2); this.tblInfo.Dock = System.Windows.Forms.DockStyle.Fill; this.tblInfo.Location = new System.Drawing.Point(4, 45); this.tblInfo.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblInfo.Name = "tblInfo"; this.tblInfo.RowCount = 7; this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 60F)); this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblInfo.Size = new System.Drawing.Size(644, 386); this.tblInfo.TabIndex = 0; // // logoPictureBox // this.logoPictureBox.Anchor = System.Windows.Forms.AnchorStyles.Top; this.logoPictureBox.Image = ((System.Drawing.Image)(resources.GetObject("logoPictureBox.Image"))); this.logoPictureBox.Location = new System.Drawing.Point(10, 5); this.logoPictureBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.logoPictureBox.Name = "logoPictureBox"; this.tblInfo.SetRowSpan(this.logoPictureBox, 7); this.logoPictureBox.Size = new System.Drawing.Size(192, 197); this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.logoPictureBox.TabIndex = 12; this.logoPictureBox.TabStop = false; // // labelProductName // this.labelProductName.Dock = System.Windows.Forms.DockStyle.Fill; this.labelProductName.Location = new System.Drawing.Point(221, 0); this.labelProductName.Margin = new System.Windows.Forms.Padding(9, 0, 4, 0); this.labelProductName.MaximumSize = new System.Drawing.Size(0, 26); this.labelProductName.Name = "labelProductName"; this.labelProductName.Size = new System.Drawing.Size(419, 26); this.labelProductName.TabIndex = 19; this.labelProductName.Text = "Product Name"; this.labelProductName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // labelVersion // this.labelVersion.BackColor = System.Drawing.Color.Transparent; this.labelVersion.Dock = System.Windows.Forms.DockStyle.Fill; this.labelVersion.Location = new System.Drawing.Point(221, 49); this.labelVersion.Margin = new System.Windows.Forms.Padding(9, 0, 4, 0); this.labelVersion.MaximumSize = new System.Drawing.Size(0, 26); this.labelVersion.Name = "labelVersion"; this.labelVersion.Size = new System.Drawing.Size(419, 26); this.labelVersion.TabIndex = 0; this.labelVersion.Text = "Version"; this.labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // okButton // this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.okButton.Location = new System.Drawing.Point(528, 346); this.okButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.okButton.Name = "okButton"; this.okButton.Size = new System.Drawing.Size(112, 35); this.okButton.TabIndex = 24; this.okButton.Text = "&OK"; // // label1 // this.label1.AutoSize = true; this.label1.Dock = System.Windows.Forms.DockStyle.Fill; this.label1.Location = new System.Drawing.Point(221, 196); this.label1.Margin = new System.Windows.Forms.Padding(9, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(419, 49); this.label1.TabIndex = 26; this.label1.Text = "Please check out these links to post questions, comments, bugs, and feature reque" + "sts!\r\n"; this.label1.UseMnemonic = false; // // lblAuthor // this.lblAuthor.AutoSize = true; this.lblAuthor.Dock = System.Windows.Forms.DockStyle.Fill; this.lblAuthor.Location = new System.Drawing.Point(221, 147); this.lblAuthor.Margin = new System.Windows.Forms.Padding(9, 0, 4, 0); this.lblAuthor.MaximumSize = new System.Drawing.Size(0, 26); this.lblAuthor.Name = "lblAuthor"; this.lblAuthor.Size = new System.Drawing.Size(419, 26); this.lblAuthor.TabIndex = 28; this.lblAuthor.Text = "Author: Nick St Laurent"; this.lblAuthor.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.lnkRepository); this.flowLayoutPanel1.Controls.Add(this.lnkThread); this.flowLayoutPanel1.Controls.Add(this.lnkThread2); this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; this.flowLayoutPanel1.Location = new System.Drawing.Point(216, 245); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(424, 81); this.flowLayoutPanel1.TabIndex = 29; // // lnkRepository // this.lnkRepository.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lnkRepository.Location = new System.Drawing.Point(4, 0); this.lnkRepository.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lnkRepository.Name = "lnkRepository"; this.lnkRepository.Size = new System.Drawing.Size(187, 38); this.lnkRepository.TabIndex = 28; this.lnkRepository.TabStop = true; this.lnkRepository.Text = "GitHub Repository"; this.lnkRepository.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; this.lnkRepository.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkRepository_LinkClicked); // // lnkThread // this.lnkThread.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lnkThread.Location = new System.Drawing.Point(4, 38); this.lnkThread.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lnkThread.Name = "lnkThread"; this.lnkThread.Size = new System.Drawing.Size(187, 38); this.lnkThread.TabIndex = 27; this.lnkThread.TabStop = true; this.lnkThread.Text = "Doomworld Thread"; this.lnkThread.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; this.lnkThread.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkThread_LinkClicked); // // lnkThread2 // this.lnkThread2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lnkThread2.Location = new System.Drawing.Point(199, 0); this.lnkThread2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lnkThread2.Name = "lnkThread2"; this.lnkThread2.Size = new System.Drawing.Size(191, 38); this.lnkThread2.TabIndex = 26; this.lnkThread2.TabStop = true; this.lnkThread2.Text = "Realm 667 Forum"; this.lnkThread2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; this.lnkThread2.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkThread2_LinkClicked); // // lblWindowsVersion // this.lblWindowsVersion.AutoSize = true; this.lblWindowsVersion.Dock = System.Windows.Forms.DockStyle.Fill; this.lblWindowsVersion.Location = new System.Drawing.Point(221, 98); this.lblWindowsVersion.Margin = new System.Windows.Forms.Padding(9, 0, 4, 0); this.lblWindowsVersion.MaximumSize = new System.Drawing.Size(0, 26); this.lblWindowsVersion.Name = "lblWindowsVersion"; this.lblWindowsVersion.Size = new System.Drawing.Size(419, 26); this.lblWindowsVersion.TabIndex = 30; this.lblWindowsVersion.Text = "Windows Version"; this.lblWindowsVersion.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Controls.Add(this.tblInfo, 0, 1); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(652, 436); this.tblMain.TabIndex = 1; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(652, 40); this.titleBar.TabIndex = 0; this.titleBar.Title = "Doom Launcher"; // // AboutBox // this.AcceptButton = this.okButton; this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(652, 436); this.Controls.Add(this.tblMain); this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "AboutBox"; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "AboutBox"; this.tblInfo.ResumeLayout(false); this.tblInfo.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit(); this.flowLayoutPanel1.ResumeLayout(false); this.tblMain.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblInfo; private System.Windows.Forms.PictureBox logoPictureBox; private System.Windows.Forms.Label labelProductName; private System.Windows.Forms.Label labelVersion; private System.Windows.Forms.Button okButton; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label lblAuthor; private System.Windows.Forms.LinkLabel lnkRepository; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.LinkLabel lnkThread; private System.Windows.Forms.LinkLabel lnkThread2; private System.Windows.Forms.TableLayoutPanel tblMain; private Controls.TitleBarControl titleBar; private System.Windows.Forms.Label lblWindowsVersion; } } ================================================ FILE: DoomLauncher/Forms/AboutBox.cs ================================================ using DoomLauncher.WindowsVersion; using System.Diagnostics; using System.Reflection; using System.Windows.Forms; namespace DoomLauncher { partial class AboutBox : Form { public AboutBox() { InitializeComponent(); Text = string.Format("About {0}", AssemblyTitle); labelProductName.Text = AssemblyProduct; labelVersion.Text = string.Format("Version {0}", AssemblyVersion); if (WindowsVersionInfo.GetOsVersionInfo(out var versionInfo)) lblWindowsVersion.Text = $"Windows Version: Major:{versionInfo.Major} Build: {versionInfo.Build}"; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } #region Assembly Attribute Accessors public string AssemblyTitle { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false); if (attributes.Length > 0) { AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0]; if (titleAttribute.Title != "") { return titleAttribute.Title; } } return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase); } } public string AssemblyVersion { get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); } } public string AssemblyDescription { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); if (attributes.Length == 0) { return ""; } return ((AssemblyDescriptionAttribute)attributes[0]).Description; } } public string AssemblyProduct { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false); if (attributes.Length == 0) { return ""; } return ((AssemblyProductAttribute)attributes[0]).Product; } } public string AssemblyCopyright { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); if (attributes.Length == 0) { return ""; } return ((AssemblyCopyrightAttribute)attributes[0]).Copyright; } } public string AssemblyCompany { get { object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); if (attributes.Length == 0) { return ""; } return ((AssemblyCompanyAttribute)attributes[0]).Company; } } #endregion private void lnkRepository_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { Process.Start(Util.GitHubRepository); } private void lnkThread_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { Process.Start(Util.DoomworldThread); } private void lnkThread2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { Process.Start(Util.Realm667Thread); } } } ================================================ FILE: DoomLauncher/Forms/AboutBox.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 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 LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/CumulativeStats.Designer.cs ================================================ namespace DoomLauncher { partial class CumulativeStats { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CumulativeStats)); this.btnOK = new System.Windows.Forms.Button(); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.lblNote = new System.Windows.Forms.Label(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.lblDisplay2 = new System.Windows.Forms.Label(); this.lblDisplay1 = new System.Windows.Forms.Label(); this.lblTimeLaunched = new System.Windows.Forms.Label(); this.lblTimePlayed = new System.Windows.Forms.Label(); this.ctrlStats = new DoomLauncher.StatsControl(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // btnOK // this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.Right; this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(355, 267); this.btnOK.Margin = new System.Windows.Forms.Padding(4); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(100, 28); this.btnOK.TabIndex = 9; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.lblNote, 0, 1); this.tblMain.Controls.Add(this.btnOK, 0, 4); this.tblMain.Controls.Add(this.tableLayoutPanel1, 0, 2); this.tblMain.Controls.Add(this.ctrlStats, 0, 3); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 5; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 60F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 128F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(459, 303); this.tblMain.TabIndex = 2; // // lblNote // this.lblNote.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblNote.AutoSize = true; this.lblNote.Location = new System.Drawing.Point(4, 43); this.lblNote.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblNote.Name = "lblNote"; this.lblNote.Size = new System.Drawing.Size(39, 16); this.lblNote.TabIndex = 10; this.lblNote.Text = "Note:"; // // tableLayoutPanel1 // this.tableLayoutPanel1.ColumnCount = 2; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 178F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.Controls.Add(this.lblDisplay2, 0, 1); this.tableLayoutPanel1.Controls.Add(this.lblDisplay1, 0, 0); this.tableLayoutPanel1.Controls.Add(this.lblTimeLaunched, 1, 0); this.tableLayoutPanel1.Controls.Add(this.lblTimePlayed, 1, 1); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(1, 71); this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(1, 0, 1, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(4, 0, 0, 0); this.tableLayoutPanel1.RowCount = 2; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanel1.Size = new System.Drawing.Size(457, 60); this.tableLayoutPanel1.TabIndex = 12; // // lblDisplay2 // this.lblDisplay2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblDisplay2.AutoSize = true; this.lblDisplay2.Location = new System.Drawing.Point(8, 37); this.lblDisplay2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblDisplay2.Name = "lblDisplay2"; this.lblDisplay2.Size = new System.Drawing.Size(128, 16); this.lblDisplay2.TabIndex = 11; this.lblDisplay2.Text = "Time Played (Stats):"; // // lblDisplay1 // this.lblDisplay1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblDisplay1.AutoSize = true; this.lblDisplay1.Location = new System.Drawing.Point(8, 7); this.lblDisplay1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblDisplay1.Name = "lblDisplay1"; this.lblDisplay1.Size = new System.Drawing.Size(157, 16); this.lblDisplay1.TabIndex = 7; this.lblDisplay1.Text = "Time Played (Launched):"; // // lblTimeLaunched // this.lblTimeLaunched.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblTimeLaunched.AutoSize = true; this.lblTimeLaunched.Location = new System.Drawing.Point(185, 7); this.lblTimeLaunched.Name = "lblTimeLaunched"; this.lblTimeLaunched.Size = new System.Drawing.Size(66, 16); this.lblTimeLaunched.TabIndex = 12; this.lblTimeLaunched.Text = "Launched"; // // lblTimePlayed // this.lblTimePlayed.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblTimePlayed.AutoSize = true; this.lblTimePlayed.Location = new System.Drawing.Point(185, 37); this.lblTimePlayed.Name = "lblTimePlayed"; this.lblTimePlayed.Size = new System.Drawing.Size(50, 16); this.lblTimePlayed.TabIndex = 13; this.lblTimePlayed.Text = "Played"; // // ctrlStats // this.ctrlStats.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlStats.Location = new System.Drawing.Point(4, 135); this.ctrlStats.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.ctrlStats.Name = "ctrlStats"; this.ctrlStats.Size = new System.Drawing.Size(451, 120); this.ctrlStats.TabIndex = 13; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(459, 32); this.titleBar.TabIndex = 14; this.titleBar.Title = "Cumulative Stats"; // // CumulativeStats // this.AcceptButton = this.btnOK; this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(459, 303); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "CumulativeStats"; this.Text = "Cumulative Stats"; this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.PerformLayout(); this.ResumeLayout(false); } #endregion private TableLayoutPanelDB tblMain; private System.Windows.Forms.Label lblDisplay1; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.Label lblNote; private System.Windows.Forms.Label lblDisplay2; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.Label lblTimeLaunched; private System.Windows.Forms.Label lblTimePlayed; private StatsControl ctrlStats; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/CumulativeStats.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class CumulativeStats : Form { public CumulativeStats() { InitializeComponent(); tblMain.CellPaint += TblMain_CellPaint; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public string Title { get { return Text; } set { Text = value; } } public void SetStatistics(IEnumerable gameFiles, IEnumerable stats) { lblNote.Text = $"Note: Search filters apply ({gameFiles.Count()} Files)"; int statsMinutes = 0; int launchMinutes = 0; foreach (IStatsData stat in stats) statsMinutes += (int)(stat.LevelTime / 60.0); foreach (IGameFile gameFile in gameFiles) launchMinutes += gameFile.MinutesPlayed; lblTimeLaunched.Text = Util.GetTimePlayedString(launchMinutes); lblTimePlayed.Text = Util.GetTimePlayedString(statsMinutes); ctrlStats.SetStatistics(gameFiles, stats); } private void TblMain_CellPaint(object sender, TableLayoutCellPaintEventArgs e) { Point pt = e.CellBounds.Location; Pen pen = new Pen(ColorTheme.Current.GridBorder); e.Graphics.DrawLine(pen, pt.X, pt.Y, pt.X, pt.Y + e.CellBounds.Height); e.Graphics.DrawLine(pen, pt.X + e.CellBounds.Width - 1, pt.Y, e.CellBounds.Width - 1, pt.Y + e.CellBounds.Height); e.Graphics.DrawLine(pen, pt.X, pt.Y + e.CellBounds.Height - 1, pt.X + e.CellBounds.Width, pt.Y + e.CellBounds.Height - 1); } } } ================================================ FILE: DoomLauncher/Forms/CumulativeStats.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/DirectoriesForm.Designer.cs ================================================ namespace DoomLauncher.Forms { partial class DirectoriesForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DirectoriesForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.toolStrip1 = new System.Windows.Forms.ToolStrip(); this.btnAdd = new System.Windows.Forms.ToolStripButton(); this.btnDelete = new System.Windows.Forms.ToolStripButton(); this.flp = new System.Windows.Forms.FlowLayoutPanel(); this.btnCancel = new System.Windows.Forms.Button(); this.btnSave = new System.Windows.Forms.Button(); this.lstDirectories = new System.Windows.Forms.ListView(); this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.tblMain.SuspendLayout(); this.toolStrip1.SuspendLayout(); this.flp.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.toolStrip1, 0, 0); this.tblMain.Controls.Add(this.flp, 0, 2); this.tblMain.Controls.Add(this.lstDirectories, 0, 1); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.Size = new System.Drawing.Size(384, 261); this.tblMain.TabIndex = 0; // // toolStrip1 // this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.btnAdd, this.btnDelete}); this.toolStrip1.Location = new System.Drawing.Point(0, 0); this.toolStrip1.Name = "toolStrip1"; this.toolStrip1.Size = new System.Drawing.Size(384, 25); this.toolStrip1.TabIndex = 4; this.toolStrip1.Text = "toolStrip1"; // // btnAdd // this.btnAdd.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; this.btnAdd.Image = ((System.Drawing.Image)(resources.GetObject("btnAdd.Image"))); this.btnAdd.ImageTransparentColor = System.Drawing.Color.Magenta; this.btnAdd.Name = "btnAdd"; this.btnAdd.Size = new System.Drawing.Size(23, 22); this.btnAdd.Text = "Add"; this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click); // // btnDelete // this.btnDelete.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; this.btnDelete.Image = ((System.Drawing.Image)(resources.GetObject("btnDelete.Image"))); this.btnDelete.ImageTransparentColor = System.Drawing.Color.Magenta; this.btnDelete.Name = "btnDelete"; this.btnDelete.Size = new System.Drawing.Size(23, 22); this.btnDelete.Text = "Remove"; this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); // // flp // this.flp.Controls.Add(this.btnCancel); this.flp.Controls.Add(this.btnSave); this.flp.Dock = System.Windows.Forms.DockStyle.Fill; this.flp.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flp.Location = new System.Drawing.Point(0, 229); this.flp.Margin = new System.Windows.Forms.Padding(0); this.flp.Name = "flp"; this.flp.Size = new System.Drawing.Size(384, 32); this.flp.TabIndex = 0; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(306, 3); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(75, 23); this.btnCancel.TabIndex = 0; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // btnSave // this.btnSave.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnSave.Location = new System.Drawing.Point(225, 3); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(75, 23); this.btnSave.TabIndex = 1; this.btnSave.Text = "Accept"; this.btnSave.UseVisualStyleBackColor = true; // // lstDirectories // this.lstDirectories.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { this.columnHeader1}); this.lstDirectories.Dock = System.Windows.Forms.DockStyle.Fill; this.lstDirectories.FullRowSelect = true; this.lstDirectories.HideSelection = false; this.lstDirectories.Location = new System.Drawing.Point(3, 35); this.lstDirectories.MultiSelect = false; this.lstDirectories.Name = "lstDirectories"; this.lstDirectories.Size = new System.Drawing.Size(378, 191); this.lstDirectories.TabIndex = 5; this.lstDirectories.UseCompatibleStateImageBehavior = false; this.lstDirectories.View = System.Windows.Forms.View.Details; // // DirectoriesForm // this.AcceptButton = this.btnSave; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(384, 261); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "DirectoriesForm"; this.Text = "Directories"; this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.toolStrip1.ResumeLayout(false); this.toolStrip1.PerformLayout(); this.flp.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.FlowLayoutPanel flp; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnSave; private System.Windows.Forms.ToolStrip toolStrip1; private System.Windows.Forms.ToolStripButton btnAdd; private System.Windows.Forms.ToolStripButton btnDelete; private System.Windows.Forms.ListView lstDirectories; private System.Windows.Forms.ColumnHeader columnHeader1; } } ================================================ FILE: DoomLauncher/Forms/DirectoriesForm.cs ================================================ using System; using System.Collections.Generic; using System.Windows.Forms; namespace DoomLauncher.Forms { public partial class DirectoriesForm : Form { public DirectoriesForm() { InitializeComponent(); lstDirectories.HeaderStyle = ColumnHeaderStyle.None; lstDirectories.Columns[0].Width = lstDirectories.Width; Icons.DpiScale = new DpiScale(CreateGraphics()); btnAdd.Image = Icons.File; btnDelete.Image = Icons.Delete; Stylizer.Stylize(this, DesignMode); } public void SetDirectories(string[] directories) { Array.ForEach(directories, x => lstDirectories.Items.Add(x)); } public List GetDirectories() { List ret = new List(); foreach (ListViewItem item in lstDirectories.Items) ret.Add(item.Text); return ret; } private void btnAdd_Click(object sender, EventArgs e) { FolderBrowserDialog dialog = new FolderBrowserDialog(); if (dialog.ShowDialog(this) == DialogResult.OK) { lstDirectories.Items.Add(dialog.SelectedPath); } } private void btnDelete_Click(object sender, EventArgs e) { if (lstDirectories.SelectedItems.Count > 0) lstDirectories.Items.Remove(lstDirectories.SelectedItems[0]); } } } ================================================ FILE: DoomLauncher/Forms/DirectoriesForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 17, 17 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGjSURBVDhPjZFLSwJRFMcvQStp1TboA7RqUZuofY9N65b1 JYKgD5CbCoWeRA/IAiELQqEXFWQSIlZYWSL2YPKZadrMOP/OnbmjRjT4h8O5nHN//zvnDDOVSo2UFWU4 ggUGhBkUZTAmSUOyaFurErF55HEC4QD2KN8aZ71W6N0S1/6XGrFdVYEVih1mnKmWu+uB+/AaLm8Q67t+ rO36pwRWk2ynyw6KaQ5xbRpfMk+xadaAxe1jdA+M/TWRbpkihVo1zNBlmh9PzcAR5XInkGgTOL2x7oXv /Fo3WfVc2AVOI6hDgeoI/EV6vboTTOow1+zqvg6bIXAyKPSdVIElytEm/WzU5jirS1EruolpJHBDWqpj ovLQ4oSboOd2ID1Of2BBoICmaSiWZEjpom7Q1T/628DUsvtUIL/1LavIfJQQf/uwNph3HQqkJrWiIV/8 xmuygPt4xtrAueETWE2lsoJk9guxlxzCD0lrA96sF19c7rOMhJRHJJZG4OatcYP6xUUTWYTu3nEWfG7c oH5xN48p+MOvOLiMN2bAs1VYGvBmI8EYYz/DeTd1SwVw7gAAAABJRU5ErkJggg== iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPYwABLy8vAzcvzz2enp5O9fX1TGBBYkFoaCiz k5NruqOjy38nZ+dv3t7egSAxqDQKsLe3ZwkKClJyd3fNgwpBBB0dnZNs7R22mVva/tfS0vrj7u5ZhW6I vLw9h6Wto5O3l9d3e3vHOVBhCLC1dZe0t3excHBw2WZlY//fzt7hv7+P50ZXV1dukLyFhQWnu7v7CkVl p9+mpubdQEt4wBrRAdBWNicnlwJlB+ffdnZO/4HeWWZubqNnaWk32dbG5r+egclkJSVjfqhynIDRzs5F 09bW6byZufV/fX3974bGRu/VtHTXychYcELVEAb6+sYOevomPy2tgOGib3RPRkZLCCpFGOibmyuoqGhd 0tUz/KdvYPLfxNTiv5a2zi2oNH5gaGgor6aufVVNXeuPhqbmGmFhCRNZWZVtBobG/1XUtDOhyrADYOhK yMorn5JTVP2vrWdwSEZGRkhcXJwbhOUU1aYbGBh/UdbWloUqRwXy9vYcMoqqiySkpT9qaRk+0dIy0IJK gRIBBwODEJ+2vsFZCSn5SUARzIQmLSfnLSah8F9VTRvoVI1WBgZxcBpAAkzKyloqysrK9wUEBORBfIgw BLAJC4u2CgmL/5eXV97FxsarBhTDlieYTU0tJ3PycZoC2ajyknLKRqys7BOBTDsg5gULYgcsQMwFYQ48 YGAAAEPWdZixdbnSAAAAAElFTkSuQmCC AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/FileDetailsEditForm.Designer.cs ================================================ namespace DoomLauncher { partial class FileDetailsEditForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FileDetailsEditForm)); this.btnSave = new System.Windows.Forms.Button(); this.btnCancel = new System.Windows.Forms.Button(); this.tblData = new DoomLauncher.TableLayoutPanelDB(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.txtDescription = new System.Windows.Forms.TextBox(); this.cmbSourcePort = new System.Windows.Forms.ComboBox(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblData.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.tblMain.SuspendLayout(); this.SuspendLayout(); // // btnSave // this.btnSave.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnSave.Location = new System.Drawing.Point(182, 5); this.btnSave.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(112, 35); this.btnSave.TabIndex = 1; this.btnSave.Text = "Save"; this.btnSave.UseVisualStyleBackColor = true; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(302, 5); this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(112, 35); this.btnCancel.TabIndex = 0; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // tblData // this.tblData.ColumnCount = 2; this.tblData.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 150F)); this.tblData.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblData.Controls.Add(this.label1, 0, 0); this.tblData.Controls.Add(this.label2, 0, 1); this.tblData.Controls.Add(this.txtDescription, 1, 1); this.tblData.Controls.Add(this.cmbSourcePort, 1, 0); this.tblData.Controls.Add(this.flowLayoutPanel1, 1, 2); this.tblData.Location = new System.Drawing.Point(4, 45); this.tblData.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblData.Name = "tblData"; this.tblData.RowCount = 3; this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblData.Size = new System.Drawing.Size(568, 353); this.tblData.TabIndex = 0; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(4, 14); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(93, 20); this.label1.TabIndex = 0; this.label1.Text = "Source Port"; // // label2 // this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(4, 166); this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(89, 20); this.label2.TabIndex = 1; this.label2.Text = "Description"; // // txtDescription // this.txtDescription.Dock = System.Windows.Forms.DockStyle.Fill; this.txtDescription.Location = new System.Drawing.Point(154, 54); this.txtDescription.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtDescription.Multiline = true; this.txtDescription.Name = "txtDescription"; this.txtDescription.Size = new System.Drawing.Size(410, 245); this.txtDescription.TabIndex = 2; // // cmbSourcePort // this.cmbSourcePort.Anchor = System.Windows.Forms.AnchorStyles.Left; this.cmbSourcePort.FormattingEnabled = true; this.cmbSourcePort.Location = new System.Drawing.Point(154, 10); this.cmbSourcePort.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbSourcePort.Name = "cmbSourcePort"; this.cmbSourcePort.Size = new System.Drawing.Size(410, 28); this.cmbSourcePort.TabIndex = 3; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.btnCancel); this.flowLayoutPanel1.Controls.Add(this.btnSave); this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flowLayoutPanel1.Location = new System.Drawing.Point(150, 304); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(418, 49); this.flowLayoutPanel1.TabIndex = 4; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.tblData, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(576, 403); this.tblMain.TabIndex = 1; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(576, 37); this.titleBar.TabIndex = 1; this.titleBar.Title = "Details"; // // FileDetailsEditForm // this.AcceptButton = this.btnSave; this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(576, 403); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "FileDetailsEditForm"; this.Text = "Details"; this.tblData.ResumeLayout(false); this.tblData.PerformLayout(); this.flowLayoutPanel1.ResumeLayout(false); this.tblMain.ResumeLayout(false); this.ResumeLayout(false); } #endregion private TableLayoutPanelDB tblData; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.TextBox txtDescription; private System.Windows.Forms.ComboBox cmbSourcePort; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnSave; private System.Windows.Forms.TableLayoutPanel tblMain; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/FileDetailsEditForm.cs ================================================ using DoomLauncher.Interfaces; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public partial class FileDetailsEditForm : Form { public FileDetailsEditForm() { InitializeComponent(); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public void Initialize(IDataSourceAdapter adapter) { Initialize(adapter, null); } public void Initialize(IDataSourceAdapter adapter, IFileData file) { DataSourceAdapter = adapter; cmbSourcePort.DisplayMember = "Name"; cmbSourcePort.ValueMember = "SourcePortID"; cmbSourcePort.DataSource = adapter.GetSourcePorts(); if (file != null) { cmbSourcePort.SelectedValue = file.SourcePortID; txtDescription.Text = file.Description; } } public void ShowDescription(bool set) { DpiScale dpiScale = new DpiScale(CreateGraphics()); if (set) { txtDescription.Visible = true; Height = dpiScale.ScaleIntY(300); MaximumSize = new Size(dpiScale.ScaleIntX(400), Height); } else { txtDescription.Visible = false; Height = dpiScale.ScaleIntY(104); MaximumSize = new Size(dpiScale.ScaleIntX(400), Height); } } public ISourcePortData SourcePort { get { return cmbSourcePort.SelectedItem as ISourcePortData; } set { cmbSourcePort.SelectedValue = value.SourcePortID; } } public string Description { get { return txtDescription.Text; } set { txtDescription.Text = value; } } public IDataSourceAdapter DataSourceAdapter { get; set; } } } ================================================ FILE: DoomLauncher/Forms/FileDetailsEditForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/FileManagementSelect.Designer.cs ================================================ namespace DoomLauncher.Forms { partial class FileManagementSelect { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FileManagementSelect)); this.cmbFileManagement = new System.Windows.Forms.ComboBox(); this.btnOK = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.panel1 = new System.Windows.Forms.Panel(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.panel1.SuspendLayout(); this.SuspendLayout(); // // cmbFileManagement // this.cmbFileManagement.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbFileManagement.FormattingEnabled = true; this.cmbFileManagement.Location = new System.Drawing.Point(10, 28); this.cmbFileManagement.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.cmbFileManagement.Name = "cmbFileManagement"; this.cmbFileManagement.Size = new System.Drawing.Size(330, 24); this.cmbFileManagement.TabIndex = 0; // // btnOK // this.btnOK.Location = new System.Drawing.Point(240, 75); this.btnOK.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(100, 28); this.btnOK.TabIndex = 1; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; this.btnOK.Click += new System.EventHandler(this.BtnOK_Click); // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(7, 8); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(273, 16); this.label1.TabIndex = 2; this.label1.Text = "Select the management method for the file(s)."; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.panel1, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 16F)); this.tblMain.Size = new System.Drawing.Size(356, 150); this.tblMain.TabIndex = 3; // // panel1 // this.panel1.Controls.Add(this.label1); this.panel1.Controls.Add(this.cmbFileManagement); this.panel1.Controls.Add(this.btnOK); this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel1.Location = new System.Drawing.Point(3, 34); this.panel1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(350, 114); this.panel1.TabIndex = 0; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(356, 32); this.titleBar.TabIndex = 1; this.titleBar.Title = "File Management"; // // FileManagementSelect // this.AcceptButton = this.btnOK; this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(356, 150); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.Name = "FileManagementSelect"; this.Text = "File Management"; this.tblMain.ResumeLayout(false); this.panel1.ResumeLayout(false); this.panel1.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ComboBox cmbFileManagement; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.Label label1; private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.Panel panel1; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/FileManagementSelect.cs ================================================ using System.Windows.Forms; namespace DoomLauncher.Forms { public partial class FileManagementSelect : Form { public FileManagementSelect() { InitializeComponent(); cmbFileManagement.DataSource = new string[] { "Managed", "Unmanaged" }; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public FileManagement GetSelectedFileManagement() { if (cmbFileManagement.SelectedIndex == 0) return FileManagement.Managed; return FileManagement.Unmanaged; } private void BtnOK_Click(object sender, System.EventArgs e) { Close(); } } } ================================================ FILE: DoomLauncher/Forms/FileManagementSelect.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/FileSelectForm.Designer.cs ================================================ namespace DoomLauncher { partial class FileSelectForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FileSelectForm)); this.btnOK = new System.Windows.Forms.Button(); this.btnCancel = new System.Windows.Forms.Button(); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.flpSearch = new System.Windows.Forms.FlowLayoutPanel(); this.ctrlSearch = new DoomLauncher.SearchControl(); this.lblText = new System.Windows.Forms.Label(); this.tabControl = new DoomLauncher.CTabControl(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.flpButtons.SuspendLayout(); this.flpSearch.SuspendLayout(); this.SuspendLayout(); // // btnOK // this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(1132, 4); this.btnOK.Margin = new System.Windows.Forms.Padding(4); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(100, 28); this.btnOK.TabIndex = 1; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(1240, 4); this.btnCancel.Margin = new System.Windows.Forms.Padding(4); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(100, 28); this.btnCancel.TabIndex = 0; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.flpButtons, 0, 3); this.tblMain.Controls.Add(this.flpSearch, 0, 1); this.tblMain.Controls.Add(this.tabControl, 0, 2); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 4; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.Size = new System.Drawing.Size(1344, 898); this.tblMain.TabIndex = 0; // // flpButtons // this.flpButtons.Controls.Add(this.btnCancel); this.flpButtons.Controls.Add(this.btnOK); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flpButtons.Location = new System.Drawing.Point(0, 859); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(1344, 39); this.flpButtons.TabIndex = 1; // // flpSearch // this.flpSearch.Controls.Add(this.ctrlSearch); this.flpSearch.Controls.Add(this.lblText); this.flpSearch.Dock = System.Windows.Forms.DockStyle.Fill; this.flpSearch.Location = new System.Drawing.Point(0, 32); this.flpSearch.Margin = new System.Windows.Forms.Padding(0); this.flpSearch.Name = "flpSearch"; this.flpSearch.Size = new System.Drawing.Size(1344, 39); this.flpSearch.TabIndex = 2; // // ctrlSearch // this.ctrlSearch.Location = new System.Drawing.Point(8, 5); this.ctrlSearch.Margin = new System.Windows.Forms.Padding(8, 5, 4, 4); this.ctrlSearch.Name = "ctrlSearch"; this.ctrlSearch.SearchText = ""; this.ctrlSearch.Size = new System.Drawing.Size(260, 28); this.ctrlSearch.TabIndex = 2; // // lblText // this.lblText.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblText.AutoSize = true; this.lblText.Location = new System.Drawing.Point(276, 10); this.lblText.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblText.Name = "lblText"; this.lblText.Size = new System.Drawing.Size(0, 16); this.lblText.TabIndex = 4; // // tabControl // this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControl.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed; this.tabControl.ItemSize = new System.Drawing.Size(0, 22); this.tabControl.Location = new System.Drawing.Point(4, 75); this.tabControl.Margin = new System.Windows.Forms.Padding(4); this.tabControl.Multiline = true; this.tabControl.Name = "tabControl"; this.tabControl.SelectedIndex = 0; this.tabControl.Size = new System.Drawing.Size(1336, 780); this.tabControl.TabIndex = 3; this.tabControl.SelectedIndexChanged += new System.EventHandler(this.TabControl_SelectedIndexChanged); // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(1344, 32); this.titleBar.TabIndex = 4; this.titleBar.Title = "Select File"; // // FileSelectForm // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(1344, 898); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "FileSelectForm"; this.Text = "Select File"; this.tblMain.ResumeLayout(false); this.flpButtons.ResumeLayout(false); this.flpSearch.ResumeLayout(false); this.flpSearch.PerformLayout(); this.ResumeLayout(false); } #endregion private TableLayoutPanelDB tblMain; private System.Windows.Forms.FlowLayoutPanel flpButtons; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.FlowLayoutPanel flpSearch; private SearchControl ctrlSearch; private DoomLauncher.CTabControl tabControl; private System.Windows.Forms.Label lblText; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/FileSelectForm.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Windows.Forms; namespace DoomLauncher { public partial class FileSelectForm : Form { private TabHandler m_tabHandler; private bool m_bOverrideInit, m_multiSelect; private IGameFileView m_lastView; public FileSelectForm() { InitializeComponent(); SetupSearchFilters(); Icons.DpiScale = new DpiScale(CreateGraphics()); m_multiSelect = m_bOverrideInit = false; ctrlSearch.SearchTextChanged += CtrlSearch_SearchTextChanged; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } private void CtrlSearch_SearchTextChanged(object sender, EventArgs e) { IGameFileView ctrl = CurrentGameFileControl; if (ctrl != null) HandleSearch(ctrl); } public void Initialize(IDataSourceAdapter adapter, ITabView tabView, IEnumerable gameFilesBind) { Initialize(adapter, new ITabView[] { tabView }); m_tabHandler.TabViews[0].SetGameFilesData(gameFilesBind); m_bOverrideInit = true; } public void Initialize(IDataSourceAdapter adapter, IEnumerable views) { DataSourceAdapter = adapter; m_tabHandler = new TabHandler(tabControl); foreach(ITabView view in views) { ITabView viewAdd = (ITabView)view.Clone(); viewAdd.GameFileViewControl.MultiSelect = MultiSelect; viewAdd.GameFileViewControl.ItemDoubleClick += GameFileViewControl_RowDoubleClicked; m_tabHandler.AddTab(viewAdd); } } private void GameFileViewControl_RowDoubleClicked(object sender, EventArgs e) { DialogResult = DialogResult.OK; Close(); } public bool MultiSelect { get { return m_multiSelect; } set { m_multiSelect = value; if (m_tabHandler != null) { foreach(ITabView view in m_tabHandler.TabViews) { view.GameFileViewControl.MultiSelect = MultiSelect; } } } } public ITabView[] TabViews { get { return m_tabHandler.TabViews; } } public void SetShowCancel(bool set) { btnCancel.Visible = set; } public void SetDisplayText(string text) { lblText.Text = text; } public void ShowSearchControl(bool set) { ctrlSearch.Visible = set; } protected override void OnShown(EventArgs e) { foreach (ITabView view in m_tabHandler.TabViews) { HandleSearch(view.GameFileViewControl); } } private void SetupSearchFilters() { Util.SetDefaultSearchFields(ctrlSearch); } public IDataSourceAdapter DataSourceAdapter { get; set; } public IGameFile[] SelectedFiles => CurrentGameFileControl.SelectedItems; private void btnSearch_Click(object sender, EventArgs e) { IGameFileView ctrl = CurrentGameFileControl; if (ctrl != null) HandleSearch(ctrl); } private void HandleSearch(IGameFileView ctrl) { if (!m_bOverrideInit) { ITabView view = m_tabHandler.TabViewForControl(ctrl); if (view != null) { if (!string.IsNullOrEmpty(ctrlSearch.SearchText)) view.SetGameFiles(Util.SearchFieldsFromSearchCtrl(ctrlSearch)); else view.SetGameFiles(); } } } private void TabControl_SelectedIndexChanged(object sender, EventArgs e) { var gameFileControl = CurrentGameFileControl; if (m_lastView != null) m_lastView.SetVisible(false); if (gameFileControl != null) gameFileControl.SetVisible(true); m_lastView = gameFileControl; } private IGameFileView CurrentGameFileControl { get { if (tabControl.SelectedTab.Controls[0] is ITabView view) return view.GameFileViewControl; return null; } } } } ================================================ FILE: DoomLauncher/Forms/FileSelectForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/FilterForm.Designer.cs ================================================ namespace DoomLauncher.Forms { partial class FilterForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); m_pen.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.SuspendLayout(); // // CrtFilterForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(800, 450); this.Name = "CrtFilterForm"; this.Text = "CrtFilterForm"; this.ResumeLayout(false); } #endregion } } ================================================ FILE: DoomLauncher/Forms/FilterForm.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Threading.Tasks; using System.Windows.Forms; namespace DoomLauncher.Forms { public partial class FilterForm : Form { private List m_rects = new List(); private List> m_lines = new List>(); private readonly Pen m_pen; public FilterForm(Screen screen, ScreenFilter filter) { Init(screen); m_pen = new Pen(new SolidBrush(Color.Black), filter.LineThickness); Opacity = filter.Opacity; Rectangle rect = screen.Bounds; if (filter.Type == ScreenFilterType.Ellipse) InitEllipse(filter, rect); else InitScanline(filter, rect); Stylizer.Stylize(this, DesignMode); } private void InitScanline(ScreenFilter filter, Rectangle rect) { if (filter.VerticalScanlines) { for (float x = rect.Left; x < rect.Right; x += filter.ScanlineSpacing) m_lines.Add(new Tuple(new PointF(x, rect.Top), new PointF(x, rect.Bottom))); } if (filter.HorizontalScanlines) { for (float y = rect.Top; y < rect.Height; y += filter.ScanlineSpacing) m_lines.Add(new Tuple(new PointF(rect.Left, y), new PointF(rect.Right, y))); } } private void InitEllipse(ScreenFilter filter, Rectangle rect) { int count = 0; float add = 0.0f; for (float x = rect.Left; x < rect.Right; x += filter.BlockSize + filter.SpacingX) { if (filter.Stagger) add = count % 2 == 0 ? 0 : filter.BlockSize / 2; for (float y = rect.Top - filter.BlockSize; y < rect.Height + filter.BlockSize; y += filter.BlockSize + filter.SpacingY) m_rects.Add(new RectangleF(new PointF(x, y + add), new SizeF(filter.BlockSize, filter.BlockSize))); count++; } } private void Init(Screen screen) { InitializeComponent(); Text = string.Empty; NativeMethods.MoveWindow(Handle, screen.Bounds.Location.X, screen.Bounds.Location.Y, screen.Bounds.Width, screen.Bounds.Height, false); ControlBox = false; FormBorderStyle = FormBorderStyle.None; TransparencyKey = BackColor; WindowState = FormWindowState.Maximized; DoubleBuffered = true; //For some reason the first call randomly doesn't take up the whole screen, so call it again NativeMethods.MoveWindow(Handle, screen.Bounds.Location.X, screen.Bounds.Location.Y, screen.Bounds.Width, screen.Bounds.Height, false); Load += CrtFilterForm_Load; } protected override void OnShown(EventArgs e) { base.OnShown(e); int wl = NativeMethods.GetWindowLong(this.Handle, NativeMethods.GWL.ExStyle); wl = wl | 0x80000 | 0x20; NativeMethods.SetWindowLong(this.Handle, NativeMethods.GWL.ExStyle, wl); } private void CrtFilterForm_Load(object sender, EventArgs e) { Task.Run(() => TopMostFunc()); } private void TopMostFunc() { try { while (true) { Invoke(new Action(SetTopMost)); System.Threading.Thread.Sleep(5); } } catch { //exception will happen when the form closes, just use this to stop the thread } } private void SetTopMost() { TopMost = true; TopLevel = true; } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); foreach (var item in m_rects) e.Graphics.DrawEllipse(m_pen, item); foreach (var item in m_lines) e.Graphics.DrawLine(m_pen, item.Item1, item.Item2); } } } ================================================ FILE: DoomLauncher/Forms/FilterForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Forms/FilterSettingsForm.Designer.cs ================================================ namespace DoomLauncher.Forms { partial class FilterSettingsForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { DoomLauncher.Controls.TitleBarControl titleBar; System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FilterSettingsForm)); this.chkStagger = new DoomLauncher.CCheckBox(); this.numBlockSize = new System.Windows.Forms.NumericUpDown(); this.numSpacingX = new System.Windows.Forms.NumericUpDown(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.btnPreview = new System.Windows.Forms.Button(); this.label3 = new System.Windows.Forms.Label(); this.numSpacingY = new System.Windows.Forms.NumericUpDown(); this.grpEllipse = new System.Windows.Forms.GroupBox(); this.grpScanline = new System.Windows.Forms.GroupBox(); this.numScanlineSize = new System.Windows.Forms.NumericUpDown(); this.label4 = new System.Windows.Forms.Label(); this.chkHorizontal = new DoomLauncher.CCheckBox(); this.chkVertical = new DoomLauncher.CCheckBox(); this.cmbMode = new System.Windows.Forms.ComboBox(); this.label5 = new System.Windows.Forms.Label(); this.btnCancel = new System.Windows.Forms.Button(); this.btnOK = new System.Windows.Forms.Button(); this.label6 = new System.Windows.Forms.Label(); this.numOpacity = new System.Windows.Forms.NumericUpDown(); this.numThickness = new System.Windows.Forms.NumericUpDown(); this.label7 = new System.Windows.Forms.Label(); this.tblInfo = new System.Windows.Forms.TableLayoutPanel(); this.label8 = new System.Windows.Forms.Label(); this.pbInfo = new System.Windows.Forms.PictureBox(); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.panel1 = new System.Windows.Forms.Panel(); titleBar = new DoomLauncher.Controls.TitleBarControl(); ((System.ComponentModel.ISupportInitialize)(this.numBlockSize)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.numSpacingX)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.numSpacingY)).BeginInit(); this.grpEllipse.SuspendLayout(); this.grpScanline.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.numScanlineSize)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.numOpacity)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.numThickness)).BeginInit(); this.tblInfo.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo)).BeginInit(); this.tblMain.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.panel1.SuspendLayout(); this.SuspendLayout(); // // titleBar // titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); titleBar.CanClose = true; titleBar.ControlBox = true; titleBar.Dock = System.Windows.Forms.DockStyle.Fill; titleBar.ForeColor = System.Drawing.Color.White; titleBar.Location = new System.Drawing.Point(0, 0); titleBar.Margin = new System.Windows.Forms.Padding(0); titleBar.Name = "titleBar"; titleBar.Size = new System.Drawing.Size(378, 32); titleBar.TabIndex = 2; titleBar.Title = "Filter Settings"; // // chkStagger // this.chkStagger.AutoSize = true; this.chkStagger.Location = new System.Drawing.Point(20, 21); this.chkStagger.Margin = new System.Windows.Forms.Padding(4); this.chkStagger.Name = "chkStagger"; this.chkStagger.Size = new System.Drawing.Size(77, 20); this.chkStagger.TabIndex = 0; this.chkStagger.Text = "Stagger"; // // numBlockSize // this.numBlockSize.Location = new System.Drawing.Point(119, 50); this.numBlockSize.Margin = new System.Windows.Forms.Padding(4); this.numBlockSize.Name = "numBlockSize"; this.numBlockSize.Size = new System.Drawing.Size(139, 22); this.numBlockSize.TabIndex = 1; this.numBlockSize.Value = new decimal(new int[] { 4, 0, 0, 0}); // // numSpacingX // this.numSpacingX.DecimalPlaces = 1; this.numSpacingX.Increment = new decimal(new int[] { 1, 0, 0, 65536}); this.numSpacingX.Location = new System.Drawing.Point(119, 82); this.numSpacingX.Margin = new System.Windows.Forms.Padding(4); this.numSpacingX.Minimum = new decimal(new int[] { 100, 0, 0, -2147483648}); this.numSpacingX.Name = "numSpacingX"; this.numSpacingX.Size = new System.Drawing.Size(139, 22); this.numSpacingX.TabIndex = 2; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(16, 52); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(70, 16); this.label1.TabIndex = 4; this.label1.Text = "Block Size"; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(20, 82); this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(68, 16); this.label2.TabIndex = 5; this.label2.Text = "X Spacing"; // // btnPreview // this.btnPreview.Location = new System.Drawing.Point(52, 170); this.btnPreview.Margin = new System.Windows.Forms.Padding(4); this.btnPreview.Name = "btnPreview"; this.btnPreview.Size = new System.Drawing.Size(216, 28); this.btnPreview.TabIndex = 7; this.btnPreview.Text = "Preview For 3 Seconds"; this.btnPreview.UseVisualStyleBackColor = true; this.btnPreview.Click += new System.EventHandler(this.btnPreview_Click); // // label3 // this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(20, 114); this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(69, 16); this.label3.TabIndex = 9; this.label3.Text = "Y Spacing"; // // numSpacingY // this.numSpacingY.DecimalPlaces = 1; this.numSpacingY.Increment = new decimal(new int[] { 1, 0, 0, 65536}); this.numSpacingY.Location = new System.Drawing.Point(119, 114); this.numSpacingY.Margin = new System.Windows.Forms.Padding(4); this.numSpacingY.Minimum = new decimal(new int[] { 100, 0, 0, -2147483648}); this.numSpacingY.Name = "numSpacingY"; this.numSpacingY.Size = new System.Drawing.Size(139, 22); this.numSpacingY.TabIndex = 8; // // grpEllipse // this.grpEllipse.Controls.Add(this.label3); this.grpEllipse.Controls.Add(this.chkStagger); this.grpEllipse.Controls.Add(this.numSpacingY); this.grpEllipse.Controls.Add(this.numBlockSize); this.grpEllipse.Controls.Add(this.numSpacingX); this.grpEllipse.Controls.Add(this.label2); this.grpEllipse.Controls.Add(this.label1); this.grpEllipse.Location = new System.Drawing.Point(5, 207); this.grpEllipse.Margin = new System.Windows.Forms.Padding(4); this.grpEllipse.Name = "grpEllipse"; this.grpEllipse.Padding = new System.Windows.Forms.Padding(4); this.grpEllipse.Size = new System.Drawing.Size(361, 150); this.grpEllipse.TabIndex = 10; this.grpEllipse.TabStop = false; this.grpEllipse.Text = "Ellipse"; // // grpScanline // this.grpScanline.Controls.Add(this.numScanlineSize); this.grpScanline.Controls.Add(this.label4); this.grpScanline.Controls.Add(this.chkHorizontal); this.grpScanline.Controls.Add(this.chkVertical); this.grpScanline.Location = new System.Drawing.Point(5, 365); this.grpScanline.Margin = new System.Windows.Forms.Padding(4); this.grpScanline.Name = "grpScanline"; this.grpScanline.Padding = new System.Windows.Forms.Padding(4); this.grpScanline.Size = new System.Drawing.Size(361, 106); this.grpScanline.TabIndex = 11; this.grpScanline.TabStop = false; this.grpScanline.Text = "Scanline"; // // numScanlineSize // this.numScanlineSize.Location = new System.Drawing.Point(119, 23); this.numScanlineSize.Margin = new System.Windows.Forms.Padding(4); this.numScanlineSize.Name = "numScanlineSize"; this.numScanlineSize.Size = new System.Drawing.Size(139, 22); this.numScanlineSize.TabIndex = 13; this.numScanlineSize.Value = new decimal(new int[] { 4, 0, 0, 0}); // // label4 // this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(20, 26); this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(33, 16); this.label4.TabIndex = 14; this.label4.Text = "Size"; // // chkHorizontal // this.chkHorizontal.AutoSize = true; this.chkHorizontal.Checked = true; this.chkHorizontal.CheckState = System.Windows.Forms.CheckState.Checked; this.chkHorizontal.Location = new System.Drawing.Point(24, 76); this.chkHorizontal.Margin = new System.Windows.Forms.Padding(4); this.chkHorizontal.Name = "chkHorizontal"; this.chkHorizontal.Size = new System.Drawing.Size(89, 20); this.chkHorizontal.TabIndex = 12; this.chkHorizontal.Text = "Horizontal"; // // chkVertical // this.chkVertical.AutoSize = true; this.chkVertical.Checked = true; this.chkVertical.CheckState = System.Windows.Forms.CheckState.Checked; this.chkVertical.Location = new System.Drawing.Point(23, 48); this.chkVertical.Margin = new System.Windows.Forms.Padding(4); this.chkVertical.Name = "chkVertical"; this.chkVertical.Size = new System.Drawing.Size(74, 20); this.chkVertical.TabIndex = 1; this.chkVertical.Text = "Vertical"; // // cmbMode // this.cmbMode.FormattingEnabled = true; this.cmbMode.Items.AddRange(new object[] { "Ellipse", "Scanline"}); this.cmbMode.Location = new System.Drawing.Point(125, 73); this.cmbMode.Margin = new System.Windows.Forms.Padding(4); this.cmbMode.Name = "cmbMode"; this.cmbMode.Size = new System.Drawing.Size(137, 24); this.cmbMode.TabIndex = 12; this.cmbMode.SelectedIndexChanged += new System.EventHandler(this.cmbMode_SelectedIndexChanged); // // label5 // this.label5.AutoSize = true; this.label5.Location = new System.Drawing.Point(14, 76); this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(42, 16); this.label5.TabIndex = 13; this.label5.Text = "Mode"; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(268, 4); this.btnCancel.Margin = new System.Windows.Forms.Padding(4); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(100, 28); this.btnCancel.TabIndex = 14; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // btnOK // this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(160, 4); this.btnOK.Margin = new System.Windows.Forms.Padding(4); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(100, 28); this.btnOK.TabIndex = 15; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; // // label6 // this.label6.AutoSize = true; this.label6.Location = new System.Drawing.Point(15, 106); this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(53, 16); this.label6.TabIndex = 16; this.label6.Text = "Opacity"; // // numOpacity // this.numOpacity.DecimalPlaces = 2; this.numOpacity.Increment = new decimal(new int[] { 5, 0, 0, 131072}); this.numOpacity.Location = new System.Drawing.Point(125, 106); this.numOpacity.Margin = new System.Windows.Forms.Padding(4); this.numOpacity.Maximum = new decimal(new int[] { 10, 0, 0, 65536}); this.numOpacity.Minimum = new decimal(new int[] { 1, 0, 0, 131072}); this.numOpacity.Name = "numOpacity"; this.numOpacity.Size = new System.Drawing.Size(139, 22); this.numOpacity.TabIndex = 17; this.numOpacity.Value = new decimal(new int[] { 3, 0, 0, 65536}); // // numThickness // this.numThickness.Location = new System.Drawing.Point(125, 138); this.numThickness.Margin = new System.Windows.Forms.Padding(4); this.numThickness.Minimum = new decimal(new int[] { 10, 0, 0, 65536}); this.numThickness.Name = "numThickness"; this.numThickness.Size = new System.Drawing.Size(139, 22); this.numThickness.TabIndex = 19; this.numThickness.Value = new decimal(new int[] { 10, 0, 0, 65536}); // // label7 // this.label7.AutoSize = true; this.label7.Location = new System.Drawing.Point(15, 138); this.label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(69, 16); this.label7.TabIndex = 18; this.label7.Text = "Thickness"; // // tblInfo // this.tblInfo.ColumnCount = 2; this.tblInfo.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 43F)); this.tblInfo.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInfo.Controls.Add(this.label8, 1, 0); this.tblInfo.Controls.Add(this.pbInfo, 0, 0); this.tblInfo.Location = new System.Drawing.Point(0, 0); this.tblInfo.Margin = new System.Windows.Forms.Padding(0); this.tblInfo.Name = "tblInfo"; this.tblInfo.RowCount = 1; this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInfo.Size = new System.Drawing.Size(365, 60); this.tblInfo.TabIndex = 20; // // label8 // this.label8.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label8.AutoSize = true; this.label8.Location = new System.Drawing.Point(47, 6); this.label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(303, 48); this.label8.TabIndex = 3; this.label8.Text = "Works best with ports that use a software renderer and do not change your resolut" + "ion like Chocolate/Crispy Doom or Odamex."; // // pbInfo // this.pbInfo.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; this.pbInfo.Dock = System.Windows.Forms.DockStyle.Fill; this.pbInfo.Location = new System.Drawing.Point(4, 4); this.pbInfo.Margin = new System.Windows.Forms.Padding(4); this.pbInfo.Name = "pbInfo"; this.pbInfo.Size = new System.Drawing.Size(35, 52); this.pbInfo.TabIndex = 0; this.pbInfo.TabStop = false; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.flowLayoutPanel1, 0, 2); this.tblMain.Controls.Add(this.panel1, 0, 1); this.tblMain.Controls.Add(titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.Size = new System.Drawing.Size(378, 562); this.tblMain.TabIndex = 21; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.btnCancel); this.flowLayoutPanel1.Controls.Add(this.btnOK); this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flowLayoutPanel1.Location = new System.Drawing.Point(3, 524); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(372, 36); this.flowLayoutPanel1.TabIndex = 0; // // panel1 // this.panel1.Controls.Add(this.tblInfo); this.panel1.Controls.Add(this.grpEllipse); this.panel1.Controls.Add(this.btnPreview); this.panel1.Controls.Add(this.grpScanline); this.panel1.Controls.Add(this.numThickness); this.panel1.Controls.Add(this.cmbMode); this.panel1.Controls.Add(this.label7); this.panel1.Controls.Add(this.label5); this.panel1.Controls.Add(this.numOpacity); this.panel1.Controls.Add(this.label6); this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel1.Location = new System.Drawing.Point(3, 34); this.panel1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(372, 486); this.panel1.TabIndex = 1; // // FilterSettingsForm // this.AcceptButton = this.btnOK; this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(378, 562); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "FilterSettingsForm"; this.Text = "Filter Settings"; ((System.ComponentModel.ISupportInitialize)(this.numBlockSize)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.numSpacingX)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.numSpacingY)).EndInit(); this.grpEllipse.ResumeLayout(false); this.grpEllipse.PerformLayout(); this.grpScanline.ResumeLayout(false); this.grpScanline.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.numScanlineSize)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.numOpacity)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.numThickness)).EndInit(); this.tblInfo.ResumeLayout(false); this.tblInfo.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo)).EndInit(); this.tblMain.ResumeLayout(false); this.flowLayoutPanel1.ResumeLayout(false); this.panel1.ResumeLayout(false); this.panel1.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.NumericUpDown numBlockSize; private System.Windows.Forms.NumericUpDown numSpacingX; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Button btnPreview; private System.Windows.Forms.Label label3; private System.Windows.Forms.NumericUpDown numSpacingY; private System.Windows.Forms.GroupBox grpEllipse; private System.Windows.Forms.GroupBox grpScanline; private System.Windows.Forms.NumericUpDown numScanlineSize; private System.Windows.Forms.Label label4; private System.Windows.Forms.ComboBox cmbMode; private System.Windows.Forms.Label label5; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.Label label6; private System.Windows.Forms.NumericUpDown numOpacity; private System.Windows.Forms.NumericUpDown numThickness; private System.Windows.Forms.Label label7; private System.Windows.Forms.TableLayoutPanel tblInfo; private System.Windows.Forms.PictureBox pbInfo; private System.Windows.Forms.Label label8; private TableLayoutPanelDB tblMain; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Panel panel1; private CCheckBox chkStagger; private CCheckBox chkVertical; private CCheckBox chkHorizontal; } } ================================================ FILE: DoomLauncher/Forms/FilterSettingsForm.cs ================================================ using System; using System.Threading.Tasks; using System.Windows.Forms; namespace DoomLauncher.Forms { public partial class FilterSettingsForm : Form { private FilterForm m_filterForm; public FilterSettingsForm(ScreenFilter settings) { InitializeComponent(); pbInfo.Image = DoomLauncher.Properties.Resources.bon2b; cmbMode.DropDownStyle = ComboBoxStyle.DropDownList; numOpacity.Value = Convert.ToDecimal(settings.Opacity); numThickness.Value = Convert.ToDecimal(settings.LineThickness); cmbMode.SelectedIndex = (int)settings.Type; numBlockSize.Value = settings.BlockSize; numSpacingX.Value = Convert.ToDecimal(settings.SpacingX); numSpacingY.Value = Convert.ToDecimal(settings.SpacingY); chkStagger.Checked = settings.Stagger; numScanlineSize.Value = Convert.ToDecimal(settings.ScanlineSpacing); chkVertical.Checked = settings.VerticalScanlines; chkHorizontal.Checked = settings.HorizontalScanlines; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public ScreenFilter GetFilterSettings() { return new ScreenFilter() { Type = (ScreenFilterType)cmbMode.SelectedIndex, Opacity = Convert.ToSingle(numOpacity.Value), LineThickness = Convert.ToInt32(numThickness.Value), BlockSize = Convert.ToInt32(numBlockSize.Value), SpacingX = Convert.ToSingle(numSpacingX.Value), SpacingY = Convert.ToSingle(numSpacingY.Value), Stagger = chkStagger.Checked, ScanlineSpacing = Convert.ToInt32(numScanlineSize.Value), VerticalScanlines = chkVertical.Checked, HorizontalScanlines = chkHorizontal.Checked }; } private void btnPreview_Click(object sender, EventArgs e) { if (m_filterForm == null) { m_filterForm = new FilterForm(Screen.FromControl(this), GetFilterSettings()); m_filterForm.Show(this); Task.Delay(3000).ContinueWith(t => CloseFilterFormTask()); } } private void CloseFilterFormTask() { Invoke(new Action(CloseFilterForm)); } private void CloseFilterForm() { m_filterForm.Close(); m_filterForm = null; } private void cmbMode_SelectedIndexChanged(object sender, EventArgs e) { grpEllipse.Enabled = cmbMode.SelectedIndex != 1; grpScanline.Enabled = cmbMode.SelectedIndex == 1; } } } ================================================ FILE: DoomLauncher/Forms/FilterSettingsForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 False AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/GameFileEditForm.Designer.cs ================================================ namespace DoomLauncher { partial class GameFileEditForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GameFileEditForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.gameFileEdit1 = new DoomLauncher.GameFileEdit(); this.tblButtons = new System.Windows.Forms.TableLayoutPanel(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.btnCancel = new System.Windows.Forms.Button(); this.btnSaveSelect = new System.Windows.Forms.Button(); this.btnCopyFrom = new System.Windows.Forms.Button(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.tblButtons.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.gameFileEdit1, 0, 1); this.tblMain.Controls.Add(this.tblButtons, 0, 3); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 4; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblMain.Size = new System.Drawing.Size(645, 960); this.tblMain.TabIndex = 1; // // gameFileEdit1 // this.gameFileEdit1.AuthorChecked = true; this.gameFileEdit1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.gameFileEdit1.CommentsChecked = true; this.gameFileEdit1.DescriptionChecked = true; this.gameFileEdit1.Dock = System.Windows.Forms.DockStyle.Fill; this.gameFileEdit1.ForeColor = System.Drawing.Color.White; this.gameFileEdit1.Location = new System.Drawing.Point(6, 48); this.gameFileEdit1.MapsChecked = true; this.gameFileEdit1.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); this.gameFileEdit1.Name = "gameFileEdit1"; this.gameFileEdit1.RatingChecked = true; this.gameFileEdit1.ReleaseDateChecked = true; this.gameFileEdit1.Size = new System.Drawing.Size(633, 806); this.gameFileEdit1.TabIndex = 0; this.gameFileEdit1.TagsChecked = true; this.gameFileEdit1.TitleChecked = true; // // tblButtons // this.tblButtons.ColumnCount = 2; this.tblButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblButtons.Controls.Add(this.flowLayoutPanel1, 1, 0); this.tblButtons.Controls.Add(this.btnCopyFrom, 0, 0); this.tblButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.tblButtons.Location = new System.Drawing.Point(0, 911); this.tblButtons.Margin = new System.Windows.Forms.Padding(0); this.tblButtons.Name = "tblButtons"; this.tblButtons.RowCount = 1; this.tblButtons.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblButtons.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblButtons.Size = new System.Drawing.Size(645, 49); this.tblButtons.TabIndex = 2; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.btnCancel); this.flowLayoutPanel1.Controls.Add(this.btnSaveSelect); this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flowLayoutPanel1.Location = new System.Drawing.Point(322, 0); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(322, 49); this.flowLayoutPanel1.TabIndex = 1; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(206, 5); this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(112, 35); this.btnCancel.TabIndex = 0; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // btnSaveSelect // this.btnSaveSelect.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnSaveSelect.Location = new System.Drawing.Point(86, 5); this.btnSaveSelect.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnSaveSelect.Name = "btnSaveSelect"; this.btnSaveSelect.Size = new System.Drawing.Size(112, 35); this.btnSaveSelect.TabIndex = 3; this.btnSaveSelect.Text = "Save"; this.btnSaveSelect.UseVisualStyleBackColor = true; // // btnCopyFrom // this.btnCopyFrom.Location = new System.Drawing.Point(4, 5); this.btnCopyFrom.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnCopyFrom.Name = "btnCopyFrom"; this.btnCopyFrom.Size = new System.Drawing.Size(166, 35); this.btnCopyFrom.TabIndex = 3; this.btnCopyFrom.Text = "Copy From File..."; this.btnCopyFrom.UseVisualStyleBackColor = true; this.btnCopyFrom.Click += new System.EventHandler(this.btnCopyFrom_Click); // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(645, 40); this.titleBar.TabIndex = 3; this.titleBar.Title = "Edit"; // // GameFileEditForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(645, 960); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "GameFileEditForm"; this.Text = "Edit"; this.tblMain.ResumeLayout(false); this.tblButtons.ResumeLayout(false); this.flowLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private GameFileEdit gameFileEdit1; private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.TableLayoutPanel tblButtons; private System.Windows.Forms.Button btnCopyFrom; private System.Windows.Forms.Button btnSaveSelect; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/GameFileEditForm.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class GameFileEditForm : Form { private IDataSourceAdapter m_adapter; private ITabView m_view; public GameFileEditForm() { InitializeComponent(); btnCopyFrom.Visible = false; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public void SetCopyFromFileAllowed(IDataSourceAdapter adapter, ITabView view) { m_adapter = adapter; m_view = view; btnCopyFrom.Visible = true; Stylizer.Stylize(this, DesignMode); } public void SetSelectDataMode() { EditControl.SetShowCheckBoxes(true); EditControl.SetShowTagCheckBox(true); btnSaveSelect.Text = "Select"; } public bool TagsChanged { get; private set; } public GameFileEdit EditControl { get { return gameFileEdit1; } } private void btnCopyFrom_Click(object sender, EventArgs e) { // Select the file you want to copy data from using (FileSelectForm fileSelect = CreateFileSelectForm()) { if (fileSelect.ShowDialog(this) == DialogResult.OK) { // Show another GameFileEdit with checkboxes to select what fields using (GameFileEditForm dataSelect = CreateDataSelectForm(fileSelect)) { if (dataSelect.ShowDialog(this) == DialogResult.OK) { // Clone GameFile otherwise we are modifying the datasource the gridview is using GameFile updateFile = ((GameFile)EditControl.DataSource).Clone() as GameFile; List fields = dataSelect.EditControl.UpdateDataSource(EditControl.DataSource); foreach (var field in fields) { var property = typeof(IGameFile).GetProperty(field.ToString("g")); var fieldData = property.GetValue(dataSelect.EditControl.DataSource); property.SetValue(updateFile, fieldData); } TagsChanged = dataSelect.EditControl.TagsChecked; if (TagsChanged) EditControl.SetDataSource(updateFile, dataSelect.EditControl.TagData); else EditControl.SetDataSource(updateFile, EditControl.TagData); } } } } } private FileSelectForm CreateFileSelectForm() { FileSelectForm fileSelect = new FileSelectForm(); fileSelect.Initialize(m_adapter, new ITabView[] { m_view }); fileSelect.StartPosition = FormStartPosition.CenterParent; fileSelect.MultiSelect = false; return fileSelect; } private GameFileEditForm CreateDataSelectForm(FileSelectForm fileSelect) { GameFileEditForm dataSelect = new GameFileEditForm { Text = "Select Fields to Copy", StartPosition = FormStartPosition.CenterParent }; IGameFile selectedFile = m_adapter.GetGameFile(fileSelect.SelectedFiles[0].FileName); IEnumerable tagMapping = m_adapter.GetTagMappings(selectedFile.GameFileID.Value); var tags = from tag in m_adapter.GetTags() join map in tagMapping on tag.TagID equals map.TagID select tag; dataSelect.EditControl.SetDataSource(selectedFile, tags); dataSelect.SetSelectDataMode(); return dataSelect; } } } ================================================ FILE: DoomLauncher/Forms/GameFileEditForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/MainForm.Designer.cs ================================================ namespace DoomLauncher { partial class MainForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); this.mnuLocal = new System.Windows.Forms.ContextMenuStrip(this.components); this.viewTextFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openZipFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.playToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); this.resyncToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.resyncIgnoreTitlepicToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.updateMetadataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); this.sortByToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.tagToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.newTagToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator6 = new System.Windows.Forms.ToolStripSeparator(); this.utilityToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.manageUtilitiesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator13 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripSeparator5 = new System.Windows.Forms.ToolStripSeparator(); this.deleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.renameToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator9 = new System.Windows.Forms.ToolStripSeparator(); this.generateTextFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator11 = new System.Windows.Forms.ToolStripSeparator(); this.cumulativeStatisticsToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator12 = new System.Windows.Forms.ToolStripSeparator(); this.createShortcutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.createAutoPlayShortcutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.tblTop = new DoomLauncher.TableLayoutPanelDB(); this.flpSearch = new DoomLauncher.FlowLayoutPanelDB(); this.toolStrip1 = new System.Windows.Forms.ToolStrip(); this.toolStripDropDownButton1 = new System.Windows.Forms.ToolStripDropDownButton(); this.addFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addDirectoryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addIWADsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addFIlesRecursivelyToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.loadWadsFromSteamMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator15 = new System.Windows.Forms.ToolStripSeparator(); this.sourcePortsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.utilitiesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addDoom64ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.createZipToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.manageTagsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); this.playNowToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.playRandomToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); this.showToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.generateTextFileToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.cumulativeStatisticsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.globalCumulativeStatisticsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator14 = new System.Windows.Forms.ToolStripSeparator(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.manualUpdateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.btnMainMenu = new DoomLauncher.FormButton(); this.ctrlSearch = new DoomLauncher.SearchControl(); this.chkIncludeAll = new DoomLauncher.CCheckBox(); this.btnPlay = new System.Windows.Forms.Button(); this.btnDownloads = new DoomLauncher.FormButton(); this.btnUpdate = new DoomLauncher.GlowButton(); this.tblDataView = new DoomLauncher.TableLayoutPanelDB(); this.splitLeftRight = new System.Windows.Forms.SplitContainer(); this.splitTagSelect = new System.Windows.Forms.SplitContainer(); this.splitTopBottom = new System.Windows.Forms.SplitContainer(); this.tabControl = new DoomLauncher.CTabControl(); this.ctrlAssociationView = new DoomLauncher.GameFileAssociationView(); this.ctrlSummary = new DoomLauncher.GameFileSummary(); this.flpTags = new System.Windows.Forms.FlowLayoutPanel(); this.btnTags = new DoomLauncher.FormButton(); this.lblSelectedTag = new System.Windows.Forms.Label(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.mnuIdGames = new System.Windows.Forms.ContextMenuStrip(this.components); this.downloadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.viewWebPageToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolTip2 = new System.Windows.Forms.ToolTip(this.components); this.mnuLocal.SuspendLayout(); this.tblMain.SuspendLayout(); this.tblTop.SuspendLayout(); this.flpSearch.SuspendLayout(); this.toolStrip1.SuspendLayout(); this.tblDataView.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitLeftRight)).BeginInit(); this.splitLeftRight.Panel1.SuspendLayout(); this.splitLeftRight.Panel2.SuspendLayout(); this.splitLeftRight.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitTagSelect)).BeginInit(); this.splitTagSelect.Panel2.SuspendLayout(); this.splitTagSelect.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitTopBottom)).BeginInit(); this.splitTopBottom.Panel1.SuspendLayout(); this.splitTopBottom.Panel2.SuspendLayout(); this.splitTopBottom.SuspendLayout(); this.flpTags.SuspendLayout(); this.mnuIdGames.SuspendLayout(); this.SuspendLayout(); // // mnuLocal // this.mnuLocal.ImageScalingSize = new System.Drawing.Size(20, 20); this.mnuLocal.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.viewTextFileToolStripMenuItem, this.openZipFileToolStripMenuItem, this.playToolStripMenuItem, this.editToolStripMenuItem, this.toolStripSeparator1, this.resyncToolStripMenuItem, this.resyncIgnoreTitlepicToolStripMenuItem, this.updateMetadataToolStripMenuItem, this.toolStripSeparator8, this.sortByToolStripMenuItem, this.tagToolStripMenuItem, this.utilityToolStripMenuItem, this.toolStripSeparator5, this.deleteToolStripMenuItem, this.renameToolStripMenuItem, this.toolStripSeparator9, this.generateTextFileToolStripMenuItem, this.toolStripSeparator11, this.cumulativeStatisticsToolStripMenuItem1, this.toolStripSeparator12, this.createShortcutToolStripMenuItem, this.createAutoPlayShortcutToolStripMenuItem}); this.mnuLocal.Name = "mnuGrid"; this.mnuLocal.Size = new System.Drawing.Size(211, 436); this.mnuLocal.Opening += new System.ComponentModel.CancelEventHandler(this.MnuLocal_Opening); // // viewTextFileToolStripMenuItem // this.viewTextFileToolStripMenuItem.Name = "viewTextFileToolStripMenuItem"; this.viewTextFileToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.viewTextFileToolStripMenuItem.Text = "View Text File..."; this.viewTextFileToolStripMenuItem.Click += new System.EventHandler(this.viewTextFileToolStripMenuItem_Click); // // openZipFileToolStripMenuItem // this.openZipFileToolStripMenuItem.Name = "openZipFileToolStripMenuItem"; this.openZipFileToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.openZipFileToolStripMenuItem.Text = "Open Archive..."; this.openZipFileToolStripMenuItem.Click += new System.EventHandler(this.openZipFileToolStripMenuItem_Click); // // playToolStripMenuItem // this.playToolStripMenuItem.Name = "playToolStripMenuItem"; this.playToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.playToolStripMenuItem.Text = "Play..."; this.playToolStripMenuItem.Click += new System.EventHandler(this.playToolStripMenuItem_Click); // // editToolStripMenuItem // this.editToolStripMenuItem.Name = "editToolStripMenuItem"; this.editToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.editToolStripMenuItem.Text = "Edit..."; this.editToolStripMenuItem.Click += new System.EventHandler(this.editToolStripMenuItem_Click); // // toolStripSeparator1 // this.toolStripSeparator1.Name = "toolStripSeparator1"; this.toolStripSeparator1.Size = new System.Drawing.Size(207, 6); // // resyncToolStripMenuItem // this.resyncToolStripMenuItem.Name = "resyncToolStripMenuItem"; this.resyncToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.resyncToolStripMenuItem.Text = "Resync"; this.resyncToolStripMenuItem.Click += new System.EventHandler(this.resyncToolStripMenuItem_Click); // // resyncIgnoreTitlepicToolStripMenuItem // this.resyncIgnoreTitlepicToolStripMenuItem.Name = "resyncIgnoreTitlepicToolStripMenuItem"; this.resyncIgnoreTitlepicToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.resyncIgnoreTitlepicToolStripMenuItem.Text = "Resync (Ignore Titlepic)"; this.resyncIgnoreTitlepicToolStripMenuItem.Click += new System.EventHandler(this.resyncIgnoreTitlepicToolStripMenuItem_Click); // // updateMetadataToolStripMenuItem // this.updateMetadataToolStripMenuItem.Name = "updateMetadataToolStripMenuItem"; this.updateMetadataToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.updateMetadataToolStripMenuItem.Text = "Update metadata..."; this.updateMetadataToolStripMenuItem.Click += new System.EventHandler(this.updateMetadataToolStripMenuItem_Click); // // toolStripSeparator8 // this.toolStripSeparator8.Name = "toolStripSeparator8"; this.toolStripSeparator8.Size = new System.Drawing.Size(207, 6); // // sortByToolStripMenuItem // this.sortByToolStripMenuItem.Name = "sortByToolStripMenuItem"; this.sortByToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.sortByToolStripMenuItem.Text = "Sort By"; // // tagToolStripMenuItem // this.tagToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.newTagToolStripMenuItem, this.toolStripSeparator6}); this.tagToolStripMenuItem.Name = "tagToolStripMenuItem"; this.tagToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.tagToolStripMenuItem.Text = "Tag"; // // newTagToolStripMenuItem // this.newTagToolStripMenuItem.Name = "newTagToolStripMenuItem"; this.newTagToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.newTagToolStripMenuItem.Text = "Manage Tags..."; this.newTagToolStripMenuItem.Click += new System.EventHandler(this.newTagToolStripMenuItem_Click); // // toolStripSeparator6 // this.toolStripSeparator6.Name = "toolStripSeparator6"; this.toolStripSeparator6.Size = new System.Drawing.Size(149, 6); // // utilityToolStripMenuItem // this.utilityToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.manageUtilitiesToolStripMenuItem, this.toolStripSeparator13}); this.utilityToolStripMenuItem.Name = "utilityToolStripMenuItem"; this.utilityToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.utilityToolStripMenuItem.Text = "Utility"; // // manageUtilitiesToolStripMenuItem // this.manageUtilitiesToolStripMenuItem.Name = "manageUtilitiesToolStripMenuItem"; this.manageUtilitiesToolStripMenuItem.Size = new System.Drawing.Size(168, 22); this.manageUtilitiesToolStripMenuItem.Text = "Manage Utilities..."; this.manageUtilitiesToolStripMenuItem.Click += new System.EventHandler(this.manageUtilitiesToolStripMenuItem_Click); // // toolStripSeparator13 // this.toolStripSeparator13.Name = "toolStripSeparator13"; this.toolStripSeparator13.Size = new System.Drawing.Size(165, 6); // // toolStripSeparator5 // this.toolStripSeparator5.Name = "toolStripSeparator5"; this.toolStripSeparator5.Size = new System.Drawing.Size(207, 6); // // deleteToolStripMenuItem // this.deleteToolStripMenuItem.Name = "deleteToolStripMenuItem"; this.deleteToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.deleteToolStripMenuItem.Text = "Delete"; this.deleteToolStripMenuItem.Click += new System.EventHandler(this.deleteToolStripMenuItem_Click); // // renameToolStripMenuItem // this.renameToolStripMenuItem.Name = "renameToolStripMenuItem"; this.renameToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.renameToolStripMenuItem.Text = "Rename..."; this.renameToolStripMenuItem.Click += new System.EventHandler(this.renameToolStripMenuItem_Click); // // toolStripSeparator9 // this.toolStripSeparator9.Name = "toolStripSeparator9"; this.toolStripSeparator9.Size = new System.Drawing.Size(207, 6); // // generateTextFileToolStripMenuItem // this.generateTextFileToolStripMenuItem.Name = "generateTextFileToolStripMenuItem"; this.generateTextFileToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.generateTextFileToolStripMenuItem.Text = "Generate Text File..."; this.generateTextFileToolStripMenuItem.Click += new System.EventHandler(this.generateTextFileToolStripMenuItem_Click); // // toolStripSeparator11 // this.toolStripSeparator11.Name = "toolStripSeparator11"; this.toolStripSeparator11.Size = new System.Drawing.Size(207, 6); // // cumulativeStatisticsToolStripMenuItem1 // this.cumulativeStatisticsToolStripMenuItem1.Name = "cumulativeStatisticsToolStripMenuItem1"; this.cumulativeStatisticsToolStripMenuItem1.Size = new System.Drawing.Size(210, 22); this.cumulativeStatisticsToolStripMenuItem1.Text = "Cumulative Statistics..."; this.cumulativeStatisticsToolStripMenuItem1.Click += new System.EventHandler(this.cumulativeStatisticsToolStripMenuItem1_Click); // // toolStripSeparator12 // this.toolStripSeparator12.Name = "toolStripSeparator12"; this.toolStripSeparator12.Size = new System.Drawing.Size(207, 6); // // createShortcutToolStripMenuItem // this.createShortcutToolStripMenuItem.Name = "createShortcutToolStripMenuItem"; this.createShortcutToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.createShortcutToolStripMenuItem.Text = "Create Shortcut"; this.createShortcutToolStripMenuItem.Click += new System.EventHandler(this.createShortcutToolStripMenuItem_Click); // // createAutoPlayShortcutToolStripMenuItem // this.createAutoPlayShortcutToolStripMenuItem.Name = "createAutoPlayShortcutToolStripMenuItem"; this.createAutoPlayShortcutToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.createAutoPlayShortcutToolStripMenuItem.Text = "Create Auto Play Shortcut"; this.createAutoPlayShortcutToolStripMenuItem.Click += new System.EventHandler(this.createAutoPlayShortcutToolStripMenuItem_Click); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.tblTop, 0, 2); this.tblMain.Controls.Add(this.tblDataView, 0, 4); this.tblMain.Controls.Add(this.flpTags, 0, 3); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 5; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 3F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(1008, 613); this.tblMain.TabIndex = 1; // // tblTop // this.tblTop.ColumnCount = 4; this.tblTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 98.03922F)); this.tblTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 1.960784F)); this.tblTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 140F)); this.tblTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 179F)); this.tblTop.Controls.Add(this.flpSearch, 0, 0); this.tblTop.Controls.Add(this.btnDownloads, 3, 0); this.tblTop.Controls.Add(this.btnUpdate, 2, 0); this.tblTop.Dock = System.Windows.Forms.DockStyle.Fill; this.tblTop.Location = new System.Drawing.Point(0, 29); this.tblTop.Margin = new System.Windows.Forms.Padding(0); this.tblTop.Name = "tblTop"; this.tblTop.RowCount = 1; this.tblTop.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblTop.Size = new System.Drawing.Size(1008, 32); this.tblTop.TabIndex = 1; // // flpSearch // this.flpSearch.AutoSize = true; this.flpSearch.CaptureArrowKeys = false; this.flpSearch.Controls.Add(this.toolStrip1); this.flpSearch.Controls.Add(this.btnMainMenu); this.flpSearch.Controls.Add(this.ctrlSearch); this.flpSearch.Controls.Add(this.chkIncludeAll); this.flpSearch.Controls.Add(this.btnPlay); this.flpSearch.Dock = System.Windows.Forms.DockStyle.Fill; this.flpSearch.Location = new System.Drawing.Point(0, 0); this.flpSearch.Margin = new System.Windows.Forms.Padding(0); this.flpSearch.Name = "flpSearch"; this.flpSearch.Size = new System.Drawing.Size(675, 32); this.flpSearch.TabIndex = 0; // // toolStrip1 // this.toolStrip1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.toolStrip1.Dock = System.Windows.Forms.DockStyle.None; this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; this.toolStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.toolStripDropDownButton1}); this.toolStrip1.Location = new System.Drawing.Point(4, 1); this.toolStrip1.Margin = new System.Windows.Forms.Padding(4, 0, 0, 0); this.toolStrip1.Name = "toolStrip1"; this.toolStrip1.Padding = new System.Windows.Forms.Padding(0, 0, 2, 0); this.toolStrip1.Size = new System.Drawing.Size(39, 27); this.toolStrip1.TabIndex = 5; this.toolStrip1.Text = "Options"; // // toolStripDropDownButton1 // this.toolStripDropDownButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; this.toolStripDropDownButton1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.addFilesToolStripMenuItem, this.addDirectoryToolStripMenuItem, this.addIWADsToolStripMenuItem, this.addFIlesRecursivelyToolStripMenuItem1, this.loadWadsFromSteamMenuItem, this.toolStripSeparator15, this.sourcePortsToolStripMenuItem, this.utilitiesToolStripMenuItem, this.addDoom64ToolStripMenuItem, this.createZipToolStripMenuItem, this.toolStripSeparator2, this.settingsToolStripMenuItem, this.manageTagsToolStripMenuItem, this.toolStripSeparator3, this.playNowToolStripMenuItem, this.playRandomToolStripMenuItem1, this.toolStripSeparator4, this.showToolStripMenuItem, this.generateTextFileToolStripMenuItem1, this.cumulativeStatisticsToolStripMenuItem, this.globalCumulativeStatisticsToolStripMenuItem, this.toolStripSeparator14, this.aboutToolStripMenuItem, this.helpToolStripMenuItem, this.manualUpdateToolStripMenuItem}); this.toolStripDropDownButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripDropDownButton1.Image"))); this.toolStripDropDownButton1.ImageTransparentColor = System.Drawing.Color.Magenta; this.toolStripDropDownButton1.Margin = new System.Windows.Forms.Padding(2, 1, 0, 2); this.toolStripDropDownButton1.Name = "toolStripDropDownButton1"; this.toolStripDropDownButton1.Size = new System.Drawing.Size(33, 24); this.toolStripDropDownButton1.Text = "Options"; this.toolStripDropDownButton1.DropDownClosed += new System.EventHandler(this.ToolStripDropDownButton1_DropDownClosed); this.toolStripDropDownButton1.DropDownOpened += new System.EventHandler(this.ToolStripDropDownButton1_DropDownOpened); // // addFilesToolStripMenuItem // this.addFilesToolStripMenuItem.Name = "addFilesToolStripMenuItem"; this.addFilesToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.addFilesToolStripMenuItem.Text = "Add Files..."; this.addFilesToolStripMenuItem.Click += new System.EventHandler(this.addFilesToolStripMenuItem_Click); // // addDirectoryToolStripMenuItem // this.addDirectoryToolStripMenuItem.Name = "addDirectoryToolStripMenuItem"; this.addDirectoryToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.addDirectoryToolStripMenuItem.Text = "Add Directory..."; this.addDirectoryToolStripMenuItem.Click += new System.EventHandler(this.addDirectoryToolStripMenuItem_Click); // // addIWADsToolStripMenuItem // this.addIWADsToolStripMenuItem.Name = "addIWADsToolStripMenuItem"; this.addIWADsToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.addIWADsToolStripMenuItem.Text = "Add IWADs..."; this.addIWADsToolStripMenuItem.Click += new System.EventHandler(this.addIWADsToolStripMenuItem_Click); // // addFIlesRecursivelyToolStripMenuItem1 // this.addFIlesRecursivelyToolStripMenuItem1.Name = "addFIlesRecursivelyToolStripMenuItem1"; this.addFIlesRecursivelyToolStripMenuItem1.Size = new System.Drawing.Size(230, 22); this.addFIlesRecursivelyToolStripMenuItem1.Text = "Add Files Recursively..."; this.addFIlesRecursivelyToolStripMenuItem1.Click += new System.EventHandler(this.addFIlesRecursivelyToolStripMenuItem1_Click); // // autoLoadFromSteamMenuItem // this.loadWadsFromSteamMenuItem.Name = "loadWadsFromSteamMenuItem"; this.loadWadsFromSteamMenuItem.Size = new System.Drawing.Size(230, 22); this.loadWadsFromSteamMenuItem.Text = "Load WADs from Steam or GOG"; this.loadWadsFromSteamMenuItem.Click += new System.EventHandler(this.loadWadsFromSteamMenuItem_Click); // // toolStripSeparator15 // this.toolStripSeparator15.Name = "toolStripSeparator15"; this.toolStripSeparator15.Size = new System.Drawing.Size(227, 6); // // sourcePortsToolStripMenuItem // this.sourcePortsToolStripMenuItem.Name = "sourcePortsToolStripMenuItem"; this.sourcePortsToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.sourcePortsToolStripMenuItem.Text = "Source Ports..."; this.sourcePortsToolStripMenuItem.Click += new System.EventHandler(this.sourcePortsToolStripMenuItem_Click); // // utilitiesToolStripMenuItem // this.utilitiesToolStripMenuItem.Name = "utilitiesToolStripMenuItem"; this.utilitiesToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.utilitiesToolStripMenuItem.Text = "Utilities..."; this.utilitiesToolStripMenuItem.Click += new System.EventHandler(this.utilitiesToolStripMenuItem_Click); // // addDoom64ToolStripMenuItem // this.addDoom64ToolStripMenuItem.Name = "addDoom64ToolStripMenuItem"; this.addDoom64ToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.addDoom64ToolStripMenuItem.Text = "Doom64..."; this.addDoom64ToolStripMenuItem.Click += new System.EventHandler(this.addDoom64ToolStripMenuItem_Click); // // createZipToolStripMenuItem // this.createZipToolStripMenuItem.Name = "createZipToolStripMenuItem"; this.createZipToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.createZipToolStripMenuItem.Text = "Create Zip From Directory..."; this.createZipToolStripMenuItem.Click += new System.EventHandler(this.createZipToolStripMenuItem_Click); // // toolStripSeparator2 // this.toolStripSeparator2.Name = "toolStripSeparator2"; this.toolStripSeparator2.Size = new System.Drawing.Size(227, 6); // // settingsToolStripMenuItem // this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; this.settingsToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.settingsToolStripMenuItem.Text = "Settings..."; this.settingsToolStripMenuItem.Click += new System.EventHandler(this.settingsToolStripMenuItem_Click); // // manageTagsToolStripMenuItem // this.manageTagsToolStripMenuItem.Name = "manageTagsToolStripMenuItem"; this.manageTagsToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.manageTagsToolStripMenuItem.Text = "Manage Tags..."; this.manageTagsToolStripMenuItem.Click += new System.EventHandler(this.manageTagsToolStripMenuItem_Click); // // toolStripSeparator3 // this.toolStripSeparator3.Name = "toolStripSeparator3"; this.toolStripSeparator3.Size = new System.Drawing.Size(227, 6); // // playNowToolStripMenuItem // this.playNowToolStripMenuItem.Name = "playNowToolStripMenuItem"; this.playNowToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.playNowToolStripMenuItem.Text = "Play"; this.playNowToolStripMenuItem.Click += new System.EventHandler(this.playNowToolStripMenuItem_Click); // // playRandomToolStripMenuItem1 // this.playRandomToolStripMenuItem1.Name = "playRandomToolStripMenuItem1"; this.playRandomToolStripMenuItem1.Size = new System.Drawing.Size(230, 22); this.playRandomToolStripMenuItem1.Text = "Play Random"; this.playRandomToolStripMenuItem1.Click += new System.EventHandler(this.playRandomToolStripMenuItem1_Click); // // toolStripSeparator4 // this.toolStripSeparator4.Name = "toolStripSeparator4"; this.toolStripSeparator4.Size = new System.Drawing.Size(227, 6); // // showToolStripMenuItem // this.showToolStripMenuItem.Name = "showToolStripMenuItem"; this.showToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.showToolStripMenuItem.Text = "Sync Status..."; this.showToolStripMenuItem.Click += new System.EventHandler(this.showToolStripMenuItem_Click); // // generateTextFileToolStripMenuItem1 // this.generateTextFileToolStripMenuItem1.Name = "generateTextFileToolStripMenuItem1"; this.generateTextFileToolStripMenuItem1.Size = new System.Drawing.Size(230, 22); this.generateTextFileToolStripMenuItem1.Text = "Generate Text File..."; this.generateTextFileToolStripMenuItem1.Click += new System.EventHandler(this.generateTextFileToolStripMenuItem1_Click); // // cumulativeStatisticsToolStripMenuItem // this.cumulativeStatisticsToolStripMenuItem.Name = "cumulativeStatisticsToolStripMenuItem"; this.cumulativeStatisticsToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.cumulativeStatisticsToolStripMenuItem.Text = "Cumulative Statistics..."; this.cumulativeStatisticsToolStripMenuItem.Click += new System.EventHandler(this.cumulativeStatisticsToolStripMenuItem_Click); // // globalCumulativeStatisticsToolStripMenuItem // this.globalCumulativeStatisticsToolStripMenuItem.Name = "globalCumulativeStatisticsToolStripMenuItem"; this.globalCumulativeStatisticsToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.globalCumulativeStatisticsToolStripMenuItem.Text = "Global Cumulative Statistics..."; this.globalCumulativeStatisticsToolStripMenuItem.Click += new System.EventHandler(this.globalCumulativeStatisticsToolStripMenuItem_Click); // // toolStripSeparator14 // this.toolStripSeparator14.Name = "toolStripSeparator14"; this.toolStripSeparator14.Size = new System.Drawing.Size(227, 6); // // aboutToolStripMenuItem // this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; this.aboutToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.aboutToolStripMenuItem.Text = "About..."; this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click); // // helpToolStripMenuItem // this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; this.helpToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.helpToolStripMenuItem.Text = "Help..."; this.helpToolStripMenuItem.Click += new System.EventHandler(this.helpToolStripMenuItem_Click); // // manualUpdateToolStripMenuItem // this.manualUpdateToolStripMenuItem.Name = "manualUpdateToolStripMenuItem"; this.manualUpdateToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.manualUpdateToolStripMenuItem.Text = "Manual Update..."; this.manualUpdateToolStripMenuItem.Click += new System.EventHandler(this.manualUpdateToolStripMenuItem_Click); // // btnMainMenu // this.btnMainMenu.Image = global::DoomLauncher.Properties.Resources.Bars; this.btnMainMenu.ImageAlign = System.Drawing.ContentAlignment.TopCenter; this.btnMainMenu.Location = new System.Drawing.Point(45, 2); this.btnMainMenu.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.btnMainMenu.Name = "btnMainMenu"; this.btnMainMenu.Size = new System.Drawing.Size(27, 21); this.btnMainMenu.TabIndex = 9; this.btnMainMenu.UseVisualStyleBackColor = true; this.btnMainMenu.Click += new System.EventHandler(this.btnMainMenu_Click); // // ctrlSearch // this.ctrlSearch.Anchor = System.Windows.Forms.AnchorStyles.Left; this.ctrlSearch.Location = new System.Drawing.Point(77, 4); this.ctrlSearch.Margin = new System.Windows.Forms.Padding(3, 2, 3, 3); this.ctrlSearch.Name = "ctrlSearch"; this.ctrlSearch.SearchText = ""; this.ctrlSearch.Size = new System.Drawing.Size(195, 20); this.ctrlSearch.TabIndex = 0; // // chkIncludeAll // this.chkIncludeAll.Anchor = System.Windows.Forms.AnchorStyles.Left; this.chkIncludeAll.AutoSize = true; this.chkIncludeAll.Location = new System.Drawing.Point(277, 6); this.chkIncludeAll.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3); this.chkIncludeAll.Name = "chkIncludeAll"; this.chkIncludeAll.Size = new System.Drawing.Size(75, 17); this.chkIncludeAll.TabIndex = 8; this.chkIncludeAll.Text = "Include All"; this.toolTip1.SetToolTip(this.chkIncludeAll, "Shows results from exclude tags"); this.chkIncludeAll.CheckedChanged += new System.EventHandler(this.chkIncludeAll_CheckedChanged); // // btnPlay // this.btnPlay.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.btnPlay.Enabled = false; this.btnPlay.FlatAppearance.BorderColor = System.Drawing.Color.Silver; this.btnPlay.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnPlay.Image = global::DoomLauncher.Properties.Resources.Play; this.btnPlay.ImageAlign = System.Drawing.ContentAlignment.MiddleRight; this.btnPlay.Location = new System.Drawing.Point(360, 2); this.btnPlay.Margin = new System.Windows.Forms.Padding(6, 2, 3, 3); this.btnPlay.Name = "btnPlay"; this.btnPlay.Size = new System.Drawing.Size(75, 24); this.btnPlay.TabIndex = 6; this.btnPlay.Text = "Play"; this.btnPlay.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText; this.btnPlay.UseVisualStyleBackColor = true; this.btnPlay.Click += new System.EventHandler(this.btnPlay_Click); // // btnDownloads // this.btnDownloads.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnDownloads.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.btnDownloads.FlatAppearance.BorderSize = 0; this.btnDownloads.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnDownloads.Image = global::DoomLauncher.Properties.Resources.th; this.btnDownloads.Location = new System.Drawing.Point(910, 2); this.btnDownloads.Margin = new System.Windows.Forms.Padding(0, 2, 2, 0); this.btnDownloads.Name = "btnDownloads"; this.btnDownloads.Size = new System.Drawing.Size(96, 22); this.btnDownloads.TabIndex = 1; this.btnDownloads.Text = "Downloads"; this.btnDownloads.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; this.btnDownloads.UseVisualStyleBackColor = true; this.btnDownloads.Click += new System.EventHandler(this.btnDownloads_Click); // // btnUpdate // this.btnUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnUpdate.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.btnUpdate.FlatAppearance.BorderSize = 0; this.btnUpdate.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnUpdate.Image = global::DoomLauncher.Properties.Resources.bon2b; this.btnUpdate.Location = new System.Drawing.Point(706, 2); this.btnUpdate.Margin = new System.Windows.Forms.Padding(0, 2, 2, 0); this.btnUpdate.Name = "btnUpdate"; this.btnUpdate.Size = new System.Drawing.Size(120, 22); this.btnUpdate.TabIndex = 2; this.btnUpdate.Text = "Update Available"; this.btnUpdate.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; this.btnUpdate.UseVisualStyleBackColor = true; this.btnUpdate.Visible = false; this.btnUpdate.Click += new System.EventHandler(this.btnUpdate_Click); // // tblDataView // this.tblDataView.ColumnCount = 1; this.tblDataView.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblDataView.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblDataView.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblDataView.Controls.Add(this.splitLeftRight, 0, 0); this.tblDataView.Dock = System.Windows.Forms.DockStyle.Fill; this.tblDataView.Location = new System.Drawing.Point(3, 87); this.tblDataView.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); this.tblDataView.Name = "tblDataView"; this.tblDataView.RowCount = 1; this.tblDataView.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblDataView.Size = new System.Drawing.Size(1002, 526); this.tblDataView.TabIndex = 4; // // splitLeftRight // this.splitLeftRight.Dock = System.Windows.Forms.DockStyle.Fill; this.splitLeftRight.Location = new System.Drawing.Point(3, 6); this.splitLeftRight.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.splitLeftRight.Name = "splitLeftRight"; // // splitLeftRight.Panel1 // this.splitLeftRight.Panel1.Controls.Add(this.splitTagSelect); // // splitLeftRight.Panel2 // this.splitLeftRight.Panel2.Controls.Add(this.ctrlSummary); this.splitLeftRight.Size = new System.Drawing.Size(996, 517); this.splitLeftRight.SplitterDistance = 677; this.splitLeftRight.TabIndex = 9; // // splitTagSelect // this.splitTagSelect.Dock = System.Windows.Forms.DockStyle.Fill; this.splitTagSelect.Location = new System.Drawing.Point(0, 0); this.splitTagSelect.Name = "splitTagSelect"; this.splitTagSelect.Panel1Collapsed = true; this.splitTagSelect.Panel1MinSize = 100; // // splitTagSelect.Panel2 // this.splitTagSelect.Panel2.Controls.Add(this.splitTopBottom); this.splitTagSelect.Panel2MinSize = 0; this.splitTagSelect.Size = new System.Drawing.Size(677, 517); this.splitTagSelect.SplitterDistance = 100; this.splitTagSelect.TabIndex = 9; // // splitTopBottom // this.splitTopBottom.Dock = System.Windows.Forms.DockStyle.Fill; this.splitTopBottom.Location = new System.Drawing.Point(0, 0); this.splitTopBottom.Margin = new System.Windows.Forms.Padding(0); this.splitTopBottom.Name = "splitTopBottom"; this.splitTopBottom.Orientation = System.Windows.Forms.Orientation.Horizontal; // // splitTopBottom.Panel1 // this.splitTopBottom.Panel1.Controls.Add(this.tabControl); // // splitTopBottom.Panel2 // this.splitTopBottom.Panel2.Controls.Add(this.ctrlAssociationView); this.splitTopBottom.Size = new System.Drawing.Size(677, 517); this.splitTopBottom.SplitterDistance = 354; this.splitTopBottom.TabIndex = 8; // // tabControl // this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControl.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed; this.tabControl.HotTrack = true; this.tabControl.ItemSize = new System.Drawing.Size(0, 18); this.tabControl.Location = new System.Drawing.Point(0, 0); this.tabControl.Multiline = true; this.tabControl.Name = "tabControl"; this.tabControl.SelectedIndex = 0; this.tabControl.Size = new System.Drawing.Size(677, 354); this.tabControl.TabIndex = 6; this.tabControl.SelectedIndexChanged += new System.EventHandler(this.tabControl_SelectedIndexChanged); // // ctrlAssociationView // this.ctrlAssociationView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.ctrlAssociationView.DataSourceAdapter = null; this.ctrlAssociationView.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlAssociationView.ForeColor = System.Drawing.Color.White; this.ctrlAssociationView.Location = new System.Drawing.Point(0, 0); this.ctrlAssociationView.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.ctrlAssociationView.Name = "ctrlAssociationView"; this.ctrlAssociationView.SaveGameDirectory = null; this.ctrlAssociationView.ScreenshotDirectory = null; this.ctrlAssociationView.Size = new System.Drawing.Size(677, 159); this.ctrlAssociationView.TabIndex = 7; // // ctrlSummary // this.ctrlSummary.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlSummary.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.ctrlSummary.Location = new System.Drawing.Point(0, 0); this.ctrlSummary.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3); this.ctrlSummary.Name = "ctrlSummary"; this.ctrlSummary.Size = new System.Drawing.Size(315, 517); this.ctrlSummary.TabIndex = 4; this.ctrlSummary.TagText = "Tags:"; // // flpTags // this.flpTags.Controls.Add(this.btnTags); this.flpTags.Controls.Add(this.lblSelectedTag); this.flpTags.Dock = System.Windows.Forms.DockStyle.Fill; this.flpTags.Location = new System.Drawing.Point(0, 61); this.flpTags.Margin = new System.Windows.Forms.Padding(0); this.flpTags.Name = "flpTags"; this.flpTags.Size = new System.Drawing.Size(1008, 26); this.flpTags.TabIndex = 6; // // btnTags // this.btnTags.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnTags.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.btnTags.FlatAppearance.BorderSize = 0; this.btnTags.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnTags.Image = global::DoomLauncher.Properties.Resources.Tags; this.btnTags.Location = new System.Drawing.Point(0, 0); this.btnTags.Margin = new System.Windows.Forms.Padding(0, 0, 2, 0); this.btnTags.Name = "btnTags"; this.btnTags.Size = new System.Drawing.Size(66, 22); this.btnTags.TabIndex = 5; this.btnTags.Text = "Tags"; this.btnTags.TextImageRelation = System.Windows.Forms.TextImageRelation.TextBeforeImage; this.btnTags.UseVisualStyleBackColor = true; this.btnTags.Click += new System.EventHandler(this.btnTags_Click); // // lblSelectedTag // this.lblSelectedTag.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblSelectedTag.AutoSize = true; this.lblSelectedTag.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lblSelectedTag.Location = new System.Drawing.Point(68, 4); this.lblSelectedTag.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0); this.lblSelectedTag.Name = "lblSelectedTag"; this.lblSelectedTag.Size = new System.Drawing.Size(79, 15); this.lblSelectedTag.TabIndex = 6; this.lblSelectedTag.Text = "Selected Tag"; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(1008, 26); this.titleBar.TabIndex = 7; this.titleBar.Title = "Doom Launcher"; // // mnuIdGames // this.mnuIdGames.ImageScalingSize = new System.Drawing.Size(20, 20); this.mnuIdGames.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.downloadToolStripMenuItem, this.viewWebPageToolStripMenuItem}); this.mnuIdGames.Name = "mnuIdGames"; this.mnuIdGames.Size = new System.Drawing.Size(165, 48); // // downloadToolStripMenuItem // this.downloadToolStripMenuItem.Name = "downloadToolStripMenuItem"; this.downloadToolStripMenuItem.Size = new System.Drawing.Size(164, 22); this.downloadToolStripMenuItem.Text = "Download..."; this.downloadToolStripMenuItem.Click += new System.EventHandler(this.downloadToolStripMenuItem_Click); // // viewWebPageToolStripMenuItem // this.viewWebPageToolStripMenuItem.Name = "viewWebPageToolStripMenuItem"; this.viewWebPageToolStripMenuItem.Size = new System.Drawing.Size(164, 22); this.viewWebPageToolStripMenuItem.Text = "View Web Page..."; this.viewWebPageToolStripMenuItem.Click += new System.EventHandler(this.viewWebPageToolStripMenuItem_Click); // // MainForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1008, 613); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MinimumSize = new System.Drawing.Size(798, 590); this.Name = "MainForm"; this.Text = "Doom Launcher"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); this.Shown += new System.EventHandler(this.MainForm_Shown); this.mnuLocal.ResumeLayout(false); this.tblMain.ResumeLayout(false); this.tblTop.ResumeLayout(false); this.tblTop.PerformLayout(); this.flpSearch.ResumeLayout(false); this.flpSearch.PerformLayout(); this.toolStrip1.ResumeLayout(false); this.toolStrip1.PerformLayout(); this.tblDataView.ResumeLayout(false); this.splitLeftRight.Panel1.ResumeLayout(false); this.splitLeftRight.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitLeftRight)).EndInit(); this.splitLeftRight.ResumeLayout(false); this.splitTagSelect.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitTagSelect)).EndInit(); this.splitTagSelect.ResumeLayout(false); this.splitTopBottom.Panel1.ResumeLayout(false); this.splitTopBottom.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitTopBottom)).EndInit(); this.splitTopBottom.ResumeLayout(false); this.flpTags.ResumeLayout(false); this.flpTags.PerformLayout(); this.mnuIdGames.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ContextMenuStrip mnuLocal; private System.Windows.Forms.ToolStripMenuItem viewTextFileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem openZipFileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem playToolStripMenuItem; private System.Windows.Forms.ToolTip toolTip1; private System.Windows.Forms.ContextMenuStrip mnuIdGames; private System.Windows.Forms.ToolStripMenuItem downloadToolStripMenuItem; private GameFileSummary ctrlSummary; private SearchControl ctrlSearch; private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem; private GameFileAssociationView ctrlAssociationView; private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; private System.Windows.Forms.ToolStripMenuItem deleteToolStripMenuItem; private DoomLauncher.FormButton btnDownloads; private System.Windows.Forms.ToolStripMenuItem viewWebPageToolStripMenuItem; private System.Windows.Forms.ToolStrip toolStrip1; private System.Windows.Forms.ToolStripDropDownButton toolStripDropDownButton1; private System.Windows.Forms.ToolStripMenuItem sourcePortsToolStripMenuItem; private System.Windows.Forms.ToolTip toolTip2; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem addFilesToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem addIWADsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem addDoom64ToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; private System.Windows.Forms.Button btnPlay; private System.Windows.Forms.ToolStripSeparator toolStripSeparator4; private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem; private System.Windows.Forms.SplitContainer splitTopBottom; private System.Windows.Forms.SplitContainer splitLeftRight; private DoomLauncher.CTabControl tabControl; private System.Windows.Forms.ToolStripMenuItem tagToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem newTagToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator5; private System.Windows.Forms.ToolStripSeparator toolStripSeparator6; private System.Windows.Forms.ToolStripMenuItem manageTagsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem updateMetadataToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator8; private System.Windows.Forms.ToolStripMenuItem showToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator9; private System.Windows.Forms.ToolStripMenuItem generateTextFileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem generateTextFileToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem renameToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem cumulativeStatisticsToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator11; private System.Windows.Forms.ToolStripMenuItem cumulativeStatisticsToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem utilityToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem utilitiesToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem createShortcutToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem createAutoPlayShortcutToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem manageUtilitiesToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator12; private System.Windows.Forms.ToolStripSeparator toolStripSeparator13; private System.Windows.Forms.ToolStripMenuItem playNowToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem playRandomToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem createZipToolStripMenuItem; private DoomLauncher.GlowButton btnUpdate; private System.Windows.Forms.ToolStripMenuItem addFIlesRecursivelyToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem loadWadsFromSteamMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator14; private System.Windows.Forms.ToolStripMenuItem sortByToolStripMenuItem; private System.Windows.Forms.FlowLayoutPanel flpTags; private DoomLauncher.FormButton btnTags; private System.Windows.Forms.Label lblSelectedTag; private System.Windows.Forms.SplitContainer splitTagSelect; private System.Windows.Forms.ToolStripMenuItem addDirectoryToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator15; private System.Windows.Forms.ToolStripMenuItem resyncToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem resyncIgnoreTitlepicToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem manualUpdateToolStripMenuItem; private Controls.TitleBarControl titleBar; private FormButton btnMainMenu; private CCheckBox chkIncludeAll; private TableLayoutPanelDB tblMain; private TableLayoutPanelDB tblTop; private FlowLayoutPanelDB flpSearch; private TableLayoutPanelDB tblDataView; private System.Windows.Forms.ToolStripMenuItem globalCumulativeStatisticsToolStripMenuItem; } } ================================================ FILE: DoomLauncher/Forms/MainForm.cs ================================================ using DoomLauncher.Controls; using DoomLauncher.DataSources; using DoomLauncher.Forms; using DoomLauncher.Handlers; using DoomLauncher.Interfaces; using DoomLauncher.Stylize; using PresentationControls; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using static System.Windows.Forms.VisualStyles.VisualStyleElement.Window; namespace DoomLauncher { public partial class MainForm : Form { public bool ShouldShowToolTip { get; private set; } = true; private readonly string m_workingDirectory; private bool m_idGamesLoaded; private IGameFile m_lastSelectedItem; private PlayForm m_currentPlayForm; private DownloadView m_downloadView; private DownloadHandler m_downloadHandler; private List m_screenshotDetectors; private List m_saveFileDetectors; private IFileData[] m_saveGames; private TabHandler m_tabHandler; private VersionHandler m_versionHandler; private readonly SplashScreen m_splash; private readonly UpdateControl m_updateControl = new UpdateControl(); private readonly TagSelectControl m_tagSelectControl = new TagSelectControl(); private readonly Dictionary m_searchByTab = new Dictionary(); private Popup m_tagPopup; private string m_launchFile; private readonly LaunchArgs m_launchArgs; private readonly Dictionary m_savedTabSearches = new Dictionary(); private readonly Dictionary m_progressBars = new Dictionary(); private FormWindowState m_windowState; private bool m_progressBarCancelled; private bool m_writeConfigOnClose = true; private IGameFile m_lastPlayRandomFile; private string m_lastPlayRandomMap; private ITabView m_lastSelectedTabView; public MainForm(LaunchArgs launchArgs, SplashScreen splashScreen) { m_launchFile = launchArgs.LaunchFileName; m_launchArgs = launchArgs; m_splash = splashScreen; InitializeComponent(); Stylizer.SetupTitleBar(this); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); InitProgressBars(); InitIcons(); ClearSummary(); m_workingDirectory = LauncherPath.GetDataDirectory(); } private void InitProgressBars() { var copyProgressBar = CreateProgressBar("Copying...", ProgressBarStyle.Continuous, true); copyProgressBar.Cancelled += m_progressBarFormCopy_Cancelled; m_progressBars[ProgressBarType.Copy] = copyProgressBar; m_progressBars[ProgressBarType.Sync] = CreateProgressBar("Syncing...", ProgressBarStyle.Continuous, false); m_progressBars[ProgressBarType.Update] = CreateProgressBar("Updating...", ProgressBarStyle.Marquee, false); m_progressBars[ProgressBarType.Delete] = CreateProgressBar("Deleting...", ProgressBarStyle.Marquee, false); m_progressBars[ProgressBarType.Search] = CreateProgressBar("Searching...", ProgressBarStyle.Marquee, false); m_progressBars[ProgressBarType.CreateZip] = CreateProgressBar("Creating zip...", ProgressBarStyle.Marquee, false); } protected override void OnClientSizeChanged(EventArgs e) { var windowState = titleBar.WindowState; if (windowState != m_windowState) { if (m_windowState != FormWindowState.Minimized && windowState == FormWindowState.Minimized) ctrlSummary.PauseSlideshow(); else if (m_windowState == FormWindowState.Minimized && windowState != FormWindowState.Minimized) ctrlSummary.ResumeSlideshow(); m_windowState = windowState; } base.OnClientSizeChanged(e); } private void InitIcons() { Icons.DpiScale = new DpiScale(CreateGraphics()); btnPlay.Image = Icons.Play; toolStripDropDownButton1.Image = Icons.Bars; btnDownloads.Image = Icons.Download; btnTags.Image = Icons.Tags; var padding = btnMainMenu.Padding; padding.Top = ctrlSearch.Location.Y - Icons.DpiScale.ScaleIntY(2); btnMainMenu.Margin = padding; } public async Task Init() { await Initialize(); InitWindow(); await CheckFirstInit(); UpdateLocal(); SetupSearchFilters(); HandleTabSelectionChange(); InvokeHideSplashScreen(); _ = Task.Run(() => CheckForAppUpdate()); } private void InvokeHideSplashScreen() { if (InvokeRequired) Invoke(new Action(HideSplashScreen)); else HideSplashScreen(); } private void HideSplashScreen() { m_splash?.Close(); } private void InitWindow() { Stylizer.StylizeControl(mnuLocal, DesignMode); Stylizer.StylizeControl(mnuIdGames, DesignMode); Stylizer.StylizeControl(toolStripDropDownButton1, DesignMode); toolStrip1.Visible = false; btnMainMenu.Image = Icons.Bars; if (m_launchArgs.LaunchGameFileID != null && m_launchArgs.AutoClose) { WindowState = FormWindowState.Minimized; return; } //Only set location and window state if the location is valid, either way we always set Width, Height, and splitter values if (ValidatePosition(AppConfiguration)) { WindowState = AppConfiguration.WindowState == FormWindowState.Minimized ? FormWindowState.Normal : AppConfiguration.WindowState; StartPosition = FormStartPosition.Manual; Location = new Point(AppConfiguration.AppX, AppConfiguration.AppY); if (AppConfiguration.AppWidth > 0 && AppConfiguration.AppHeight > 0) Size = new Size(AppConfiguration.AppWidth, AppConfiguration.AppHeight); titleBar.HandleWindowStateChange(AppConfiguration.WindowState); } SetSplitters(); m_windowState = titleBar.WindowState; } private void SetSplitters() { splitTopBottom.SplitterDistance = GetSplitterDistancePixels(AppConfiguration.SplitTopBottom, Height, 0.64); splitLeftRight.SplitterDistance = GetSplitterDistancePixels(AppConfiguration.SplitLeftRight, Width, 0.78); splitTagSelect.SplitterDistance = GetSplitterDistancePixels(AppConfiguration.SplitTagSelect, Width, 0.1); } private static int GetSplitterDistancePixels(double configValue, int windowDimension, double defaultPercentage) { configValue = Math.Abs(configValue); // Previous configurations were set in pixels if (configValue > 1) { int value = (int)configValue; if (configValue >= windowDimension) return (int)(defaultPercentage * windowDimension); return (int)configValue; } //New configuration is percentage 0 - 1 return Math.Max((int)(configValue * windowDimension), 32); } public IGameFileView GetCurrentViewControl() { ITabView view = GetCurrentTabView(); return view?.GameFileViewControl; } private void ctrlAssociationView_FileAdded(object sender, EventArgs e) { UpdateCurrentView(); } void ctrlAssociationView_FileDeleted(object sender, EventArgs e) { UpdateCurrentView(); } void ctrlAssociationView_FileOrderChanged(object sender, EventArgs e) { UpdateCurrentView(); } void CtrlAssociationView_FileDetailsChanged(object sender, EventArgs e) { UpdateCurrentView(); } private void UpdateCurrentView() { IGameFileView view = GetCurrentViewControl(); view.UpdateGameFile(view.SelectedItem); HandleSelectionChange(view, true); } private void chkIncludeAll_CheckedChanged(object sender, EventArgs e) { HandleSearch(); } void DownloadView_UserPlay(object sender, EventArgs e) { if (m_downloadView.SelectedItem != null) { IGameFile gameFile = DataSourceAdapter.GetGameFile(m_downloadView.SelectedItem.Key.ToString()); if (gameFile != null) HandlePlay(new IGameFile[] { gameFile }); } } void ctrlView_SelectionChange(object sender, EventArgs e) { HandleSelectionChange(sender, false); } void ctrlView_RowDoubleClicked(object sender, EventArgs e) { HandleRowDoubleClicked(sender as IGameFileView); } void ctrlView_GridKeyPress(object sender, KeyPressEventArgs e) { HandleKeyPress(e); } private void HandleRowDoubleClicked(IGameFileView ctrl) { if (ctrl != null) { ITabView tabView = m_tabHandler.TabViewForControl(ctrl); if (tabView != null && tabView is IdGamesTabViewCtrl) HandleDownload(AppConfiguration.TempDirectory); else if (tabView != null && tabView.IsPlayAllowed) HandlePlay(); } } private void HandleSearch() { var ctrl = GetCurrentViewControl(); if (ctrl == null) return; ITabView tabView = m_tabHandler.TabViewForControl(ctrl); if (tabView == null) return; if (tabView != null && tabView.IsSearchAllowed) { if (string.IsNullOrEmpty(ctrlSearch.SearchText.Trim())) { tabView.SetGameFiles(); UpdateSavedTabSearch(tabView, null); } else { var searchFields = Util.SearchFieldsFromSearchCtrl(ctrlSearch); UpdateSavedTabSearch(tabView, searchFields); tabView.SetGameFiles(searchFields); } HandleSelectionChange(GetCurrentViewControl(), false); } } private void UpdateSavedTabSearch(ITabView tabView, GameFileSearchField[] searchFields) { if (searchFields == null) m_savedTabSearches.Remove(tabView); else m_savedTabSearches[tabView] = searchFields; } private void CleanTempDirectory() { DirectoryInfo dir = new DirectoryInfo(AppConfiguration.TempDirectory.GetFullPath()); if (dir.Exists) { foreach (FileInfo fi in dir.GetFiles()) { try { fi.Delete(); } catch { //failed, nothing to do } } } } private void viewTextFileToolStripMenuItem_Click(object sender, EventArgs e) { HandleViewTextFile(); } private void openZipFileToolStripMenuItem_Click(object sender, EventArgs e) { HandleOpenArchiveFile(); } private void playToolStripMenuItem_Click(object sender, EventArgs e) { HandlePlay(PlayOptions.ForceDialog); } private void btnPlay_Click(object sender, EventArgs e) { HandlePlay(); } private void playNowToolStripMenuItem_Click(object sender, EventArgs e) { HandlePlay(); } private void deleteToolStripMenuItem_Click(object sender, EventArgs e) { HandleDelete(); } private void ToolStripDropDownButton1_DropDownOpened(object sender, EventArgs e) { ShouldShowToolTip = false; } private void ToolStripDropDownButton1_DropDownClosed(object sender, EventArgs e) { ShouldShowToolTip = true; } private void HandleRename() { IGameFile gameFile = SelectedItems(GetCurrentViewControl()).FirstOrDefault(); if (gameFile == null) return; bool success = false; TextBoxForm form = new TextBoxForm(false, MessageBoxButtons.OKCancel); form.SetMaxLength(72); form.DisplayText = gameFile.FileNameNoPath; form.StartPosition = FormStartPosition.CenterParent; form.Title = $"Rename {gameFile.FileNameNoPath}"; int idx = form.DisplayText.IndexOf('.'); if (idx != -1) form.SelectDisplayText(0, idx); while (!success && form.ShowDialog(this) == DialogResult.OK) { success = RenameGameFile(gameFile, form.DisplayText); idx = form.DisplayText.IndexOf('.'); if (idx != -1) form.SelectDisplayText(0, idx); } } private bool RenameGameFile(IGameFile gameFile, string fileName) { string error = null; bool valid = VerifyFileName(fileName); try { if (valid) { if (!string.IsNullOrEmpty(fileName) && fileName != gameFile.FileName) error = HandleRenameFile(gameFile, fileName); else error = "The new file name must be different and not empty."; } else { error = "The entered file name is invalid."; } } catch (IOException ex) { error = ex.Message; } catch (Exception ex) { Util.DisplayUnexpectedException(this, ex); } if (!string.IsNullOrEmpty(error)) { StyledMessageBox.Show(this, error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } return true; } private string HandleRenameFile(IGameFile gameFile, string fileName) { FileInfo fi = GetRenameFileInfo(gameFile, fileName, out string oldFilePath, out string newFilePath, out string gameFileUpdateFileName); if (!fi.Exists) return string.Format($"Could not find {gameFile.FileName} to rename."); FileInfo fiCheck = new FileInfo(newFilePath); if (fiCheck.Exists) return string.Format($"The file {fileName} already exists."); fi.MoveTo(newFilePath); IGameFile gameFileUpdate = DataSourceAdapter.GetGameFile(gameFile.FileName); //Need to to populate all info before updating gameFile.FileName = gameFileUpdateFileName; gameFileUpdate.FileName = gameFileUpdateFileName; DataSourceAdapter.UpdateGameFile(gameFileUpdate, new GameFileFieldType[] { GameFileFieldType.Filename }); UpdateAdditinalFileReferences(oldFilePath, fileName); HandleSelectionChange(GetCurrentViewControl(), true); return null; } private FileInfo GetRenameFileInfo(IGameFile gameFile, string fileName, out string oldFilePath, out string newFilePath, out string gameFileUpdateFileName) { if (gameFile.IsUnmanaged()) { FileInfo fi = new FileInfo(gameFile.FileName); oldFilePath = gameFile.FileName; newFilePath = Path.Combine(fi.DirectoryName, fileName); gameFileUpdateFileName = newFilePath; return fi; } oldFilePath = gameFile.FileName; newFilePath = Path.Combine(AppConfiguration.GameFileDirectory.GetFullPath(), fileName); gameFileUpdateFileName = fileName; return new FileInfo(Path.Combine(AppConfiguration.GameFileDirectory.GetFullPath(), gameFile.FileName)); } private void UpdateAdditinalFileReferences(string oldFileName, string newFileName) { GameFileFieldType[] updateFields = new GameFileFieldType[] { GameFileFieldType.SettingsFiles }; GameFileGetOptions options = new GameFileGetOptions(new GameFileFieldType[] { GameFileFieldType.GameFileID, GameFileFieldType.SettingsFiles }); var gameFiles = DataSourceAdapter.GetGameFiles(options).Where(x => x.SettingsFiles.Length > 0); foreach (var databaseGameFile in gameFiles) { string[] files = Util.SplitString(databaseGameFile.SettingsFiles); if (files.Contains(oldFileName)) { databaseGameFile.SettingsFiles = GetRenamedAdditionalFileSetting(files, oldFileName, newFileName); DataSourceAdapter.UpdateGameFile(databaseGameFile, updateFields); } } var sourcePorts = DataSourceAdapter.GetSourcePorts(); foreach (var databaseSourcePort in sourcePorts) { string[] files = Util.SplitString(databaseSourcePort.SettingsFiles); if (files.Contains(oldFileName)) { databaseSourcePort.SettingsFiles = GetRenamedAdditionalFileSetting(files, oldFileName, newFileName); DataSourceAdapter.UpdateSourcePort(databaseSourcePort); } } } private static string GetRenamedAdditionalFileSetting(string[] files, string oldFileName, string newFileName) { for (int i = 0; i < files.Length; i++) { if (files[i] == oldFileName) { files[i] = newFileName; break; } } return string.Join(";", files); } private static bool VerifyFileName(string fileName) { return fileName.Except(Path.GetInvalidFileNameChars()).Count() == fileName.Distinct().Count() && fileName.Except(Path.GetInvalidPathChars()).Count() == fileName.Distinct().Count(); } private void HandleViewTextFile() { if (GetCurrentViewControl() == null) return; IGameFile[] items = SelectedItems(GetCurrentViewControl()); foreach (IGameFile item in items) { if (item != null && AssertFile(Path.Combine(AppConfiguration.GameFileDirectory.GetFullPath(), item.FileName))) { using (IArchiveReader reader = ArchiveReader.Create(Path.Combine(AppConfiguration.GameFileDirectory.GetFullPath(), item.FileName))) { var entries = reader.Entries.Where(x => x.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase)); if (entries.Any()) { var entry = entries.First(); if (entries.Count() > 1) { entry = null; SimpleFileSelectForm form = new SimpleFileSelectForm(); form.Initialize(GetSortedTextFiles(item, entries)); form.StartPosition = FormStartPosition.CenterParent; if (form.ShowDialog(this) == DialogResult.OK) entry = entries.FirstOrDefault(x => x.Name == form.SelectedFile); } if (entry != null) { string extractedFileName = Path.Combine(AppConfiguration.TempDirectory.GetFullPath(), entry.Name); entry.ExtractToFile(extractedFileName, true); Process.Start(extractedFileName); } } else { StyledMessageBox.Show(this, "No text file found.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } } } private IEnumerable GetSortedTextFiles(IGameFile item, IEnumerable entries) { FileInfo fi = new FileInfo(item.FileName); string baseFile = fi.Name; if (fi.Extension.Length > 0) baseFile = fi.Name.Replace(fi.Extension, string.Empty); var find = entries.Select(x => x.Name).ToList(); var first = find.FirstOrDefault(x => x.StartsWith(baseFile, StringComparison.InvariantCultureIgnoreCase)); if (first != null) { find.Remove(first); find.Insert(0, first); } return find; } private void HandleOpenArchiveFile() { if (GetCurrentViewControl() == null) return; IGameFile[] items = SelectedItems(GetCurrentViewControl()); if (items.Length == 0) return; IGameFile failedOpen = null; foreach (IGameFile item in items) { if (item == null) continue; bool isDirectory = item.IsUnmanaged() && Util.IsDirectory(item.FileName); if (!isDirectory && !Util.GetReadablePkExtensions().Contains(Path.GetExtension(item.FileName)) && !ArchiveUtil.ShouldReadPackagedArchive(item.FileName)) continue; string path = item.IsUnmanaged() ? item.FileName : Path.Combine(AppConfiguration.GameFileDirectory.GetFullPath(), item.FileName); if (!AssertFile(path)) continue; try { Process.Start(path); return; } catch { failedOpen = item; break; } } if (failedOpen == null) failedOpen = items[0]; StyledMessageBox.Show(this, $"Could not open {failedOpen.FileNameNoPath}", "Cannot Open", MessageBoxButtons.OK, MessageBoxIcon.Error); } private void HandleDelete() { if (GetCurrentViewControl() != null) { MessageCheckBox messageBox = null; ITabView tabView = m_tabHandler.TabViewForControl(GetCurrentViewControl()); bool showDialog = true; bool update = false; if (tabView != null && tabView.IsDeleteAllowed && tabView.IsLocal) { IGameFile[] items = SelectedItems(GetCurrentViewControl()); bool hasUnmanaged = items.Any(x => x.IsUnmanaged()); foreach (IGameFile gameFile in items) { if (showDialog) { string text = $"Delete {gameFile.FileName} and all associated data?"; if (hasUnmanaged) text += "\n\n(Unmanaged files will only be removed from Doom Launcher)"; messageBox = new MessageCheckBox("Confirm", text, $"Do this for all {items.Length} items", SystemIcons.Question, MessageBoxButtons.OKCancel); messageBox.SetShowCheckBox(items.Length > 1); } if (gameFile != null && (!showDialog || messageBox.ShowDialog(this) == DialogResult.OK)) { try { DeleteGameFileAndAssociations(gameFile); update = true; } catch { StyledMessageBox.Show(this, string.Format("The file {0} appears to be in use and could not be deleted.", gameFile.FileName), "In Use", MessageBoxButtons.OK, MessageBoxIcon.Error); } } if (messageBox != null && messageBox.Checked) showDialog = false; if (messageBox != null && messageBox.Checked && messageBox.DialogResult == DialogResult.Cancel) break; } } if (update) { GetCurrentViewControl().SelectedItem = null; UpdateLocal(); HandleSelectionChange(GetCurrentViewControl(), true); } } } private void DeleteGameFileAndAssociations(IGameFile gameFile) { DeleteLocalFileAssociations(gameFile); IIWadData iwadFind = DataSourceAdapter.GetIWad(gameFile.GameFileID.Value); if (iwadFind != null) DataSourceAdapter.DeleteIWad(iwadFind); //note: appears sqlite we re-use deleted auto-inc ids so we have to be careful and delete everything if (!gameFile.IsUnmanaged()) DirectoryDataSourceAdapter.DeleteGameFile(gameFile); DataSourceAdapter.DeleteGameFile(gameFile); if (gameFile.GameFileID.HasValue) DataSourceAdapter.DeleteStatsByFile(gameFile.GameFileID.Value); // If we're deleting Doom64, delete the source port too if (gameFile.IsDoom64) { var doom64SourcePort = DataSourceAdapter.GetDoom64().FirstOrDefault(); DataSourceAdapter.DeleteSourcePort(doom64SourcePort); } var tagMapping = DataSourceAdapter.GetTagMappings(gameFile.GameFileID.Value); tagMapping.ToList().ForEach(x => DataSourceAdapter.DeleteTagMapping(x)); var profiles = DataSourceAdapter.GetGameProfiles(gameFile.GameFileID.Value); profiles.ToList().ForEach(x => DataSourceAdapter.DeleteGameProfile(x.GameProfileID)); DataCache.Instance.TagMapLookup.RemoveGameFile(gameFile); } private void DeleteLocalFileAssociations(IGameFile gameFIle) { IEnumerable files = DataSourceAdapter.GetFiles(gameFIle); foreach (IFileData file in files) { string path = DirectoryForFileType(file.FileTypeID).GetFullPath(); FileInfo fi = new FileInfo(Path.Combine(path, file.FileName)); if (fi.Exists) fi.Delete(); DataSourceAdapter.DeleteFile(file); } } private LauncherPath DirectoryForFileType(FileType fileTypeID) { switch (fileTypeID) { case FileType.Screenshot: return AppConfiguration.ScreenshotDirectory; case FileType.Demo: return AppConfiguration.DemoDirectory; case FileType.SaveGame: return AppConfiguration.SaveGameDirectory; case FileType.Thumbnail: return AppConfiguration.ThumbnailDirectory; default: throw new NotImplementedException(); } } private void HandleSelectionChange(object sender, bool forceChange) { if (!(sender is IGameFileView)) return; IGameFile item = null; IGameFile[] items = SelectedItems(GetCurrentViewControl()); if (items.Length > 0) item = items.First(); if (!forceChange && !AssertCurrentViewItem(item)) { if (item == null) { m_lastSelectedItem = null; ctrlAssociationView.ClearData(); ClearSummary(); btnPlay.Enabled = false; } return; } if (GetCurrentTabView() is IdGamesTabViewCtrl) ctrlAssociationView.SetButtonsAllButtonsEnabled(false); else ctrlAssociationView.SetData(item); if (item != null) { IEnumerable stats = Array.Empty(); if (item.GameFileID.HasValue) stats = DataSourceAdapter.GetStats(item.GameFileID.Value); SetSummary(item); ctrlSummary.SetStatistics(item, stats); } else { btnPlay.Enabled = false; ctrlAssociationView.ClearData(); } if (forceChange) GetCurrentViewControl().RefreshData(); } private bool AssertCurrentViewItem(IGameFile item) { if (item == null || (m_lastSelectedItem != null && m_lastSelectedItem.Equals(item))) { return false; } m_lastSelectedItem = item; return true; } private void SetSummary(IGameFile item) { ctrlSummary.SetTitle(item.Title); ctrlSummary.SetDescription(item.Description); ctrlSummary.TagText = BuildTagText(item); ctrlSummary.SetTimePlayed(item.MinutesPlayed); if (!string.IsNullOrEmpty(item.Comments)) ctrlSummary.SetComments(item.Comments); else ctrlSummary.ClearComments(); List imagePaths = new List(); if (item.GameFileID.HasValue) { SetGameFileImages(item, imagePaths); if (imagePaths.Count == 0 && item.IWadID.HasValue) { var iwad = ThumbnailManager.IWads.FirstOrDefault(x => x.IWadID == item.IWadID.Value); if (iwad != null) { // If this is an IWAD attempt to get user set images, otherwise use pre-defined tile image if exists if (iwad.GameFileID.HasValue && iwad.GameFileID == item.GameFileID) SetGameFileImages(iwad, imagePaths); if (imagePaths.Count == 0 && ThumbnailManager.IWadTileImages.TryGetValue(item.IWadID.Value, out var fileData)) imagePaths.Add(new PreviewImage(fileData.FileName, string.Empty)); } } } if (imagePaths.Count > 0) SetPreviewImages(imagePaths); else ctrlSummary.SetPreviewImage(DataCache.Instance.DefaultImage); } private void SetGameFileImages(IGameFile item, List imagePaths) { foreach (var screenshot in DataSourceAdapter.GetFiles(item, FileType.Screenshot)) { string path = Path.Combine(DataCache.Instance.AppConfiguration.ScreenshotDirectory.GetFullPath(), screenshot.FileName); imagePaths.Add(new PreviewImage(path, FileData.GetTitle(screenshot))); } } private void ClearSummary() { ctrlSummary.SetTitle(string.Empty); ctrlSummary.SetDescription(string.Empty); ctrlSummary.ClearPreviewImage(); ctrlSummary.TagText = string.Empty; ctrlSummary.SetTimePlayed(0); ctrlSummary.ClearComments(); } private void SetPreviewImages(List imagePaths) { bool success; try { success = ctrlSummary.SetPreviewImages(imagePaths); } catch { success = false; } if (!success) ctrlSummary.SetPreviewImage(DataCache.Instance.DefaultImage); } private string BuildTagText(IGameFile gameFile) { if (gameFile.GameFileID.HasValue) { IEnumerable tags = GetTagsFromFile(gameFile); if (tags.Any()) return string.Join(", ", tags.Select(x => x.Name)); } return "N/A"; } private IEnumerable GetTagsFromFile(IGameFile gameFile) { return DataCache.Instance.TagMapLookup.GetTags(gameFile); } private IGameFile[] SelectedItems(IGameFileView ctrl) { return ctrl.SelectedItems; } private void SetSelectedItem(IGameFileView ctrl, IGameFile gameFile) { IGameFile ctrlItem = ctrl.DataSource.FirstOrDefault(x => x.Equals(gameFile)); if (ctrlItem != null) ctrl.SelectedItem = ctrlItem; } private void UpdateDataSourceViews(IGameFile gameFile) { foreach (ITabView tab in m_tabHandler.TabViews) tab.UpdateDataSourceFile(gameFile); } private void btnSearch_Click(object sender, EventArgs e) { HandleSearch(); } private IGameFile CurrentDownloadFile { get; set; } private void HandleDownload(LauncherPath directory) { ITabView tabView = m_tabHandler.TabViews.FirstOrDefault(x => x is IdGamesTabViewCtrl); bool displayDownloads = false; if (tabView == null) return; IGameFile[] dsItems = SelectedItems(tabView.GameFileViewControl); bool showAlreadyDownloading = true; bool doForAll = false; bool download = true; List errorFiles = new List(); try { foreach (IGameFile dsItem in dsItems) { if (dsItem != null && dsItem is IGameFileDownloadable dlItem) { GameFileGetOptions options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.GameFileID, ((IdGamesGameFile)dsItem).id.ToString())); IGameFile dsItemFull = tabView.Adapter.GetGameFiles(options).FirstOrDefault(); if (dsItemFull == null) { errorFiles.Add(dlItem); continue; } dlItem = dsItemFull as IGameFileDownloadable; if (!doForAll) download = PromptUserDownload(dsItems, ref showAlreadyDownloading, ref doForAll, dlItem, dsItemFull, dsItems.Length > 1); if (dlItem != null && download) { CurrentDownloadFile = dsItemFull; dlItem.DownloadCompleted += dlItem_DownloadCompleted; m_downloadHandler.DownloadDirectory = directory; m_downloadHandler.Download(tabView.Adapter, dlItem); displayDownloads = true; } } } } catch (WebException) { ShowBadConnectionError(); } if (errorFiles.Count > 0) StyledMessageBox.Show(this, $"An error occured when trying to download {string.Join(",", errorFiles.Select(x => x.FileName))}", "Download Error", MessageBoxButtons.OK, MessageBoxIcon.Error); if (displayDownloads) DisplayDownloads(); } private void ShowBadConnectionError() { StyledMessageBox.Show(this, "Unable to reach server. Lost connection?", "Bad Connection", MessageBoxButtons.OK, MessageBoxIcon.Error); } private bool PromptUserDownload(IGameFile[] dsItems, ref bool showAlreadyDownloading, ref bool doForAll, IGameFileDownloadable dlItem, IGameFile dsItemFull, bool showCheckBox) { if (showAlreadyDownloading && m_downloadHandler.IsDownloading(dlItem)) { MessageCheckBox messageBox = ShowAlreadyDownloading(dlItem, showCheckBox); showAlreadyDownloading = !messageBox.Checked; } if (!doForAll) { IGameFile dsCheck = DataSourceAdapter.GetGameFile(dsItemFull.FileName); if (dsCheck != null) { MessageCheckBox messageBox = ShowAlreadyExists(dsItems, dsCheck, showCheckBox); doForAll = messageBox.Checked; if (messageBox.DialogResult == DialogResult.Cancel) return false; } } return true; } private MessageCheckBox ShowAlreadyExists(IGameFile[] dsItems, IGameFile dsCheck, bool showCheckBox) { MessageCheckBox messageBox = new MessageCheckBox("Already Exists", string.Format("The file {0} already exists in the library. Continue Download?", dsCheck.FileName), string.Format("Do this for all {0} items", dsItems.Length), SystemIcons.Warning, MessageBoxButtons.OKCancel); messageBox.SetShowCheckBox(showCheckBox); messageBox.ShowDialog(this); return messageBox; } private MessageCheckBox ShowAlreadyDownloading(IGameFileDownloadable dlItem, bool showCheckBox) { MessageCheckBox messageBox = new MessageCheckBox("Already Downloading", string.Format("The file {0} is already downloading", dlItem.FileName), "Do not show this message again", SystemIcons.Error, MessageBoxButtons.OK); messageBox.SetShowCheckBox(showCheckBox); messageBox.ShowDialog(this); return messageBox; } private void HandleEdit() { IGameFileView ctrl = GetCurrentViewControl(); if (ctrl == null) return; ITabView tabView = m_tabHandler.TabViewForControl(ctrl); IGameFile[] gameFiles = SelectedItems(ctrl); if (!CheckEdit(tabView, gameFiles)) return; IGameFile gameFile = DataSourceAdapter.GetGameFile(gameFiles.First().FileName); IEnumerable tags = GetTagsFromFile(gameFile); ITabView localView = m_tabHandler.TabViews.FirstOrDefault(x => x.Key.Equals(TabKeys.LocalKey)); if (localView == null) return; GameFileEditForm form = new GameFileEditForm(); form.SetCopyFromFileAllowed(DataSourceAdapter, localView); form.StartPosition = FormStartPosition.CenterParent; form.EditControl.SetShowCheckBoxes(false); form.EditControl.SetDataSource(gameFile, tags); if (gameFiles.Length > 1) { form.Text = "*** Multiple Edit"; form.EditControl.SetShowCheckBoxes(true); form.EditControl.SetCheckBoxesChecked(false); } if (form.ShowDialog(this) != DialogResult.OK) return; foreach (IGameFile updateGameFile in gameFiles) { var fields = form.EditControl.UpdateDataSource(updateGameFile); if (form.TagsChanged || form.EditControl.TagsChanged) DataCache.Instance.UpdateGameFileTags(new [] { updateGameFile }, form.EditControl.TagData); tabView.Adapter.UpdateGameFile(updateGameFile, fields.ToArray()); UpdateDataSourceViews(updateGameFile); } if (gameFiles.Any()) HandleSelectionChange(ctrl, true); } private static bool CheckEdit(ITabView tabView, IGameFile[] gameFiles) { return gameFiles.Length > 0 && tabView != null && tabView.IsEditAllowed; } private void HandleViewWebPage() { IGameFileView ctrl = GetCurrentViewControl(); if (ctrl != null) { IGameFile dsItem = SelectedItems(ctrl).FirstOrDefault(); if (dsItem != null && dsItem is IdGamesGameFile) { try { GameFileGetOptions options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.GameFileID, ((IdGamesGameFile)dsItem).id.ToString())); IdGamesGameFile dsItemFull = IdGamesDataSourceAdapter.GetGameFiles(options).FirstOrDefault() as IdGamesGameFile; if (dsItemFull != null) Process.Start(string.Format("{0}?file={1}{2}", AppConfiguration.IdGamesUrl, dsItemFull.dir, dsItemFull.FileName)); } catch (WebException) { ShowBadConnectionError(); } } } } private void dlItem_DownloadCompleted(object sender, AsyncCompletedEventArgs e) { HandleDownloadComplete(sender, e); } private async void HandleDownloadComplete(object sender, AsyncCompletedEventArgs e) { if (!e.Cancelled) { IGameFileDownloadable dlFile = sender as IGameFileDownloadable; if (e.Error != null) { StyledMessageBox.Show(this, e.Error.Message + "\n\nIf this error keeps occuring try chaning your mirror in the Settings menu.", "Download Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { if (dlFile != null) await WriteDownloadFile(dlFile); } try { IDisposable dlDispose = sender as IDisposable; if (dlDispose != null) dlDispose.Dispose(); } catch (Exception ex) { Util.DisplayUnexpectedException(this, ex); } } } private async Task WriteDownloadFile(IGameFileDownloadable dlFile) { try { FileInfo fi = new FileInfo(Path.Combine(AppConfiguration.TempDirectory.GetFullPath(), dlFile.FileName)); fi.CopyTo(Path.Combine(AppConfiguration.GameFileDirectory.GetFullPath(), dlFile.FileName), true); fi.Delete(); await SyncLocalDatabase(new string[] { fi.Name }, FileManagement.Managed, true); } catch (IOException) { StyledMessageBox.Show(this, string.Format("The file {0} is in use and cannot be written.", dlFile.FileName), "File Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (Exception ex) { Util.DisplayUnexpectedException(this, ex); } } private void downloadToolStripMenuItem_Click(object sender, EventArgs e) { HandleDownload(AppConfiguration.TempDirectory); } private void editToolStripMenuItem_Click(object sender, EventArgs e) { HandleEdit(); } private void tabControl_SelectedIndexChanged(object sender, EventArgs e) { HandleTabSelectionChange(); } private void HandleTabSelectionChange() { if (tabControl.SelectedTab == null) return; m_lastSelectedTabView?.GameFileViewControl.SetVisible(false); ITabView tabView = GetCurrentTabView(); if (tabView == null) return; bool isIdGamesTab = tabView is IdGamesTabViewCtrl; ctrlSearch.SetSearchText(GetTabSearchText(tabView.Key.ToString()), !isIdGamesTab); m_lastSelectedTabView = tabView; btnPlay.Enabled = tabView.IsPlayAllowed; chkIncludeAll.Enabled = tabView.IsAutoSearchAllowed; if (isIdGamesTab && !m_idGamesLoaded) { tabView.SetGameFiles(); m_idGamesLoaded = true; } tabView.GameFileViewControl.Focus(); tabView.GameFileViewControl.SetVisible(true); AppConfiguration.LastSelectedTabIndex = tabControl.SelectedIndex; SetSelectedTabText(tabView); HandleSelectionChange(tabView.GameFileViewControl, false); } private void SetSelectedTabText(ITabView tabView) { if (tabControl.SelectedTab != null) lblSelectedTag.Text = $"{tabControl.SelectedTab.Text.Trim()} ({tabView.GameFileViewControl.DataSource.Count()} Files)"; } private ITabView GetCurrentTabView() { return tabControl.SelectedTab.Controls[0] as ITabView; } private void sourcePortsToolStripMenuItem_Click(object sender, EventArgs e) { HandleEditSourcePorts(false); } private void viewWebPageToolStripMenuItem_Click(object sender, EventArgs e) { HandleViewWebPage(); } private void btnDownloads_Click(object sender, EventArgs e) { DisplayDownloads(); } private void btnTags_Click(object sender, EventArgs e) { DisplayTags(); } private void btnUpdate_Click(object sender, EventArgs e) { DisplayUpdate(); } private void HandleEditSourcePorts(bool initSetup) { SourcePortViewForm form = new SourcePortViewForm(DataSourceAdapter, AppConfiguration, GetAdditionalTabViews().ToArray(), SourcePortLaunchType.SourcePort); form.StartPosition = FormStartPosition.CenterParent; form.SourcePortLaunched += form_SourcePortLaunched; if (initSetup) form.DisplayInitSetupButton(); form.ShowDialog(this); HandleSelectionChange(GetCurrentViewControl(), true); } private void HandleEditDoom64() { SourcePortEditForm editForm = new SourcePortEditForm(DataSourceAdapter, GetAdditionalTabViews().ToArray(), SourcePortLaunchType.Doom64); editForm.StartPosition = FormStartPosition.CenterParent; ISourcePortData sourcePort = DataSourceAdapter.GetDoom64(false).FirstOrDefault(); bool isNew = sourcePort == null; if (!isNew) { editForm.SetDataSource(sourcePort); if (editForm.ShowDialog(this) == DialogResult.OK) { editForm.UpdateDataSource(sourcePort); DataSourceAdapter.UpdateSourcePort(sourcePort); } } else // new { if (editForm.ShowDialog(this) == DialogResult.OK) { var newSourcePort = new SourcePortData(); editForm.UpdateDataSource(newSourcePort); newSourcePort.LaunchType = SourcePortLaunchType.Doom64; DataSourceAdapter.InsertSourcePort(newSourcePort); } } // var doom64Directory = Path.GetDirectoryName(sourcePort.GetFullExecutablePath()); //var wadFile = Path.Combine(doom64Directory, "DOOM64.WAD"); } private void HandleEditUtilities() { SourcePortViewForm form = new SourcePortViewForm(DataSourceAdapter, AppConfiguration, GetAdditionalTabViews().ToArray(), SourcePortLaunchType.Utility); form.ShowPlayButton(false); form.StartPosition = FormStartPosition.CenterParent; form.ShowDialog(this); RebuildUtilityToolStrip(); } void form_SourcePortLaunched(object sender, EventArgs e) { SourcePortViewForm form = sender as SourcePortViewForm; if (form != null && form.GetSelectedSourcePort() != null) HandlePlay(null, form.GetSelectedSourcePort()); } private void DisplayDownloads() { DpiScale dpiScale = new DpiScale(CreateGraphics()); Popup popup = new Popup(m_downloadView) { Width = dpiScale.ScaleIntX(300), Height = m_downloadView.Height }; popup.Show(btnDownloads); } private void DisplayUpdate() { DpiScale dpiScale = new DpiScale(CreateGraphics()); Popup popup = new Popup(m_updateControl) { Width = dpiScale.ScaleIntX(300), Height = m_updateControl.Height }; popup.Show(btnUpdate); } private void DisplayTags() { if (m_tagSelectControl.Pinned) return; DpiScale dpiScale = new DpiScale(CreateGraphics()); m_tagSelectControl.ClearSelections(); m_tagPopup = new Popup(m_tagSelectControl) { Width = dpiScale.ScaleIntX((int)(Width * AppConfiguration.SplitTagSelect)), Height = Height - PointToClient(btnTags.PointToScreen(btnTags.Location)).Y - btnTags.Height - dpiScale.ScaleIntY(40) }; m_tagPopup.Show(btnTags); } private void settingsToolStripMenuItem_Click(object sender, EventArgs e) { bool success = false; bool firstRun = true; DialogResult result; do { success = ShowSettings(firstRun, out result); firstRun = false; } while (!success); } private bool ShowSettings(bool allowCancel, out DialogResult result) { SettingsForm form = new SettingsForm(DataSourceAdapter, AppConfiguration); form.SetCancelAllowed(allowCancel); form.StartPosition = FormStartPosition.CenterParent; result = form.ShowDialog(this); if (result == DialogResult.OK) { try { AppConfiguration.Refresh(); } catch (DirectoryNotFoundException ex) { StyledMessageBox.Show(this, string.Format("The directory {0} was not found. DoomLauncher will not operate correctly with invalid paths. " + "Make sure the directory you are setting contains all folders required (Demos, SaveGames, Screenshots, Temp)", ex.Message), "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } catch (Exception ex) { Util.DisplayUnexpectedException(this, ex); return false; } RefreshConfigItems(); HandleSelectionChange(GetCurrentViewControl(), true); } return true; } private void RefreshConfigItems() { IdGamesDataSourceAdapter = new IdGamesDataAdapater(AppConfiguration.IdGamesUrl, AppConfiguration.ApiPage, AppConfiguration.MirrorUrl); if (m_tabHandler != null) { ITabView tabView = m_tabHandler.TabViews.FirstOrDefault(x => x is IdGamesTabViewCtrl); if (tabView != null) tabView.Adapter = IdGamesDataSourceAdapter; } ctrlAssociationView.Initialize(DataSourceAdapter, AppConfiguration); SetShowTabHeaders(); } private void CtrlAssociationView_RequestScreenshots(object sender, RequestScreenshotsEventArgs e) { List screens = DataSourceAdapter.GetFiles(e.GameFile, FileType.Screenshot).ToList(); ctrlAssociationView.SetScreenshots(screens); } private void addFilesToolStripMenuItem_Click(object sender, EventArgs e) { HandleAddFiles(); } private void addDirectoryToolStripMenuItem_Click(object sender, EventArgs e) { HandleAddDirectory(); } private async void HandleAddDirectory() { await HandleAddFiles(AddFileType.GameFile, Array.Empty(), "Select Folder", browseDirectory: true); } private async void HandleAddFiles() { await HandleAddFiles(AddFileType.GameFile, new string[] { "Zip", "WAD", "pk3", "rar", "7z", "txt", "zdl" }, "Select Game Files"); } private async void addIWADsToolStripMenuItem_Click(object sender, EventArgs e) { await HandleAddIWads(); } private async Task HandleAddIWads() { await HandleAddFiles(AddFileType.IWad, new string[] { "WAD", "iwad", "ipk3" }, "Select IWADs"); } private void addDoom64ToolStripMenuItem_Click(object sender, EventArgs e) { HandleEditDoom64(); } private void UpdateLocal() { foreach (ITabView tab in m_tabHandler.TabViews) UpdateLocalTabData(tab); } private void UpdateLocalTabData(ITabView tab) { if (!tab.IsLocal) return; if (m_savedTabSearches.ContainsKey(tab)) tab.SetGameFiles(m_savedTabSearches[tab]); else tab.SetGameFiles(); } private IGameFile[] m_pendingZdlFiles; // A non-null value for pullTitlepic will override the users configuration value for AutomaticallyPullTitlepic. // The value will be restored on completion. private async Task HandleAddGameFiles(AddFileType type, string[] files, ITagData tag = null, FileManagement? overrideManagement = null, bool? overridePullTitlepic = null) { Activate(); if (!VerifyAddFiles(type, files)) return; List libraryFiles = new List(files); string[] zdlFiles = GetZdlFiles(files).ToArray(); libraryFiles = libraryFiles.Except(zdlFiles).ToList(); string[] zdlLibraryFiles = HandleZdlFiles(zdlFiles); libraryFiles.AddRange(zdlLibraryFiles); //only launch zdl file if it's the only one if (m_launchFile == null && zdlLibraryFiles.Length == 1 && libraryFiles.Count == 1) { FileInfo fi = new FileInfo(zdlLibraryFiles[0]); m_launchFile = fi.Name.Replace(fi.Extension, ".zip"); } string[] missingFiles = libraryFiles.Where(x => !File.Exists(x) && !Directory.Exists(x)).ToArray(); if (missingFiles.Length > 0) { StringBuilder sb = new StringBuilder(); Array.ForEach(missingFiles, x => sb.Append(x + "\n")); StyledMessageBox.Show(this, "The following files were not found and will not be added:" + Environment.NewLine + Environment.NewLine + sb.ToString(), "Files Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error); libraryFiles = libraryFiles.Except(missingFiles).ToList(); } string[] libraryFilesAsDirectories = libraryFiles.Where(x => Util.IsDirectory(x)).ToArray(); string[] libraryFilesAsFiles = libraryFiles.Except(libraryFilesAsDirectories).ToArray(); if (libraryFiles.Count > 0) { bool saveTitlepicValue = AppConfiguration.AutomaticallyPullTitlpic; if (overridePullTitlepic != null) AppConfiguration.AutomaticallyPullTitlpic = overridePullTitlepic.Value; if (libraryFilesAsFiles.Length > 0) await HandleCopyFiles(type, libraryFilesAsFiles, overrideManagement ?? GetUserSelectedFileManagement(), tag); if (libraryFilesAsDirectories.Length > 0) await HandleCopyFiles(type, libraryFilesAsDirectories, FileManagement.Unmanaged, tag); AppConfiguration.AutomaticallyPullTitlpic = saveTitlepicValue; } else if (m_zdlInvalidFiles.Count > 0) { DisplayInvalidFilesError(m_zdlInvalidFiles); } } private bool VerifyAddFiles(AddFileType type, string[] files) { List warnFiles = new List(); foreach (string file in files) { IWadInfo info = IWadInfo.GetIWadInfo(file); if (type == AddFileType.GameFile && info != null) warnFiles.Add(Path.GetFileName(file)); else if (type == AddFileType.IWad && info == null) warnFiles.Add(Path.GetFileName(file)); } if (warnFiles.Count > 0) { StringBuilder warn = new StringBuilder(); if (type == AddFileType.GameFile) warn.Append("The following file(s) were detected be IWADS and are being added as game files:"); else warn.Append("The following files(s) were not detected to be IWADS and are being added as IWADS:"); warn.AppendLine(); warn.Append(string.Join(", ", warnFiles)); warn.Append("\n\nContinue?"); bool ret = StyledMessageBox.Show(this, warn.ToString(), "File Verification", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes; return ret; } return true; } private async Task HandleAddFiles(AddFileType type, string[] extensions, string dialogTitle, bool browseDirectory = false) { if (browseDirectory) { FolderBrowserDialog folderDialog = new FolderBrowserDialog(); if (folderDialog.ShowDialog(this) == DialogResult.OK) await HandleAddGameFiles(type, new string[] { folderDialog.SelectedPath }); return; } OpenFileDialog dialog = new OpenFileDialog(); dialog.Title = dialogTitle; dialog.Multiselect = true; dialog.Filter = GetDialogFilter("Game Files", extensions); if (dialog.ShowDialog(this) == DialogResult.OK) await HandleAddGameFiles(type, dialog.FileNames); } private async Task HandleCopyFiles(AddFileType type, string[] fileNames, FileManagement fileManagement, ITagData tag) { var copyProgressBar = ProgressBarStart(ProgressBarType.Copy); FileAddResults fileAddResults = await CopyFiles(fileNames, fileManagement, copyProgressBar); string[] files = fileAddResults.GetAllFiles().ToArray(); ProgressBarEnd(ProgressBarType.Copy); switch (type) { case AddFileType.GameFile: await SyncLocalDatabase(files, fileManagement, true, tag); break; case AddFileType.IWad: var handler = await SyncLocalDatabase(files, fileManagement, fileAddResults.ReplacedFiles.Count > 0); SyncIWads(handler.AddedGameFiles); break; default: break; } if (fileAddResults.Errors.Count > 0) { string start = fileAddResults.Errors.Count > 1 ? string.Concat("Errors:", Environment.NewLine) : string.Empty; string tab = fileAddResults.Errors.Count > 1 ? "\t" : string.Empty; StringBuilder sb = new StringBuilder(start); fileAddResults.Errors.ForEach(x => sb.Append(string.Concat(tab, x.FileName, ": ", x.Error, Environment.NewLine))); MessageBox.Show(this, sb.ToString(), "Failed to Add", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private async Task CopyFiles(string[] fileNames, FileManagement fileManagement, ProgressBarForm progressBar) { FileAddResults fileAddResults = new FileAddResults(); if (fileManagement == FileManagement.Managed) await Task.Run(() => fileAddResults = CopyFiles(fileNames, AppConfiguration.GameFileDirectory.GetFullPath(), progressBar)); else fileAddResults = UnmanagedAddCheck(fileNames, AppConfiguration.GameFileDirectory.GetFullPath()); return fileAddResults; } private FileAddResults UnmanagedAddCheck(string[] fileNames, string directory) { FileAddResults results = new FileAddResults(); string managedError = "File already exists as a managed file."; string unmanagedError = "File already exists as an unmanaged file."; foreach (string fileName in fileNames) { string zipName = Path.Combine(directory, Path.GetFileNameWithoutExtension(fileName) + ".zip"); IGameFile existingGameFile = DataSourceAdapter.GetGameFile(fileName); if (File.Exists(zipName)) { results.Errors.Add(new FileError { FileName = fileName, Error = managedError }); } else if (existingGameFile != null) { if (!existingGameFile.IsUnmanaged() && Path.IsPathRooted(existingGameFile.FileName)) results.Errors.Add(new FileError { FileName = fileName, Error = unmanagedError }); else results.NewFiles.Add(fileName); } else { results.NewFiles.Add(fileName); } } return results; } private FileManagement GetUserSelectedFileManagement() { FileManagement fileManagement = AppConfiguration.FileManagement; if (fileManagement == FileManagement.Prompt) { Activate(); FileManagementSelect select = new FileManagementSelect(); select.StartPosition = FormStartPosition.CenterParent; select.ShowDialog(this); fileManagement = select.GetSelectedFileManagement(); } return fileManagement; } private List m_zdlInvalidFiles = new List(); private string[] HandleZdlFiles(string[] files) { m_zdlInvalidFiles = new List(); List libraryFiles = new List(); List pendingGameFiles = new List(); ZdlParser parser = new ZdlParser(DataSourceAdapter.GetSourcePorts(), DataSourceAdapter.GetIWads()); foreach (string file in files) { IGameFile[] gameFiles = parser.Parse(File.ReadAllText(file)); foreach (IGameFile gameFile in gameFiles) { FileInfo fi = new FileInfo(gameFile.FileName); libraryFiles.Add(gameFile.FileName); gameFile.FileName = fi.Name.Replace(fi.Extension, ".zip"); //set to name only, doomlauncher uses zip extension pendingGameFiles.Add(gameFile); if (gameFile == gameFiles.First()) //first file is the 'launch' file and rest are 'additional files' as far as we are concerned { StringBuilder sb = new StringBuilder(); Array.ForEach(gameFiles.Select(x => Path.GetFileName(x.FileName).Replace(Path.GetExtension(x.FileName), ".zip")).ToArray(), x => sb.Append(x + ';')); sb.Remove(sb.Length - 1, 1); gameFile.SettingsFiles = sb.ToString(); } } if (parser.Errors.Length > 0) m_zdlInvalidFiles.Add(new InvalidFile(file, string.Join(", ", parser.Errors))); } m_pendingZdlFiles = pendingGameFiles.ToArray(); return libraryFiles.ToArray(); } private IEnumerable GetZdlFiles(string[] files) { return files.Where(x => Path.GetExtension(x).Equals(".zdl", StringComparison.OrdinalIgnoreCase)); } private string GetDialogFilter(string name, string[] extensions) { StringBuilder sb = new StringBuilder(); foreach (string ext in extensions) { sb.Append(string.Format("*.{0};", ext.ToLower())); } sb.Remove(sb.Length - 1, 1); return string.Format("{0} ({1})|{1}|All Files (*.*)|*.*", name, sb.ToString()); } private FileAddResults CopyFiles(string[] files, string directory, ProgressBarForm progressBar) { FileAddResults results = new FileAddResults(); HashSet addedNames = new HashSet(); List fileNames = files.ToList(); fileNames.Sort(); int count = 0; bool promptOverwrite = true; bool overwrite = false; foreach (string file in fileNames) { if (m_progressBarCancelled) break; // Ignore. This is the same file and should be from a Resync call. if (file.StartsWith(AppConfiguration.GameFileDirectory.GetFullPath())) { results.ReplacedFiles.Add(Path.GetFileName(file)); continue; } if (progressBar != null) UpdateProgressBar(progressBar, $"Copying {file}...", Convert.ToInt32(count / (double)fileNames.Count * 100)); FileInfo fi = new FileInfo(file); if (ArchiveUtil.IsTransformableToZip(fi.Extension)) { if (!ArchiveUtil.CreateZipFrom(fi, AppConfiguration.TempDirectory.GetFullPath(), out fi)) { results.Errors.Add(new FileError { FileName = file, Error = "Failed to create zip from file." }); continue; } } string baseName = fi.Name.Replace(fi.Extension, string.Empty); if (!IsZipFile(fi) && addedNames.Contains(baseName)) //archive with this name exists, add the file (match .txt with .wad etc) AddEntryToExistingFile(directory, file, fi, baseName); if (!addedNames.Contains(baseName)) addedNames.Add(baseName); string zipName = Path.Combine(directory, fi.Name); bool isZip = IsZipFile(fi); if (!isZip) zipName = Path.Combine(directory, baseName + ".zip"); IEnumerable existingFileNames = DataSourceAdapter.GetGameFileNames(); try { string existingFile = existingFileNames.FirstOrDefault(x => Path.GetFileName(x).Equals(fi.Name)); if (existingFile != null && GameFile.IsUnmanaged(existingFile)) { results.Errors.Add(new FileError { FileName = baseName, Error = "File already exists as an unmanaged file." }); } else if (File.Exists(zipName)) { if (promptOverwrite) { Tuple result = PromptCopyFileOverwrite(baseName); overwrite = result.Item1; promptOverwrite = !result.Item2; } if (overwrite) { results.ReplacedFiles.Add(baseName + ".zip"); if (isZip) fi.CopyTo(zipName, true); else HandleNonZipReplacement(fi, zipName); } } else { results.NewFiles.Add(baseName + ".zip"); if (IsZipFile(fi)) { fi.CopyTo(Path.Combine(directory, fi.Name)); } else { string newZipName = Path.Combine(directory, baseName + ".zip"); AddZipEntry(file, fi.Name, newZipName); } } } catch (IOException) { results.Errors.Add(new FileError { FileName = baseName, Error = "File is in use." }); } catch (Exception ex) { results.Errors.Add(new FileError { FileName = baseName, Error = string.Concat("Unknown error: ", ex.HResult) }); //Shouldn't happen } count++; } return results; } private static void AddZipEntry(string file, string name, string newZipName) { using (ZipArchive za = ZipFile.Open(newZipName, ZipArchiveMode.Create)) { using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { var entry = za.CreateEntry(name); using (var destStream = entry.Open()) fileStream.CopyTo(destStream); } } } private void HandleNonZipReplacement(FileInfo fi, string zipName) { string tempFile = Path.Combine(AppConfiguration.TempDirectory.GetFullPath(), fi.Name); if (File.Exists(tempFile)) File.Delete(tempFile); fi.CopyTo(tempFile); using (ZipArchive za = ZipFile.Open(zipName, File.Exists(zipName) ? ZipArchiveMode.Update : ZipArchiveMode.Create)) { var existingEntries = za.Entries.Where(x => x.Name == fi.Name).ToArray(); if (existingEntries.Any()) { foreach (var entry in existingEntries) { entry.Delete(); za.CreateEntryFromFile(tempFile, Path.Combine(Path.GetDirectoryName(entry.FullName), fi.Name)); } } else { za.CreateEntryFromFile(tempFile, fi.Name); } } } private static void AddEntryToExistingFile(string directory, string file, FileInfo fi, string baseName) { string newZipName = Path.Combine(directory, baseName, ".zip"); if (File.Exists(newZipName)) { using (ZipArchive za = ZipFile.Open(newZipName, ZipArchiveMode.Update)) { if (!za.Entries.Any(x => x.Name == fi.Name)) //make sure this file does not already exist in the archive za.CreateEntryFromFile(file, fi.Name); } } } private Tuple PromptCopyFileOverwrite(string file) { if (InvokeRequired) { return (Tuple)Invoke(new Func>(PromptCopyFileOverwrite), new object[] { file }); } else { MessageCheckBox msg = new MessageCheckBox("Overwrite", string.Format("The file {0} already exists. Overwrite?", file), "Accept For All", SystemIcons.Question, MessageBoxButtons.OKCancel); if (msg.ShowDialog() == DialogResult.OK) return new Tuple(true, msg.Checked); return new Tuple(false, msg.Checked); } } private static bool IsZipFile(FileInfo fi) { return fi.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase); } void m_progressBarFormCopy_Cancelled(object sender, EventArgs e) { m_progressBarCancelled = true; if (sender is ProgressBarForm progressBarForm) ProgressBarEnd(ProgressBarType.Copy); } private void UpdateProgressBar(ProgressBarForm form, string text, int value) { if (InvokeRequired) { Invoke(new Action(UpdateProgressBar), new object[] { form, text, value }); } else { form.DisplayText = text; form.Value = value; } } private async void ctrlView_DragDrop(object sender, DragEventArgs e) { if (sender is IGameFileView ctrl && e.Data.GetData(DataFormats.FileDrop) is string[] files) { ITagData tag = null; if (m_tabHandler.TabViewForControl(ctrl) is TagTabView tagTabView) tag = tagTabView.TagDataSource; if (ctrl.DoomLauncherParent != null && ctrl.DoomLauncherParent is IWadTabViewCtrl) await HandleAddGameFiles(AddFileType.IWad, files); else await HandleAddGameFiles(AddFileType.GameFile, files, tag); } } private void ctrlView_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy; } private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { AboutBox about = new AboutBox(); about.ShowDialog(this); } private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { HandleFormClosing(); } private void MnuLocal_Opening(object sender, CancelEventArgs e) { RebuildTagToolStrip(); if (!(GetCurrentViewControl() is IGameFileSortableView sortableView)) return; ToolStripMenuItem sortToolStrip = GetToolStripItem(mnuLocal, "Sort By"); IGameFileView view = GetCurrentViewControl(); sortToolStrip.Visible = view is GameFileTileViewControl; SetVisibleLocalMenuItems(view); for (int i = 0; i < GameFileViewFactory.DefaultColumnTextFields.Length; i++) { ColumnField columnField = GameFileViewFactory.DefaultColumnTextFields[i]; string text = columnField.Title; if (columnField.DataKey.Equals(sortableView.GetSortedColumnKey(), StringComparison.InvariantCultureIgnoreCase) && sortableView.GetColumnSort(sortableView.GetSortedColumnKey()) != SortOrder.None) { if (sortableView.GetColumnSort(sortableView.GetSortedColumnKey()) == SortOrder.Ascending) text += " ▲"; else text += " ▼"; } sortToolStrip.DropDownItems[i].Text = text; } Stylizer.StylizeControl(mnuLocal, DesignMode); } private void SetVisibleLocalMenuItems(IGameFileView view) { IGameFile[] gameFiles = SelectedItems(view); if (gameFiles.Length == 0) return; bool nonDirectory = gameFiles.All(x => !x.IsDirectory()); foreach (var item in GetNonDirectoryMenuItems()) item.Visible = nonDirectory; } private ToolStripMenuItem[] GetNonDirectoryMenuItems() { return new ToolStripMenuItem[] { GetToolStripItem(mnuLocal, MenuConstants.Rename), //GetToolStripItem(mnuLocal, MenuConstants.OpenArchive), GetToolStripItem(mnuLocal, MenuConstants.Utility) }; } ToolStripMenuItem GetToolStripItem(ContextMenuStrip strip, string text) => strip.Items.Cast().FirstOrDefault(x => x.Text == text) as ToolStripMenuItem; private void newTagToolStripMenuItem_Click(object sender, EventArgs e) { HandleManageTags(); } private void manageTagsToolStripMenuItem_Click(object sender, EventArgs e) { HandleManageTags(); } private void sortToolStripItem_Click(object sender, EventArgs e) { if (!(GetCurrentViewControl() is IGameFileSortableView sortableView)) return; IGameFileView view = GetCurrentViewControl(); ToolStripItem strip = sender as ToolStripItem; ToolStripMenuItem sortToolStrip = GetSortByToolStrip(); int index = 0; for (int i = 0; i < sortToolStrip.DropDownItems.Count; i++) { if (sortToolStrip.DropDownItems[i] == strip) { index = i; break; } } ColumnField columnField = GameFileViewFactory.DefaultColumnTextFields[index]; SortOrder sortOrder; if (sortableView.GetColumnSort(columnField.DataKey) == SortOrder.Descending) sortOrder = SortOrder.Ascending; else sortOrder = SortOrder.Descending; sortableView.SetSortedColumn(columnField.DataKey, sortOrder); view.DataSource = GetViewSort(view, view.DataSource); } private void TabView_DataSourceChanging(object sender, GameFileListEventArgs e) { if (sender is ITabView tabView) { if (!(sender is IWadTabViewCtrl)) e.GameFiles = RemoveExcludeTags(tabView, e.GameFiles); e.GameFiles = GetViewSort(tabView.GameFileViewControl, e.GameFiles); } } private void TabView_DataSourceChanged(object sender, GameFileListEventArgs e) { if (sender is ITabView tabView && tabView.Key == GetCurrentTabView().Key) SetSelectedTabText(tabView); } private IEnumerable RemoveExcludeTags(ITabView tabView, IEnumerable gameFiles) { if (chkIncludeAll.Checked) return gameFiles; ITagData currentTag = null; if (tabView is TagTabView tagTabView) currentTag = tagTabView.TagDataSource; List gameFilesInclude = new List(gameFiles.Count()); foreach (IGameFile gameFile in gameFiles) { var tags = DataCache.Instance.TagMapLookup.GetTags(gameFile); // This tab is for this tag, include it if (currentTag != null && currentTag.ExcludeFromOtherTabs && tags.Any(x => x.TagID == currentTag.TagID)) { gameFilesInclude.Add(gameFile); continue; } if (!tags.Any(x => x.ExcludeFromOtherTabs)) gameFilesInclude.Add(gameFile); } return gameFilesInclude; } private IEnumerable GetViewSort(IGameFileView view, IEnumerable gameFiles) { if (!(view is IGameFileSortableView sortableView) || string.IsNullOrEmpty(sortableView.GetSortedColumnKey())) return gameFiles; ColumnField columnField = GameFileViewFactory.DefaultColumnTextFields.FirstOrDefault(x => x.DataKey.Equals(sortableView.GetSortedColumnKey(), StringComparison.InvariantCultureIgnoreCase)); if (columnField != null) { var property = typeof(IGameFile).GetProperty(columnField.DataKey); var sort = sortableView.GetColumnSort(sortableView.GetSortedColumnKey()); if (sort == SortOrder.Ascending) return gameFiles.OrderBy(x => property.GetValue(x)); else if (sort == SortOrder.Descending) return gameFiles.OrderByDescending(x => property.GetValue(x)); } return gameFiles; } private void HandleManageTags() { TagForm form = new TagForm(); form.Init(DataSourceAdapter); form.StartPosition = FormStartPosition.CenterParent; form.ShowDialog(this); DataCache.Instance.TagMapLookup.Refresh(new ITagData[] { }); if (form.TagControl.AddedTags.Length > 0 && GameFileViewFactory.IsUsingColumnView) { UpdateColumnConfig(); //the ordered tab insert will use this column configuration AppConfiguration.RefreshColumnConfig(); } foreach (ITagData tag in form.TagControl.AddedTags) { if (tag.HasTab) OrderedTagTabInsert(tag); } foreach (ITagData tag in form.TagControl.EditedTags) { ITabView tabView = m_tabHandler.TabViews.FirstOrDefault(x => x.Key.Equals(tag.TagID) && x is TagTabView); if (tabView != null) { if (tag.HasTab) m_tabHandler.UpdateTabTitle(tabView, tag.FavoriteName); else m_tabHandler.RemoveTab(tabView); } else { if (tag.HasTab) OrderedTagTabInsert(tag); } } UpdateTagColumnConfig(form.TagControl.EditedTags); foreach (ITagData tag in form.TagControl.DeletedTags) { ITabView tabView = m_tabHandler.TabViews.FirstOrDefault(x => x.Key.Equals(tag.TagID) && x is TagTabView); if (tabView != null) m_tabHandler.RemoveTab(tabView); } DataCache.Instance.UpdateTags(); UpdateTabOrder(); UpdateLocal(); HandleSelectionChange(GetCurrentViewControl(), false); } private void UpdateTabOrder() { int index = 1; foreach (string name in TabKeys.KeyNames) { if (AppConfiguration.VisibleViews.Contains(name)) index++; } foreach (var tag in DataCache.Instance.Tags) { if (!tag.HasTab) continue; ITabView tabView = m_tabHandler.TabViews.FirstOrDefault(x => x.Key.Equals(tag.TagID) && x is TagTabView); if (tabView == null) continue; int checkIndex = m_tabHandler.GetTabIndex(tabView); if (checkIndex != -1 && checkIndex != index) m_tabHandler.SetTabIndex(index, tabView); index++; } } private void UpdateTagColumnConfig(ITagData[] editedTags) { ColumnConfig[] columnConfig = DataCache.Instance.GetColumnConfig(); foreach (ITagData tag in editedTags) { ITagData previousRevision = DataCache.Instance.PreviousTags.FirstOrDefault(x => x.TagID == tag.TagID); if (previousRevision == null) continue; var updateColumns = columnConfig.Where(x => x.Parent == previousRevision.Name); foreach (var col in updateColumns) col.Parent = tag.Name; } IEnumerable config = DataSourceAdapter.GetConfiguration(); DataCache.Instance.UpdateConfig(config, AppConfiguration.ColumnConfigName, SerializeColumnConfig(columnConfig.ToList())); AppConfiguration.RefreshColumnConfig(); } private void OrderedTagTabInsert(ITagData tag) { int start = m_tabHandler.TabViews.Length - 1; int end = 1; ITabView[] tabViews = m_tabHandler.TabViews; while (start > end && tabViews[start] is TagTabView && tabViews[start].Title.CompareTo(tag.Name) > 0) { start--; } m_tabHandler.InsertTab(start + 1, CreateTagTab(GameFileViewFactory.DefaultColumnTextFields, DataCache.Instance.GetColumnConfig(), tag.FavoriteName, tag, true)); } private void utilityToolStripItem_Click(object sender, EventArgs e) { ToolStripItem strip = sender as ToolStripItem; var utilities = DataSourceAdapter.GetUtilities(); var utility = utilities.FirstOrDefault(x => x.Name == strip.Text); if (utility == null) { MessageBox.Show(this, "Failed to find the utility.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } LaunchData launchData = GetLaunchFiles(SelectedItems(GetCurrentViewControl()), checkActiveSessions: false); if (!launchData.Success) { if (!string.IsNullOrEmpty(launchData.ErrorTitle)) MessageBox.Show(this, launchData.ErrorDescription, launchData.ErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } UtilityHandler handler = new UtilityHandler(this, AppConfiguration, utility); if (!handler.RunUtility(launchData.GameFile)) MessageBox.Show(this, "The utility was an invalid application or not found.", "Invalid Utility", MessageBoxButtons.OK, MessageBoxIcon.Error); } private void doom64ToolStripItem_Click(object sender, EventArgs e) { ToolStripItem strip = sender as ToolStripItem; var doom64s = DataSourceAdapter.GetDoom64(); var doom64 = doom64s.FirstOrDefault(x => x.Name == strip.Text); if (doom64 == null) { MessageBox.Show(this, "Failed to find Doom64.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } LaunchData launchData = GetLaunchFiles(SelectedItems(GetCurrentViewControl()), checkActiveSessions: false); if (!launchData.Success) { if (!string.IsNullOrEmpty(launchData.ErrorTitle)) MessageBox.Show(this, launchData.ErrorDescription, launchData.ErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } UtilityHandler handler = new UtilityHandler(this, AppConfiguration, doom64); if (!handler.RunUtility(launchData.GameFile)) MessageBox.Show(this, "The utility was an invalid application or not found.", "Invalid Utility", MessageBoxButtons.OK, MessageBoxIcon.Error); } private void tagToolStripItem_Click(object sender, EventArgs e) { if (!(sender is ToolStripMenuItem strip)) return; // This is the tag that was clicked ITagData tag = DataCache.Instance.Tags.FirstOrDefault(x => x.TagID.Equals(strip.Tag)); if (tag == null) return; IGameFile[] gameFiles = SelectedItems(GetCurrentViewControl()); if (strip.Checked) DataCache.Instance.AddGameFileTag(gameFiles, tag, out List alreadyTagged); else DataCache.Instance.RemoveGameFileTag(gameFiles, tag); // Update mutable cache of tag mappings & publish an event DataCache.Instance.TagMapLookup.Refresh(new ITagData[] { tag }); UpdateTagTabData(tag.TagID); // Update the current view foreach (IGameFile gameFile in gameFiles) GetCurrentViewControl().UpdateGameFile(gameFile); HandleSelectionChange(GetCurrentViewControl(), true); } private void playRandomToolStripMenuItem1_Click(object sender, EventArgs e) { CheckLastPlayRandom(); PlayRandomForm form = new PlayRandomForm(); form.Initialize(GetCurrentTabView(), AppConfiguration, m_lastPlayRandomFile, m_lastPlayRandomMap); form.StartPosition = FormStartPosition.CenterParent; if (form.ShowDialog(this) == DialogResult.OK) { if (form.GeneratedGameFile == null) { MessageBox.Show(this, "No file was generated.", "None Found", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } ITabView tabView = GetCurrentTabView(); if (form.SelectedType != PlayRandomType.CurrentTab) { tabControl.SelectedTab = tabControl.TabPages[1]; tabView = m_tabHandler.TabViews.FirstOrDefault(x => x.Key.Equals(TabKeys.LocalKey)); if (tabView == null) return; } tabView.GameFileViewControl.SelectedItem = form.GeneratedGameFile; PlayOptions playOptions = form.ShowPlayDialog ? PlayOptions.ForceDialog : PlayOptions.None; if (form.GeneratedGameFile != null) { m_lastPlayRandomFile = form.GeneratedGameFile; m_lastPlayRandomMap = form.GeneratedMap; HandlePlay(new IGameFile[] { form.GeneratedGameFile }, map: form.GeneratedMap, playOptions: playOptions); } } } private void CheckLastPlayRandom() { if (m_lastPlayRandomFile == null || !m_lastPlayRandomFile.GameFileID.HasValue) { m_lastPlayRandomFile = null; m_lastPlayRandomMap = null; return; } var options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.GameFileID, m_lastPlayRandomFile.GameFileID.Value.ToString())); var existingFile = DataSourceAdapter.GetGameFiles(options).FirstOrDefault(); if (existingFile != null) return; m_lastPlayRandomFile = null; m_lastPlayRandomMap = null; } private void UpdateTagTabData(int tagID) { UpdateTagTabData(new int[] { tagID }); } private void UpdateTagTabData(IEnumerable tagIDs) { foreach (var tagID in tagIDs) { ITabView tab = m_tabHandler.TabViews.FirstOrDefault(x => x.Key.Equals(tagID) && x is TagTabView); if (tab != null) tab.SetGameFiles(); } ITabView untaggedView = m_tabHandler.TabViews.FirstOrDefault(x => x is UntaggedTabView); if (untaggedView != null) untaggedView.SetGameFiles(); } private void showToolStripMenuItem_Click(object sender, EventArgs e) { HandleSyncStatus(); } private void ShowTextBoxForm(string title, string header, string text, bool dialog) { TextBoxForm form = new TextBoxForm(); form.StartPosition = FormStartPosition.CenterParent; form.Title = title; form.HeaderText = header; form.DisplayText = text; if (dialog) form.Show(this); else form.ShowDialog(this); } private void generateTextFileToolStripMenuItem_Click(object sender, EventArgs e) { IGameFile file = SelectedItems(GetCurrentViewControl()).FirstOrDefault(); file = DataSourceAdapter.GetGameFile(file.FileName); //populate all the date for the file if (file != null) ShowTextFileGenerator(file); } private void generateTextFileToolStripMenuItem1_Click(object sender, EventArgs e) { ShowTextFileGenerator(null); } private void ShowTextFileGenerator(IGameFile file) { TxtGenerator generator = new TxtGenerator(); generator.SetData(DataSourceAdapter, file); generator.StartPosition = FormStartPosition.CenterParent; generator.ShowDialog(this); } private void renameToolStripMenuItem_Click(object sender, EventArgs e) { HandleRename(); } private string m_typeSearch; private DateTime m_typeSearchLastPress; private void HandleKeyPress(KeyPressEventArgs e) { if (GetCurrentViewControl() != null) { if (m_typeSearch != null && DateTime.Now.Subtract(m_typeSearchLastPress).TotalMilliseconds > 700) m_typeSearch = null; if (m_typeSearch == null) m_typeSearch = char.ToLower(e.KeyChar).ToString(); else m_typeSearch += char.ToLower(e.KeyChar).ToString(); m_typeSearchLastPress = DateTime.Now; if (!SelectItem(GetCurrentViewControl(), m_typeSearch)) System.Media.SystemSounds.Beep.Play(); } } private bool SelectItem(IGameFileView ctrl, string search) { bool success = false, isIdGames = false; ITabView tabView = m_tabHandler.TabViewForControl(ctrl); if (tabView != null && tabView is IdGamesTabViewCtrl) isIdGames = true; foreach (IGameFile item in GetCurrentViewControl().DataSource) { if (isIdGames) success = item.Title.ToLower().StartsWith(search); else success = item.FileName.ToLower().StartsWith(search); if (success) { SetSelectedItem(GetCurrentViewControl(), item); break; } } return success; } private void cumulativeStatisticsToolStripMenuItem_Click(object sender, EventArgs e) { HandleCumulativeStatistics(); } private void cumulativeStatisticsToolStripMenuItem1_Click(object sender, EventArgs e) { HandleCumulativeStatistics(); } private void globalCumulativeStatisticsToolStripMenuItem_Click(object sender, EventArgs e) { HandleCumulativeStatistics(true); } private void HandleCumulativeStatistics(bool global = false) { var gameFiles = Enumerable.Empty(); string tabText; if (global) { tabText = "Global"; var localTabView = m_tabHandler.TabViews.FirstOrDefault(x => x.Key.Equals(TabKeys.LocalKey)); if (localTabView != null) gameFiles = localTabView.GameFileViewControl.DataSource; var iwadTabView = m_tabHandler.TabViews.FirstOrDefault(x => x.Key.Equals(TabKeys.IWadsKey)); if (iwadTabView != null) gameFiles = gameFiles.Union(iwadTabView.GameFileViewControl.DataSource); } else { IGameFileView view = GetCurrentViewControl(); if (view == null) return; ITabView tabView = m_tabHandler.TabViewForControl(view); tabText = tabView == null ? string.Empty : tabView.Title; gameFiles = view.DataSource; } CumulativeStats form = new CumulativeStats(); form.Title = $"Cumulative Stats - {tabText.Replace("● ", string.Empty)}"; form.SetStatistics(gameFiles, DataSourceAdapter.GetStats(gameFiles)); form.StartPosition = FormStartPosition.CenterParent; form.ShowDialog(this); } private string GetTabSearchText(string tabKey) { if (m_searchByTab.TryGetValue(tabKey, out var text)) return text; return string.Empty; } private ProgressBarForm CreateProgressBar(string text, ProgressBarStyle style, bool cancelAllowed) { ProgressBarForm form = new ProgressBarForm(); form.StartPosition = FormStartPosition.CenterParent; form.DisplayText = text; form.SetCancelAllowed(cancelAllowed); if (style == ProgressBarStyle.Marquee) { form.ProgressBarStyle = ProgressBarStyle.Marquee; } else { form.Minimum = 0; form.Maximum = 100; } return form; } private ProgressBarForm ProgressBarStart(ProgressBarType type) { m_progressBarCancelled = false; if (InvokeRequired) { return (ProgressBarForm)Invoke(new Func(ProgressBarStart), type); } else { UseWaitCursor = true; if (!m_progressBars.TryGetValue(type, out var progressBar)) return null; progressBar.StartPosition = FormStartPosition.CenterParent; progressBar.Show(this); return progressBar; } } private void ProgressBarEnd(ProgressBarType type) { if (InvokeRequired) { Invoke(new Action(ProgressBarEnd), new object[] { type }); } else { UseWaitCursor = false; if (!m_progressBars.TryGetValue(type, out var progressBar)) return; progressBar.Hide(); } } private void utilitiesToolStripMenuItem_Click(object sender, EventArgs e) { HandleEditUtilities(); } private void manageUtilitiesToolStripMenuItem_Click(object sender, EventArgs e) { HandleEditUtilities(); } private void createAutoPlayShortcutToolStripMenuItem_Click(object sender, EventArgs e) { CreateShortcut(true); } private void createShortcutToolStripMenuItem_Click(object sender, EventArgs e) { CreateShortcut(false); } private void CreateShortcut(bool autoPlay) { try { IGameFile[] gameFiles = SelectedItems(GetCurrentViewControl()); StringBuilder sbFileNames = new StringBuilder(); foreach (IGameFile gameFile in gameFiles) { string fileName = string.IsNullOrEmpty(gameFile.Title) ? gameFile.FileName : gameFile.Title; Array.ForEach(Path.GetInvalidFileNameChars(), x => fileName = fileName.Replace(x, ' ')); string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), string.Concat(fileName, ".lnk")); IWshRuntimeLibrary.WshShell wsh = new IWshRuntimeLibrary.WshShell(); IWshRuntimeLibrary.IWshShortcut shortcut = wsh.CreateShortcut(filePath) as IWshRuntimeLibrary.IWshShortcut; if (autoPlay) shortcut.Arguments = $"-{nameof(LaunchArgs.LaunchGameFileID)} {gameFile.GameFileID} -{nameof(LaunchArgs.AutoClose)}"; else shortcut.Arguments = gameFile.FileName; shortcut.TargetPath = string.Format(Path.Combine(Directory.GetCurrentDirectory(), Util.GetExecutableNoPath())); shortcut.WindowStyle = 1; shortcut.Description = string.Concat("Doom Launcher - ", gameFile.FileName); shortcut.WorkingDirectory = Directory.GetCurrentDirectory(); shortcut.IconLocation = string.Format(Path.Combine(Directory.GetCurrentDirectory(), "DoomLauncher.ico")); shortcut.Save(); sbFileNames.Append(fileName); sbFileNames.Append(", "); } sbFileNames.Length -= 2; MessageBox.Show(this, string.Format("Shortcut successfully created for: {0}", sbFileNames.ToString()), "Shortcut Success", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { Util.DisplayUnexpectedException(this, ex); } } private void helpToolStripMenuItem_Click(object sender, EventArgs e) { try { Process.Start("Help.pdf"); } catch { MessageBox.Show(this, "The help document is missing and could not be opened.", "Help Missing", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void createZipToolStripMenuItem_Click(object sender, EventArgs e) { HandleCreateZip(); } private async void HandleCreateZip() { FolderBrowserDialog folderDialog = new FolderBrowserDialog(); if (folderDialog.ShowDialog(this) == DialogResult.OK) { SaveFileDialog fileDialog = new SaveFileDialog(); fileDialog.Filter = "Zip|*.zip"; if (fileDialog.ShowDialog(this) == DialogResult.OK && !string.IsNullOrEmpty(fileDialog.FileName)) { ProgressBarStart(ProgressBarType.CreateZip); bool success = false; await Task.Run(() => success = CreateZipFromDirectory(folderDialog.SelectedPath, fileDialog.FileName)); ProgressBarEnd(ProgressBarType.CreateZip); if (!success) MessageBox.Show(this, "Failed to create zip file. File may be in use.", "Zip Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } private static bool CreateZipFromDirectory(string folderPath, string zipFileName) { try { if (File.Exists(zipFileName)) File.Delete(zipFileName); ZipFile.CreateFromDirectory(folderPath, zipFileName); } catch { return false; } return true; } private async void addFIlesRecursivelyToolStripMenuItem1_Click(object sender, EventArgs e) { FolderBrowserDialog dialog = new FolderBrowserDialog(); if (dialog.ShowDialog(this) == DialogResult.OK) { List extensions = Util.GetPkExtenstions().Union(Util.GetDehackedExtensions()).ToList(); extensions.Add(".wad"); for (int i = 0; i < extensions.Count; i++) extensions[i] = "*" + extensions[i]; IEnumerable files = new List(); foreach (string ext in extensions) files = files.Union(Directory.EnumerateFiles(dialog.SelectedPath, ext, SearchOption.AllDirectories)); await HandleAddGameFiles(AddFileType.GameFile, files.ToArray()); } } private async void loadWadsFromSteamMenuItem_Click(object sender, EventArgs e) { await AutoLoadSteamWads(); } private async void resyncToolStripMenuItem_Click(object sender, EventArgs e) => await HandleResync(true); private async void resyncIgnoreTitlepicToolStripMenuItem_Click(object sender, EventArgs e) => await HandleResync(false); private void manualUpdateToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog dialog = new OpenFileDialog(); dialog.Filter = "Zip (*.zip)|*.zip|All Files (*.*)|*.*"; if (dialog.ShowDialog(this) == DialogResult.OK) { string copyFilePath = Path.Combine(AppConfiguration.TempDirectory.GetFullPath(), UpdateControl.AppUpdateFileName); File.Copy(dialog.FileName, copyFilePath, true); ApplicationUpdater applicationUpdater = new ApplicationUpdater(dialog.FileName, AppDomain.CurrentDomain.BaseDirectory); if (!applicationUpdater.Execute()) UpdateControl.CreateUpdateFailureForm(applicationUpdater).ShowDialog(this); } } private void btnMainMenu_Click(object sender, EventArgs e) { Stylizer.StylizeControl(toolStripDropDownButton1, DesignMode); toolStripDropDownButton1.ShowDropDown(); } private AppConfiguration AppConfiguration => DataCache.Instance.AppConfiguration; private IDataSourceAdapter DataSourceAdapter => DataCache.Instance.DataSourceAdapter; private IGameFileDataSourceAdapter DirectoryDataSourceAdapter { get; set; } private IGameFileDataSourceAdapter IdGamesDataSourceAdapter { get; set; } } } ================================================ FILE: DoomLauncher/Forms/MainForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 17, 17 434, 17 iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAB/SURBVEhL7ZS5CgAhDET9cPForDw+OctgwEIrNbuL+GAI BB3QHOryH1JKpLUmay0ZY6aEu/CAF9tWOLFVMUbESs65O7CqUgpiA4kQwhZ15meAQjvnyHu/JHi80kXc OBWJLhq+QuyLzgB1GA3NjLpBE18VvJi2athFYuv68iFKPYzcxuepFjI+AAAAAElFTkSuQmCC 116, 17 213, 17 337, 17 45 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/MainForm_Config.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class MainForm { private void HandleFormClosing() { if (DataSourceAdapter == null || !m_writeConfigOnClose) return; IEnumerable config = DataSourceAdapter.GetConfiguration(); if (WindowState != FormWindowState.Minimized) { // Too many problems when the form is minimized, not supported if (splitTopBottom.SplitterDistance > 0) DataCache.Instance.UpdateConfig(config, AppConfiguration.SplitTopBottomName, GetSplitterPercent(splitTopBottom, Height).ToString(AppConfiguration.Culture)); if (splitLeftRight.SplitterDistance > 0) DataCache.Instance.UpdateConfig(config, AppConfiguration.SplitLeftRightName, GetSplitterPercent(splitLeftRight, Width).ToString(AppConfiguration.Culture)); if (splitTagSelect.SplitterDistance > 0) DataCache.Instance.UpdateConfig(config, AppConfiguration.SplitTagSelectName, GetSplitterPercent(splitTagSelect, Width).ToString(AppConfiguration.Culture)); DataCache.Instance.UpdateConfig(config, AppConfiguration.AppWidthName, Size.Width.ToString()); DataCache.Instance.UpdateConfig(config, AppConfiguration.AppHeightName, Size.Height.ToString()); DataCache.Instance.UpdateConfig(config, AppConfiguration.AppXName, Location.X.ToString()); DataCache.Instance.UpdateConfig(config, AppConfiguration.AppYName, Location.Y.ToString()); DataCache.Instance.UpdateConfig(config, AppConfiguration.WindowStateName, WindowState.ToString()); } DataCache.Instance.UpdateConfig(config, AppConfiguration.ColumnConfigName, BuildColumnConfig()); DataCache.Instance.UpdateConfig(config, AppConfiguration.ItemsPerPageName, AppConfiguration.ItemsPerPage.ToString()); DataCache.Instance.UpdateConfig(config, AppConfiguration.LastSelectedTabIndexName, tabControl.SelectedIndex.ToString()); DataCache.Instance.UpdateConfig(config, AppConfiguration.TagSelectPinnedName, m_tagSelectControl.Pinned.ToString()); } private static double GetSplitterPercent(SplitContainer splitter, int windowDimension) { if (windowDimension <= 0) return 0.1; return splitter.SplitterDistance / (double)windowDimension; } private void UpdateColumnConfig() { IEnumerable config = DataSourceAdapter.GetConfiguration(); DataCache.Instance.UpdateConfig(config, AppConfiguration.ColumnConfigName, BuildColumnConfig()); } private string BuildColumnConfig() { List columnViewConfig = new List(); List tileViewConfig = new List(); HashSet tileViewKeys = new HashSet(); foreach (ITabView tab in m_tabHandler.TabViews) { if (tab.GameFileViewControl is IGameFileColumnView) { columnViewConfig.AddRange(tab.GetColumnConfig()); } else { tileViewConfig.AddRange(tab.GetColumnConfig()); tileViewKeys.Add(tab.Key.ToString()); } } // Only select columns from views that are tile views List config = DataCache.Instance.GetColumnConfig().Where(x => tileViewKeys.Contains(x.Parent)).ToList(); // Tile views use IGameFileSortableView which only stores the single column that is sorted, so clear all sorting on all columns config.ForEach(x => x.Sort = SortOrder.None); foreach (ColumnConfig viewColumn in tileViewConfig) { ColumnConfig existingColumn = config.FirstOrDefault(x => x.Parent == viewColumn.Parent && x.Column == viewColumn.Column); if (existingColumn != null) existingColumn.Sort = viewColumn.Sort; else config.Add(viewColumn); } config.AddRange(columnViewConfig); return SerializeColumnConfig(config); } private string SerializeColumnConfig(List config) { try { return DataCache.SerializeColumnConfig(config); } catch (Exception ex) { Util.DisplayUnexpectedException(this, ex); } return string.Empty; } } } ================================================ FILE: DoomLauncher/Forms/MainForm_Init.cs ================================================ using DoomLauncher.Controls; using DoomLauncher.DataSources; using DoomLauncher.Forms; using DoomLauncher.Interfaces; using DoomLauncher.GameStores; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using System.Windows.Forms; namespace DoomLauncher { public partial class MainForm { private void SetupTabBase(ITabView tabView, ColumnField[] columnTextFields, ColumnConfig[] colConfig, ContextMenuStrip menu, bool dragDrop) { tabView.SetColumnConfig(columnTextFields, colConfig); tabView.GameFileViewControl.SetContextMenuStrip(menu); tabView.GameFileViewControl.AllowDrop = dragDrop; tabView.DataSourceChanging += TabView_DataSourceChanging; tabView.DataSourceChanged += TabView_DataSourceChanged; SetGameFileViewEvents(tabView.GameFileViewControl, dragDrop); } private void SetupTabs() { SetShowTabHeaders(); List tabViews = new List(); ColumnConfig[] colConfig = DataCache.Instance.GetColumnConfig(); GameFileViewFactory = new GameFileViewFactory(this, AppConfiguration.GameFileViewType); GameFileTileManager.Instance.Init(GameFileViewFactory); if (AppConfiguration.VisibleViews.Contains(TabKeys.RecentKey)) tabViews.Add(CreateTabViewRecent(colConfig)); // User can't remove local tabViews.Add(CreateTabViewLocal(colConfig)); if (AppConfiguration.VisibleViews.Contains(TabKeys.UntaggedKey)) tabViews.Add(CreateTabViewUntagged(colConfig)); if (AppConfiguration.VisibleViews.Contains(TabKeys.IWadsKey)) tabViews.Add(CreateTabViewIwad(colConfig)); if (AppConfiguration.VisibleViews.Contains(TabKeys.IdGamesKey)) tabViews.Add(CreateTabViewIdGames(colConfig)); tabViews.AddRange(CreateTagTabs(GameFileViewFactory.DefaultColumnTextFields, colConfig)); m_tabHandler = new TabHandler(tabControl); m_tabHandler.SetTabs(tabViews); } private void SetShowTabHeaders() { tabControl.SetShowHeaders(AppConfiguration.ShowTabHeaders); } private IdGamesTabViewCtrl CreateTabViewIdGames(ColumnConfig[] colConfig) { ColumnField[] columnTextFields = new ColumnField[] { new ColumnField("Title", "Title"), new ColumnField("Author", "Author"), new ColumnField("ReleaseDate", "Release Date"), new ColumnField("Description", "Description"), new ColumnField("Rating", "Rating"), }; IdGamesDataSourceAdapter = new IdGamesDataAdapater(AppConfiguration.IdGamesUrl, AppConfiguration.ApiPage, AppConfiguration.MirrorUrl); var factory = new GameFileViewFactory(this, GameFileViewType.GridView); IdGamesTabViewCtrl tabViewIdGames = new IdGamesTabViewCtrl(TabKeys.IdGamesKey, StaticTagData.GetFavoriteName(TabKeys.IdGamesKey), IdGamesDataSourceAdapter, DefaultGameFileSelectFields, factory); SetupTabBase(tabViewIdGames, columnTextFields, colConfig, mnuIdGames, false); return tabViewIdGames; } private IWadTabViewCtrl CreateTabViewIwad(ColumnConfig[] colConfig) { ColumnField[] columnTextFields = new ColumnField[] { new ColumnField("FileNameNoPath", "File"), new ColumnField("LastDirectory", "Directory"), new ColumnField("Title", "Title"), new ColumnField("LastPlayed", "Last Played"), }; IWadTabViewCtrl tabViewIwads = new IWadTabViewCtrl(TabKeys.IWadsKey, StaticTagData.GetFavoriteName(TabKeys.IWadsKey), DataSourceAdapter, DefaultGameFileSelectFields, DataCache.Instance.TagMapLookup, GameFileViewFactory); SetupTabBase(tabViewIwads, columnTextFields, colConfig, mnuLocal, true); return tabViewIwads; } private LocalTabViewCtrl CreateTabViewUntagged(ColumnConfig[] colConfig) { LocalTabViewCtrl tabViewUntagged = new UntaggedTabView(TabKeys.UntaggedKey, StaticTagData.GetFavoriteName(TabKeys.UntaggedKey), DataSourceAdapter, DefaultGameFileSelectFields, DataCache.Instance.TagMapLookup, GameFileViewFactory); SetupTabBase(tabViewUntagged, GameFileViewFactory.DefaultColumnTextFields, colConfig, mnuLocal, true); return tabViewUntagged; } private LocalTabViewCtrl CreateTabViewLocal(ColumnConfig[] colConfig) { LocalTabViewCtrl tabViewLocal = new LocalTabViewCtrl(TabKeys.LocalKey, StaticTagData.GetFavoriteName(TabKeys.LocalKey), DataSourceAdapter, DefaultGameFileSelectFields, DataCache.Instance.TagMapLookup, GameFileViewFactory); SetupTabBase(tabViewLocal, GameFileViewFactory.DefaultColumnTextFields, colConfig, mnuLocal, true); return tabViewLocal; } private OptionsTabViewCtrl CreateTabViewRecent(ColumnConfig[] colConfig) { OptionsTabViewCtrl tabViewRecent = new OptionsTabViewCtrl(TabKeys.RecentKey, StaticTagData.GetFavoriteName(TabKeys.RecentKey), DataSourceAdapter, DefaultGameFileSelectFields, DataCache.Instance.TagMapLookup, GameFileViewFactory); SetupTabBase(tabViewRecent, GameFileViewFactory.DefaultColumnTextFields, colConfig, mnuLocal, true); tabViewRecent.Options = new GameFileGetOptions(); tabViewRecent.Options.Limit = 10; tabViewRecent.Options.OrderBy = OrderType.Desc; tabViewRecent.Options.OrderField = GameFileFieldType.Downloaded; return tabViewRecent; } private List CreateTagTabs(ColumnField[] columnTextFields, ColumnConfig[] colConfig) { List ret = new List(); DataCache.Instance.UpdateTags(); IEnumerable tags = DataCache.Instance.Tags.Where(x => x.HasTab); foreach (ITagData tag in tags) ret.Add(CreateTagTab(columnTextFields, colConfig, tag.FavoriteName, tag, false)); return ret; } private TagTabView CreateTagTab(ColumnField[] columnTextFields, ColumnConfig[] colConfig, string name, ITagData tag, bool isNew) { //use the local tab configuration for new tabs if (isNew) { colConfig = colConfig.Where(x => x.Parent == "Local").ToArray(); Array.ForEach(colConfig, x => x.Parent = tag.Name); } TagTabView tabView = new TagTabView(tag.TagID, name, DataSourceAdapter, DefaultGameFileSelectFields, tag, GameFileViewFactory); SetupTabBase(tabView, columnTextFields, colConfig, mnuLocal, true); if (tabView.GameFileViewControl is IGameFileColumnView columnView) { columnView.SetColumnFormat("ReleaseDate", CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); columnView.SetColumnFormat("Downloaded", CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); columnView.SetColumnFormat("LastPlayed", CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); } return tabView; } private void RebuildUtilityToolStrip() { var utilities = DataSourceAdapter.GetUtilities(); ToolStripMenuItem utilityToolStrip = mnuLocal.Items.Cast().FirstOrDefault(x => x.Text == "Utility") as ToolStripMenuItem; while (utilityToolStrip.DropDownItems.Count > 2) utilityToolStrip.DropDownItems.RemoveAt(utilityToolStrip.DropDownItems.Count - 1); foreach (var utility in utilities) utilityToolStrip.DropDownItems.Add(utility.Name, null, utilityToolStripItem_Click); } private void RebuildTagToolStrip() { IGameFileView currentControl = GetCurrentViewControl(); if (currentControl != null) { DataCache.Instance.UpdateTags(); var selectedGameFiles = SelectedItems(currentControl); var selectedGameFileTags = selectedGameFiles.SelectMany(DataCache.Instance.TagMapLookup.GetTags); ToolStripMenuItem tagToolStrip = mnuLocal.Items.Cast().FirstOrDefault(x => x.Text == "Tag") as ToolStripMenuItem; if (tagToolStrip != null) { BuildTagToolStrip(tagToolStrip, selectedGameFileTags, tagToolStripItem_Click); } } } private void BuildTagToolStrip(ToolStripMenuItem tagToolStrip, IEnumerable myTags, EventHandler handler) { while (tagToolStrip.DropDownItems.Count > 2) tagToolStrip.DropDownItems.RemoveAt(tagToolStrip.DropDownItems.Count - 1); var allTags = DataCache.Instance.Tags; foreach (ITagData tag in allTags) { var tagItem = new ToolStripMenuItem(); tagItem.Text = tag.FavoriteName; tagItem.CheckOnClick = true; tagItem.Checked = myTags.Any(t => t.TagID == tag.TagID); tagItem.Click += handler; tagItem.Tag = tag.TagID; tagToolStrip.DropDownItems.Add(tagItem); } } private void SetGameFileViewEvents(IGameFileView ctrl, bool dragDrop) { ctrl.ItemDoubleClick += ctrlView_RowDoubleClicked; ctrl.SelectionChange += ctrlView_SelectionChange; ctrl.ViewKeyPress += ctrlView_GridKeyPress; if (dragDrop) { ctrl.DragDrop += ctrlView_DragDrop; ctrl.DragEnter += ctrlView_DragEnter; ctrl.ViewKeyDown += ctrlView_GridKeyDown; } } private void ctrlView_GridKeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Delete) HandleDelete(); } private async Task Initialize() { string dataSource = Path.Combine(LauncherPath.GetDataDirectory(), DbDataSourceAdapter.DatabaseFileName); DataAccess access = new DataAccess(new SqliteDatabaseAdapter(), DbDataSourceAdapter.CreateConnectionString(dataSource)); m_versionHandler = new VersionHandler(access, DbDataSourceAdapter.CreateAdapter(true), AppConfiguration); if (m_versionHandler.UpdateRequired()) { m_versionHandler.UpdateProgress += handler_UpdateProgress; ProgressBarStart(ProgressBarType.Update); await Task.Run(() => ExecuteVersionUpdate()); ProgressBarEnd(ProgressBarType.Update); AppConfiguration.Refresh(); //We have to refresh here because a column may have been added to the Configuration table } if (AppConfiguration.CleanTemp) CleanTempDirectory(); DirectoryDataSourceAdapter = new DirectoryDataSourceAdapter(AppConfiguration.GameFileDirectory); DataCache.Instance.AppConfiguration.GameFileViewTypeChanged += AppConfiguration_GameFileViewTypeChanged; DataCache.Instance.AppConfiguration.VisibleViewsChanged += AppConfiguration_VisibleViewsChanged; DataCache.Instance.AppConfiguration.ColorThemeChanged += AppConfiguration_ColorThemeChanged; DataCache.Instance.TagMapLookup.TagMappingChanged += TagMapLookup_TagMappingChanged; DataCache.Instance.TagsChanged += DataCache_TagsChanged; CleanUpFiles(); SetupTabs(); RebuildUtilityToolStrip(); BuildUtilityToolStrip(); InitTagSelectControl(); InitDownloadView(); ctrlAssociationView.Initialize(DataSourceAdapter, AppConfiguration); ctrlAssociationView.FileAdded += ctrlAssociationView_FileAdded; ctrlAssociationView.FileDeleted += ctrlAssociationView_FileDeleted; ctrlAssociationView.FileOrderChanged += ctrlAssociationView_FileOrderChanged; ctrlAssociationView.RequestScreenshots += CtrlAssociationView_RequestScreenshots; ctrlAssociationView.FileDetailsChanged += CtrlAssociationView_FileDetailsChanged; } private async Task AutoLoadSteamWads() { var gameStoreCheck = new AutomaticGameStoreCheck(StoreGameLoader.LoadAllStoreGamesFromRegistry, DbDataSourceAdapter.CreateAdapter()); await gameStoreCheck.LoadGamesFromGameStores( async iwads => await HandleAddGameFiles(AddFileType.IWad, iwads.ToArray(), null, FileManagement.Managed), async pwads => await HandleAddGameFiles(AddFileType.GameFile, pwads.ToArray(), null, FileManagement.Managed), HandleAddDoom64); } private async Task HandleAddDoom64(string doom64Exe) { await Task.Run(() => { var existingDoom64 = DataSourceAdapter.GetDoom64().FirstOrDefault(); if (existingDoom64 == null) { var extensions = new List { ".wad" }; extensions.AddRange(Util.GetExtraDoom64Extensions()); var extensionString = string.Join(",", extensions); SourcePortData newDoom64 = new SourcePortData() { Executable = Path.GetFileName(doom64Exe), Name = "Doom 64", LaunchType = SourcePortLaunchType.Doom64, SupportedExtensions = extensionString, Directory = new LauncherPath(Path.GetDirectoryName(doom64Exe)), FileOption = "-file" }; DataSourceAdapter.InsertSourcePort(newDoom64); } }); } private void CleanUpFiles() { var cleanupFiles = DataSourceAdapter.GetCleanupFiles(); foreach (var cleanupFile in cleanupFiles) { try { File.Delete(cleanupFile.FileName); DataSourceAdapter.DeleteCleanupFile(cleanupFile); } catch { // Try again next time... } } } private void InitDownloadView() { m_downloadView = new DownloadView(); m_downloadView.UserPlay += DownloadView_UserPlay; m_downloadHandler = new DownloadHandler(AppConfiguration.TempDirectory, m_downloadView); } private void InitTagSelectControl() { m_tagSelectControl.BorderStyle = BorderStyle.FixedSingle; m_tagSelectControl.TagSelectionChanged += TagSelectCtrl_TagSelectionChanged; m_tagSelectControl.StaticSelectionChanged += TagSelectCtrl_StaticSelectionChanged; m_tagSelectControl.PinChanged += TagSelectControl_PinChanged; m_tagSelectControl.ManageTags += TagSelectControl_ManageTags; m_tagSelectControl.Init(new TagSelectOptions() { HasTabOnly = true, ShowStatic = true, AllowRowSelect = true, ShowPin = true, ShowMenu = true }); m_tagSelectControl.SetPinned(AppConfiguration.TagSelectPinned); if (AppConfiguration.TagSelectPinned) SetTagControlPinned(); } private void TagSelectControl_ManageTags(object sender, EventArgs e) { HandleManageTags(); } private void TagSelectControl_PinChanged(object sender, EventArgs e) { if (m_tagPopup != null) { m_tagPopup.Hide(); // For some reason the Popup messes with Min/Max Size... m_tagSelectControl.MaximumSize = new Size(0, 0); m_tagSelectControl.MinimumSize = new Size(0, 0); } if (m_tagSelectControl.Pinned) { SetTagControlPinned(); } else { splitTagSelect.Panel1Collapsed = true; splitTagSelect.Panel1.Controls.Remove(m_tagSelectControl); DisplayTags(); } } private void SetTagControlPinned() { splitTagSelect.Panel1Collapsed = false; splitTagSelect.Panel1.Controls.Add(m_tagSelectControl); m_tagSelectControl.Dock = DockStyle.Fill; } private void TagMapLookup_TagMappingChanged(object sender, ITagData[] tags) { if (InvokeRequired) Invoke(new Action(HandleTagMappingChanged), new object[] { tags }); else HandleTagMappingChanged(tags); } private void HandleTagMappingChanged(ITagData[] tags) { Array.ForEach(tags, x => UpdateTagTabData(x.TagID)); if (tags.Any(x => x.ExcludeFromOtherTabs)) UpdateLocal(); } private void DataCache_TagsChanged(object sender, EventArgs e) { if (m_tabHandler == null) return; foreach (var tabView in m_tabHandler.TabViews) { if (tabView is TagTabView tagTabView) { ITagData findTag = DataCache.Instance.Tags.FirstOrDefault(x => x.TagID == tagTabView.TagDataSource.TagID); if (findTag != null) tagTabView.TagDataSource = findTag; } } } private void TagSelectCtrl_StaticSelectionChanged(object sender, string name) { m_tabHandler.SelectTabFromKey(name); } private void TagSelectCtrl_TagSelectionChanged(object sender, ITagData tag) { ITabView tagView = m_tabHandler.TabViewForTag(tag); if (tagView != null) m_tabHandler.SelectTabView(tagView); } private void AppConfiguration_GameFileViewTypeChanged(object sender, EventArgs e) { if (GameFileViewFactory.IsBaseViewTypeChange(GameFileViewFactory.DefaultType, AppConfiguration.GameFileViewType)) { Restart(); return; } GameFileViewFactory.UpdateDefaultType(AppConfiguration.GameFileViewType); GameFileTileManager.Instance.Init(GameFileViewFactory); } private void AppConfiguration_VisibleViewsChanged(object sender, EventArgs e) { Restart(); } private void AppConfiguration_ColorThemeChanged(object sender, EventArgs e) { Restart(); } private void Restart(bool writeConfigChanges = true) { // Write any settings the user may have changed before the application is killed m_writeConfigOnClose = writeConfigChanges; if (writeConfigChanges) HandleFormClosing(); Process.Start(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Util.GetExecutableNoPath())); } private void BuildUtilityToolStrip() { ToolStripMenuItem sortToolStrip = GetSortByToolStrip(); foreach (var col in GameFileViewFactory.DefaultColumnTextFields) sortToolStrip.DropDownItems.Add(col.Title, null, sortToolStripItem_Click); } private ToolStripMenuItem GetSortByToolStrip() { return mnuLocal.Items.Cast().FirstOrDefault(x => x.Text == "Sort By") as ToolStripMenuItem; } private async Task CheckForAppUpdate() { try { ApplicationUpdate applicationUpdate = new ApplicationUpdate(TimeSpan.FromSeconds(30)); ApplicationUpdateInfo info = await applicationUpdate.GetUpdateApplicationInfo(Assembly.GetExecutingAssembly().GetName().Version); if (info != null) SetUpdateAvailable(info); else ApplicationUpdater.CleanupUpdateFiles(AppDomain.CurrentDomain.BaseDirectory); } catch { // no internet connection or bad connection, try again next time } } private void SetUpdateAvailable(ApplicationUpdateInfo info) { if (InvokeRequired) { Invoke(new Action(SetUpdateAvailable), new object[] { info }); } else { btnUpdate.Visible = true; btnUpdate.GlowOnce(); m_updateControl.Initialize(AppConfiguration, info); } } private async Task CheckFirstInit() { if (!DataSourceAdapter.GetSourcePorts().Any()) //If no source ports setup then it's the first time setup, display welcome/setup info { titleBar.SetNormal(); InvokeHideSplashScreen(); DisplayWelcome(); HandleEditSourcePorts(true); } if (!DataSourceAdapter.GetIWads().Any()) //If no iwads then get them from Steam { await AutoLoadSteamWads(); } if (!DataSourceAdapter.GetIWads().Any()) //If still no iwads then prompt to add iwads { InvokeHideSplashScreen(); await HandleAddIWads(); Invoke((MethodInvoker)delegate { tabControl.SelectedIndex = 3; }); //the user has only added iwads on setup, so set the tab to iwads on first launch so there is something to see DisplayInitSettings(); //give user the change set default port, iwad, skill } else { Invoke((MethodInvoker)delegate { tabControl.SelectedIndex = AppConfiguration.LastSelectedTabIndex; }); } } private void DisplayInitSettings() { SettingsForm settings = new SettingsForm(DataSourceAdapter, AppConfiguration); settings.SetToLaunchSettingsTab(); settings.StartPosition = FormStartPosition.CenterParent; settings.ShowDialog(); AppConfiguration.Refresh(); } private void DisplayWelcome() { Welcome welcome = new Welcome(); welcome.StartPosition = FormStartPosition.CenterParent; welcome.ShowDialog(); } private async void MainForm_Shown(object sender, EventArgs e) { if (m_launchFile != null) { string addFile = m_launchFile; IGameFile launchFile = DataSourceAdapter.GetGameFile(m_launchFile); m_launchFile = null; if (launchFile == null && File.Exists(addFile)) await HandleAddGameFiles(AddFileType.GameFile, new string[] { addFile }); else HandlePlay(new IGameFile[] { launchFile }); return; } if (m_launchArgs.LaunchGameFileID != null) { GameFileGetOptions options = new GameFileGetOptions(); options.SearchField = new GameFileSearchField(GameFileFieldType.GameFileID, m_launchArgs.LaunchGameFileID.ToString()); var gameFile = DataSourceAdapter.GetGameFiles(options).FirstOrDefault(); if (gameFile == null) { MessageBox.Show(this, $"Failed to find game file by id: {m_launchArgs.LaunchGameFileID}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } HandlePlay(new IGameFile[] { gameFile }, playOptions: PlayOptions.AutoPlay); } } private static bool ValidatePosition(AppConfiguration config) { if (config.WindowState == FormWindowState.Maximized) { //Maximized goes outside the bounds to hide the border, bring the rectangle in a more than safe amount to check if the monitor is still there //Windows 7 can be -4, later version are -6, could change based on DPI int offs = 32; Rectangle formRectangle = new Rectangle(config.AppX + offs, config.AppY + offs, config.AppWidth - offs * 2, config.AppHeight - offs * 2); return Screen.AllScreens.Any(x => x.WorkingArea.Contains(formRectangle)); } else if (config.WindowState != FormWindowState.Minimized) { Point formPt = new Point(config.AppX, config.AppY); return Screen.AllScreens.Any(x => x.WorkingArea.Contains(formPt)); } return true; } private void ExecuteVersionUpdate() { var results = m_versionHandler.HandleVersionUpdate(); if (results.RestartRequired) { Invoke(new Action(() => { MessageBox.Show(this, "Update was successful. Doom Launcher requires a restart for this update.", "Restart Required", MessageBoxButtons.OK, MessageBoxIcon.Information); })); Restart(false); Invoke(new Action(() => { Close(); })); } } void handler_UpdateProgress(object sender, EventArgs e) { if (InvokeRequired) Invoke(new Action(UpdateVersionProgress)); else UpdateVersionProgress(); } void UpdateVersionProgress() { if (m_progressBars.TryGetValue(ProgressBarType.Update, out var progressBar)) progressBar.Value = m_versionHandler.ProgressPercent; } private void SetupSearchFilters() { ctrlSearch.SearchTextChanged += CtrlSearch_SearchTextChanged; ctrlSearch.SearchTextChangedNoDebounce += CtrlSearch_SearchTextChangedNoDebounce; Util.SetDefaultSearchFields(ctrlSearch); } private void CtrlSearch_SearchTextChangedNoDebounce(object sender, EventArgs e) { var ctrl = GetCurrentViewControl(); if (ctrl == null) return; ITabView tabView = m_tabHandler.TabViewForControl(ctrl); if (tabView == null) return; m_searchByTab[tabView.Key.ToString()] = ctrlSearch.SearchText; } void CtrlSearch_SearchTextChanged(object sender, EventArgs e) { if (GetCurrentTabView() != null) HandleSearch(); } private GameFileFieldType[] DefaultGameFileSelectFields { get { return new GameFileFieldType[] { GameFileFieldType.GameFileID, GameFileFieldType.Filename, GameFileFieldType.Author, GameFileFieldType.Title, GameFileFieldType.Description, GameFileFieldType.Downloaded, GameFileFieldType.LastPlayed, GameFileFieldType.ReleaseDate, GameFileFieldType.Comments, GameFileFieldType.Rating, GameFileFieldType.MapCount, GameFileFieldType.MinutesPlayed, GameFileFieldType.IWadID }; } } private GameFileViewFactory GameFileViewFactory { get; set; } } } ================================================ FILE: DoomLauncher/Forms/MainForm_Meta.cs ================================================ using DoomLauncher.Forms; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; namespace DoomLauncher { public partial class MainForm { private bool m_cancelMetaUpdate; private async void updateMetadataToolStripMenuItem_Click(object sender, EventArgs e) { IdGamesDataAdapater adapter = new IdGamesDataAdapater(AppConfiguration.IdGamesUrl, AppConfiguration.ApiPage, AppConfiguration.MirrorUrl); IGameFile[] localFiles = SelectedItems(GetCurrentViewControl()); if (localFiles.Length == 0) return; bool showForm = true, showError = true, updateView = false; m_cancelMetaUpdate = false; DialogResult result = DialogResult.Cancel; MetaDataForm form = CreateMetaForm(); ProgressBarForm progress = InitMetaProgressBar(); List iwadWarn = new List(); HashSet iwads = new HashSet(DataSourceAdapter.GetGameFileIWads().Select(x => x.GameFileID.Value)); foreach (IGameFile localFile in localFiles) { if (iwads.Contains(localFile.GameFileID.Value)) { IWadInfo info = IWadInfo.GetIWadInfo(localFile.FileNameNoPath); if (info != null && !info.HasMetadata) { iwadWarn.Add(localFile.FileNameNoPath); continue; } } try { Enabled = false; progress.DisplayText = string.Format("Searching for {0}...", localFile.FileNameNoPath); progress.Show(this); IEnumerable remoteFiles = await Task.Run(() => GetMetaFiles(adapter, localFile.FileNameNoPath)); Enabled = true; progress.Hide(); if (remoteFiles == null || m_cancelMetaUpdate) break; if (!remoteFiles.Any()) { if (showError) showError = HandleMetaError(localFile); } else { IGameFile remoteFile = HandleMultipleMetaFilesFound(localFile, remoteFiles); if (remoteFile != null) { form.GameFileEdit.SetDataSource(remoteFile, new ITagData[] { }); if (showForm) //OK = Accept current file, Yes = Accept All files result = form.ShowDialog(this); if (result != DialogResult.Cancel) { List fields = form.GameFileEdit.UpdateDataSource(localFile); showForm = (result == DialogResult.OK); if (fields.Count > 0) updateView = HandleUpdateMetaFields(localFile, fields); } } } } catch { Enabled = true; progress.Hide(); MessageBox.Show(this, "Failed to fetch metadata from the id games mirror.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); break; //not expected, break from loop } } if (updateView) HandleSelectionChange(GetCurrentViewControl(), true); if (iwadWarn.Count > 0) { MessageBox.Show(this, "The following are IWADs and will not exist in idgames: " + string.Join(", ", iwadWarn), "IWAD Files", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } private IEnumerable GetMetaFiles(IdGamesDataAdapater adapter, string metaFileName) { return adapter.GetGameFilesByName(metaFileName); } private ProgressBarForm InitMetaProgressBar() { ProgressBarForm progress = new ProgressBarForm { Title = "Fetching data...", Minimum = 0, Maximum = 0 }; progress.SetCancelAllowed(false); return progress; } private IGameFile HandleMultipleMetaFilesFound(IGameFile localFile, IEnumerable remoteFiles) { if (remoteFiles.Count() == 1) return remoteFiles.First(); FillFileSize(localFile); IEnumerable check = remoteFiles.Where(x => x.FileSizeBytes == localFile.FileSizeBytes); if (check.Count() == 1) return check.First(); FileSelectForm form = new FileSelectForm(); form.Initialize(DataSourceAdapter, m_tabHandler.TabViews.First(x => x.Key.Equals(TabKeys.IdGamesKey)), remoteFiles); form.ShowSearchControl(false); string display = localFile.FileName; if (!string.IsNullOrEmpty(localFile.Title)) display = string.Format("{0}({1})", localFile.Title, localFile.FileNameNoPath); form.SetDisplayText(string.Format("Multiple files found for {0}. Please select intended file.", display)); form.MultiSelect = false; form.StartPosition = FormStartPosition.CenterParent; if (form.ShowDialog() != DialogResult.Cancel) { IGameFile[] selectedFiles = form.SelectedFiles; if (selectedFiles.Length > 0) return selectedFiles.First(); } return null; } private void FillFileSize(IGameFile localFile) { FileInfo fi = new FileInfo(Path.Combine(AppConfiguration.GameFileDirectory.GetFullPath(), localFile.FileName)); if (fi.Exists) localFile.FileSizeBytes = Convert.ToInt32(fi.Length); } private MetaDataForm CreateMetaForm() { MetaDataForm form = new MetaDataForm(); form.StartPosition = FormStartPosition.CenterParent; form.GameFileEdit.SetCheckBoxesChecked(true); form.GameFileEdit.CommentsChecked = false; form.GameFileEdit.MapsChecked = false; return form; } private bool HandleUpdateMetaFields(IGameFile localFile, List fields) { DataSourceAdapter.UpdateGameFile(localFile, fields.ToArray()); UpdateDataSourceViews(localFile); return true; } private bool HandleMetaError(IGameFile localFile) { MessageCheckBox errorForm = new MessageCheckBox("Meta", string.Format("Failed to find {0} from the id games mirror.\n\nIf you are sure this file should exist try changing your mirror in the Settings menu.", localFile.FileNameNoPath), "Don't show this error again", SystemIcons.Error); errorForm.StartPosition = FormStartPosition.CenterParent; errorForm.ShowDialog(this); return !errorForm.Checked; } } } ================================================ FILE: DoomLauncher/Forms/MainForm_Play.cs ================================================ using DoomLauncher.Adapters; using DoomLauncher.Adapters.Launch; using DoomLauncher.DataSources; using DoomLauncher.Forms; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using DoomLauncher.Stylize; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using static DoomLauncher.GameLauncher; namespace DoomLauncher { public partial class MainForm { private readonly List m_activeSessions = new List(); private FilterForm m_filterForm; private void HandlePlay(PlayOptions playOptions = PlayOptions.None) { if (GetCurrentViewControl() != null) HandlePlay(SelectedItems(GetCurrentViewControl()), playOptions: playOptions); } private bool AssertFile(string file) { bool exists; if (Util.IsDirectory(file)) exists = Directory.Exists(file); else exists = File.Exists(file); if (!exists) MessageBox.Show(this, string.Format("The file {0} does not exist.", file), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return exists; } [Flags] private enum PlayOptions { None, AutoPlay, ForceDialog } private void HandlePlay(IEnumerable gameFiles, ISourcePortData sourcePort = null, string map = null, PlayOptions playOptions = PlayOptions.None) { LaunchData launchData = GetLaunchFiles(gameFiles, checkActiveSessions: true); if (!launchData.Success) { if (!string.IsNullOrEmpty(launchData.ErrorTitle)) MessageBox.Show(this, launchData.ErrorDescription, launchData.ErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (launchData.GameFile == null) { var iwad = DataSourceAdapter.GetIWad((int)AppConfiguration.GetTypedConfigValue(ConfigType.DefaultIWad, typeof(int))); if (iwad != null) { GameFileGetOptions options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.GameFileID, iwad.GameFileID.Value.ToString())); launchData.GameFile = DataSourceAdapter.GetGameFiles(options).FirstOrDefault(); } } if (launchData.GameFile == null) return; SetupPlayForm(launchData.GameFile); if (sourcePort != null) m_currentPlayForm.SelectedSourcePort = sourcePort; if (map != null) m_currentPlayForm.SelectedMap = map; bool autoPlay = (playOptions.HasFlag(PlayOptions.AutoPlay) || !AppConfiguration.ShowPlayDialog) && !playOptions.HasFlag(PlayOptions.ForceDialog); if (autoPlay || m_currentPlayForm.ShowDialog(this) == DialogResult.OK) { try { HandlePlaySettings(m_currentPlayForm, m_currentPlayForm.SelectedGameProfile); if (m_currentPlayForm.SelectedSourcePort == null) return; if (!StartPlay(launchData.GameFile, launchData.AdditionalGameFiles, m_currentPlayForm.SelectedSourcePort, m_currentPlayForm.ScreenFilter)) return; ctrlSummary.PauseSlideshow(); ShouldShowToolTip = false; } catch (IOException) { MessageBox.Show(this, "The file is in use and cannot be launched. Please close any programs that may be using the file and try again.", "File In Use", MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { HandleSelectionChange(GetCurrentViewControl(), true); } } private LaunchData GetLaunchFiles(IEnumerable gameFiles, bool checkActiveSessions) { IGameFile gameFile = null; if (gameFiles != null) { if (gameFiles.Count() > 1) { gameFile = PromptUserMainFile(gameFiles, out bool accepted); //ask user which file to tie all stats to if (!accepted) return new LaunchData(string.Empty, string.Empty); } else { gameFile = gameFiles.FirstOrDefault(); } } if (checkActiveSessions && m_activeSessions.Any() && !AppConfiguration.AllowMultiplePlaySessions) return new LaunchData("Already Playing", "There is already a game in progress. Please exit that game first.\n\nCheck the 'Allow Multiple Play Sessions' in the settings to enable this feature."); if (!DataSourceAdapter.GetSourcePorts().Any()) return new LaunchData("No Source Ports", "You must have at least one source port configured to play! Click the settings menu on the top left and select Source Ports to configure."); if (!DataSourceAdapter.GetIWads().Any()) return new LaunchData("No IWADs", "You must have at least one IWAD configured to play! Click the settings menu on the top left and select IWads to configure."); if (gameFile != null && GetCurrentViewControl() != null) { ITabView tabView = m_tabHandler.TabViewForControl(GetCurrentViewControl()); if (tabView != null) gameFile = DataSourceAdapter.GetGameFile(gameFile.FileName); //this file came from the grid, which does not have all info populated to save performance if (gameFiles.Count() > 1) //for when the user selected more than one file { HandleMultiSelectPlay(gameFile, gameFiles.Except(new IGameFile[] { gameFile })); //sets SettingsFiles with all the other game files List gameFilesList = new List() { gameFile }; Array.ForEach(gameFiles.Skip(1).ToArray(), x => gameFilesList.Add(x)); gameFiles = gameFilesList; } } return new LaunchData(gameFile, (GameFile)gameFile, gameFiles); } private IGameFile PromptUserMainFile(IEnumerable gameFiles, out bool accepted) { accepted = false; FileSelectForm form = new FileSelectForm(); ITabView tabView = m_tabHandler.TabViews.FirstOrDefault(x => x.Key.Equals(TabKeys.LocalKey)); form.Initialize(DataSourceAdapter, tabView, gameFiles); form.StartPosition = FormStartPosition.CenterParent; form.SetDisplayText("Please select the main file that all data will be associated with. (Screenshots, demos, save games, etc.)"); form.MultiSelect = false; form.ShowSearchControl(false); if (form.ShowDialog(this) == DialogResult.OK && form.SelectedFiles.Length > 0) { accepted = true; return form.SelectedFiles[0]; } return gameFiles.First(); } private void HandleMultiSelectPlay(IGameFile firstGameFile, IEnumerable gameFiles) { //If the user selected multiple files //Take all the files after the first and set them as additional files to the first StringBuilder sbAdditionalFiles = new StringBuilder(); foreach(IGameFile gameFile in gameFiles) { sbAdditionalFiles.Append(gameFile.FileName); sbAdditionalFiles.Append(';'); } firstGameFile.SettingsFiles = sbAdditionalFiles.ToString(); } private void HandlePlaySettings(PlayForm form, IGameProfile gameProfile) { if (!form.RememberSettings || gameProfile == null) return; form.UpdateGameProfile(gameProfile); form.GameFile.SettingsGameProfileID = form.SelectedGameProfile.GameProfileID; DataSourceAdapter.UpdateGameFile(form.GameFile, new [] { GameFileFieldType.SettingsGameProfileID }); if (gameProfile is IGameFile gameFile) { DataSourceAdapter.UpdateGameFile(gameFile, new [] { GameFileFieldType.SourcePortID, GameFileFieldType.IWadID, GameFileFieldType.SettingsMap, GameFileFieldType.SettingsSkill, GameFileFieldType.SettingsFiles, GameFileFieldType.SettingsExtraParams, GameFileFieldType.SettingsSpecificFiles, GameFileFieldType.SettingsStat, GameFileFieldType.SettingsFilesIWAD, GameFileFieldType.SettingsFilesSourcePort, GameFileFieldType.SettingsSaved, GameFileFieldType.SettingsLoadLatestSave, GameFileFieldType.SettingsExtraParamsOnly }); return; } DataSourceAdapter.UpdateGameProfile(gameProfile); } private void SetupPlayForm(IGameFile gameFile) { m_currentPlayForm = new PlayForm(AppConfiguration, DataSourceAdapter); m_currentPlayForm.SaveSettings += m_currentPlayForm_SaveSettings; m_currentPlayForm.OnPreviewLaunchParameters += m_currentPlayForm_OnPreviewLaunchParameters; m_currentPlayForm.StartPosition = FormStartPosition.CenterParent; m_currentPlayForm.Initialize(GetAdditionalTabViews(), gameFile, m_activeSessions.Any()); m_currentPlayForm.SetGameProfile(GetGameProfile(gameFile)); } private IGameProfile GetGameProfile(IGameFile gameFile) { if (gameFile.SettingsGameProfileID.HasValue) { var profile = DataSourceAdapter.GetGameProfiles(gameFile.GameFileID.Value).FirstOrDefault(x => x.GameProfileID == gameFile.SettingsGameProfileID.Value); if (profile != null) return profile; profile = DataSourceAdapter.GetGlobalGameProfiles() .FirstOrDefault(x => x.GameProfileID == gameFile.SettingsGameProfileID.Value); if (profile != null) return profile; } return (GameFile)gameFile; } private void m_currentPlayForm_OnPreviewLaunchParameters(object sender, EventArgs e) { GameLauncher playAdapter = CreateGameLauncher(m_currentPlayForm, playAdapter_ProcessExited, AppConfiguration, false); if (m_currentPlayForm.SettingsValid(out string err)) ShowLaunchParameters(playAdapter, m_currentPlayForm.GameFile, m_currentPlayForm.GetAdditionalFiles(), m_currentPlayForm.SelectedSourcePort); else MessageBox.Show(this, err, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } void m_currentPlayForm_SaveSettings(object sender, EventArgs e) { HandlePlaySettings(m_currentPlayForm, m_currentPlayForm.SelectedGameProfile); if (m_currentPlayForm.GameFile != null) { UpdateDataSourceViews(m_currentPlayForm.GameFile); HandleSelectionChange(GetCurrentViewControl(), true); } } private List GetAdditionalTabViews() { List views = new List(); views.AddRange(m_tabHandler.TabViews.Where(x => TabKeys.LocalKey.Equals(x.Key))); views.AddRange(m_tabHandler.TabViews.Where(x => x is TagTabView)); return views; } private bool StartPlay(IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort, bool screenFilter) { GameLauncher playAdapter = CreateGameLauncher(m_currentPlayForm, playAdapter_ProcessExited, AppConfiguration, true); m_saveGames = Array.Empty(); if (AppConfiguration.CopySaveFiles) CopySaveGames(gameFile, sourcePort); CreateFileDetectors(sourcePort); bool isGameFileIwad = IsGameFileIwad(gameFile); IStatisticsReader statisticsReader = null; if (m_currentPlayForm.SaveStatistics) statisticsReader = SetupStatsReader(sourcePort, gameFile); var launchResult = playAdapter.Launch(gameFile, addFiles, sourcePort, isGameFileIwad); if (!launchResult.Failed) { m_activeSessions.Add(new PlaySession(launchResult.GameLaunchInfo, statisticsReader, DateTime.Now)); if (gameFile != null) { gameFile.LastPlayed = DateTime.Now; DataSourceAdapter.UpdateGameFile(gameFile, new GameFileFieldType[] { GameFileFieldType.LastPlayed }); UpdateDataSourceViews(gameFile); HandleSelectionChange(GetCurrentViewControl(), true); } } else { UpdateDataSourceViews(gameFile); HandleSelectionChange(GetCurrentViewControl(), true); StyledMessageBox.Show(this, launchResult.ErrorMessage, "Launch Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } if (screenFilter) { m_filterForm = new FilterForm(Screen.FromControl(this), m_currentPlayForm.GetFilterSettings()); m_filterForm.Show(this); } return true; } private void CopySaveGames(IGameFile gameFile, ISourcePortData sourcePort) { if (gameFile != null) { HandleCopySaveGames(gameFile, sourcePort); } else if (IsGameFileIwad(gameFile)) { gameFile = GetGameFileForIWad(gameFile); HandleCopySaveGames(gameFile, sourcePort); } } private void HandleCopySaveGames(IGameFile gameFile, ISourcePortData sourcePort) { m_saveGames = DataSourceAdapter.GetFiles(gameFile, FileType.SaveGame).Where(x => x.SourcePortID == sourcePort.SourcePortID).ToArray(); SaveGameHandler saveGameHandler = new SaveGameHandler(DataSourceAdapter, AppConfiguration.SaveGameDirectory); saveGameHandler.CopySaveGamesToSourcePort(sourcePort, m_saveGames); } private void ShowLaunchParameters(GameLauncher launcher, IGameFile gameFile, IEnumerable addFiles, ISourcePortData sourcePort) { TextBoxForm form = new TextBoxForm { Title = "Launch Parameters", StartPosition = FormStartPosition.CenterParent }; LaunchParameters launchParameters = launcher.GetLaunchParameters(gameFile, addFiles, sourcePort, IsGameFileIwad(gameFile)); if (!launchParameters.Failed) { var paramString = launchParameters.LaunchString; paramString = paramString.Replace(@" -", string.Concat(Environment.NewLine, " -")); paramString = paramString.Replace("\" \"", string.Concat("\"", Environment.NewLine, " \"")); if (paramString.StartsWith(Environment.NewLine)) paramString = paramString.Substring(Environment.NewLine.Length); string individualFiles = string.Empty; if (m_currentPlayForm.SpecificFiles != null && m_currentPlayForm.SpecificFiles.Length > 0) individualFiles = Environment.NewLine + string.Format("Selected Files: {0}", string.Join(", ", m_currentPlayForm.SpecificFiles)); string sourcePortParams = string.Empty; if (!string.IsNullOrEmpty(sourcePort.ExtraParameters)) sourcePortParams = string.Concat(Environment.NewLine, Environment.NewLine, "Parameters from source port: ", sourcePort.ExtraParameters); form.DisplayText = string.Concat(paramString, Environment.NewLine, Environment.NewLine, string.Format("Supported Extensions: {0}", sourcePort.SupportedExtensions), individualFiles, sourcePortParams, Environment.NewLine, Environment.NewLine, "*** If files appear to be missing check the 'Select Individual Files' option and supported extensions options in the Source Port form of the selected source port."); } else { form.DisplayText = $"Failed to generate launch parameters: {launchParameters.ErrorMessage}"; } form.SelectDisplayText(0, 0); form.ShowDialog(this); } private IStatisticsReader SetupStatsReader(ISourcePortData sourcePort, IGameFile gameFile) { IStatisticsReader statisticsReader = CreateStatisticsReader(sourcePort, gameFile); if (statisticsReader != null) { statisticsReader.NewStastics += m_statsReader_NewStastics; statisticsReader.Start(); } return statisticsReader; } private void CreateFileDetectors(ISourcePortData sourcePortData) { ISourcePortFlavor sourcePortFlavor = sourcePortData.GetFlavor(); CreateScreenshotDetectors(sourcePortData, sourcePortFlavor); CreateSaveGameDetectors(sourcePortData, sourcePortFlavor); } private void CreateSaveGameDetectors(ISourcePortData sourcePortData, ISourcePortFlavor sourcePort) { m_saveFileDetectors = CreateDefaultSaveGameDetectors(); m_saveFileDetectors.Add(CreateSaveGameDetector(sourcePortData.GetReadSavePath().GetFullPath())); foreach (var saveDir in sourcePort.GetSaveGameDirectories()) { if (!string.IsNullOrEmpty(saveDir) && Directory.Exists(saveDir)) m_saveFileDetectors.Add(CreateSaveGameDetector(saveDir)); } Array.ForEach(m_saveFileDetectors.ToArray(), x => x.StartDetection()); } private void CreateScreenshotDetectors(ISourcePortData sourcePortData, ISourcePortFlavor sourcePort) { if (!AppConfiguration.ImportScreenshots) { m_screenshotDetectors = new List(); return; } m_screenshotDetectors = CreateDefaultScreenshotDetectors(); m_screenshotDetectors.Add(CreateScreenshotDetector(sourcePortData.Directory.GetFullPath())); foreach (var screenshotDir in sourcePort.GetScreenshotDirectories()) { if (!string.IsNullOrEmpty(screenshotDir) && Directory.Exists(screenshotDir)) m_screenshotDetectors.Add(CreateScreenshotDetector(screenshotDir)); } Array.ForEach(m_screenshotDetectors.ToArray(), x => x.StartDetection()); } private GameLauncher CreateGameLauncher(PlayForm form, GameLaunchExitHandler processExited, AppConfiguration appConfig, bool extractFiles) { var features = new List(); if (form.SelectedIWad != null) features.Add(new IWadLaunchFeature(form.SelectedIWad, extractFiles)); features.AddRange(new List() { new MapSkillLaunchFeature(form.SelectedMap, form.SelectedSkill), new GameFilesLaunchFeature(form.GetAdditionalFiles(), form.SpecificFiles?.ToList(), extractFiles), new ExtraParametersLaunchFeature(form.ExtraParameters, form.ExtraParametersOnly), new SourcePortExtraParametersLaunchFeature() }); if (form.Record) features.Add(new RecordLaunchFeature()); if (form.PlayDemo && form.SelectedDemo != null) { var demoFile = Path.Combine(appConfig.DemoDirectory.GetFullPath(), form.SelectedDemo.FileName); features.Add(new PlayDemoLaunchFeature(demoFile)); } if (form.SaveStatistics) features.Add(new StatisticsReaderLaunchFeature()); if (form.LoadLatestSave) features.Add(new LoadSaveLaunchFeature(GetLoadLatestSave(form.GameFile, form.SelectedSourcePort))); GameLauncher playAdapter = new GameLauncher(appConfig, features); playAdapter.ProcessExited += processExited; return playAdapter; } private string GetLoadLatestSave(IGameFile gameFile, ISourcePortData sourcePortData) { var saveFile = DataSourceAdapter.GetFiles(gameFile, FileType.SaveGame).Where(x => x.SourcePortID == sourcePortData.SourcePortID) .OrderByDescending(x => x.DateCreated).FirstOrDefault(); if (saveFile != null) return Path.Combine(sourcePortData.GetLoadSavePath().GetFullPath(), saveFile.OriginalFileName); return string.Empty; } private IStatisticsReader CreateStatisticsReader(ISourcePortData sourcePort, IGameFile gameFile) { List existingStats = new List(); if (gameFile != null && gameFile.GameFileID.HasValue) existingStats = DataSourceAdapter.GetStats(gameFile.GameFileID.Value).ToList(); return sourcePort.GetFlavor().CreateStatisticsReader(gameFile, existingStats); } void m_statsReader_NewStastics(object sender, NewStatisticsEventArgs e) { if (e.Statistics == null || !(sender is IStatisticsReader statisticsReader)) return; PlaySession session = m_activeSessions.FirstOrDefault(x => statisticsReader.Equals(x.StatisticsReader)); if (session == null || session.GameLaunchInfo.GameFile == null) return; e.Statistics.MapName = e.Statistics.MapName.ToUpper(); e.Statistics.GameFileID = session.GameLaunchInfo.GameFile.GameFileID.Value; e.Statistics.SourcePortID = m_currentPlayForm.SelectedSourcePort.SourcePortID; if (e.Update) { IStatsData stats = DataSourceAdapter.GetStats(e.Statistics.GameFileID).LastOrDefault(x => x.MapName == e.Statistics.MapName); if (stats != null) DataSourceAdapter.DeleteStats(stats.StatID); } DataSourceAdapter.InsertStats(e.Statistics); } private bool IsGameFileIwad(IGameFile gameFile) { return DataSourceAdapter.GetGameFileIWads().Any(x => x.GameFileID.Value == gameFile.GameFileID.Value); } void playAdapter_ProcessExited(GameLaunchInfo launchInfo) { if (InvokeRequired) { Invoke(new Action(HandleProcessExited), new object[] { launchInfo }); } else { HandleProcessExited(launchInfo); } } private void HandleProcessExited(GameLaunchInfo gameLaunchInfo) { if (m_filterForm != null) { m_filterForm.Close(); m_filterForm = null; } PlaySession session = m_activeSessions.FirstOrDefault(x => x.GameLaunchInfo.Equals(gameLaunchInfo)); DateTime dtExit = DateTime.Now; Directory.SetCurrentDirectory(m_workingDirectory); if (gameLaunchInfo.SourcePort != null) { IGameFile gameFile = gameLaunchInfo.GameFile; if (gameFile != null && session != null) SetMinutesPlayed(session, dtExit); if (!string.IsNullOrEmpty(gameLaunchInfo.RecordedFileName)) HandleRecordedDemo(gameLaunchInfo, gameFile); HandleDetectorFiles(gameLaunchInfo.SourcePort, gameFile); if (session != null && session.StatisticsReader != null) { IStatisticsReader statsReader = session.StatisticsReader; statsReader.Stop(); if (statsReader.ReadOnClose) statsReader.ReadNow(); if (statsReader.Errors.Length > 0) HandleStatReaderErrors(statsReader); } } if (session != null) m_activeSessions.Remove(session); if (m_launchArgs.AutoClose) { m_writeConfigOnClose = false; Close(); return; } IGameFileView view = GetCurrentViewControl(); view.UpdateGameFile(gameLaunchInfo.GameFile); HandleSelectionChange(view, true); ShouldShowToolTip = true; } private IGameFile GetGameFileForIWad(IGameFile gameFile) { return DataSourceAdapter.GetGameFileIWads().FirstOrDefault(x => x.GameFileID.Value == gameFile.GameFileID.Value); } private void SetMinutesPlayed(PlaySession session, DateTime dtExit) { IGameFile gameFile = session.GameLaunchInfo.GameFile; gameFile.MinutesPlayed += Convert.ToInt32(dtExit.Subtract(session.Start).TotalMinutes); DataSourceAdapter.UpdateGameFile(gameFile, new GameFileFieldType[] { GameFileFieldType.MinutesPlayed }); UpdateDataSourceViews(gameFile); } private void HandleStatReaderErrors(IStatisticsReader m_statsReader) { TextBoxForm form = new TextBoxForm(); form.StartPosition = FormStartPosition.CenterParent; form.Title = "Statistic Reader Errors"; form.HeaderText = string.Concat("The following errors were reported by the statistics reader.", Environment.NewLine, "The statistics may be incomplete or missing."); StringBuilder sb = new StringBuilder(); foreach(string error in m_statsReader.Errors) { sb.Append(error); sb.Append(Environment.NewLine); } form.DisplayText = sb.ToString(); form.ShowDialog(this); } private void HandleDetectorFiles(ISourcePortData sourcePort, IGameFile gameFile) { ScreenshotHandler.HandleNewScreenshots(sourcePort, gameFile, GetNewScreenshots()); SaveGameHandler savegameHandler = new SaveGameHandler(DataSourceAdapter, AppConfiguration.SaveGameDirectory); savegameHandler.HandleNewSaveGames(sourcePort, gameFile, GetNewSaveGames(m_saveFileDetectors, m_saveGames)); savegameHandler.HandleUpdateSaveGames(sourcePort, gameFile, m_saveGames); savegameHandler.HandleDeleteSaveGames(GetDeletedSaveGames(m_saveFileDetectors), m_saveGames); } private void HandleRecordedDemo(GameLaunchInfo gameLaunchInfo, IGameFile gameFile) { DirectoryInfo di = new DirectoryInfo(AppConfiguration.TempDirectory.GetFullPath()); FileInfo fiTemp = new FileInfo(gameLaunchInfo.RecordedFileName); FileInfo fi = di.GetFiles().FirstOrDefault(x => x.Name.Contains(fiTemp.Name)); if (fi != null && fi.Exists) { DemoHandler demoHandler = new DemoHandler(DataSourceAdapter, AppConfiguration.DemoDirectory); demoHandler.HandleNewDemo(gameLaunchInfo.SourcePort, gameFile, fi.FullName, m_currentPlayForm.RecordDescriptionText); } else { MessageBox.Show(this, "Could not find the demo file. Does this source port support recording?", "Demo Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } private List CreateDefaultScreenshotDetectors() { List ret = new List(); foreach (string dir in AppConfiguration.ScreenshotCaptureDirectories) { if (Directory.Exists(dir)) ret.Add(CreateScreenshotDetector(dir)); } return ret; } private List CreateDefaultSaveGameDetectors() { return new List(); } private INewFileDetector CreateScreenshotDetector(string dir) { return new NewFileDetector(new string[] { ".png", ".jpg", ".bmp" }, dir, true); //future - should be configurable } private INewFileDetector CreateSaveGameDetector(string dir) { return new NewFileDetector(new string[] { ".zds", ".dsg", ".esg", ".hsg" }, dir, true); //future - should be configurable } private string[] GetNewScreenshots() { IEnumerable newFiles = new string[] { }; m_screenshotDetectors.ForEach(x => newFiles = newFiles.Union(x.GetNewFiles())); return newFiles.ToArray(); } private static string[] GetNewSaveGames(List saveFileDetectors, IFileData[] existingSaves) { IEnumerable newFiles = new string[] { }; saveFileDetectors.ForEach(x => newFiles = newFiles.Union(x.GetNewFiles())); IEnumerable modifiedFiles = new string[] { }; saveFileDetectors.ForEach(x => modifiedFiles = modifiedFiles.Union(x.GetModifiedFiles())); //modified files uses full path, m_saveGames does not. This section checks for modified files that were not part of the gamefile's save games //e.g save0.zds was a save game for this gamefile. User overwrites save1.zds for this gamefile. We now need to keep track of save1.zds as well. IEnumerable saveFiles = existingSaves.Select(x => x.OriginalFileName); List ret = newFiles.ToList(); foreach(string modifiedFile in modifiedFiles) { FileInfo fi = new FileInfo(modifiedFile); if (!saveFiles.Contains(fi.Name) && !ret.Contains(fi.Name)) ret.Add(modifiedFile); } return ret.ToArray(); } private static string[] GetDeletedSaveGames(List saveFileDetectors) { IEnumerable deletedFiles = new string[] { }; saveFileDetectors.ForEach(x => deletedFiles = deletedFiles.Union(x.GetDeletedFiles())); return deletedFiles.ToArray(); } } } ================================================ FILE: DoomLauncher/Forms/MainForm_Sync.cs ================================================ using DoomLauncher.Handlers.Sync; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace DoomLauncher { public partial class MainForm { private async Task SyncLocalDatabase(string[] fileNames, FileManagement fileManagement, bool updateViews, ITagData tag = null) { var pg = ProgressBarStart(ProgressBarType.Sync); pg.Text = $"Syncing {fileNames.Count()} files..."; SyncResult syncResult = await Task.Run(() => ExecuteSyncHandler(fileNames, fileManagement, tag)); ProgressBarEnd(ProgressBarType.Sync); SyncLocalDatabaseComplete(syncResult, updateViews); return syncResult; } void SyncLocalDatabaseComplete(SyncResult syncResult, bool updateViews) { if (updateViews) { UpdateLocal(); HandleTabSelectionChange(); foreach (IGameFile updateGameFile in syncResult.UpdatedGameFiles) UpdateDataSourceViews(updateGameFile); IGameFileView view = GetCurrentViewControl(); IGameFile selectedFile = view.SelectedItem; if (selectedFile != null && syncResult.UpdatedGameFiles.Contains(selectedFile)) HandleSelectionChange(view, true); } if (syncResult != null && syncResult.FailedTitlePicFiles.Count > 0) { StringBuilder sb = new StringBuilder("The following files had title images but failed to convert to a valid image:"); sb.AppendLine(); sb.AppendLine(); foreach (IGameFile gameFile in syncResult.FailedTitlePicFiles) sb.Append(gameFile.FileNameNoPath); MessageBox.Show(this, sb.ToString(), "Image Conversion Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } if (syncResult != null && (syncResult.InvalidFiles.Count > 0 || m_zdlInvalidFiles.Count > 0)) { DisplayInvalidFilesError(syncResult.InvalidFiles.Union(m_zdlInvalidFiles)); } else if (m_launchFile != null) { IGameFile launchFile = DataSourceAdapter.GetGameFile(Path.GetFileName(m_launchFile)); m_launchFile = null; if (launchFile != null) HandlePlay(new IGameFile[] { launchFile }); } } private void DisplayInvalidFilesError(IEnumerable invalidFiles) { StringBuilder sb = new StringBuilder(); foreach (InvalidFile file in invalidFiles) { sb.Append(file.FileName); sb.Append(": "); sb.Append(file.Reason); sb.Append(Environment.NewLine); } ShowTextBoxForm("Processing Errors", "The information on these files may be incomplete.\n\nFor ZDL files adding the missing Source Port/IWAD name and re-adding will update the information.\n\nFor zip archive/pk3 errors: Doom Launcher uses a zip library that has very strict zip rules that not all applications respect.\n\nVerify the zip by opening it with your favorite zip application. Create a new zip file and extract the files from the original zip into the newly created one. Then add the newly created zip to Doom Launcher.", sb.ToString(), false); } private SyncResult ExecuteSyncHandler(string[] files, FileManagement fileManagement, ITagData tag = null) { SyncLibraryHandler handler = null; SyncResult syncResult = SyncResult.EMPTY; try { var syncActions = new List() { // Lower on the list is higher priority new TextFileSyncAction(new IdGamesTextFileParser(AppConfiguration.DateParseFormats).Parse), new Doom64SyncAction(DataSourceAdapter), new MapStringSyncAction(AppConfiguration.TempDirectory), new GameInfoSyncAction(), new StartupImageSyncAction(), new TitlePicSyncAction(DataSourceAdapter, DataCache.Instance.DefaultPalette, DataCache.Instance.HexenPalette, DataCache.Instance.HereticPalette).OnlyIf(AppConfiguration.AutomaticallyPullTitlpic), new Doom64TitlePicSyncAction(), new IWadTitlesSyncAction(), new GameConfSyncAction(DataSourceAdapter), new KnownWadsSyncAction(DataSourceAdapter) }; handler = new SyncLibraryHandler(DataSourceAdapter, DirectoryDataSourceAdapter, AppConfiguration, fileManagement, syncActions); handler.SyncFileChanged += syncHandler_SyncFileChanged; handler.GameFileDataNeeded += syncHandler_GameFileDataNeeded; syncResult = handler.SyncManyFiles(files); SyncTitlePics(syncResult); if (m_pendingZdlFiles != null) { SyncPendingZdlFiles(); m_pendingZdlFiles = null; } if (tag != null) TagSyncFiles(syncResult, tag); } catch (Exception ex) { Util.DisplayUnexpectedException(this, ex); } return syncResult; } private void SyncTitlePics(SyncResult syncResult) { foreach (IGameFile gameFile in syncResult.AddedOrUpdatedFiles) { if (!syncResult.GetTitlePic(gameFile, out Image image)) continue; image = image.ScaleDoomImage(); var screenshots = DataSourceAdapter.GetFiles(gameFile, FileType.Screenshot); if (ScreenshotHandler.FindScreenshot(screenshots, image, out MemoryStream imageStream)) continue; if (imageStream == null) continue; ScreenshotHandler.InsertScreenshot(gameFile, imageStream, screenshots, out _); imageStream?.Dispose(); } } private void TagSyncFiles(SyncResult syncResult, ITagData tag) { DataCache.Instance.AddGameFileTag(syncResult.AddedGameFiles, tag, out _); DataCache.Instance.AddGameFileTag(syncResult.UpdatedGameFiles, tag, out _); DataCache.Instance.TagMapLookup.Refresh(new ITagData[] { tag }); } private void SyncPendingZdlFiles() { foreach(IGameFile gameFile in m_pendingZdlFiles) { IGameFile libraryGameFile = DataSourceAdapter.GetGameFile(gameFile.FileName); if (libraryGameFile != null) { libraryGameFile.SettingsSkill = gameFile.SettingsSkill; libraryGameFile.SettingsMap = gameFile.SettingsMap; libraryGameFile.SettingsExtraParams = gameFile.SettingsExtraParams; libraryGameFile.SourcePortID = gameFile.SourcePortID; libraryGameFile.IWadID = gameFile.IWadID; libraryGameFile.SettingsSkill = gameFile.SettingsSkill; libraryGameFile.SettingsFiles = gameFile.SettingsFiles; if (string.IsNullOrEmpty(libraryGameFile.Comments)) libraryGameFile.Comments = gameFile.Comments; DataSourceAdapter.UpdateGameFile(libraryGameFile); } } } void syncHandler_SyncFileChanged(SyncLibraryHandler.SyncProgressEvent e) { if (InvokeRequired) Invoke(new Action(ProgressBarUpdate), new object[] { e }); else ProgressBarUpdate(e); } void syncHandler_GameFileDataNeeded(SyncLibraryHandler.GameFileDataNeededEvent e) { if (InvokeRequired) Invoke(new Action(HandleGameFileDataNeeded), new object[] { e }); else HandleGameFileDataNeeded(e); } void HandleGameFileDataNeeded(SyncLibraryHandler.GameFileDataNeededEvent e) { if (CurrentDownloadFile != null && CurrentDownloadFile.FileName == e.CurrentGameFile.FileName) { e.CurrentGameFile.Title = CurrentDownloadFile.Title; e.CurrentGameFile.Author = CurrentDownloadFile.Author; e.CurrentGameFile.ReleaseDate = CurrentDownloadFile.ReleaseDate; } } void ProgressBarUpdate(SyncLibraryHandler.SyncProgressEvent e) { if (m_progressBars.TryGetValue(ProgressBarType.Sync, out var progressBar)) { progressBar.Maximum = e.SyncFileCount; progressBar.Value = e.SyncFileCurrent; progressBar.DisplayText = string.Format("Reading {0}...", e.CurrentSyncFileName); } } void ProgressBarForm_Cancelled(object sender, EventArgs e) { Enabled = true; BringToFront(); } private void SyncIWads(IEnumerable gameFiles) { foreach (var gameFile in gameFiles) { DataSourceAdapter.InsertIWad(new IWadData() { GameFileID = gameFile.GameFileID.Value, FileName = gameFile.FileName, Name = gameFile.FileName }); var iwad = DataSourceAdapter.GetIWads().OrderBy(x => x.IWadID).LastOrDefault(); IWadInfo wadInfo = IWadInfo.GetIWadInfo(gameFile.FileName); gameFile.Title = wadInfo == null ? Path.GetFileNameWithoutExtension(gameFile.FileName).ToUpper() : wadInfo.Title; DataSourceAdapter.UpdateGameFile(gameFile, new GameFileFieldType[] { GameFileFieldType.Title }); if (iwad != null) { gameFile.IWadID = iwad.IWadID; DataSourceAdapter.UpdateGameFile(gameFile, new[] { GameFileFieldType.IWadID }); } } ThumbnailManager.SetIWads(DataSourceAdapter.GetGameFileIWads().ToList()); UpdateLocal(); HandleTabSelectionChange(); } private async void HandleSyncStatus() { IEnumerable dsFiles = DirectoryDataSourceAdapter.GetGameFileNames(); IEnumerable dbFiles = DataSourceAdapter.GetGameFileNames(); IEnumerable diff = dsFiles.Except(dbFiles); SyncStatusForm form = ShowSyncStatusForm("Sync Status", "Files that exist in the GameFiles directory but not the Database:", diff, new string[] { "Do Nothing", "Add to Library", "Delete" }); Task task = HandleSyncStatusGameFilesOption((SyncFileOption)form.SelectedOptionIndex, form.GetSelectedFiles()); await task; diff = dbFiles.Except(dsFiles); form = ShowSyncStatusForm("Sync Status", "Files that exist in the Database but not the GameFiles directory:", diff, new string[] { "Do Nothing", "Find in idgames", "Delete" }); task = HandleSyncStatusLibraryOptions((SyncFileOption)form.SelectedOptionIndex, form.GetSelectedFiles()); await task; } private async Task HandleSyncStatusGameFilesOption(SyncFileOption option, IEnumerable files) { switch (option) { case SyncFileOption.Add: ProgressBarStart(ProgressBarType.Sync); SyncResult syncResult = await Task.Run(() => ExecuteSyncHandler(files.ToArray(), FileManagement.Managed)); ProgressBarEnd(ProgressBarType.Sync); SyncLocalDatabaseComplete(syncResult, true); break; case SyncFileOption.Delete: ProgressBarStart(ProgressBarType.Delete); await Task.Run(() => DeleteLocalGameFiles(files)); ProgressBarEnd(ProgressBarType.Delete); break; default: break; } } private void DeleteLocalGameFiles(IEnumerable files) { foreach (string file in files) { try { File.Delete(Path.Combine(AppConfiguration.GameFileDirectory.GetFullPath(), file)); } catch { //failed, nothing to do } } } private async Task HandleSyncStatusLibraryOptions(SyncFileOption option, IEnumerable files) { switch (option) { case SyncFileOption.Add: ProgressBarStart(ProgressBarType.Search); List gameFiles = await Task.Run(() => FindIdGamesFiles(files)); if (gameFiles == null) { MessageBox.Show(this, "Failed to connect to id games.", "Connection Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { foreach (IGameFile gameFile in gameFiles) m_downloadHandler.Download(IdGamesDataSourceAdapter, gameFile as IGameFileDownloadable); } ProgressBarEnd(ProgressBarType.Search); if (gameFiles != null) { DisplayFilesNotFound(files, gameFiles); if (gameFiles.Count > 0) DisplayDownloads(); } break; case SyncFileOption.Delete: ProgressBarStart(ProgressBarType.Delete); await Task.Run(() => DeleteLibraryGameFiles(files)); ProgressBarEnd(ProgressBarType.Delete); UpdateLocal(); break; default: break; } } private void DisplayFilesNotFound(IEnumerable files, List gameFiles) { IEnumerable filesNotFound = files.Except(gameFiles.Select(x => x.FileName)); if (filesNotFound.Any()) { StringBuilder sb = new StringBuilder(); foreach (string file in filesNotFound) { sb.Append(file); sb.Append(Environment.NewLine); } TextBoxForm form = new TextBoxForm(true, MessageBoxButtons.OK); form.Title = "Files Not Found"; form.HeaderText = "The following files were not found in the idgames database:"; form.DisplayText = sb.ToString(); form.ShowDialog(this); } } private List FindIdGamesFiles(IEnumerable files) { List gameFiles = new List(); try { foreach (string file in files) { IGameFile gameFile = IdGamesDataSourceAdapter.GetGameFile(file); if (gameFile != null) gameFiles.Add(gameFile); } } catch { return null; } return gameFiles; } private void DeleteLibraryGameFiles(IEnumerable files) { foreach (string file in files) { IGameFile gameFile = DataSourceAdapter.GetGameFile(file); if (gameFile != null && gameFile.GameFileID.HasValue) DeleteGameFileAndAssociations(gameFile); } } private SyncStatusForm ShowSyncStatusForm(string title, string header, IEnumerable files, IEnumerable dropDownOptions) { SyncStatusForm form = new SyncStatusForm(); form.Text = title; form.SetHeaderText(header); form.SetData(files, dropDownOptions); form.StartPosition = FormStartPosition.CenterParent; form.ShowDialog(this); return form; } private async Task HandleResync(bool pullTitlepic) { IGameFileView view = GetCurrentViewControl(); if (view == null) return; bool? setPullTitlepic = null; if (!pullTitlepic) setPullTitlepic = false; var allGameFiles = SelectedItems(view); AddFileType addFileType = view.DoomLauncherParent is IWadTabViewCtrl ? AddFileType.IWad : AddFileType.GameFile; var managed = allGameFiles.Where(x => !x.IsUnmanaged()).Select(x => Path.Combine(AppConfiguration.GameFileDirectory.GetFullPath(), x.FileName)).ToArray(); if (managed.Length > 0) await HandleAddGameFiles(addFileType, managed, overrideManagement: FileManagement.Managed, overridePullTitlepic: setPullTitlepic); var unmanaged = allGameFiles.Where(x => x.IsUnmanaged()).Select(x => x.FileName).ToArray(); if (unmanaged.Length > 0) await HandleAddGameFiles(addFileType, unmanaged, overrideManagement: FileManagement.Unmanaged, overridePullTitlepic: setPullTitlepic); } } } ================================================ FILE: DoomLauncher/Forms/MessageCheckBox.Designer.cs ================================================ namespace DoomLauncher.Forms { partial class MessageCheckBox { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MessageCheckBox)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.checkBox1 = new System.Windows.Forms.CheckBox(); this.tblMessage = new System.Windows.Forms.TableLayoutPanel(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.btnCancel = new System.Windows.Forms.Button(); this.btnOK = new System.Windows.Forms.Button(); this.lblText = new DoomLauncher.GrowLabel(); this.tblMain.SuspendLayout(); this.tblMessage.SuspendLayout(); this.flpButtons.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.checkBox1, 0, 2); this.tblMain.Controls.Add(this.tblMessage, 0, 0); this.tblMain.Controls.Add(this.flpButtons, 0, 1); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 79F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.Size = new System.Drawing.Size(533, 156); this.tblMain.TabIndex = 0; // // checkBox1 // this.checkBox1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.checkBox1.AutoSize = true; this.checkBox1.Location = new System.Drawing.Point(4, 127); this.checkBox1.Margin = new System.Windows.Forms.Padding(4); this.checkBox1.Name = "checkBox1"; this.checkBox1.Size = new System.Drawing.Size(185, 20); this.checkBox1.TabIndex = 0; this.checkBox1.Text = "Don\'t show this error again"; this.checkBox1.UseVisualStyleBackColor = true; // // tblMessage // this.tblMessage.ColumnCount = 2; this.tblMessage.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 64F)); this.tblMessage.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMessage.Controls.Add(this.lblText, 1, 0); this.tblMessage.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMessage.Location = new System.Drawing.Point(0, 0); this.tblMessage.Margin = new System.Windows.Forms.Padding(0); this.tblMessage.Name = "tblMessage"; this.tblMessage.RowCount = 1; this.tblMessage.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMessage.Size = new System.Drawing.Size(533, 79); this.tblMessage.TabIndex = 2; // // flpButtons // this.flpButtons.Controls.Add(this.btnCancel); this.flpButtons.Controls.Add(this.btnOK); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flpButtons.Location = new System.Drawing.Point(0, 79); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(533, 39); this.flpButtons.TabIndex = 3; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(429, 7); this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 7, 4, 4); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(100, 28); this.btnCancel.TabIndex = 2; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // btnOK // this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.None; this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(321, 7); this.btnOK.Margin = new System.Windows.Forms.Padding(4, 7, 4, 4); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(100, 28); this.btnOK.TabIndex = 1; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; // // lblText // this.lblText.AutoSize = true; this.lblText.IsPath = false; this.lblText.Location = new System.Drawing.Point(68, 0); this.lblText.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblText.Name = "lblText"; this.lblText.Size = new System.Drawing.Size(0, 0); this.lblText.TabIndex = 0; // // MessageCheckBox // this.AcceptButton = this.btnOK; this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(533, 156); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "MessageCheckBox"; this.Text = "MessageCheckBox"; this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.tblMessage.ResumeLayout(false); this.tblMessage.PerformLayout(); this.flpButtons.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.CheckBox checkBox1; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.TableLayoutPanel tblMessage; private GrowLabel lblText; private System.Windows.Forms.FlowLayoutPanel flpButtons; private System.Windows.Forms.Button btnCancel; } } ================================================ FILE: DoomLauncher/Forms/MessageCheckBox.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DoomLauncher.Forms { public partial class MessageCheckBox : Form { private readonly Icon m_icon; private readonly int m_height; public MessageCheckBox(string title, string text, string checkBoxText, Icon icon) : this(title, text, checkBoxText, icon, MessageBoxButtons.OK) { } public MessageCheckBox(string title, string text, string checkBoxText, Icon icon, MessageBoxButtons buttons, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1) { InitializeComponent(); StartPosition = FormStartPosition.CenterParent; if (buttons == MessageBoxButtons.OK) btnCancel.Visible = false; if (buttons == MessageBoxButtons.YesNo || buttons == MessageBoxButtons.YesNoCancel) { btnOK.Text = "Yes"; btnCancel.Text = "No"; btnOK.DialogResult = DialogResult.Yes; btnCancel.DialogResult = DialogResult.No; } if (buttons == MessageBoxButtons.RetryCancel) { btnOK.Text = "Retry"; btnCancel.Text = "Cancel"; btnOK.DialogResult = DialogResult.Retry; btnCancel.DialogResult = DialogResult.Cancel; } if (defaultButton == MessageBoxDefaultButton.Button1) { TabIndex = 0; AcceptButton = btnOK; } else { TabIndex = 1; AcceptButton = btnCancel; } Text = title; lblText.Text = text; checkBox1.Text = checkBoxText; m_icon = icon; DpiScale dpiScale = new DpiScale(CreateGraphics()); int textHeight = lblText.Height + dpiScale.ScaleIntY(16); int textRowHeight = (int)tblMain.RowStyles[0].Height; if (textHeight > textRowHeight) { int addHeight = textHeight - textRowHeight; Height += addHeight; tblMain.RowStyles[0].Height += addHeight; } tblMessage.Paint += tblMessage_Paint; lblText.Anchor = AnchorStyles.Left; m_height = Height; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public void SetShowCheckBox(bool set) { checkBox1.Visible = set; if (set) { Height = m_height; return; } DpiScale dpiScale = new DpiScale(CreateGraphics()); Height -= dpiScale.ScaleIntY(28); } void tblMessage_Paint(object sender, PaintEventArgs e) { int height = (int)tblMain.RowStyles[0].Height; DpiScale dpiScale = new DpiScale(CreateGraphics()); Rectangle rect = new Rectangle(dpiScale.ScaleIntX(14), (height - dpiScale.ScaleIntY(32)) / 2, dpiScale.ScaleIntX(32), dpiScale.ScaleIntY(32)); e.Graphics.DrawIcon(m_icon, rect); } public bool Checked => checkBox1.Checked; } } ================================================ FILE: DoomLauncher/Forms/MessageCheckBox.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/MetaDataForm.Designer.cs ================================================ namespace DoomLauncher { partial class MetaDataForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MetaDataForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.gameFileEdit1 = new DoomLauncher.GameFileEdit(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.btnCancel = new System.Windows.Forms.Button(); this.button1 = new System.Windows.Forms.Button(); this.btnAccept = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.gameFileEdit1, 0, 1); this.tblMain.Controls.Add(this.flowLayoutPanel1, 0, 3); this.tblMain.Controls.Add(this.label1, 0, 2); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 4; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 62F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblMain.Size = new System.Drawing.Size(645, 878); this.tblMain.TabIndex = 2; // // gameFileEdit1 // this.gameFileEdit1.AuthorChecked = false; this.gameFileEdit1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.gameFileEdit1.CommentsChecked = true; this.gameFileEdit1.DescriptionChecked = false; this.gameFileEdit1.Dock = System.Windows.Forms.DockStyle.Fill; this.gameFileEdit1.ForeColor = System.Drawing.Color.White; this.gameFileEdit1.Location = new System.Drawing.Point(0, 40); this.gameFileEdit1.MapsChecked = true; this.gameFileEdit1.Margin = new System.Windows.Forms.Padding(0); this.gameFileEdit1.Name = "gameFileEdit1"; this.gameFileEdit1.RatingChecked = false; this.gameFileEdit1.ReleaseDateChecked = false; this.gameFileEdit1.Size = new System.Drawing.Size(645, 727); this.gameFileEdit1.TabIndex = 0; this.gameFileEdit1.TagsChecked = true; this.gameFileEdit1.TitleChecked = false; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.btnCancel); this.flowLayoutPanel1.Controls.Add(this.button1); this.flowLayoutPanel1.Controls.Add(this.btnAccept); this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 829); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(645, 49); this.flowLayoutPanel1.TabIndex = 1; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(529, 5); this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(112, 35); this.btnCancel.TabIndex = 0; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // button1 // this.button1.DialogResult = System.Windows.Forms.DialogResult.Yes; this.button1.Location = new System.Drawing.Point(409, 5); this.button1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(112, 35); this.button1.TabIndex = 2; this.button1.Text = "Accept All"; this.button1.UseVisualStyleBackColor = true; // // btnAccept // this.btnAccept.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnAccept.Location = new System.Drawing.Point(289, 5); this.btnAccept.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnAccept.Name = "btnAccept"; this.btnAccept.Size = new System.Drawing.Size(112, 35); this.btnAccept.TabIndex = 1; this.btnAccept.Text = "Accept"; this.btnAccept.UseVisualStyleBackColor = true; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.Location = new System.Drawing.Point(4, 778); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(626, 40); this.label1.TabIndex = 2; this.label1.Text = "The fields from id games are displayed above. To accept meta changes, check the b" + "ox next to the items you wish to update and click accept."; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(645, 40); this.titleBar.TabIndex = 3; this.titleBar.Title = "Metadata"; // // MetaDataForm // this.AcceptButton = this.btnAccept; this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(645, 878); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "MetaDataForm"; this.Text = "Metadata"; this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.flowLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private GameFileEdit gameFileEdit1; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnAccept; private System.Windows.Forms.Label label1; private System.Windows.Forms.Button button1; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/MetaDataForm.cs ================================================ using System.Windows.Forms; namespace DoomLauncher { public partial class MetaDataForm : Form { public MetaDataForm() { InitializeComponent(); gameFileEdit1.SetShowCheckBoxes(true); gameFileEdit1.SetShowMaps(false); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public GameFileEdit GameFileEdit { get { return gameFileEdit1; } } } } ================================================ FILE: DoomLauncher/Forms/MetaDataForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/PlayForm.Designer.cs ================================================ using DoomLauncher.Controls; namespace DoomLauncher { partial class PlayForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PlayForm)); this.cmbSourcePorts = new System.Windows.Forms.ComboBox(); this.btnOK = new System.Windows.Forms.Button(); this.btnCancel = new System.Windows.Forms.Button(); this.cmbIwad = new System.Windows.Forms.ComboBox(); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.tblProfile = new System.Windows.Forms.TableLayoutPanel(); this.lblProfile = new System.Windows.Forms.Label(); this.cmbProfiles = new DoomLauncher.Controls.CComboBox(); this.panel2 = new System.Windows.Forms.Panel(); this.profileToolStrip = new System.Windows.Forms.ToolStrip(); this.toolStripDropDownButton1 = new System.Windows.Forms.ToolStripDropDownButton(); this.newProfileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.newGlobalProfileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.editProfileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.deleteProfileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.btnProfileMenu = new DoomLauncher.FormButton(); this.label2 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.cmbMap = new System.Windows.Forms.ComboBox(); this.label4 = new System.Windows.Forms.Label(); this.cmbSkill = new System.Windows.Forms.ComboBox(); this.txtDescription = new System.Windows.Forms.TextBox(); this.cmbDemo = new System.Windows.Forms.ComboBox(); this.groupBox2 = new System.Windows.Forms.GroupBox(); this.lnkOpenDemo = new System.Windows.Forms.LinkLabel(); this.label3 = new System.Windows.Forms.Label(); this.txtParameters = new System.Windows.Forms.TextBox(); this.chkMap = new DoomLauncher.CCheckBox(); this.chkDemo = new DoomLauncher.CCheckBox(); this.chkRecord = new DoomLauncher.CCheckBox(); this.lnkMore = new System.Windows.Forms.LinkLabel(); this.groupBox4 = new System.Windows.Forms.GroupBox(); this.lnkLoadSaveMore = new System.Windows.Forms.LinkLabel(); this.chkLoadLatestSave = new DoomLauncher.CCheckBox(); this.lnkFilterSettings = new System.Windows.Forms.LinkLabel(); this.chkScreenFilter = new DoomLauncher.CCheckBox(); this.lnkPreviewLaunchParameters = new System.Windows.Forms.LinkLabel(); this.chkExtraParamsOnly = new DoomLauncher.CCheckBox(); this.chkSaveStats = new DoomLauncher.CCheckBox(); this.groupBox3 = new System.Windows.Forms.GroupBox(); this.tblFiles = new DoomLauncher.TableLayoutPanelDB(); this.ctrlFiles = new DoomLauncher.FilesCtrl(); this.panel1 = new System.Windows.Forms.Panel(); this.lblInfo = new System.Windows.Forms.Label(); this.pbInfo = new System.Windows.Forms.PictureBox(); this.flp1 = new System.Windows.Forms.FlowLayoutPanel(); this.lnkSpecific = new System.Windows.Forms.LinkLabel(); this.lnkCustomParameters = new System.Windows.Forms.LinkLabel(); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.tblInner = new DoomLauncher.TableLayoutPanelDB(); this.pnlLeft = new System.Windows.Forms.Panel(); this.pnlBottom = new System.Windows.Forms.Panel(); this.btnSaveSettings = new System.Windows.Forms.Button(); this.flpButtons = new DoomLauncher.FlowLayoutPanelDB(); this.chkRemember = new DoomLauncher.CCheckBox(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.groupBox1.SuspendLayout(); this.tblProfile.SuspendLayout(); this.panel2.SuspendLayout(); this.profileToolStrip.SuspendLayout(); this.groupBox2.SuspendLayout(); this.groupBox4.SuspendLayout(); this.groupBox3.SuspendLayout(); this.tblFiles.SuspendLayout(); this.panel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo)).BeginInit(); this.flp1.SuspendLayout(); this.tblMain.SuspendLayout(); this.tblInner.SuspendLayout(); this.pnlLeft.SuspendLayout(); this.pnlBottom.SuspendLayout(); this.flpButtons.SuspendLayout(); this.SuspendLayout(); // // cmbSourcePorts // this.cmbSourcePorts.DisplayMember = "Name"; this.cmbSourcePorts.FormattingEnabled = true; this.cmbSourcePorts.Location = new System.Drawing.Point(51, 46); this.cmbSourcePorts.Name = "cmbSourcePorts"; this.cmbSourcePorts.Size = new System.Drawing.Size(193, 21); this.cmbSourcePorts.TabIndex = 0; this.cmbSourcePorts.TabStop = false; // // btnOK // this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(41, 4); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(75, 23); this.btnOK.TabIndex = 1; this.btnOK.TabStop = false; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; this.btnOK.Click += new System.EventHandler(this.btnOK_Click); // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(122, 4); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(75, 23); this.btnCancel.TabIndex = 2; this.btnCancel.TabStop = false; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // cmbIwad // this.cmbIwad.DisplayMember = "FileName"; this.cmbIwad.FormattingEnabled = true; this.cmbIwad.Location = new System.Drawing.Point(51, 74); this.cmbIwad.Name = "cmbIwad"; this.cmbIwad.Size = new System.Drawing.Size(193, 21); this.cmbIwad.TabIndex = 3; this.cmbIwad.TabStop = false; this.cmbIwad.ValueMember = "GameFileID"; // // groupBox1 // this.groupBox1.Controls.Add(this.tblProfile); this.groupBox1.Controls.Add(this.label2); this.groupBox1.Controls.Add(this.label1); this.groupBox1.Controls.Add(this.cmbSourcePorts); this.groupBox1.Controls.Add(this.cmbIwad); this.groupBox1.Location = new System.Drawing.Point(3, 3); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(256, 107); this.groupBox1.TabIndex = 5; this.groupBox1.TabStop = false; // // tblProfile // this.tblProfile.ColumnCount = 3; this.tblProfile.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 45F)); this.tblProfile.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblProfile.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 27F)); this.tblProfile.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 15F)); this.tblProfile.Controls.Add(this.lblProfile, 0, 0); this.tblProfile.Controls.Add(this.cmbProfiles, 1, 0); this.tblProfile.Controls.Add(this.panel2, 2, 0); this.tblProfile.Location = new System.Drawing.Point(3, 16); this.tblProfile.Margin = new System.Windows.Forms.Padding(0); this.tblProfile.Name = "tblProfile"; this.tblProfile.RowCount = 1; this.tblProfile.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblProfile.Size = new System.Drawing.Size(241, 25); this.tblProfile.TabIndex = 9; // // lblProfile // this.lblProfile.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblProfile.AutoSize = true; this.lblProfile.Location = new System.Drawing.Point(3, 6); this.lblProfile.Name = "lblProfile"; this.lblProfile.Size = new System.Drawing.Size(36, 13); this.lblProfile.TabIndex = 10; this.lblProfile.Text = "Profile"; // // cmbProfiles // this.cmbProfiles.BackColor = System.Drawing.Color.White; this.cmbProfiles.DisplayMember = "Name"; this.cmbProfiles.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbProfiles.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; this.cmbProfiles.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.cmbProfiles.ForeColor = System.Drawing.SystemColors.WindowText; this.cmbProfiles.FormattingEnabled = true; this.cmbProfiles.Location = new System.Drawing.Point(48, 3); this.cmbProfiles.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); this.cmbProfiles.Name = "cmbProfiles"; this.cmbProfiles.Size = new System.Drawing.Size(166, 21); this.cmbProfiles.TabIndex = 9; this.cmbProfiles.TabStop = false; this.cmbProfiles.ValueMember = "GameProfileID"; // // panel2 // this.panel2.Controls.Add(this.profileToolStrip); this.panel2.Controls.Add(this.btnProfileMenu); this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; this.panel2.Location = new System.Drawing.Point(214, 0); this.panel2.Margin = new System.Windows.Forms.Padding(0); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(27, 25); this.panel2.TabIndex = 14; // // profileToolStrip // this.profileToolStrip.Anchor = System.Windows.Forms.AnchorStyles.Left; this.profileToolStrip.Dock = System.Windows.Forms.DockStyle.None; this.profileToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; this.profileToolStrip.ImageScalingSize = new System.Drawing.Size(20, 20); this.profileToolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.toolStripDropDownButton1}); this.profileToolStrip.Location = new System.Drawing.Point(0, 3); this.profileToolStrip.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0); this.profileToolStrip.Name = "profileToolStrip"; this.profileToolStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.System; this.profileToolStrip.Size = new System.Drawing.Size(38, 26); this.profileToolStrip.TabIndex = 12; this.profileToolStrip.Text = "Options"; // // toolStripDropDownButton1 // this.toolStripDropDownButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image; this.toolStripDropDownButton1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.newProfileToolStripMenuItem, this.newGlobalProfileToolStripMenuItem, this.editProfileToolStripMenuItem, this.deleteProfileToolStripMenuItem}); this.toolStripDropDownButton1.Image = global::DoomLauncher.Properties.Resources.Bars; this.toolStripDropDownButton1.ImageTransparentColor = System.Drawing.Color.Magenta; this.toolStripDropDownButton1.Margin = new System.Windows.Forms.Padding(2, 0, 0, 2); this.toolStripDropDownButton1.Name = "toolStripDropDownButton1"; this.toolStripDropDownButton1.Size = new System.Drawing.Size(33, 24); this.toolStripDropDownButton1.Text = "Options"; // // newProfileToolStripMenuItem // this.newProfileToolStripMenuItem.Image = global::DoomLauncher.Properties.Resources.File; this.newProfileToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.newProfileToolStripMenuItem.Name = "newProfileToolStripMenuItem"; this.newProfileToolStripMenuItem.Size = new System.Drawing.Size(183, 24); this.newProfileToolStripMenuItem.Text = "New Profile..."; this.newProfileToolStripMenuItem.Click += new System.EventHandler(this.newProfileToolStripMenuItem_Click); // // newGlobalProfileToolStripMenuItem // this.newGlobalProfileToolStripMenuItem.Image = global::DoomLauncher.Properties.Resources.File; this.newGlobalProfileToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.newGlobalProfileToolStripMenuItem.Name = "newGlobalProfileToolStripMenuItem"; this.newGlobalProfileToolStripMenuItem.Size = new System.Drawing.Size(183, 24); this.newGlobalProfileToolStripMenuItem.Text = "New Global Profile..."; this.newGlobalProfileToolStripMenuItem.Click += new System.EventHandler(this.newGlobalProfileToolStripMenuItem_Click); // // editProfileToolStripMenuItem // this.editProfileToolStripMenuItem.Image = global::DoomLauncher.Properties.Resources.Edit; this.editProfileToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.editProfileToolStripMenuItem.Name = "editProfileToolStripMenuItem"; this.editProfileToolStripMenuItem.Size = new System.Drawing.Size(183, 24); this.editProfileToolStripMenuItem.Text = "Rename Profile..."; this.editProfileToolStripMenuItem.Click += new System.EventHandler(this.editProfileToolStripMenuItem_Click); // // deleteProfileToolStripMenuItem // this.deleteProfileToolStripMenuItem.Image = global::DoomLauncher.Properties.Resources.Delete; this.deleteProfileToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.deleteProfileToolStripMenuItem.Name = "deleteProfileToolStripMenuItem"; this.deleteProfileToolStripMenuItem.Size = new System.Drawing.Size(183, 24); this.deleteProfileToolStripMenuItem.Text = "Delete Profile"; this.deleteProfileToolStripMenuItem.Click += new System.EventHandler(this.deleteProfileToolStripMenuItem_Click); // // btnProfileMenu // this.btnProfileMenu.Image = global::DoomLauncher.Properties.Resources.Bars; this.btnProfileMenu.Location = new System.Drawing.Point(0, 5); this.btnProfileMenu.Margin = new System.Windows.Forms.Padding(0); this.btnProfileMenu.Name = "btnProfileMenu"; this.btnProfileMenu.Size = new System.Drawing.Size(23, 21); this.btnProfileMenu.TabIndex = 13; this.btnProfileMenu.UseVisualStyleBackColor = true; this.btnProfileMenu.Click += new System.EventHandler(this.btnProfileMenu_Click); // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(6, 77); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(36, 13); this.label2.TabIndex = 8; this.label2.Text = "IWAD"; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(6, 49); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(26, 13); this.label1.TabIndex = 7; this.label1.Text = "Port"; // // cmbMap // this.cmbMap.DisplayMember = "Name"; this.cmbMap.Enabled = false; this.cmbMap.FormattingEnabled = true; this.cmbMap.Location = new System.Drawing.Point(86, 19); this.cmbMap.Name = "cmbMap"; this.cmbMap.Size = new System.Drawing.Size(160, 21); this.cmbMap.TabIndex = 6; this.cmbMap.TabStop = false; // // label4 // this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(23, 49); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(26, 13); this.label4.TabIndex = 11; this.label4.Text = "Skill"; // // cmbSkill // this.cmbSkill.DisplayMember = "Name"; this.cmbSkill.Enabled = false; this.cmbSkill.FormattingEnabled = true; this.cmbSkill.Location = new System.Drawing.Point(86, 46); this.cmbSkill.Name = "cmbSkill"; this.cmbSkill.Size = new System.Drawing.Size(160, 21); this.cmbSkill.TabIndex = 10; this.cmbSkill.TabStop = false; // // txtDescription // this.txtDescription.Enabled = false; this.txtDescription.Location = new System.Drawing.Point(86, 112); this.txtDescription.Name = "txtDescription"; this.txtDescription.Size = new System.Drawing.Size(160, 20); this.txtDescription.TabIndex = 13; this.txtDescription.TabStop = false; // // cmbDemo // this.cmbDemo.DisplayMember = "Description"; this.cmbDemo.Enabled = false; this.cmbDemo.FormattingEnabled = true; this.cmbDemo.Location = new System.Drawing.Point(86, 73); this.cmbDemo.Name = "cmbDemo"; this.cmbDemo.Size = new System.Drawing.Size(160, 21); this.cmbDemo.TabIndex = 14; this.cmbDemo.TabStop = false; this.cmbDemo.ValueMember = "FileID"; // // groupBox2 // this.groupBox2.Controls.Add(this.lnkOpenDemo); this.groupBox2.Controls.Add(this.label3); this.groupBox2.Controls.Add(this.txtParameters); this.groupBox2.Controls.Add(this.cmbMap); this.groupBox2.Controls.Add(this.chkMap); this.groupBox2.Controls.Add(this.cmbSkill); this.groupBox2.Controls.Add(this.chkDemo); this.groupBox2.Controls.Add(this.label4); this.groupBox2.Controls.Add(this.cmbDemo); this.groupBox2.Controls.Add(this.chkRecord); this.groupBox2.Controls.Add(this.txtDescription); this.groupBox2.Location = new System.Drawing.Point(3, 116); this.groupBox2.Name = "groupBox2"; this.groupBox2.Size = new System.Drawing.Size(256, 173); this.groupBox2.TabIndex = 17; this.groupBox2.TabStop = false; // // lnkOpenDemo // this.lnkOpenDemo.AutoSize = true; this.lnkOpenDemo.Location = new System.Drawing.Point(83, 97); this.lnkOpenDemo.Name = "lnkOpenDemo"; this.lnkOpenDemo.Size = new System.Drawing.Size(92, 13); this.lnkOpenDemo.TabIndex = 19; this.lnkOpenDemo.TabStop = true; this.lnkOpenDemo.Text = "Open Demo File..."; this.lnkOpenDemo.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkOpenDemo_LinkClicked); // // label3 // this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(5, 142); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(69, 13); this.label3.TabIndex = 18; this.label3.Text = "Extra Params"; // // txtParameters // this.txtParameters.Location = new System.Drawing.Point(86, 139); this.txtParameters.Name = "txtParameters"; this.txtParameters.Size = new System.Drawing.Size(160, 20); this.txtParameters.TabIndex = 17; this.txtParameters.TabStop = false; this.txtParameters.Click += new System.EventHandler(this.TxtParameters_Click); // // chkMap // this.chkMap.AutoSize = true; this.chkMap.Location = new System.Drawing.Point(7, 21); this.chkMap.Name = "chkMap"; this.chkMap.Size = new System.Drawing.Size(47, 17); this.chkMap.TabIndex = 16; this.chkMap.TabStop = false; this.chkMap.Text = "Map"; this.chkMap.CheckedChanged += new System.EventHandler(this.chkMap_CheckedChanged); // // chkDemo // this.chkDemo.AutoSize = true; this.chkDemo.Location = new System.Drawing.Point(7, 75); this.chkDemo.Name = "chkDemo"; this.chkDemo.Size = new System.Drawing.Size(77, 17); this.chkDemo.TabIndex = 15; this.chkDemo.TabStop = false; this.chkDemo.Text = "Play Demo"; this.chkDemo.CheckedChanged += new System.EventHandler(this.chkDemo_CheckedChanged); // // chkRecord // this.chkRecord.AutoSize = true; this.chkRecord.Location = new System.Drawing.Point(6, 116); this.chkRecord.Name = "chkRecord"; this.chkRecord.Size = new System.Drawing.Size(61, 17); this.chkRecord.TabIndex = 12; this.chkRecord.TabStop = false; this.chkRecord.Text = "Record"; this.chkRecord.CheckedChanged += new System.EventHandler(this.chkRecord_CheckedChanged); // // lnkMore // this.lnkMore.AutoSize = true; this.lnkMore.Location = new System.Drawing.Point(163, 21); this.lnkMore.Name = "lnkMore"; this.lnkMore.Size = new System.Drawing.Size(61, 13); this.lnkMore.TabIndex = 20; this.lnkMore.TabStop = true; this.lnkMore.Text = "More Info..."; this.lnkMore.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkMore_LinkClicked); // // groupBox4 // this.groupBox4.Controls.Add(this.lnkLoadSaveMore); this.groupBox4.Controls.Add(this.chkLoadLatestSave); this.groupBox4.Controls.Add(this.lnkFilterSettings); this.groupBox4.Controls.Add(this.chkScreenFilter); this.groupBox4.Controls.Add(this.lnkPreviewLaunchParameters); this.groupBox4.Controls.Add(this.chkExtraParamsOnly); this.groupBox4.Controls.Add(this.chkSaveStats); this.groupBox4.Controls.Add(this.lnkMore); this.groupBox4.Location = new System.Drawing.Point(3, 294); this.groupBox4.Name = "groupBox4"; this.groupBox4.Size = new System.Drawing.Size(256, 116); this.groupBox4.TabIndex = 20; this.groupBox4.TabStop = false; // // lnkLoadSaveMore // this.lnkLoadSaveMore.AutoSize = true; this.lnkLoadSaveMore.Location = new System.Drawing.Point(163, 44); this.lnkLoadSaveMore.Name = "lnkLoadSaveMore"; this.lnkLoadSaveMore.Size = new System.Drawing.Size(61, 13); this.lnkLoadSaveMore.TabIndex = 26; this.lnkLoadSaveMore.TabStop = true; this.lnkLoadSaveMore.Text = "More Info..."; this.lnkLoadSaveMore.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkLoadSaveMore_LinkClicked); // // chkLoadLatestSave // this.chkLoadLatestSave.AutoSize = true; this.chkLoadLatestSave.Location = new System.Drawing.Point(6, 43); this.chkLoadLatestSave.Name = "chkLoadLatestSave"; this.chkLoadLatestSave.Size = new System.Drawing.Size(110, 17); this.chkLoadLatestSave.TabIndex = 25; this.chkLoadLatestSave.TabStop = false; this.chkLoadLatestSave.Text = "Load Latest Save"; // // lnkFilterSettings // this.lnkFilterSettings.AutoSize = true; this.lnkFilterSettings.Location = new System.Drawing.Point(163, 66); this.lnkFilterSettings.Name = "lnkFilterSettings"; this.lnkFilterSettings.Size = new System.Drawing.Size(45, 13); this.lnkFilterSettings.TabIndex = 24; this.lnkFilterSettings.TabStop = true; this.lnkFilterSettings.Text = "Settings"; this.lnkFilterSettings.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkFilterSettings_LinkClicked); // // chkScreenFilter // this.chkScreenFilter.AutoSize = true; this.chkScreenFilter.Location = new System.Drawing.Point(6, 66); this.chkScreenFilter.Name = "chkScreenFilter"; this.chkScreenFilter.Size = new System.Drawing.Size(85, 17); this.chkScreenFilter.TabIndex = 23; this.chkScreenFilter.TabStop = false; this.chkScreenFilter.Text = "Screen Filter"; this.chkScreenFilter.CheckedChanged += new System.EventHandler(this.chkScreenFilter_CheckedChanged); // // lnkPreviewLaunchParameters // this.lnkPreviewLaunchParameters.AutoSize = true; this.lnkPreviewLaunchParameters.Location = new System.Drawing.Point(163, 88); this.lnkPreviewLaunchParameters.Name = "lnkPreviewLaunchParameters"; this.lnkPreviewLaunchParameters.Size = new System.Drawing.Size(90, 13); this.lnkPreviewLaunchParameters.TabIndex = 22; this.lnkPreviewLaunchParameters.TabStop = true; this.lnkPreviewLaunchParameters.Text = "Show Parameters"; this.lnkPreviewLaunchParameters.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkPreviewLaunchParameters_LinkClicked); // // chkExtraParamsOnly // this.chkExtraParamsOnly.AutoSize = true; this.chkExtraParamsOnly.Location = new System.Drawing.Point(6, 88); this.chkExtraParamsOnly.Name = "chkExtraParamsOnly"; this.chkExtraParamsOnly.Size = new System.Drawing.Size(112, 17); this.chkExtraParamsOnly.TabIndex = 21; this.chkExtraParamsOnly.TabStop = false; this.chkExtraParamsOnly.Text = "Extra Params Only"; // // chkSaveStats // this.chkSaveStats.AutoSize = true; this.chkSaveStats.Checked = true; this.chkSaveStats.CheckState = System.Windows.Forms.CheckState.Checked; this.chkSaveStats.Location = new System.Drawing.Point(6, 20); this.chkSaveStats.Name = "chkSaveStats"; this.chkSaveStats.Size = new System.Drawing.Size(96, 17); this.chkSaveStats.TabIndex = 19; this.chkSaveStats.TabStop = false; this.chkSaveStats.Text = "Save Statistics"; // // groupBox3 // this.groupBox3.Controls.Add(this.tblFiles); this.groupBox3.Dock = System.Windows.Forms.DockStyle.Fill; this.groupBox3.Location = new System.Drawing.Point(265, 3); this.groupBox3.Name = "groupBox3"; this.groupBox3.Size = new System.Drawing.Size(236, 407); this.groupBox3.TabIndex = 18; this.groupBox3.TabStop = false; this.groupBox3.Text = "Additional Files / Load Order"; // // tblFiles // this.tblFiles.ColumnCount = 1; this.tblFiles.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblFiles.Controls.Add(this.ctrlFiles, 0, 1); this.tblFiles.Controls.Add(this.panel1, 0, 0); this.tblFiles.Controls.Add(this.flp1, 0, 2); this.tblFiles.Dock = System.Windows.Forms.DockStyle.Fill; this.tblFiles.Location = new System.Drawing.Point(3, 16); this.tblFiles.Margin = new System.Windows.Forms.Padding(0); this.tblFiles.Name = "tblFiles"; this.tblFiles.RowCount = 3; this.tblFiles.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblFiles.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblFiles.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F)); this.tblFiles.Size = new System.Drawing.Size(230, 388); this.tblFiles.TabIndex = 0; // // ctrlFiles // this.ctrlFiles.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.ctrlFiles.Dock = System.Windows.Forms.DockStyle.Fill; this.ctrlFiles.ForeColor = System.Drawing.Color.White; this.ctrlFiles.Location = new System.Drawing.Point(4, 44); this.ctrlFiles.Margin = new System.Windows.Forms.Padding(4); this.ctrlFiles.Name = "ctrlFiles"; this.ctrlFiles.Size = new System.Drawing.Size(222, 315); this.ctrlFiles.TabIndex = 20; // // panel1 // this.panel1.Controls.Add(this.lblInfo); this.panel1.Controls.Add(this.pbInfo); this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel1.Location = new System.Drawing.Point(0, 0); this.panel1.Margin = new System.Windows.Forms.Padding(0); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(230, 40); this.panel1.TabIndex = 21; // // lblInfo // this.lblInfo.AutoSize = true; this.lblInfo.Location = new System.Drawing.Point(25, 7); this.lblInfo.Name = "lblInfo"; this.lblInfo.Size = new System.Drawing.Size(29, 13); this.lblInfo.TabIndex = 25; this.lblInfo.Text = "label"; // // pbInfo // this.pbInfo.Location = new System.Drawing.Point(3, 7); this.pbInfo.Name = "pbInfo"; this.pbInfo.Size = new System.Drawing.Size(16, 16); this.pbInfo.TabIndex = 24; this.pbInfo.TabStop = false; // // flp1 // this.flp1.Controls.Add(this.lnkSpecific); this.flp1.Controls.Add(this.lnkCustomParameters); this.flp1.Dock = System.Windows.Forms.DockStyle.Fill; this.flp1.Location = new System.Drawing.Point(0, 363); this.flp1.Margin = new System.Windows.Forms.Padding(0); this.flp1.Name = "flp1"; this.flp1.Size = new System.Drawing.Size(230, 25); this.flp1.TabIndex = 22; // // lnkSpecific // this.lnkSpecific.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lnkSpecific.AutoSize = true; this.lnkSpecific.Location = new System.Drawing.Point(3, 3); this.lnkSpecific.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); this.lnkSpecific.Name = "lnkSpecific"; this.lnkSpecific.Size = new System.Drawing.Size(118, 13); this.lnkSpecific.TabIndex = 20; this.lnkSpecific.TabStop = true; this.lnkSpecific.Text = "Select Individual Files..."; this.lnkSpecific.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkSpecific_LinkClicked); // // lnkCustomParameters // this.lnkCustomParameters.AutoSize = true; this.lnkCustomParameters.Location = new System.Drawing.Point(127, 3); this.lnkCustomParameters.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); this.lnkCustomParameters.Name = "lnkCustomParameters"; this.lnkCustomParameters.Size = new System.Drawing.Size(89, 13); this.lnkCustomParameters.TabIndex = 21; this.lnkCustomParameters.TabStop = true; this.lnkCustomParameters.Text = "Custom Params..."; this.lnkCustomParameters.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkCustomParameters_LinkClicked); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.tblInner, 0, 1); this.tblMain.Controls.Add(this.pnlBottom, 0, 2); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.Size = new System.Drawing.Size(504, 471); this.tblMain.TabIndex = 21; // // tblInner // this.tblInner.ColumnCount = 2; this.tblInner.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 262F)); this.tblInner.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInner.Controls.Add(this.pnlLeft, 0, 0); this.tblInner.Controls.Add(this.groupBox3, 1, 0); this.tblInner.Dock = System.Windows.Forms.DockStyle.Fill; this.tblInner.Location = new System.Drawing.Point(0, 26); this.tblInner.Margin = new System.Windows.Forms.Padding(0); this.tblInner.Name = "tblInner"; this.tblInner.RowCount = 1; this.tblInner.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInner.Size = new System.Drawing.Size(504, 413); this.tblInner.TabIndex = 0; // // pnlLeft // this.pnlLeft.Controls.Add(this.groupBox1); this.pnlLeft.Controls.Add(this.groupBox4); this.pnlLeft.Controls.Add(this.groupBox2); this.pnlLeft.Dock = System.Windows.Forms.DockStyle.Fill; this.pnlLeft.Location = new System.Drawing.Point(0, 0); this.pnlLeft.Margin = new System.Windows.Forms.Padding(0); this.pnlLeft.Name = "pnlLeft"; this.pnlLeft.Size = new System.Drawing.Size(262, 413); this.pnlLeft.TabIndex = 0; // // pnlBottom // this.pnlBottom.Controls.Add(this.btnSaveSettings); this.pnlBottom.Controls.Add(this.flpButtons); this.pnlBottom.Controls.Add(this.chkRemember); this.pnlBottom.Dock = System.Windows.Forms.DockStyle.Fill; this.pnlBottom.Location = new System.Drawing.Point(0, 439); this.pnlBottom.Margin = new System.Windows.Forms.Padding(0); this.pnlBottom.Name = "pnlBottom"; this.pnlBottom.Size = new System.Drawing.Size(504, 32); this.pnlBottom.TabIndex = 1; // // btnSaveSettings // this.btnSaveSettings.Location = new System.Drawing.Point(133, 5); this.btnSaveSettings.Name = "btnSaveSettings"; this.btnSaveSettings.Size = new System.Drawing.Size(82, 23); this.btnSaveSettings.TabIndex = 6; this.btnSaveSettings.TabStop = false; this.btnSaveSettings.Text = "Save Settings"; this.btnSaveSettings.UseVisualStyleBackColor = true; this.btnSaveSettings.Click += new System.EventHandler(this.btnSaveSettings_Click); // // flpButtons // this.flpButtons.CaptureArrowKeys = false; this.flpButtons.Controls.Add(this.btnCancel); this.flpButtons.Controls.Add(this.btnOK); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Right; this.flpButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flpButtons.Location = new System.Drawing.Point(304, 0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Padding = new System.Windows.Forms.Padding(0, 1, 0, 0); this.flpButtons.Size = new System.Drawing.Size(200, 32); this.flpButtons.TabIndex = 5; // // chkRemember // this.chkRemember.AutoSize = true; this.chkRemember.Checked = true; this.chkRemember.CheckState = System.Windows.Forms.CheckState.Checked; this.chkRemember.Location = new System.Drawing.Point(9, 9); this.chkRemember.Name = "chkRemember"; this.chkRemember.Size = new System.Drawing.Size(118, 17); this.chkRemember.TabIndex = 4; this.chkRemember.TabStop = false; this.chkRemember.Text = "Remember Settings"; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(504, 26); this.titleBar.TabIndex = 2; this.titleBar.Title = "Launch"; // // PlayForm // this.AcceptButton = this.btnOK; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(504, 471); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "PlayForm"; this.Text = "Launch"; this.groupBox1.ResumeLayout(false); this.groupBox1.PerformLayout(); this.tblProfile.ResumeLayout(false); this.tblProfile.PerformLayout(); this.panel2.ResumeLayout(false); this.panel2.PerformLayout(); this.profileToolStrip.ResumeLayout(false); this.profileToolStrip.PerformLayout(); this.groupBox2.ResumeLayout(false); this.groupBox2.PerformLayout(); this.groupBox4.ResumeLayout(false); this.groupBox4.PerformLayout(); this.groupBox3.ResumeLayout(false); this.tblFiles.ResumeLayout(false); this.panel1.ResumeLayout(false); this.panel1.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo)).EndInit(); this.flp1.ResumeLayout(false); this.flp1.PerformLayout(); this.tblMain.ResumeLayout(false); this.tblInner.ResumeLayout(false); this.pnlLeft.ResumeLayout(false); this.pnlBottom.ResumeLayout(false); this.pnlBottom.PerformLayout(); this.flpButtons.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ComboBox cmbSourcePorts; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.ComboBox cmbIwad; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.ComboBox cmbMap; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label4; private System.Windows.Forms.ComboBox cmbSkill; private System.Windows.Forms.TextBox txtDescription; private System.Windows.Forms.ComboBox cmbDemo; private System.Windows.Forms.GroupBox groupBox2; private System.Windows.Forms.Label label3; private System.Windows.Forms.TextBox txtParameters; private System.Windows.Forms.LinkLabel lnkMore; private System.Windows.Forms.GroupBox groupBox4; private System.Windows.Forms.GroupBox groupBox3; private FilesCtrl ctrlFiles; private System.Windows.Forms.Panel pnlLeft; private System.Windows.Forms.Panel pnlBottom; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Label lblInfo; private System.Windows.Forms.PictureBox pbInfo; private System.Windows.Forms.Button btnSaveSettings; private System.Windows.Forms.LinkLabel lnkOpenDemo; private System.Windows.Forms.LinkLabel lnkPreviewLaunchParameters; private System.Windows.Forms.FlowLayoutPanel flp1; private System.Windows.Forms.LinkLabel lnkSpecific; private System.Windows.Forms.LinkLabel lnkCustomParameters; private System.Windows.Forms.LinkLabel lnkFilterSettings; private System.Windows.Forms.Label lblProfile; private CComboBox cmbProfiles; private System.Windows.Forms.TableLayoutPanel tblProfile; private System.Windows.Forms.ToolStrip profileToolStrip; private System.Windows.Forms.ToolStripDropDownButton toolStripDropDownButton1; private System.Windows.Forms.ToolStripMenuItem newProfileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem newGlobalProfileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem editProfileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem deleteProfileToolStripMenuItem; private System.Windows.Forms.LinkLabel lnkLoadSaveMore; private Controls.TitleBarControl titleBar; private FormButton btnProfileMenu; private System.Windows.Forms.Panel panel2; private CCheckBox chkRemember; private CCheckBox chkRecord; private CCheckBox chkDemo; private CCheckBox chkMap; private CCheckBox chkSaveStats; private CCheckBox chkExtraParamsOnly; private CCheckBox chkScreenFilter; private CCheckBox chkLoadLatestSave; private TableLayoutPanelDB tblMain; private TableLayoutPanelDB tblInner; private FlowLayoutPanelDB flpButtons; private TableLayoutPanelDB tblFiles; } } ================================================ FILE: DoomLauncher/Forms/PlayForm.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Demo; using DoomLauncher.Forms; using DoomLauncher.Handlers; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using System.Xml.Serialization; namespace DoomLauncher { public partial class PlayForm : Form { private ITabView[] m_additionalFileViews; private bool m_demoChangedAdditionalFiles; private ISourcePortData m_lastSourcePort; private IGameFile m_lastIwad; public event EventHandler SaveSettings; public event EventHandler OnPreviewLaunchParameters; private FileLoadHandler m_handler; private readonly AppConfiguration m_appConfig; private readonly IDataSourceAdapter m_adapter; private ScreenFilter m_filterSettings; private bool m_playSessionInProgress; private IList m_globalProfiles; private readonly Control[] m_tabControls; private AppConfiguration AppConfiguration => DataCache.Instance.AppConfiguration; public PlayForm(AppConfiguration appConfig, IDataSourceAdapter adapter) { InitializeComponent(); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); Stylizer.StylizeControl(toolStripDropDownButton1, DesignMode); ctrlFiles.Initialize("GameFileID", "FileNameNoPath"); ctrlFiles.CellFormatting += ctrlFiles_CellFormatting; ctrlFiles.NewItemNeeded += ctrlFiles_NewItemNeeded; ctrlFiles.ItemAdded += CtrlFiles_ItemAdded; ctrlFiles.ItemRemoving += CtrlFiles_ItemRemoving; ctrlFiles.ItemRemoved += CtrlFiles_ItemRemoved; Load += PlayForm_Load; lnkCustomParameters.Visible = false; m_appConfig = appConfig; m_adapter = adapter; m_filterSettings = GetFilterSettings(); chkScreenFilter.Checked = m_filterSettings.Enabled; Icons.DpiScale = new DpiScale(CreateGraphics()); newProfileToolStripMenuItem.Image = Icons.File; deleteProfileToolStripMenuItem.Image = Icons.Delete; editProfileToolStripMenuItem.Image = Icons.Edit; toolStripDropDownButton1.Image = Icons.Bars; m_tabControls = new Control[] { cmbProfiles, cmbSourcePorts, cmbIwad, chkMap, cmbMap, cmbSkill, chkDemo, cmbDemo, chkRecord, txtDescription, txtParameters, chkSaveStats, chkLoadLatestSave, chkScreenFilter, chkExtraParamsOnly, chkRemember, btnSaveSettings, ctrlFiles, lnkSpecific, lnkCustomParameters, btnOK, btnCancel }; InitTabIndicies(); profileToolStrip.Visible = false; toolStripDropDownButton1.Visible = false; int padTop = Icons.DpiScale.ScaleIntY(1); btnProfileMenu.Location = new Point(0, padTop); btnProfileMenu.Image = Icons.Bars; m_globalProfiles = Array.Empty(); cmbProfiles.StyleItem += CmbProfiles_StyleItem; } private void CmbProfiles_StyleItem(object sender, Controls.ComboBoxItemStyle e) { if (!int.TryParse(e.ValueMember, out int gameProfileId)) return; if (!m_globalProfiles.Any(x => x.GameProfileID == gameProfileId)) return; e.Text = "● " + e.Text; if (m_globalProfiles.Count > 0 && gameProfileId == m_globalProfiles.Last().GameProfileID) { var bottomLeft = new Point(e.DrawItem.Bounds.X, e.DrawItem.Bounds.Bottom - 1); var bottomRight = new Point(e.DrawItem.Bounds.Right, e.DrawItem.Bounds.Bottom - 1); e.DrawItem.Graphics.DrawLine(new Pen(cmbProfiles.ForeColor), bottomLeft, bottomRight); } } private void InitTabIndicies() { foreach (var control in this.GetChildElements()) control.TabStop = false; // Another user had an issue with tab indicies starting 0 // Starting at 100 mostly fixes it... ugh // This still isn't 100% but it's way better than it was // The built in functionality for this is very screwed up, about ready to entirely write a custom one int index = 100; foreach (var control in m_tabControls) { control.TabStop = true; control.TabIndex = index++; } } private void PlayForm_Load(object sender, EventArgs e) { if (m_tabControls.Length > 0) { ActiveControl = m_tabControls[0]; m_tabControls[0].Focus(); } } public void Initialize(IEnumerable additionalFileViews, IGameFile gameFile, bool playSessionInProgress) { m_playSessionInProgress = playSessionInProgress; m_additionalFileViews = additionalFileViews.ToArray(); GameFile = gameFile; AutoCompleteCombo.SetAutoCompleteCustomSource(cmbSourcePorts, GetSourcePortsForComboBox(gameFile), typeof(ISourcePortData), "Name"); AutoCompleteCombo.SetAutoCompleteCustomSource(cmbIwad, GetIWadsForComboBox(gameFile, m_adapter), typeof(IIWadData), "FileName"); if (gameFile != null) { Text = "Launch - " + (string.IsNullOrEmpty(gameFile.Title) ? gameFile.FileName : gameFile.Title); if (!string.IsNullOrEmpty(gameFile.Map)) AutoCompleteCombo.SetAutoCompleteCustomSource(cmbMap, MapSplit(gameFile), null, null); } AutoCompleteCombo.SetAutoCompleteCustomSource(cmbSkill, Util.GetSkills().ToList(), null, null); cmbSkill.SelectedItem = "3"; LoadProfiles(); } private IEnumerable GetSourcePortsForComboBox(IGameFile gameFile) { return gameFile.IsDoom64 ? m_adapter.GetDoom64() : m_adapter.GetSourcePorts(); } private IEnumerable GetIWadsForComboBox(IGameFile gameFile, IDataSourceAdapter adapter) { return gameFile.IsDoom64 ? new List() : Util.GetIWadsDataSource(adapter); } public void SetGameProfile(IGameProfile gameProfile) { SetIwadInfoLabel(); UnregisterEvents(); m_handler = new FileLoadHandler(m_adapter, GameFile, gameProfile); SetDefaultSelections(); GameProfile.ApplyDefaultsToProfile(gameProfile, m_appConfig); cmbProfiles.SelectedValue = gameProfile.GameProfileID; if (GameFile != null) { chkSaveStats.Checked = gameProfile.SettingsStat; chkLoadLatestSave.Checked = gameProfile.SettingsLoadLatestSave; chkExtraParamsOnly.Checked = gameProfile.SettingsExtraParamsOnly; if (gameProfile.SourcePortID.HasValue) SelectedSourcePort = m_adapter.GetSourcePort(gameProfile.SourcePortID.Value); var isDoom64 = GameFile.IsDoom64; isDoom64 = false; if (isDoom64) { SelectedSourcePort = m_adapter.GetDoom64().FirstOrDefault(); cmbSourcePorts.Enabled = false; groupBox4.Visible = false; cmbIwad.Visible = false; label2.Visible = false; // IWAD label chkDemo.Visible = false; cmbDemo.Visible = false; chkRecord.Visible = false; txtDescription.Visible = false; lnkOpenDemo.Visible = false; } // Selected GameFile is an IWAD so lock the IWAD selection if (IsIwad(GameFile)) { cmbIwad.Enabled = false; SelectedIWad = GameFile; } else if (gameProfile.IWadID.HasValue) { SelectedIWad = m_adapter.GetGameFileIWads().FirstOrDefault(x => x.IWadID == gameProfile.IWadID); } if (!string.IsNullOrEmpty(gameProfile.SettingsMap)) SelectedMap = gameProfile.SettingsMap; if (!string.IsNullOrEmpty(gameProfile.SettingsSkill)) SelectedSkill = gameProfile.SettingsSkill; if (!string.IsNullOrEmpty(gameProfile.SettingsExtraParams)) ExtraParameters = gameProfile.SettingsExtraParams; SpecificFiles = GetSpecificFilesFromProfile(gameProfile); } bool reset = ShouldRecalculateAdditionalFiles(); HandleSourcePortSelectionChange(reset); HandleIwadSelectionChanged(reset); SetAdditionalFiles(reset); HandleDemoChange(); RegisterEvents(); lnkSpecific.Enabled = !gameProfile.IsGlobal; if (gameProfile.IsGlobal) lnkSpecific.Text = "Individual files not supported with global profiles"; else lnkSpecific.Text = "Select Individual Files..."; } private string[] GetSpecificFilesFromProfile(IGameProfile gameProfile) { // Not yet supported if (gameProfile.IsGlobal || string.IsNullOrEmpty(gameProfile.SettingsSpecificFiles)) return null; return Util.SplitString(gameProfile.SettingsSpecificFiles); } private void SetIwadInfoLabel() { DpiScale dpiScale = new DpiScale(CreateGraphics()); float infoHeight = dpiScale.ScaleFloatY(40); lblInfo.BackColor = ColorTheme.Current.Window; lblInfo.ForeColor = ColorTheme.Current.Text; if (m_playSessionInProgress) { tblFiles.RowStyles[0].Height = infoHeight; pbInfo.Image = Properties.Resources.bon2b; lblInfo.BackColor = ColorTheme.Current.Window; lblInfo.ForeColor = ColorTheme.Current.HighlightText; lblInfo.Text = string.Format("Play session already in progress. Features{0}like statistics tracking may not function.", Environment.NewLine); return; } if (GameFile != null && IsIwad(GameFile) && SelectedGameProfile is GameFile) { tblFiles.RowStyles[0].Height = infoHeight; pbInfo.Image = Properties.Resources.bon2b; lblInfo.Text = string.Format("These files will automatically be added{0} when this IWAD is selected for play.", Environment.NewLine); } else { tblFiles.RowStyles[0].Height = 0; } } private void SetDefaultSelections() { if (cmbMap.Items.Count > 0) cmbMap.SelectedIndex = 0; else cmbMap.SelectedIndex = -1; chkMap.Checked = false; txtParameters.Text = string.Empty; SpecificFiles = null; ctrlFiles.SetDataSource(new List()); } private void RegisterEvents() { cmbProfiles.SelectedIndexChanged += CmbProfiles_SelectedIndexChanged; cmbSourcePorts.SelectedIndexChanged += cmbSourcePorts_SelectedIndexChanged; cmbIwad.SelectedIndexChanged += cmbIwad_SelectedIndexChanged; cmbDemo.SelectedIndexChanged += cmbDemo_SelectedIndexChanged; chkRecord.CheckedChanged += chkRecord_CheckedChanged; chkDemo.CheckedChanged += chkDemo_CheckedChanged; } private void UnregisterEvents() { cmbProfiles.SelectedIndexChanged -= CmbProfiles_SelectedIndexChanged; cmbSourcePorts.SelectedIndexChanged -= cmbSourcePorts_SelectedIndexChanged; cmbIwad.SelectedIndexChanged -= cmbIwad_SelectedIndexChanged; cmbDemo.SelectedIndexChanged -= cmbDemo_SelectedIndexChanged; chkRecord.CheckedChanged -= chkRecord_CheckedChanged; chkDemo.CheckedChanged -= chkDemo_CheckedChanged; } private IList LoadProfiles() { var profiles = GameProfileUtil.GetAllProfiles(m_adapter, (GameFile)GameFile, out m_globalProfiles); AutoCompleteCombo.SetAutoCompleteCustomSource(cmbProfiles, profiles, typeof(IGameProfile), "Name"); return profiles; } private static string[] MapSplit(IGameFile gameFile) => DataSources.GameFile.GetMaps(gameFile); private bool IsIwad(IGameFile gameFile) { if (gameFile.GameFileID.HasValue) return m_adapter.GetIWad(gameFile.GameFileID.Value) != null; return false; } public IGameFile GameFile { get; private set; } public IGameProfile SelectedGameProfile => (IGameProfile)cmbProfiles.SelectedItem; public ISourcePortData SelectedSourcePort { get => cmbSourcePorts.SelectedItem as ISourcePortData; set => cmbSourcePorts.SelectedItem = value; } public IGameFile SelectedIWad { get { if (cmbIwad.SelectedItem != null) return m_adapter.GetGameFileIWads().FirstOrDefault(x => x.GameFileID == ((IIWadData)cmbIwad.SelectedItem).GameFileID); return null; } set { if (value == null) cmbIwad.SelectedIndex = 0; else cmbIwad.SelectedItem = Util.GetIWadsDataSource(m_adapter).FirstOrDefault(x => x.IWadID == value.IWadID); } } public IFileData SelectedDemo { get => cmbDemo.SelectedItem as IFileData; set => cmbDemo.SelectedItem = value; } public List GetAdditionalFiles() { //return all the files in order, the user can determine the order of any file, whether it was added by source port or iwad selection return ctrlFiles.GetFiles().Cast().ToList(); } public List GetIWadAdditionalFiles() { return GetAdditionalFiles().Intersect(m_handler.GetIWadFiles()).ToList(); } public List GetSourcePortAdditionalFiles() { return GetAdditionalFiles().Intersect(m_handler.GetSourcePortFiles()).ToList(); } public string SelectedMap { get { if (!chkMap.Checked) return null; return cmbMap.SelectedItem as string; } set { if (value == null) { chkMap.Checked = false; } else { chkMap.Checked = true; cmbMap.SelectedItem = value; } } } public string SelectedSkill { get => cmbSkill.SelectedItem as string; set => cmbSkill.SelectedItem = value; } public bool RememberSettings => chkRemember.Checked; public bool Record => chkRecord.Checked; public bool PlayDemo => chkDemo.Checked; public string RecordDescriptionText => txtDescription.Text; public string ExtraParameters { get => txtParameters.Text; set => txtParameters.Text = value; } public string[] SpecificFiles { get; set; } public bool SaveStatistics => chkSaveStats.Enabled && chkSaveStats.Checked; public bool LoadLatestSave => chkLoadLatestSave.Enabled && chkLoadLatestSave.Checked; public bool ExtraParametersOnly => chkExtraParamsOnly.Checked; public bool ScreenFilter => chkScreenFilter.Checked; public bool ShouldSaveAdditionalFiles() { return !m_demoChangedAdditionalFiles; } private void chkRecord_CheckedChanged(object sender, EventArgs e) { txtDescription.Enabled = chkRecord.Checked; cmbDemo.Enabled = false; chkDemo.CheckedChanged -= chkDemo_CheckedChanged; chkDemo.Checked = false; chkDemo.CheckedChanged += chkDemo_CheckedChanged; } private void chkDemo_CheckedChanged(object sender, EventArgs e) { txtDescription.Enabled = false; cmbDemo.Enabled = chkDemo.Checked; chkRecord.CheckedChanged -= chkRecord_CheckedChanged; chkRecord.Checked = false; chkRecord.CheckedChanged += chkRecord_CheckedChanged; HandleDemoChange(); } private void cmbSourcePorts_SelectedIndexChanged(object sender, EventArgs e) { HandleSourcePortSelectionChange(); } private void HandleSourcePortSelectionChange(bool resetAdditionalFiles = true) { if (cmbSourcePorts.SelectedItem != null && GameFile != null) { ISourcePortData sourcePort = cmbSourcePorts.SelectedItem as ISourcePortData; chkSaveStats.Enabled = SaveStatisticsSupported(sourcePort); chkLoadLatestSave.Enabled = LoadLatestSaveSupported(sourcePort); SetAdditionalFiles(resetAdditionalFiles); PopulateDemos(); } m_lastSourcePort = SelectedSourcePort; } private void PopulateDemos() { ISourcePortData sourcePort = cmbSourcePorts.SelectedItem as ISourcePortData; if (GameFile.GameFileID.HasValue) { IEnumerable demoFiles = m_adapter.GetFiles(GameFile, FileType.Demo) .Where(x => x.SourcePortID == sourcePort.SourcePortID); AutoCompleteCombo.SetAutoCompleteCustomSource(cmbDemo, demoFiles.ToList(), typeof(IFileData), "Description"); } } private bool SaveStatisticsSupported(ISourcePortData sourcePort) { return sourcePort.GetFlavor().StatisticsSupported(); } private bool LoadLatestSaveSupported(ISourcePortData sourcePort) { return sourcePort.GetFlavor().LoadSaveGameSupported(); } private void chkMap_CheckedChanged(object sender, EventArgs e) { cmbMap.Enabled = cmbSkill.Enabled = chkMap.Checked; } private void ctrlFiles_NewItemNeeded(object sender, AdditionalFilesEventArgs e) { using (FileSelectForm fileSelect = new FileSelectForm()) { fileSelect.Initialize(m_adapter, m_additionalFileViews); fileSelect.MultiSelect = true; fileSelect.StartPosition = FormStartPosition.CenterParent; if (fileSelect.ShowDialog(this) == DialogResult.OK) { IGameFile[] selectedFiles = fileSelect.SelectedFiles.Except(new [] { GameFile }).Union(GetAdditionalFiles()).ToArray(); if (selectedFiles.Length > 0) { e.NewItems = selectedFiles.Cast().ToList(); try { ResetSpecificFilesSelections(selectedFiles); } catch (FileNotFoundException ex) { MessageBox.Show(this, string.Format("The Game File {0} is missing from the library.", ex.FileName), "File Not Found"); } catch (Exception ex) { Util.DisplayUnexpectedException(this, ex); } } } SetColumnConfigToMain(fileSelect.TabViews); } } private void SetColumnConfigToMain(ITabView[] tabViews) { foreach (ITabView tabFrom in tabViews) { var tabTo = m_additionalFileViews.FirstOrDefault(x => x.Title == tabFrom.Title); if (tabTo != null && tabTo.GameFileViewControl is IGameFileColumnView columnView) tabTo.SetColumnConfig(columnView.ColumnFields, tabFrom.GetColumnConfig().ToArray()); } } private void CtrlFiles_ItemRemoving(object sender, AdditionalFilesEventArgs e) { if (e.Item.Equals(GameFile)) { MessageBox.Show(this, string.Format("Cannot remove {0}. This is the file you will be launching!", GameFile.FileName), "Cannot Remove", MessageBoxButtons.OK, MessageBoxIcon.Error); e.Cancel = true; } else { if (SpecificFiles != null) SpecificFiles = SpecificFiles.Except(GetSupportedFiles((IGameFile)e.Item)).ToArray(); } } private void CtrlFiles_ItemAdded(object sender, AdditionalFilesEventArgs e) { } private void CtrlFiles_ItemRemoved(object sender, AdditionalFilesEventArgs e) { } private void ResetSpecificFilesSelections(IGameFile[] selectedFiles) { foreach (IGameFile gameFile in selectedFiles) { if (SpecificFiles == null) SpecificFiles = GetSupportedFiles(GameFile); SpecificFiles = SpecificFiles.Union(GetSupportedFiles(gameFile)).ToArray(); } } private string[] GetSupportedFiles(IGameFile gameFile) { return SpecificFilesForm.GetSupportedFiles(m_appConfig.GameFileDirectory.GetFullPath(), gameFile, SourcePortData.GetSupportedExtensions(SelectedSourcePort)); } private void cmbIwad_SelectedIndexChanged(object sender, EventArgs e) { HandleIwadSelectionChanged(); } private void HandleIwadSelectionChanged(bool resetAdditionalFiles = true) { SetAdditionalFiles(resetAdditionalFiles); if (GameFile == null || (GameFile != null && string.IsNullOrEmpty(GameFile.Map))) SetMapsFromIwad(); m_lastIwad = SelectedIWad; } private void SetMapsFromIwad() { if (SelectedIWad == null) return; var gameFileIwad = m_adapter.GetGameFileIWads().FirstOrDefault(x => x.GameFileID == SelectedIWad.GameFileID); if (gameFileIwad != null) AutoCompleteCombo.SetAutoCompleteCustomSource(cmbMap, MapSplit(gameFileIwad), null, null); } private void SetAdditionalFiles(bool reset) { if (m_handler == null) return; if (reset && m_lastIwad != null && m_lastSourcePort != null) { m_handler.CalculateAdditionalFiles(m_lastIwad, m_lastSourcePort); m_handler.CalculateAdditionalFiles(SelectedIWad, cmbSourcePorts.SelectedItem as ISourcePortData); ResetSpecificFilesSelections(m_handler.GetCurrentAdditionalNewFiles().ToArray()); } ctrlFiles.SetDataSource(m_handler.GetCurrentAdditionalFiles()); ctrlFiles.Refresh(); //the port or iwad in () may have changed so invalidate to force update } private bool ShouldRecalculateAdditionalFiles() { //For files that have been launcned before: Selected index changed fires a lot on init, so ignore those //If the file has never been launched, then we need to set the additional files on init return SelectedGameProfile != null && !SelectedGameProfile.SettingsSaved; } private void lnkSpecific_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { SpecificFilesForm form = new SpecificFilesForm(); form.StartPosition = FormStartPosition.CenterParent; List gameFiles = new List(); gameFiles.AddRange(GetAdditionalFiles()); form.Initialize(m_appConfig.GameFileDirectory, gameFiles, SourcePortData.GetSupportedExtensions(SelectedSourcePort), SpecificFiles); if (form.ShowDialog(this) == DialogResult.OK) { SpecificFiles = form.GetSpecificFiles(); } } private void lnkMore_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { StatsInfo form = new StatsInfo(); form.StartPosition = FormStartPosition.CenterParent; form.ShowDialog(this); } private void lnkLoadSaveMore_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { SaveInfo form = new SaveInfo(); form.StartPosition = FormStartPosition.CenterParent; form.ShowDialog(this); } public bool SettingsValid(out string error) { error = null; if (chkRecord.Checked && string.IsNullOrEmpty(txtDescription.Text)) error = "Please enter a description for the demo to record."; else if (SelectedSourcePort == null) error = "A source port must be selected."; else if (chkMap.Checked && SelectedMap == null) error = "A map must be selected."; else if (chkMap.Checked && SelectedSkill == null) error = "A skill must be selected"; else if (chkDemo.Checked && SelectedDemo == null) error = "A demo must be selected."; else if (SelectedGameProfile == null) error = "A profile must be selected."; return error == null; } private void btnOK_Click(object sender, EventArgs e) { Accept(); } public void Accept() { if (SettingsValid(out string err)) { DialogResult = DialogResult.OK; } else { MessageBox.Show(this, err, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); DialogResult = DialogResult.None; } } private void ctrlFiles_CellFormatting(object sender, AdditionalFilesEventArgs e) { IGameFile gameFile = e.Item as IGameFile; IGameFile iwad = SelectedIWad; ISourcePortData port = SelectedSourcePort; if (iwad != null && m_handler.IsIWadFile(gameFile)) e.DisplayText = string.Format("{0} ({1})", gameFile.FileName, Path.GetFileNameWithoutExtension(iwad.FileName)); if (port != null && m_handler.IsSourcePortFile(gameFile)) e.DisplayText = string.Format("{0} ({1})", gameFile.FileName, port.Name); } private void btnSaveSettings_Click(object sender, EventArgs e) { if (SelectedGameProfile == null) MessageBox.Show(this, "A profile must be selected to save.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); else SaveSettings?.Invoke(this, EventArgs.Empty); } private void lnkOpenDemo_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { GenericFileView genericFileView = new GenericFileView(); List demoFiles = genericFileView.CreateFileAssociation(this, m_adapter, m_appConfig.DemoDirectory, FileType.Demo, GameFile, cmbSourcePorts.SelectedItem as ISourcePortData); if (demoFiles.Count > 0) { PopulateDemos(); SelectedSourcePort = m_adapter.GetSourcePort(demoFiles.First().SourcePortID); cmbDemo.SelectedValue = demoFiles.First().FileID; chkDemo.Checked = true; //will trigger HandleDemoChange } } private void cmbDemo_SelectedIndexChanged(object sender, EventArgs e) { HandleDemoChange(); } private void HandleDemoChange() { if (chkDemo.Checked && cmbDemo.SelectedItem != null) { var file = cmbDemo.SelectedItem as IFileData; var parser = DemoUtil.GetDemoParser(Path.Combine(m_appConfig.DemoDirectory.GetFullPath(), file.FileName)); if (parser != null) { m_handler.Reset(); SetAdditionalFiles(true); string[] requiredFiles = parser.GetRequiredFiles(); List unavailable = new List(); List iwads = new List(); List gameFiles = GetGameFiles(requiredFiles, unavailable, iwads); ctrlFiles.SetDataSource(gameFiles); if (iwads.Count > 0) SelectedIWad = iwads.First(); if (unavailable.Count > 0) { TextBoxForm form = new TextBoxForm(true, MessageBoxButtons.OK) { StartPosition = FormStartPosition.CenterParent, Title = "Not Found", HeaderText = "The following required files were not found:", DisplayText = string.Join(Environment.NewLine, unavailable.ToArray()) }; form.ShowDialog(this); } m_demoChangedAdditionalFiles = true; ResetSpecificFilesSelections(ctrlFiles.GetFiles().Cast().ToArray()); //don't use the handler in this case, we are overriding it } } else { m_demoChangedAdditionalFiles = false; } } private void lnkPreviewLaunchParameters_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { OnPreviewLaunchParameters?.Invoke(this, new EventArgs()); } private void lnkCustomParameters_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { throw new NotImplementedException(); } private void lnkFilterSettings_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { FilterSettingsForm form; try { form = new FilterSettingsForm(m_filterSettings); } catch { m_filterSettings = CreateDefaultFilterSettings(); //this can happen due to an update and the xml not having the property, reset to default form = new FilterSettingsForm(m_filterSettings); } form.StartPosition = FormStartPosition.CenterParent; if (form.ShowDialog(this) == DialogResult.OK) { m_filterSettings = form.GetFilterSettings(); m_filterSettings.Enabled = chkScreenFilter.Checked; WriteFilterSettings(m_filterSettings); } } private static readonly string s_filterFile = "FilterSettings.xml"; private static string GetFilterFile() { return Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), s_filterFile); } private void WriteFilterSettings(ScreenFilter settings) { try { XmlSerializer x = new XmlSerializer(typeof(ScreenFilter)); using (FileStream fs = new FileStream(GetFilterFile(), FileMode.Create)) x.Serialize(fs, settings); } catch { //oh well, at least we tried } } public ScreenFilter GetFilterSettings() { try { XmlSerializer x = new XmlSerializer(typeof(ScreenFilter)); using (FileStream fs = new FileStream(GetFilterFile(), FileMode.Open)) return (ScreenFilter)x.Deserialize(fs); } catch { if (File.Exists(GetFilterFile())) File.Delete(GetFilterFile()); return CreateDefaultFilterSettings(); } } private ScreenFilter CreateDefaultFilterSettings() { return new ScreenFilter() { Type = ScreenFilterType.Ellipse, Opacity = 0.5f, LineThickness = 1, BlockSize = 4, SpacingX = 0, SpacingY = 0, Stagger = true, ScanlineSpacing = 4, VerticalScanlines = true, HorizontalScanlines = true, Enabled = chkScreenFilter.Checked }; } private void chkScreenFilter_CheckedChanged(object sender, EventArgs e) { m_filterSettings = GetFilterSettings(); m_filterSettings.Enabled = chkScreenFilter.Checked; WriteFilterSettings(m_filterSettings); } private bool RenameGameProfile(int gameProfileID, string name) { if (!GameProfileUtil.IsValidProfileName(m_adapter, GameFile, m_globalProfiles, gameProfileID, name, out var error)) { MessageBox.Show(this, error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } SelectedGameProfile.Name = name; m_adapter.UpdateGameProfile(SelectedGameProfile); cmbProfiles.SelectedIndexChanged -= CmbProfiles_SelectedIndexChanged; LoadProfiles(); cmbProfiles.SelectedValue = gameProfileID; cmbProfiles.SelectedIndexChanged += CmbProfiles_SelectedIndexChanged; return true; } private static TextBoxForm CreateProfileTextBoxForm(string displayText, bool showCheckBox) { TextBoxForm form = new TextBoxForm(false, MessageBoxButtons.OKCancel); form.SetMaxLength(48); form.DisplayText = displayText; form.StartPosition = FormStartPosition.CenterParent; form.Title = "Enter Profile Name"; form.SelectDisplayText(0, form.DisplayText.Length); if (showCheckBox) form.SetCheckBox("Copy current profile"); return form; } public void UpdateGameProfile(IGameProfile gameProfile) { gameProfile.SettingsSaved = true; gameProfile.SourcePortID = gameProfile.IWadID = null; if (SelectedSourcePort != null) gameProfile.SourcePortID = SelectedSourcePort.SourcePortID; if (SelectedIWad != null) gameProfile.IWadID = SelectedIWad.IWadID; if (SelectedMap != null) gameProfile.SettingsMap = SelectedMap; else gameProfile.SettingsMap = string.Empty; //this setting can be turned off if (SelectedSkill != null) gameProfile.SettingsSkill = SelectedSkill; if (ExtraParameters != null) gameProfile.SettingsExtraParams = ExtraParameters; gameProfile.SettingsStat = SaveStatistics; gameProfile.SettingsLoadLatestSave = LoadLatestSave; gameProfile.SettingsExtraParamsOnly = ExtraParametersOnly; if (!ShouldSaveAdditionalFiles()) return; var additionalGameFiles = GetAdditionalFiles(); if (gameProfile.IsGlobal) additionalGameFiles = additionalGameFiles .Where(x => x.GameFileID != GameFile.GameFileID).ToList(); gameProfile.SettingsFiles = string.Join(";", additionalGameFiles.Select(x => x.FileName).ToArray()); gameProfile.SettingsFilesIWAD = string.Join(";", GetIWadAdditionalFiles().Select(x => x.FileName).ToArray()); gameProfile.SettingsFilesSourcePort = string.Join(";", GetSourcePortAdditionalFiles().Select(x => x.FileName).ToArray()); if (SpecificFiles != null) gameProfile.SettingsSpecificFiles = string.Join(";", SpecificFiles); else gameProfile.SettingsSpecificFiles = string.Empty; //this setting can be turned off } private void CmbProfiles_SelectedIndexChanged(object sender, EventArgs e) { SetGameProfile(SelectedGameProfile); } private void newGlobalProfileToolStripMenuItem_Click(object sender, EventArgs e) { CreateNewProfile(true); } private void newProfileToolStripMenuItem_Click(object sender, EventArgs e) { CreateNewProfile(false); } private void CreateNewProfile(bool global) { TextBoxForm form = CreateProfileTextBoxForm(global ? "New Global Profile" :"New Profile", true); bool success = false; while (!success && form.ShowDialog(this) == DialogResult.OK) { success = GameProfileUtil.IsValidProfileName(m_adapter, GameFile, m_globalProfiles, -1, form.DisplayText.Trim(), out var error); if (success) { GameProfile gameProfile; if (global) gameProfile = GameProfile.CreateGlobalProfile(form.DisplayText); else gameProfile = new GameProfile(GameFile.GameFileID.Value, form.DisplayText); GameProfile.ApplyDefaultsToProfile(gameProfile, m_appConfig); if (GameFile != null && IsIwad(GameFile)) gameProfile.IWadID = GameFile.IWadID.Value; if (form.CheckBoxChecked) UpdateGameProfile(gameProfile); m_adapter.InsertGameProfile(gameProfile); cmbProfiles.SelectedIndexChanged -= CmbProfiles_SelectedIndexChanged; var profiles = LoadProfiles(); cmbProfiles.SelectedIndexChanged += CmbProfiles_SelectedIndexChanged; cmbProfiles.SelectedValue = profiles.Max(x => x.GameProfileID); } else { MessageBox.Show(this, error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } private void editProfileToolStripMenuItem_Click(object sender, EventArgs e) { if (SelectedGameProfile is GameFile) { MessageBox.Show(this, "The default profile cannot be renamed.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } TextBoxForm form = CreateProfileTextBoxForm(SelectedGameProfile.Name, false); int gameProfileID = SelectedGameProfile.GameProfileID; bool success = false; while (!success && form.ShowDialog(this) == DialogResult.OK) { success = RenameGameProfile(gameProfileID, form.DisplayText.Trim()); } } private void deleteProfileToolStripMenuItem_Click(object sender, EventArgs e) { if (SelectedGameProfile == null) return; if (SelectedGameProfile is GameFile) { MessageBox.Show(this, "The default profile cannot be deleted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (MessageBox.Show(this, $"Are you sure you want to delete {SelectedGameProfile.Name}?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes) return; m_adapter.DeleteGameProfile(SelectedGameProfile.GameProfileID); var profiles = LoadProfiles(); if (profiles.Count == 0) return; cmbProfiles.SelectedItem = GameFile; } private List GetGameFiles(string[] fileNames, List unavailable, List iwads) { List gameFiles = new List(); var knowniwads = m_adapter.GetGameFileIWads(); foreach (string file in fileNames) { // This currently doesn't work for unmanaged files var gameFile = m_adapter.GetGameFile(file.Replace(Path.GetExtension(file), ".zip")); if (gameFile == null) { unavailable.Add(file); continue; } if (knowniwads.Any(x => x.FileName.Equals(gameFile.FileName, StringComparison.InvariantCultureIgnoreCase))) iwads.Add(gameFile); else gameFiles.Add(gameFile); } return gameFiles; } private void TxtParameters_Click(object sender, EventArgs e) { TextBoxForm form = new TextBoxForm(true, MessageBoxButtons.OKCancel) { Title = "Extra Parameters", DisplayText = txtParameters.Text, StartPosition = FormStartPosition.CenterParent, AcceptButton = null }; form.SelectDisplayText(0, 0); if (form.ShowDialog(this) == DialogResult.OK) txtParameters.Text = form.DisplayText; } private void btnProfileMenu_Click(object sender, EventArgs e) { Stylizer.StylizeControl(toolStripDropDownButton1.DropDown, DesignMode); toolStripDropDownButton1.ShowDropDown(); } } } ================================================ FILE: DoomLauncher/Forms/PlayForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 539, 17 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/PlayRandomForm.Designer.cs ================================================ namespace DoomLauncher.Forms { partial class PlayRandomForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PlayRandomForm)); this.cmbType = new System.Windows.Forms.ComboBox(); this.btnOK = new System.Windows.Forms.Button(); this.btnCancel = new System.Windows.Forms.Button(); this.btnGenerate = new System.Windows.Forms.Button(); this.lblText = new System.Windows.Forms.Label(); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.panel1 = new System.Windows.Forms.Panel(); this.chkShowPlayDialog = new DoomLauncher.CCheckBox(); this.chkRandomMap = new DoomLauncher.CCheckBox(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.panel1.SuspendLayout(); this.SuspendLayout(); // // cmbType // this.cmbType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbType.FormattingEnabled = true; this.cmbType.Location = new System.Drawing.Point(19, 18); this.cmbType.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.cmbType.Name = "cmbType"; this.cmbType.Size = new System.Drawing.Size(256, 28); this.cmbType.TabIndex = 0; // // btnOK // this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(262, 240); this.btnOK.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(88, 35); this.btnOK.TabIndex = 2; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; this.btnOK.Click += new System.EventHandler(this.btnOK_Click); // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(357, 240); this.btnCancel.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(82, 35); this.btnCancel.TabIndex = 3; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // btnGenerate // this.btnGenerate.Location = new System.Drawing.Point(284, 18); this.btnGenerate.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.btnGenerate.Name = "btnGenerate"; this.btnGenerate.Size = new System.Drawing.Size(158, 30); this.btnGenerate.TabIndex = 4; this.btnGenerate.Text = "Generate"; this.btnGenerate.UseVisualStyleBackColor = true; this.btnGenerate.Click += new System.EventHandler(this.btnGenerate_Click); // // lblText // this.lblText.AutoSize = true; this.lblText.Location = new System.Drawing.Point(19, 96); this.lblText.Name = "lblText"; this.lblText.Size = new System.Drawing.Size(58, 20); this.lblText.TabIndex = 5; this.lblText.Text = "Output"; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Controls.Add(this.panel1, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(454, 323); this.tblMain.TabIndex = 6; // // panel1 // this.panel1.Controls.Add(this.chkShowPlayDialog); this.panel1.Controls.Add(this.cmbType); this.panel1.Controls.Add(this.lblText); this.panel1.Controls.Add(this.chkRandomMap); this.panel1.Controls.Add(this.btnGenerate); this.panel1.Controls.Add(this.btnOK); this.panel1.Controls.Add(this.btnCancel); this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel1.Location = new System.Drawing.Point(0, 40); this.panel1.Margin = new System.Windows.Forms.Padding(0); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(454, 283); this.panel1.TabIndex = 0; // // chkShowPlayDialog // this.chkShowPlayDialog.AutoSize = true; this.chkShowPlayDialog.Location = new System.Drawing.Point(197, 62); this.chkShowPlayDialog.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.chkShowPlayDialog.Name = "chkShowPlayDialog"; this.chkShowPlayDialog.Size = new System.Drawing.Size(157, 24); this.chkShowPlayDialog.TabIndex = 6; this.chkShowPlayDialog.Text = "Show Play Dialog"; // // chkRandomMap // this.chkRandomMap.AutoSize = true; this.chkRandomMap.Checked = true; this.chkRandomMap.CheckState = System.Windows.Forms.CheckState.Checked; this.chkRandomMap.Location = new System.Drawing.Point(19, 62); this.chkRandomMap.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.chkRandomMap.Name = "chkRandomMap"; this.chkRandomMap.Size = new System.Drawing.Size(180, 24); this.chkRandomMap.TabIndex = 1; this.chkRandomMap.Text = "Select Random Map"; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(454, 40); this.titleBar.TabIndex = 1; this.titleBar.Title = "Play Random"; // // PlayRandomForm // this.AcceptButton = this.btnOK; this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(454, 323); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.Name = "PlayRandomForm"; this.Text = "Play Random"; this.tblMain.ResumeLayout(false); this.panel1.ResumeLayout(false); this.panel1.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.ComboBox cmbType; private CCheckBox chkRandomMap; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnGenerate; private System.Windows.Forms.Label lblText; private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.Panel panel1; private Controls.TitleBarControl titleBar; private CCheckBox chkShowPlayDialog; } } ================================================ FILE: DoomLauncher/Forms/PlayRandomForm.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Linq; using System.ComponentModel; using System.Windows.Forms; using System.Collections.Generic; using DoomLauncher.DataSources; using static System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar; namespace DoomLauncher.Forms { public enum PlayRandomType { [Description("Any")] Any, [Description("Unplayed")] Unplayed, [Description("Unrated")] Unrated, [Description("Current Tab")] CurrentTab, } public partial class PlayRandomForm : Form { public IGameFile GeneratedGameFile { get; private set; } public string GeneratedMap { get; private set; } public PlayRandomType SelectedType => (PlayRandomType)cmbType.SelectedIndex; public bool ShowPlayDialog => chkShowPlayDialog.Checked; private ITabView m_tabView; public PlayRandomForm() { InitializeComponent(); lblText.Text = string.Empty; List items = new List(); foreach (var item in Enum.GetValues(typeof(PlayRandomType))) items.Add(((PlayRandomType)item).GetDescription()); cmbType.DataSource = items; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public void Initialize(ITabView tabView, AppConfiguration config, IGameFile initGameFile = null, string initMap = null) { GeneratedGameFile = initGameFile; GeneratedMap = initMap; m_tabView = tabView; if (config.ShowPlayDialog) chkShowPlayDialog.Visible = false; chkShowPlayDialog.Checked = config.ShowPlayDialog; if (GeneratedGameFile != null) SetData(); } private void btnGenerate_Click(object sender, EventArgs e) { GeneratedRandom(); } private void btnOK_Click(object sender, EventArgs e) { if (GeneratedGameFile != null) return; GeneratedRandom(); } private void GeneratedRandom() { IEnumerable gameFiles = GetGameFilesBySelectedType(); if (!gameFiles.Any()) { lblText.Text = "No files found."; return; } Random rand = new Random(DateTime.Now.Millisecond); int index = rand.Next() % gameFiles.Count(); GeneratedGameFile = gameFiles.ElementAt(index); GameFileGetOptions options = new GameFileGetOptions(); options.SearchField = new GameFileSearchField(GameFileFieldType.GameFileID, GeneratedGameFile.GameFileID.ToString()); GeneratedGameFile = DataCache.Instance.DataSourceAdapter.GetGameFiles(options).FirstOrDefault(); GeneratedMap = null; if (chkRandomMap.Checked && GetRandomMap(rand, GeneratedGameFile, out string map)) GeneratedMap = map; SetData(); } private void SetData() { if (GeneratedGameFile == null) { lblText.Text = $"Current tab ({m_tabView.Key}) is not supported in this context."; return; } if (GeneratedMap != null) { lblText.Text = $"{GeneratedGameFile.FileNameNoPath} - {GeneratedMap}"; return; } lblText.Text = $"{GeneratedGameFile.FileNameNoPath}"; } private IEnumerable GetGameFilesBySelectedType() { switch (SelectedType) { case PlayRandomType.Any: return GetGameFiles(null); case PlayRandomType.Unplayed: return GetGameFiles(GameFileFieldType.LastPlayed); case PlayRandomType.Unrated: return GetGameFiles(GameFileFieldType.Rating); case PlayRandomType.CurrentTab: return m_tabView.GameFileViewControl.DataSource; default: break; } return Array.Empty(); } private IEnumerable GetGameFiles(GameFileFieldType? field) { IDataSourceAdapter adapter = DataCache.Instance.DataSourceAdapter; IGameFileGetOptions options = new GameFileGetOptions(new GameFileFieldType[] { GameFileFieldType.GameFileID, GameFileFieldType.LastPlayed, GameFileFieldType.Rating }); IEnumerable gameFiles = adapter.GetGameFiles(options); if (field != null) { if (field.Value == GameFileFieldType.LastPlayed) gameFiles = gameFiles.Where(x => !x.LastPlayed.HasValue); else if (field.Value == GameFileFieldType.Rating) gameFiles = gameFiles.Where(x => !x.Rating.HasValue); } return gameFiles; } private bool GetRandomMap(Random rand, IGameFile gameFile, out string map) { map = string.Empty; string[] maps = GameFile.GetMaps(gameFile); if (maps.Length == 0) return false; map = maps[rand.Next() % maps.Length]; return true; } } } ================================================ FILE: DoomLauncher/Forms/PlayRandomForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/ProgressBarForm.Designer.cs ================================================ namespace DoomLauncher { partial class ProgressBarForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ProgressBarForm)); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.btnCancel = new System.Windows.Forms.Button(); this.panel1 = new System.Windows.Forms.Panel(); this.lblProcess = new System.Windows.Forms.Label(); this.progressBar1 = new DoomLauncher.CProgressBar(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.panel1.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.btnCancel, 0, 2); this.tblMain.Controls.Add(this.panel1, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.Size = new System.Drawing.Size(482, 141); this.tblMain.TabIndex = 0; // // btnCancel // this.btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Right; this.btnCancel.Location = new System.Drawing.Point(378, 107); this.btnCancel.Margin = new System.Windows.Forms.Padding(4); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(100, 28); this.btnCancel.TabIndex = 0; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); // // panel1 // this.panel1.Controls.Add(this.lblProcess); this.panel1.Controls.Add(this.progressBar1); this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; this.panel1.Location = new System.Drawing.Point(0, 32); this.panel1.Margin = new System.Windows.Forms.Padding(0); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(482, 70); this.panel1.TabIndex = 1; // // lblProcess // this.lblProcess.AutoSize = true; this.lblProcess.Location = new System.Drawing.Point(16, 9); this.lblProcess.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblProcess.Name = "lblProcess"; this.lblProcess.Size = new System.Drawing.Size(84, 16); this.lblProcess.TabIndex = 1; this.lblProcess.Text = "Processing..."; // // progressBar1 // this.progressBar1.Location = new System.Drawing.Point(16, 28); this.progressBar1.Margin = new System.Windows.Forms.Padding(4); this.progressBar1.Name = "progressBar1"; this.progressBar1.Size = new System.Drawing.Size(453, 28); this.progressBar1.TabIndex = 0; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(482, 32); this.titleBar.TabIndex = 2; this.titleBar.Title = "Progress"; // // ProgressBarForm // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(482, 141); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.Name = "ProgressBarForm"; this.ShowIcon = false; this.Text = "Progress"; this.tblMain.ResumeLayout(false); this.panel1.ResumeLayout(false); this.panel1.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Label lblProcess; private Controls.TitleBarControl titleBar; private TableLayoutPanelDB tblMain; private CProgressBar progressBar1; } } ================================================ FILE: DoomLauncher/Forms/ProgressBarForm.cs ================================================ using System; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public partial class ProgressBarForm : Form { public event EventHandler Cancelled; public ProgressBarForm() { InitializeComponent(); Load += ProgressBarForm_Load; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } void ProgressBarForm_Load(object sender, EventArgs e) { if (Owner != null) { Location = new Point(Owner.Location.X + Owner.Width / 2 - Width / 2, Owner.Location.Y + Owner.Height / 2 - Height / 2); } } public void SetCancelAllowed(bool set) { btnCancel.Visible = set; titleBar.CanClose = set; } public ProgressBarStyle ProgressBarStyle { get { return progressBar1.Style; } set { progressBar1.Style = value; } } public int Value { get { return progressBar1.Value; } set { progressBar1.Value = value; } } public int Minimum { get { return progressBar1.Minimum; } set { progressBar1.Minimum = value; } } public int Maximum { get { return progressBar1.Maximum; } set { progressBar1.Maximum = value; } } public string DisplayText { get { return lblProcess.Text; } set { lblProcess.Text = value; } } public string Title { get { return titleBar.Title; } set { titleBar.Title = value; } } private void btnCancel_Click(object sender, EventArgs e) { Cancelled?.Invoke(this, new EventArgs()); } } } ================================================ FILE: DoomLauncher/Forms/ProgressBarForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAyOUWYBgwU8xceKOu/vbzqwsLB6q+vruqysrHqsrKx6rKyseqysrHqr6+u6sLCweq9vbrqFRcc6xAY IvNATmCXQExd/zI7Rf8aIi7/hoSE/8rJyP+5ubn/t7e3/7e3t/+3t7f/t7e3/7m5uf/KyMj/iIaH/wQF CP8eJCn/ICcw/yAnLusuNDz/Jiw0/wYIDP+npqX/0dHQ/7u7u/+8vLz/vLy8/7u7u//Qz8//qKen/xUb I/8dISf/KCwx/yUqMesvNDrqISIk/w0MDP8tLzL/3N3c/8TEw//BwcH/wMDA/8DAwP/BwcH/wsLB/93b 2v8+R1H/Iys1/zI4P/8pLDHqMzg/6i4wNP8jIyT/Gx4i/6Oio//k4+L/v7++/83NzP/NzMz/v7++/+Xk 4v+hoaH/HCIp/y83QP8wNkH/MTY86jA0N+o7RE3/O0JM/x0iKv8pKiv/9vTx/+Tk5P++vr//wMDA/+jn 5//19PP/Ki0x/w4QFP82PUb/Tltp/0JLVuo2OT3qTlll/1Rfb/9JUlv/GiIp/1tiaP/U1NT/sK+u/7Kx sP/Ly8v/U1hd/xofJf8vMjj/Z3qO/3iMoP8+SFTqNzk86lxodP+QobT/Xmp4/zk9Qv8gKTL/LTQ9/2Rs dv9YXWH/Ki80/x8kK/84ODr/RlBb/2J0hv9DS1b/O0JL6klMUepVYG3/coCO/1JYYf8tMjf/QERI/0dO WP8/R1H/Nz5H/0hPWP88Qkn/LC8y/0dMUv84PkX/O0BG/0FGTupMUVjqU15r/z9HUf9SWF//S09T/y01 QP8uMjf/QUhP/zxFTv8uMzr/R05X/11fYf83ODr/UVpm/01YYv9AQkXqVV1m6k9WYf9JUFn/QUhP/+Hh 4f+EiY//XmBk/3Z9hf+Lk53/TlRZ/3l3d/+sr7T/Slln/1ZeZ/9GS1L/PT5B6lpgZ+pPU1r/RkhK/z0/ Qv/DxMT/7Ovp/9HR0P/Gxsb/z87O/83Ozv/m5OL/rLK5/zlBSf9APTv/SElL/256iepZX2bqUlZZ/0ZH Sf9SUU//l5iY/9HQ0P+wsLH/vr28/5mcnv/R0tP//////6+xtP8uNT3/S0tM/0RFR/94hJLqYmhv61da XP9OTlD/TExP/76+vv/b29z/zc3O/8nLzf/O0NL/xsbG/9XV0/+xt77/SVJe/1VYXv9ZX2X/T1RZ611g ZP9hY2j/bHN7/3J9iv+FkJz/TlBU/2ZtdP9veof/iZWl/3F8iP9KUVf/e4iW/252gP9YXGH/VldZ/2Bl bf9iZ2+VXV9n83R+jOx6g4/qZmlv6oqZqep0gpHqW2Jt6mZxgOpiaHHqZm536mRsd+psdH/qYmhy611h Z/NiZmuUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAA== ================================================ FILE: DoomLauncher/Forms/SaveInfo.Designer.cs ================================================  namespace DoomLauncher.Forms { partial class SaveInfo { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SaveInfo)); this.tblInfo = new System.Windows.Forms.TableLayoutPanel(); this.lblSave = new System.Windows.Forms.Label(); this.pbInfo1 = new System.Windows.Forms.PictureBox(); this.label3 = new System.Windows.Forms.Label(); this.tblContainer = new System.Windows.Forms.TableLayoutPanel(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblInfo.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo1)).BeginInit(); this.tblContainer.SuspendLayout(); this.SuspendLayout(); // // tblInfo // this.tblInfo.ColumnCount = 2; this.tblInfo.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblInfo.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInfo.Controls.Add(this.lblSave, 1, 1); this.tblInfo.Controls.Add(this.pbInfo1, 0, 0); this.tblInfo.Controls.Add(this.label3, 1, 0); this.tblInfo.Dock = System.Windows.Forms.DockStyle.Fill; this.tblInfo.Location = new System.Drawing.Point(0, 26); this.tblInfo.Margin = new System.Windows.Forms.Padding(0); this.tblInfo.Name = "tblInfo"; this.tblInfo.RowCount = 2; this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInfo.Size = new System.Drawing.Size(384, 85); this.tblInfo.TabIndex = 4; // // lblSave // this.lblSave.AutoSize = true; this.lblSave.Location = new System.Drawing.Point(35, 40); this.lblSave.Name = "lblSave"; this.lblSave.Size = new System.Drawing.Size(322, 26); this.lblSave.TabIndex = 3; this.lblSave.Text = "Loading a save file is supported for all ports, except the Doomsday Engine."; // // pbInfo1 // this.pbInfo1.Dock = System.Windows.Forms.DockStyle.Fill; this.pbInfo1.InitialImage = null; this.pbInfo1.Location = new System.Drawing.Point(0, 0); this.pbInfo1.Margin = new System.Windows.Forms.Padding(0); this.pbInfo1.Name = "pbInfo1"; this.pbInfo1.Size = new System.Drawing.Size(32, 40); this.pbInfo1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; this.pbInfo1.TabIndex = 0; this.pbInfo1.TabStop = false; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(35, 7); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(343, 26); this.label3.TabIndex = 2; this.label3.Text = "The \'Load Latest Save\' option will become available when a supported source port " + "is selected."; // // tblContainer // this.tblContainer.ColumnCount = 1; this.tblContainer.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblContainer.Controls.Add(this.titleBar, 0, 0); this.tblContainer.Controls.Add(this.tblInfo, 0, 1); this.tblContainer.Dock = System.Windows.Forms.DockStyle.Fill; this.tblContainer.Location = new System.Drawing.Point(0, 0); this.tblContainer.Margin = new System.Windows.Forms.Padding(0); this.tblContainer.Name = "tblContainer"; this.tblContainer.RowCount = 2; this.tblContainer.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblContainer.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblContainer.Size = new System.Drawing.Size(384, 111); this.tblContainer.TabIndex = 5; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(384, 26); this.titleBar.TabIndex = 0; this.titleBar.Title = "Load Latest Save"; // // SaveInfo // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(384, 111); this.Controls.Add(this.tblContainer); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "SaveInfo"; this.Text = "Load Latest Save"; this.tblInfo.ResumeLayout(false); this.tblInfo.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo1)).EndInit(); this.tblContainer.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblInfo; private System.Windows.Forms.Label lblSave; private System.Windows.Forms.PictureBox pbInfo1; private System.Windows.Forms.Label label3; private System.Windows.Forms.TableLayoutPanel tblContainer; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/SaveInfo.cs ================================================ using System.Windows.Forms; namespace DoomLauncher.Forms { public partial class SaveInfo : Form { public SaveInfo() { InitializeComponent(); pbInfo1.Image = DoomLauncher.Properties.Resources.bon2b; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } } } ================================================ FILE: DoomLauncher/Forms/SaveInfo.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/ScreenshotEditForm.Designer.cs ================================================ namespace DoomLauncher.Forms { partial class ScreenshotEditForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ScreenshotEditForm)); this.tblData = new DoomLauncher.TableLayoutPanelDB(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.txtDescription = new System.Windows.Forms.TextBox(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.button1 = new System.Windows.Forms.Button(); this.btnSave = new System.Windows.Forms.Button(); this.label3 = new System.Windows.Forms.Label(); this.cmbMap = new System.Windows.Forms.ComboBox(); this.txtTitle = new System.Windows.Forms.TextBox(); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.chkMap = new DoomLauncher.CCheckBox(); this.tblData.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.tblMain.SuspendLayout(); this.SuspendLayout(); // // tblData // this.tblData.ColumnCount = 3; this.tblData.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F)); this.tblData.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblData.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblData.Controls.Add(this.label1, 0, 0); this.tblData.Controls.Add(this.chkMap, 1, 1); this.tblData.Controls.Add(this.label2, 0, 2); this.tblData.Controls.Add(this.txtDescription, 2, 2); this.tblData.Controls.Add(this.flowLayoutPanel1, 2, 3); this.tblData.Controls.Add(this.label3, 0, 1); this.tblData.Controls.Add(this.txtTitle, 2, 0); this.tblData.Controls.Add(this.cmbMap, 2, 1); this.tblData.Dock = System.Windows.Forms.DockStyle.Fill; this.tblData.Location = new System.Drawing.Point(4, 36); this.tblData.Margin = new System.Windows.Forms.Padding(4); this.tblData.Name = "tblData"; this.tblData.RowCount = 4; this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblData.Size = new System.Drawing.Size(703, 487); this.tblData.TabIndex = 1; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(4, 11); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(33, 16); this.label1.TabIndex = 0; this.label1.Text = "Title"; // // label2 // this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(4, 255); this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(75, 16); this.label2.TabIndex = 1; this.label2.Text = "Description"; // // txtDescription // this.txtDescription.Dock = System.Windows.Forms.DockStyle.Fill; this.txtDescription.Location = new System.Drawing.Point(136, 82); this.txtDescription.Margin = new System.Windows.Forms.Padding(4); this.txtDescription.Multiline = true; this.txtDescription.Name = "txtDescription"; this.txtDescription.Size = new System.Drawing.Size(563, 362); this.txtDescription.TabIndex = 2; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.button1); this.flowLayoutPanel1.Controls.Add(this.btnSave); this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flowLayoutPanel1.Location = new System.Drawing.Point(132, 448); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(571, 39); this.flowLayoutPanel1.TabIndex = 4; // // button1 // this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.button1.Location = new System.Drawing.Point(467, 4); this.button1.Margin = new System.Windows.Forms.Padding(4); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(100, 28); this.button1.TabIndex = 0; this.button1.Text = "Cancel"; this.button1.UseVisualStyleBackColor = true; // // btnSave // this.btnSave.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnSave.Location = new System.Drawing.Point(359, 4); this.btnSave.Margin = new System.Windows.Forms.Padding(4); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(100, 28); this.btnSave.TabIndex = 1; this.btnSave.Text = "Save"; this.btnSave.UseVisualStyleBackColor = true; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(3, 50); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(34, 16); this.label3.TabIndex = 6; this.label3.Text = "Map"; // // cmbMap // this.cmbMap.DisplayMember = "Name"; this.cmbMap.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbMap.Enabled = false; this.cmbMap.FormattingEnabled = true; this.cmbMap.Location = new System.Drawing.Point(136, 47); this.cmbMap.Margin = new System.Windows.Forms.Padding(4, 8, 4, 4); this.cmbMap.Name = "cmbMap"; this.cmbMap.Size = new System.Drawing.Size(563, 24); this.cmbMap.TabIndex = 7; this.cmbMap.TabStop = false; // // txtTitle // this.txtTitle.Dock = System.Windows.Forms.DockStyle.Fill; this.txtTitle.Location = new System.Drawing.Point(135, 8); this.txtTitle.Margin = new System.Windows.Forms.Padding(3, 8, 3, 2); this.txtTitle.Name = "txtTitle"; this.txtTitle.Size = new System.Drawing.Size(565, 22); this.txtTitle.TabIndex = 5; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 18F)); this.tblMain.Controls.Add(this.tblData, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(711, 527); this.tblMain.TabIndex = 2; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(711, 32); this.titleBar.TabIndex = 2; this.titleBar.Title = "Edit"; // // chkMap // this.chkMap.AutoSize = true; this.chkMap.Dock = System.Windows.Forms.DockStyle.Fill; this.chkMap.Location = new System.Drawing.Point(103, 51); this.chkMap.Margin = new System.Windows.Forms.Padding(3, 12, 3, 3); this.chkMap.Name = "chkMap"; this.chkMap.Size = new System.Drawing.Size(26, 24); this.chkMap.TabIndex = 8; this.chkMap.CheckedChanged += new System.EventHandler(this.chkMap_CheckedChanged); // // ScreenshotEditForm // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(711, 527); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.Name = "ScreenshotEditForm"; this.Text = "Edit"; this.tblData.ResumeLayout(false); this.tblData.PerformLayout(); this.flowLayoutPanel1.ResumeLayout(false); this.tblMain.ResumeLayout(false); this.ResumeLayout(false); } #endregion private TableLayoutPanelDB tblData; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.TextBox txtDescription; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button button1; private System.Windows.Forms.Button btnSave; private System.Windows.Forms.TextBox txtTitle; private System.Windows.Forms.Label label3; private System.Windows.Forms.ComboBox cmbMap; private System.Windows.Forms.TableLayoutPanel tblMain; private Controls.TitleBarControl titleBar; private CCheckBox chkMap; } } ================================================ FILE: DoomLauncher/Forms/ScreenshotEditForm.cs ================================================ using DoomLauncher.Handlers; using DoomLauncher.Interfaces; using System.Linq; using System.Windows.Forms; namespace DoomLauncher.Forms { public partial class ScreenshotEditForm : Form { public ScreenshotEditForm() { InitializeComponent(); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public static bool ShowDialogAndUpdate(IWin32Window owner, IDataSourceAdapter adapter, IGameFile gameFile, IFileData fileData) { ScreenshotEditForm screenshotEditForm = new ScreenshotEditForm(); screenshotEditForm.StartPosition = FormStartPosition.CenterParent; screenshotEditForm.SetData(gameFile, fileData); if (screenshotEditForm.ShowDialog(owner) == DialogResult.OK) { fileData.UserTitle = screenshotEditForm.Title; fileData.UserDescription = screenshotEditForm.Description; fileData.Map = screenshotEditForm.Map; if (!screenshotEditForm.HasMap) fileData.Map = null; adapter.UpdateFile(fileData); return true; } return false; } public void SetData(IGameFile gameFile, IFileData fileData) { SetMaps(gameFile); if (fileData == null) { cmbMap.Enabled = chkMap.Checked = false; return; } TrySetMapData(fileData); txtTitle.Text = fileData.UserTitle; txtDescription.Text = fileData.UserDescription; } private void SetMaps(IGameFile gameFile) { if (!string.IsNullOrEmpty(gameFile.Map)) { AutoCompleteCombo.SetAutoCompleteCustomSource(cmbMap, DataSources.GameFile.GetMaps(gameFile), null, null); return; } int iwadId = -1; if (gameFile.IWadID.HasValue) iwadId = gameFile.IWadID.Value; else iwadId = (int)DataCache.Instance.AppConfiguration.GetTypedConfigValue(ConfigType.DefaultIWad, typeof(int)); var iwad = DataCache.Instance.DataSourceAdapter.GetGameFileIWads().FirstOrDefault(x => x.IWadID == iwadId); if (iwad == null) return; AutoCompleteCombo.SetAutoCompleteCustomSource(cmbMap, DataSources.GameFile.GetMaps(iwad), null, null); } private void TrySetMapData(IFileData fileData) { if (cmbMap.DataSource == null || cmbMap.Items.Count == 0) { cmbMap.Enabled = false; chkMap.Checked = false; return; } try { if (!string.IsNullOrEmpty(fileData.Map)) cmbMap.SelectedItem = fileData.Map; else cmbMap.SelectedIndex = 0; cmbMap.Enabled = chkMap.Checked; chkMap.Checked = !string.IsNullOrEmpty(fileData.Map); } catch { cmbMap.Enabled = false; chkMap.Checked = false; } } public bool HasMap => chkMap.Checked; public string Map => chkMap.Checked ? cmbMap.SelectedItem as string : null; public string Title { get => txtTitle.Text; } public string Description { get => txtDescription.Text; } private void chkMap_CheckedChanged(object sender, System.EventArgs e) { cmbMap.Enabled = chkMap.Checked; } } } ================================================ FILE: DoomLauncher/Forms/ScreenshotEditForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/ScreenshotViewerForm.Designer.cs ================================================ namespace DoomLauncher.Forms { partial class ScreenshotViewerForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ScreenshotViewerForm)); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.tblContainer = new System.Windows.Forms.TableLayoutPanel(); this.tblData = new System.Windows.Forms.TableLayoutPanel(); this.statisticsView = new DoomLauncher.StatisticsView(); this.statsControl = new DoomLauncher.StatsControl(); this.lblTitle = new DoomLauncher.GrowLabel(); this.flpData = new System.Windows.Forms.FlowLayoutPanel(); this.lblDescription = new DoomLauncher.GrowLabel(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblButtons = new System.Windows.Forms.TableLayoutPanel(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.btnSave = new DoomLauncher.FormButton(); this.btnSlideshow = new DoomLauncher.FormButton(); this.btnEdit = new DoomLauncher.FormButton(); this.btnPrev = new System.Windows.Forms.Button(); this.btnNext = new System.Windows.Forms.Button(); this.tblMain.SuspendLayout(); this.tblContainer.SuspendLayout(); this.tblData.SuspendLayout(); this.flpData.SuspendLayout(); this.tblButtons.SuspendLayout(); this.flpButtons.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.tblContainer, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Controls.Add(this.tblButtons, 0, 2); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 44F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Size = new System.Drawing.Size(1378, 844); this.tblMain.TabIndex = 1; // // tblContainer // this.tblContainer.AutoScroll = true; this.tblContainer.ColumnCount = 2; this.tblContainer.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblContainer.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblContainer.Controls.Add(this.tblData, 1, 0); this.tblContainer.Dock = System.Windows.Forms.DockStyle.Fill; this.tblContainer.Location = new System.Drawing.Point(0, 40); this.tblContainer.Margin = new System.Windows.Forms.Padding(0); this.tblContainer.Name = "tblContainer"; this.tblContainer.RowCount = 1; this.tblContainer.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblContainer.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblContainer.Size = new System.Drawing.Size(1378, 760); this.tblContainer.TabIndex = 2; // // tblData // this.tblData.ColumnCount = 1; this.tblData.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblData.Controls.Add(this.statisticsView, 0, 2); this.tblData.Controls.Add(this.statsControl, 0, 1); this.tblData.Controls.Add(this.lblTitle, 0, 0); this.tblData.Controls.Add(this.flpData, 0, 3); this.tblData.Dock = System.Windows.Forms.DockStyle.Fill; this.tblData.Location = new System.Drawing.Point(689, 0); this.tblData.Margin = new System.Windows.Forms.Padding(0); this.tblData.Name = "tblData"; this.tblData.RowCount = 4; this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 60F)); this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 120F)); this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 200F)); this.tblData.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblData.Size = new System.Drawing.Size(689, 760); this.tblData.TabIndex = 0; // // statisticsView // this.statisticsView.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.statisticsView.DataSourceAdapter = null; this.statisticsView.Dock = System.Windows.Forms.DockStyle.Fill; this.statisticsView.ForeColor = System.Drawing.Color.White; this.statisticsView.GameFile = null; this.statisticsView.Location = new System.Drawing.Point(4, 190); this.statisticsView.Margin = new System.Windows.Forms.Padding(4, 10, 4, 5); this.statisticsView.Name = "statisticsView"; this.statisticsView.Size = new System.Drawing.Size(681, 185); this.statisticsView.TabIndex = 2; // // statsControl // this.statsControl.Dock = System.Windows.Forms.DockStyle.Fill; this.statsControl.Location = new System.Drawing.Point(4, 65); this.statsControl.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.statsControl.Name = "statsControl"; this.statsControl.Size = new System.Drawing.Size(681, 110); this.statsControl.TabIndex = 3; // // lblTitle // this.lblTitle.Anchor = System.Windows.Forms.AnchorStyles.None; this.lblTitle.AutoSize = true; this.lblTitle.Font = new System.Drawing.Font("Arial", 12F, System.Drawing.FontStyle.Bold); this.lblTitle.IsPath = false; this.lblTitle.Location = new System.Drawing.Point(313, 15); this.lblTitle.Name = "lblTitle"; this.lblTitle.Size = new System.Drawing.Size(63, 29); this.lblTitle.TabIndex = 0; this.lblTitle.Text = "Title"; this.lblTitle.UseMnemonic = false; // // flpData // this.flpData.Controls.Add(this.lblDescription); this.flpData.Dock = System.Windows.Forms.DockStyle.Fill; this.flpData.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; this.flpData.Location = new System.Drawing.Point(0, 380); this.flpData.Margin = new System.Windows.Forms.Padding(0); this.flpData.Name = "flpData"; this.flpData.Size = new System.Drawing.Size(689, 380); this.flpData.TabIndex = 2; // // lblDescription // this.lblDescription.AutoSize = true; this.lblDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lblDescription.IsPath = false; this.lblDescription.Location = new System.Drawing.Point(6, 20); this.lblDescription.Margin = new System.Windows.Forms.Padding(6, 20, 6, 0); this.lblDescription.Name = "lblDescription"; this.lblDescription.Size = new System.Drawing.Size(109, 25); this.lblDescription.TabIndex = 1; this.lblDescription.Text = "Description"; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(1378, 40); this.titleBar.TabIndex = 3; this.titleBar.Title = "Screenshot"; // // tblButtons // this.tblButtons.ColumnCount = 4; this.tblButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F)); this.tblButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F)); this.tblButtons.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblButtons.Controls.Add(this.flpButtons, 0, 0); this.tblButtons.Controls.Add(this.btnPrev, 1, 0); this.tblButtons.Controls.Add(this.btnNext, 2, 0); this.tblButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.tblButtons.Location = new System.Drawing.Point(0, 800); this.tblButtons.Margin = new System.Windows.Forms.Padding(0); this.tblButtons.Name = "tblButtons"; this.tblButtons.RowCount = 1; this.tblButtons.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblButtons.Size = new System.Drawing.Size(1378, 44); this.tblButtons.TabIndex = 4; // // flpButtons // this.flpButtons.Controls.Add(this.btnSave); this.flpButtons.Controls.Add(this.btnSlideshow); this.flpButtons.Controls.Add(this.btnEdit); this.flpButtons.Location = new System.Drawing.Point(0, 0); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(134, 40); this.flpButtons.TabIndex = 4; // // btnSave // this.btnSave.Image = global::DoomLauncher.Properties.Resources.Save; this.btnSave.Location = new System.Drawing.Point(0, 0); this.btnSave.Margin = new System.Windows.Forms.Padding(0); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(40, 40); this.btnSave.TabIndex = 0; this.btnSave.UseVisualStyleBackColor = true; this.btnSave.Click += new System.EventHandler(this.btnSave_Click); // // btnSlideshow // this.btnSlideshow.Image = global::DoomLauncher.Properties.Resources.Video; this.btnSlideshow.Location = new System.Drawing.Point(40, 0); this.btnSlideshow.Margin = new System.Windows.Forms.Padding(0); this.btnSlideshow.Name = "btnSlideshow"; this.btnSlideshow.Size = new System.Drawing.Size(40, 40); this.btnSlideshow.TabIndex = 1; this.btnSlideshow.UseVisualStyleBackColor = true; this.btnSlideshow.Click += new System.EventHandler(this.btnSlideshow_Click); // // btnEdit // this.btnEdit.Image = global::DoomLauncher.Properties.Resources.Edit; this.btnEdit.Location = new System.Drawing.Point(80, 0); this.btnEdit.Margin = new System.Windows.Forms.Padding(0); this.btnEdit.Name = "btnEdit"; this.btnEdit.Size = new System.Drawing.Size(40, 40); this.btnEdit.TabIndex = 2; this.btnEdit.UseVisualStyleBackColor = true; this.btnEdit.Click += new System.EventHandler(this.btnEdit_Click); // // btnPrev // this.btnPrev.BackColor = System.Drawing.SystemColors.Control; this.btnPrev.Dock = System.Windows.Forms.DockStyle.Fill; this.btnPrev.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnPrev.Font = new System.Drawing.Font("MS Gothic", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnPrev.ForeColor = System.Drawing.SystemColors.ControlText; this.btnPrev.Location = new System.Drawing.Point(609, 0); this.btnPrev.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.btnPrev.Name = "btnPrev"; this.btnPrev.Size = new System.Drawing.Size(77, 44); this.btnPrev.TabIndex = 1; this.btnPrev.Text = "⇦"; this.btnPrev.UseVisualStyleBackColor = false; this.btnPrev.Click += new System.EventHandler(this.btnPrev_Click); // // btnNext // this.btnNext.Dock = System.Windows.Forms.DockStyle.Fill; this.btnNext.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnNext.Font = new System.Drawing.Font("MS Gothic", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btnNext.ForeColor = System.Drawing.SystemColors.ControlText; this.btnNext.Location = new System.Drawing.Point(692, 0); this.btnNext.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); this.btnNext.Name = "btnNext"; this.btnNext.Size = new System.Drawing.Size(77, 44); this.btnNext.TabIndex = 0; this.btnNext.Text = "⇨"; this.btnNext.TextImageRelation = System.Windows.Forms.TextImageRelation.TextAboveImage; this.btnNext.UseVisualStyleBackColor = true; this.btnNext.Click += new System.EventHandler(this.btnNext_Click); // // ScreenshotViewerForm // this.ClientSize = new System.Drawing.Size(1378, 844); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "ScreenshotViewerForm"; this.tblMain.ResumeLayout(false); this.tblContainer.ResumeLayout(false); this.tblData.ResumeLayout(false); this.tblData.PerformLayout(); this.flpData.ResumeLayout(false); this.flpData.PerformLayout(); this.tblButtons.ResumeLayout(false); this.flpButtons.ResumeLayout(false); this.ResumeLayout(false); } #endregion private TableLayoutPanelDB tblMain; private System.Windows.Forms.Button btnNext; private System.Windows.Forms.Button btnPrev; private System.Windows.Forms.TableLayoutPanel tblContainer; private System.Windows.Forms.TableLayoutPanel tblData; private GrowLabel lblDescription; private GrowLabel lblTitle; private System.Windows.Forms.FlowLayoutPanel flpData; private StatisticsView statisticsView; private StatsControl statsControl; private Controls.TitleBarControl titleBar; private System.Windows.Forms.FlowLayoutPanel flpButtons; private FormButton btnSave; private FormButton btnSlideshow; private FormButton btnEdit; private System.Windows.Forms.TableLayoutPanel tblButtons; } } ================================================ FILE: DoomLauncher/Forms/ScreenshotViewerForm.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Windows.Forms; namespace DoomLauncher.Forms { public partial class ScreenshotViewerForm : Form { private string[] m_images = Array.Empty(); private IList m_files = Array.Empty(); private int m_index; private bool m_slideshow; private IGameFile m_gameFile; private IDataSourceAdapter m_adapter; private readonly SlideShowPictureBox pbMain = new SlideShowPictureBox(); public ScreenshotViewerForm() { InitializeComponent(); pbMain.Dock = DockStyle.Fill; tblContainer.Controls.Add(pbMain, 0, 0); statsControl.SetMapsVisible(false); statisticsView.SetMapsVisible(false); Icons.DpiScale = new DpiScale(CreateGraphics()); btnSave.Image = Icons.Save; btnSlideshow.Image = Icons.Video; btnEdit.Image = Icons.Edit; btnPrev.ForeColor = ColorTheme.Current.Text; btnNext.ForeColor = ColorTheme.Current.Text; KeyPreview = true; KeyUp += ScreenshotViewerForm_KeyUp; MouseWheel += ScreenshotViewerForm_MouseWheel; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } private void ScreenshotViewerForm_MouseWheel(object sender, MouseEventArgs e) { if (e.Delta < 0) SetNextImage(); else if (e.Delta > 0) SetPreviousImage(); } protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { //this is to set the focus of the left/right buttons if (!msg.HWnd.Equals(this.Handle) && (keyData == Keys.Left || keyData == Keys.Right)) { if (keyData == Keys.Right) btnNext.Focus(); else if (keyData == Keys.Left) btnPrev.Focus(); return true; } return base.ProcessCmdKey(ref msg, keyData); } private void ScreenshotViewerForm_KeyUp(object sender, KeyEventArgs e) { switch (e.KeyCode) { case Keys.Right: SetNextImage(); break; case Keys.Left: SetPreviousImage(); break; case Keys.Home: SetFirst(); break; case Keys.End: SetEnd(); break; default: break; } } private void SetEnd() { if (m_images.Length > 0) m_index = m_images.Length - 1; SetImage(); } private void SetFirst() { m_index = 0; SetImage(); } public void SetImageFileData(IDataSourceAdapter adapter, IGameFile gameFile, string[] imagePaths, IList screenshots) { m_adapter = adapter; m_gameFile = gameFile; m_images = imagePaths; m_files = screenshots; statisticsView.DataSourceAdapter = adapter; } public void SetImage(string filename) { for (int i = 0; i < m_images.Length; i++) { if (m_images[i] == filename) { m_index = i; SetImage(); break; } } } private void SetImage() { SetSlideshow(false); if (m_images.Length > 0) { Image image = pbMain.GetImage(); pbMain.SetImage(ImageExtensions.FromFileOrDefault(GetImageFilename())); image?.Dispose(); Text = string.Format("Screenshot Viewer - {0}/{1}", m_index + 1, m_images.Length); SetUserDescription(GetFileData()); } } private void SetUserDescription(IFileData fileData) { if (string.IsNullOrEmpty(fileData.UserTitle) && string.IsNullOrEmpty(fileData.UserDescription) && string.IsNullOrEmpty(fileData.Map)) { ShowImageUserData(false); return; } ShowImageUserData(true); lblTitle.Text = GetTitle(fileData); lblDescription.Text = string.IsNullOrEmpty(fileData.UserDescription) ? string.Empty : fileData.UserDescription; if (string.IsNullOrEmpty(fileData.Map)) { statsControl.Visible = false; statisticsView.Visible = false; UpdateStatsHeights(); return; } statsControl.Visible = true; statisticsView.Visible = true; var stats = statisticsView.SetDataByMap(m_gameFile, fileData.Map); statsControl.Visible = stats.Any(); statsControl.SetStatistics(m_gameFile, stats); UpdateStatsHeights(); } private string GetTitle(IFileData fileData) { string title = FileData.GetTitle(fileData); return string.IsNullOrEmpty(title) ? "N/A" : fileData.UserTitle; } private void UpdateStatsHeights() { if (statsControl.Visible) tblData.RowStyles[1].Height = 120; else tblData.RowStyles[1].Height = 0; if (statisticsView.Visible) tblData.RowStyles[2].Height = 200; else tblData.RowStyles[2].Height = 0; } private void ShowImageUserData(bool set) { if (set) tblContainer.ColumnStyles[1].Width = 50; else tblContainer.ColumnStyles[1].Width = 0; } private string GetImageFilename() { return m_images[m_index]; } private IFileData GetFileData() { return m_files[m_index]; } private void btnPrev_Click(object sender, EventArgs e) { SetPreviousImage(); } private void SetPreviousImage() { m_index = (m_images.Length + --m_index) % m_images.Length; SetImage(); } private void btnNext_Click(object sender, EventArgs e) { SetNextImage(); } private void SetNextImage() { m_index = ++m_index % m_images.Length; SetImage(); } private void btnSave_Click(object sender, EventArgs e) { SaveFileDialog dialog = new SaveFileDialog(); string ext = Path.GetExtension(GetImageFilename()); if (!string.IsNullOrEmpty(ext)) dialog.Filter = string.Format("{0}|*{0}", ext); if (dialog.ShowDialog(this) == DialogResult.OK) { try { File.Copy(GetImageFilename(), dialog.FileName, true); } catch { MessageBox.Show(this, "Unable to save file.", "Unable to Save", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } private void btnSlideshow_Click(object sender, EventArgs e) { SetSlideshow(!m_slideshow); if (m_slideshow) { Text = "Slideshow"; pbMain.SetImages(m_images.ToList(), m_index); } else { SetImage(); } } private void btnEdit_Click(object sender, EventArgs e) { if (!ScreenshotEditForm.ShowDialogAndUpdate(this, m_adapter, m_gameFile, GetFileData())) return; SetImage(); } private void SetSlideshow(bool set) { ShowImageUserData(!set); m_slideshow = set; if (set) btnSlideshow.BackColor = ColorTheme.Current.Highlight; else btnSlideshow.BackColor = ColorTheme.Current.Window; } } } ================================================ FILE: DoomLauncher/Forms/ScreenshotViewerForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 25 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/SettingsForm.Designer.cs ================================================ namespace DoomLauncher { partial class SettingsForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SettingsForm)); this.tblOuter = new DoomLauncher.TableLayoutPanelDB(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.btnCancel = new System.Windows.Forms.Button(); this.btnSave = new System.Windows.Forms.Button(); this.tabControl = new DoomLauncher.CTabControl(); this.tabPageConfig = new System.Windows.Forms.TabPage(); this.tabPageDefault = new System.Windows.Forms.TabPage(); this.pictureBox1 = new System.Windows.Forms.PictureBox(); this.lblLaunchSettings = new System.Windows.Forms.Label(); this.cmbSkill = new System.Windows.Forms.ComboBox(); this.label4 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.cmbSourcePorts = new System.Windows.Forms.ComboBox(); this.cmbIwad = new System.Windows.Forms.ComboBox(); this.tabPageFileManagement = new System.Windows.Forms.TabPage(); this.pictureBox2 = new System.Windows.Forms.PictureBox(); this.tblFileOptions = new DoomLauncher.TableLayoutPanelDB(); this.label9 = new System.Windows.Forms.Label(); this.label8 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.label7 = new System.Windows.Forms.Label(); this.cmbFileManagement = new System.Windows.Forms.ComboBox(); this.label10 = new System.Windows.Forms.Label(); this.tabPageView = new System.Windows.Forms.TabPage(); this.cmbTheme = new System.Windows.Forms.ComboBox(); this.label14 = new System.Windows.Forms.Label(); this.label13 = new System.Windows.Forms.Label(); this.chkListViews = new DoomLauncher.Controls.CheckBoxList(); this.pnlViewRestart = new System.Windows.Forms.Panel(); this.label12 = new System.Windows.Forms.Label(); this.label11 = new System.Windows.Forms.Label(); this.cmbViewType = new System.Windows.Forms.ComboBox(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblOuter.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.tabControl.SuspendLayout(); this.tabPageDefault.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.tabPageFileManagement.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit(); this.tblFileOptions.SuspendLayout(); this.tabPageView.SuspendLayout(); this.pnlViewRestart.SuspendLayout(); this.SuspendLayout(); // // tblOuter // this.tblOuter.ColumnCount = 1; this.tblOuter.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblOuter.Controls.Add(this.flowLayoutPanel1, 0, 2); this.tblOuter.Controls.Add(this.tabControl, 0, 1); this.tblOuter.Controls.Add(this.titleBar, 0, 0); this.tblOuter.Dock = System.Windows.Forms.DockStyle.Fill; this.tblOuter.Location = new System.Drawing.Point(0, 0); this.tblOuter.Margin = new System.Windows.Forms.Padding(4); this.tblOuter.Name = "tblOuter"; this.tblOuter.RowCount = 3; this.tblOuter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblOuter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblOuter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblOuter.Size = new System.Drawing.Size(540, 413); this.tblOuter.TabIndex = 0; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.btnCancel); this.flowLayoutPanel1.Controls.Add(this.btnSave); this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 374); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(540, 39); this.flowLayoutPanel1.TabIndex = 0; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(436, 4); this.btnCancel.Margin = new System.Windows.Forms.Padding(4); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(100, 28); this.btnCancel.TabIndex = 1; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // btnSave // this.btnSave.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnSave.Location = new System.Drawing.Point(328, 4); this.btnSave.Margin = new System.Windows.Forms.Padding(4); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(100, 28); this.btnSave.TabIndex = 0; this.btnSave.Text = "Save"; this.btnSave.UseVisualStyleBackColor = true; this.btnSave.Click += new System.EventHandler(this.btnSave_Click); // // tabControl // this.tabControl.Controls.Add(this.tabPageConfig); this.tabControl.Controls.Add(this.tabPageDefault); this.tabControl.Controls.Add(this.tabPageFileManagement); this.tabControl.Controls.Add(this.tabPageView); this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControl.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed; this.tabControl.Location = new System.Drawing.Point(4, 36); this.tabControl.Margin = new System.Windows.Forms.Padding(4); this.tabControl.Multiline = true; this.tabControl.Name = "tabControl"; this.tabControl.SelectedIndex = 0; this.tabControl.Size = new System.Drawing.Size(532, 334); this.tabControl.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; this.tabControl.TabIndex = 1; // // tabPageConfig // this.tabPageConfig.BackColor = System.Drawing.SystemColors.Control; this.tabPageConfig.Location = new System.Drawing.Point(4, 29); this.tabPageConfig.Margin = new System.Windows.Forms.Padding(4); this.tabPageConfig.Name = "tabPageConfig"; this.tabPageConfig.Padding = new System.Windows.Forms.Padding(4); this.tabPageConfig.Size = new System.Drawing.Size(524, 301); this.tabPageConfig.TabIndex = 0; this.tabPageConfig.Text = "Configuration"; // // tabPageDefault // this.tabPageDefault.BackColor = System.Drawing.SystemColors.Control; this.tabPageDefault.Controls.Add(this.pictureBox1); this.tabPageDefault.Controls.Add(this.lblLaunchSettings); this.tabPageDefault.Controls.Add(this.cmbSkill); this.tabPageDefault.Controls.Add(this.label4); this.tabPageDefault.Controls.Add(this.label2); this.tabPageDefault.Controls.Add(this.label1); this.tabPageDefault.Controls.Add(this.cmbSourcePorts); this.tabPageDefault.Controls.Add(this.cmbIwad); this.tabPageDefault.Location = new System.Drawing.Point(4, 29); this.tabPageDefault.Margin = new System.Windows.Forms.Padding(0); this.tabPageDefault.Name = "tabPageDefault"; this.tabPageDefault.Padding = new System.Windows.Forms.Padding(4); this.tabPageDefault.Size = new System.Drawing.Size(524, 301); this.tabPageDefault.TabIndex = 1; this.tabPageDefault.Text = "Launch Settings"; // // pictureBox1 // this.pictureBox1.Image = global::DoomLauncher.Properties.Resources.bon2b; this.pictureBox1.Location = new System.Drawing.Point(13, 124); this.pictureBox1.Margin = new System.Windows.Forms.Padding(4); this.pictureBox1.Name = "pictureBox1"; this.pictureBox1.Size = new System.Drawing.Size(28, 22); this.pictureBox1.TabIndex = 16; this.pictureBox1.TabStop = false; // // lblLaunchSettings // this.lblLaunchSettings.AutoSize = true; this.lblLaunchSettings.Location = new System.Drawing.Point(49, 124); this.lblLaunchSettings.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblLaunchSettings.Name = "lblLaunchSettings"; this.lblLaunchSettings.Size = new System.Drawing.Size(33, 16); this.lblLaunchSettings.TabIndex = 15; this.lblLaunchSettings.Text = "Text"; // // cmbSkill // this.cmbSkill.DisplayMember = "Name"; this.cmbSkill.FormattingEnabled = true; this.cmbSkill.Location = new System.Drawing.Point(64, 82); this.cmbSkill.Margin = new System.Windows.Forms.Padding(4); this.cmbSkill.Name = "cmbSkill"; this.cmbSkill.Size = new System.Drawing.Size(261, 24); this.cmbSkill.TabIndex = 13; // // label4 // this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(11, 86); this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(32, 16); this.label4.TabIndex = 14; this.label4.Text = "Skill"; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(9, 53); this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(42, 16); this.label2.TabIndex = 12; this.label2.Text = "IWAD"; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(9, 20); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(31, 16); this.label1.TabIndex = 11; this.label1.Text = "Port"; // // cmbSourcePorts // this.cmbSourcePorts.DisplayMember = "Name"; this.cmbSourcePorts.FormattingEnabled = true; this.cmbSourcePorts.Location = new System.Drawing.Point(64, 16); this.cmbSourcePorts.Margin = new System.Windows.Forms.Padding(4); this.cmbSourcePorts.Name = "cmbSourcePorts"; this.cmbSourcePorts.Size = new System.Drawing.Size(261, 24); this.cmbSourcePorts.TabIndex = 9; this.cmbSourcePorts.ValueMember = "SourcePortID"; // // cmbIwad // this.cmbIwad.DisplayMember = "FileName"; this.cmbIwad.FormattingEnabled = true; this.cmbIwad.Location = new System.Drawing.Point(64, 49); this.cmbIwad.Margin = new System.Windows.Forms.Padding(4); this.cmbIwad.Name = "cmbIwad"; this.cmbIwad.Size = new System.Drawing.Size(261, 24); this.cmbIwad.TabIndex = 10; this.cmbIwad.ValueMember = "IWadID"; // // tabPageFileManagement // this.tabPageFileManagement.BackColor = System.Drawing.SystemColors.Control; this.tabPageFileManagement.Controls.Add(this.pictureBox2); this.tabPageFileManagement.Controls.Add(this.tblFileOptions); this.tabPageFileManagement.Controls.Add(this.cmbFileManagement); this.tabPageFileManagement.Controls.Add(this.label10); this.tabPageFileManagement.Location = new System.Drawing.Point(4, 29); this.tabPageFileManagement.Margin = new System.Windows.Forms.Padding(4); this.tabPageFileManagement.Name = "tabPageFileManagement"; this.tabPageFileManagement.Size = new System.Drawing.Size(524, 301); this.tabPageFileManagement.TabIndex = 2; this.tabPageFileManagement.Text = "File Management"; // // pictureBox2 // this.pictureBox2.Image = global::DoomLauncher.Properties.Resources.bon2b; this.pictureBox2.Location = new System.Drawing.Point(19, 217); this.pictureBox2.Margin = new System.Windows.Forms.Padding(4); this.pictureBox2.Name = "pictureBox2"; this.pictureBox2.Size = new System.Drawing.Size(28, 22); this.pictureBox2.TabIndex = 20; this.pictureBox2.TabStop = false; // // tblFileOptions // this.tblFileOptions.ColumnCount = 2; this.tblFileOptions.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F)); this.tblFileOptions.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblFileOptions.Controls.Add(this.label9, 1, 2); this.tblFileOptions.Controls.Add(this.label8, 1, 1); this.tblFileOptions.Controls.Add(this.label3, 0, 0); this.tblFileOptions.Controls.Add(this.label5, 0, 1); this.tblFileOptions.Controls.Add(this.label6, 0, 2); this.tblFileOptions.Controls.Add(this.label7, 1, 0); this.tblFileOptions.Location = new System.Drawing.Point(19, 68); this.tblFileOptions.Margin = new System.Windows.Forms.Padding(4); this.tblFileOptions.Name = "tblFileOptions"; this.tblFileOptions.RowCount = 4; this.tblFileOptions.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblFileOptions.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblFileOptions.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblFileOptions.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 10F)); this.tblFileOptions.Size = new System.Drawing.Size(488, 142); this.tblFileOptions.TabIndex = 19; // // label9 // this.label9.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label9.AutoSize = true; this.label9.Location = new System.Drawing.Point(124, 89); this.label9.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label9.Name = "label9"; this.label9.Size = new System.Drawing.Size(266, 16); this.label9.TabIndex = 5; this.label9.Text = "Prompted to choose when file(s) are added."; // // label8 // this.label8.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label8.AutoSize = true; this.label8.Location = new System.Drawing.Point(124, 42); this.label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(360, 32); this.label8.TabIndex = 4; this.label8.Text = "Files are referenced by their original path and not managed by Doom Launcher."; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label3.Location = new System.Drawing.Point(4, 11); this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(74, 17); this.label3.TabIndex = 0; this.label3.Text = "Managed"; // // label5 // this.label5.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label5.AutoSize = true; this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label5.Location = new System.Drawing.Point(4, 50); this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(94, 17); this.label5.TabIndex = 1; this.label5.Text = "Unmanaged"; // // label6 // this.label6.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label6.AutoSize = true; this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label6.Location = new System.Drawing.Point(4, 89); this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(59, 17); this.label6.TabIndex = 2; this.label6.Text = "Prompt"; // // label7 // this.label7.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label7.AutoSize = true; this.label7.Location = new System.Drawing.Point(124, 3); this.label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(330, 32); this.label7.TabIndex = 3; this.label7.Text = "Doom Launcher manages and compresses files in the GameFiles directory for you."; // // cmbFileManagement // this.cmbFileManagement.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbFileManagement.FormattingEnabled = true; this.cmbFileManagement.Location = new System.Drawing.Point(19, 17); this.cmbFileManagement.Margin = new System.Windows.Forms.Padding(4); this.cmbFileManagement.Name = "cmbFileManagement"; this.cmbFileManagement.Size = new System.Drawing.Size(261, 24); this.cmbFileManagement.TabIndex = 10; // // label10 // this.label10.AutoSize = true; this.label10.Location = new System.Drawing.Point(55, 220); this.label10.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label10.Name = "label10"; this.label10.Size = new System.Drawing.Size(359, 16); this.label10.TabIndex = 6; this.label10.Text = "Downloaded files are always managed by Doom Launcher."; // // tabPageView // this.tabPageView.BackColor = System.Drawing.SystemColors.Control; this.tabPageView.Controls.Add(this.cmbTheme); this.tabPageView.Controls.Add(this.label14); this.tabPageView.Controls.Add(this.label13); this.tabPageView.Controls.Add(this.chkListViews); this.tabPageView.Controls.Add(this.pnlViewRestart); this.tabPageView.Controls.Add(this.label11); this.tabPageView.Controls.Add(this.cmbViewType); this.tabPageView.Location = new System.Drawing.Point(4, 29); this.tabPageView.Margin = new System.Windows.Forms.Padding(4); this.tabPageView.Name = "tabPageView"; this.tabPageView.Size = new System.Drawing.Size(524, 301); this.tabPageView.TabIndex = 3; this.tabPageView.Text = "View"; // // cmbTheme // this.cmbTheme.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbTheme.FormattingEnabled = true; this.cmbTheme.Location = new System.Drawing.Point(120, 17); this.cmbTheme.Margin = new System.Windows.Forms.Padding(4); this.cmbTheme.Name = "cmbTheme"; this.cmbTheme.Size = new System.Drawing.Size(261, 24); this.cmbTheme.TabIndex = 17; this.cmbTheme.SelectedIndexChanged += new System.EventHandler(this.cmbTheme_SelectedIndexChanged); // // label14 // this.label14.AutoSize = true; this.label14.Location = new System.Drawing.Point(10, 20); this.label14.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label14.Name = "label14"; this.label14.Size = new System.Drawing.Size(50, 16); this.label14.TabIndex = 16; this.label14.Text = "Theme"; // // label13 // this.label13.AutoSize = true; this.label13.Location = new System.Drawing.Point(10, 100); this.label13.Name = "label13"; this.label13.Size = new System.Drawing.Size(87, 16); this.label13.TabIndex = 15; this.label13.Text = "Visible Views"; // // chkListViews // this.chkListViews.Location = new System.Drawing.Point(13, 119); this.chkListViews.Name = "chkListViews"; this.chkListViews.Size = new System.Drawing.Size(374, 116); this.chkListViews.TabIndex = 14; // // pnlViewRestart // this.pnlViewRestart.Controls.Add(this.label12); this.pnlViewRestart.Location = new System.Drawing.Point(1, 222); this.pnlViewRestart.Location = new System.Drawing.Point(1, 235); this.pnlViewRestart.Margin = new System.Windows.Forms.Padding(4); this.pnlViewRestart.Name = "pnlViewRestart"; this.pnlViewRestart.Size = new System.Drawing.Size(519, 75); this.pnlViewRestart.TabIndex = 13; this.pnlViewRestart.Visible = false; // // label12 // this.label12.AutoSize = true; this.label12.Location = new System.Drawing.Point(63, 22); this.label12.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label12.Name = "label12"; this.label12.Size = new System.Drawing.Size(305, 16); this.label12.TabIndex = 0; this.label12.Text = "Changing view settings will restart Doom Launcher."; // // label11 // this.label11.AutoSize = true; this.label11.Location = new System.Drawing.Point(10, 54); this.label11.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label11.Name = "label11"; this.label11.Size = new System.Drawing.Size(71, 16); this.label11.TabIndex = 12; this.label11.Text = "View Type"; // // cmbViewType // this.cmbViewType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbViewType.FormattingEnabled = true; this.cmbViewType.Location = new System.Drawing.Point(120, 51); this.cmbViewType.Margin = new System.Windows.Forms.Padding(4); this.cmbViewType.Name = "cmbViewType"; this.cmbViewType.Size = new System.Drawing.Size(261, 24); this.cmbViewType.TabIndex = 11; this.cmbViewType.SelectedIndexChanged += new System.EventHandler(this.CmbViewType_SelectedIndexChanged); // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(540, 32); this.titleBar.TabIndex = 2; this.titleBar.Title = "Settings"; // // SettingsForm // this.AcceptButton = this.btnSave; this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(540, 413); this.Controls.Add(this.tblOuter); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "SettingsForm"; this.Text = "Settings"; this.tblOuter.ResumeLayout(false); this.flowLayoutPanel1.ResumeLayout(false); this.tabControl.ResumeLayout(false); this.tabPageDefault.ResumeLayout(false); this.tabPageDefault.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.tabPageFileManagement.ResumeLayout(false); this.tabPageFileManagement.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit(); this.tblFileOptions.ResumeLayout(false); this.tblFileOptions.PerformLayout(); this.tabPageView.ResumeLayout(false); this.tabPageView.PerformLayout(); this.pnlViewRestart.ResumeLayout(false); this.pnlViewRestart.PerformLayout(); this.ResumeLayout(false); } #endregion private DoomLauncher.TableLayoutPanelDB tblOuter; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnSave; private CTabControl tabControl; private System.Windows.Forms.TabPage tabPageConfig; private System.Windows.Forms.TabPage tabPageDefault; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label1; private System.Windows.Forms.ComboBox cmbSourcePorts; private System.Windows.Forms.ComboBox cmbIwad; private System.Windows.Forms.ComboBox cmbSkill; private System.Windows.Forms.Label label4; private System.Windows.Forms.PictureBox pictureBox1; private System.Windows.Forms.Label lblLaunchSettings; private System.Windows.Forms.TabPage tabPageFileManagement; private System.Windows.Forms.ComboBox cmbFileManagement; private DoomLauncher.TableLayoutPanelDB tblFileOptions; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label5; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label label7; private System.Windows.Forms.Label label8; private System.Windows.Forms.Label label9; private System.Windows.Forms.Label label10; private System.Windows.Forms.PictureBox pictureBox2; private System.Windows.Forms.TabPage tabPageView; private System.Windows.Forms.Label label11; private System.Windows.Forms.ComboBox cmbViewType; private System.Windows.Forms.Panel pnlViewRestart; private System.Windows.Forms.Label label12; private System.Windows.Forms.Label label13; private Controls.CheckBoxList chkListViews; private Controls.TitleBarControl titleBar; private System.Windows.Forms.ComboBox cmbTheme; private System.Windows.Forms.Label label14; } } ================================================ FILE: DoomLauncher/Forms/SettingsForm.cs ================================================ using DoomLauncher.Config; using DoomLauncher.Controls; using DoomLauncher.DataSources; using DoomLauncher.Forms; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class SettingsForm : Form { private const int TrackBarStep = 20; private readonly List> m_configValues = new List>(); private TextBox m_gameFileDirectory, m_screenshotDirectories; private readonly Dictionary m_trackBarToLabel = new Dictionary(); private readonly IDataSourceAdapter m_adapter; private readonly AppConfiguration m_appConfig; private Padding m_controlMargin = new Padding(4, 0, 0, 0); private static readonly int TextBoxWidth = 190; private static readonly int FullControlWidth = 270; public SettingsForm(IDataSourceAdapter adapter, AppConfiguration appConfig) { InitializeComponent(); lblLaunchSettings.Text = string.Concat("These are the default settings for a game file", Environment.NewLine, " that does not have a specific configuration saved."); m_adapter = adapter; m_appConfig = appConfig; pnlViewRestart.Paint += PnlViewRestart_Paint; PopulateDefaultSettings(m_adapter); PopulateConfiguration(); chkListViews.BorderStyle = BorderStyle.FixedSingle; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); PopulateViews(); } private void PopulateViews() { HashSet visibleViews = DataCache.Instance.AppConfiguration.VisibleViews; chkListViews.SetItems(TabKeys.KeyNames.Except(new string[] { TabKeys.LocalKey })); foreach (var view in visibleViews) chkListViews.SetChecked(view, true); chkListViews.ItemCheck += chkListViews_ItemCheck; } private void PnlViewRestart_Paint(object sender, PaintEventArgs e) { e.Graphics.DrawIcon(SystemIcons.Warning, 8, 8); } public void SetToLaunchSettingsTab() { tabControl.SelectedIndex = 1; } public void SetCancelAllowed(bool set) { btnCancel.Visible = set; titleBar.SetControlBox(set); } private int GetDefaultControlWidth() { DpiScale dpiScale = new DpiScale(CreateGraphics()); return dpiScale.ScaleIntX(FullControlWidth); } private void PopulateConfiguration() { DpiScale dpiScale = new DpiScale(CreateGraphics()); m_controlMargin = new Padding(dpiScale.ScaleIntX(4), 0, 0, 0); IEnumerable configItems = m_adapter.GetConfiguration().Where(x => x.UserCanModify); TableLayoutPanel tblMain = new TableLayoutPanel { Dock = DockStyle.Top, CellBorderStyle = TableLayoutPanelCellBorderStyle.Single, }; int height = dpiScale.ScaleIntY(8); int labelWidth = dpiScale.ScaleIntX(134); tblMain.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, labelWidth)); int height32 = dpiScale.ScaleIntY(32); int rowPadding = dpiScale.ScaleIntX(1); foreach (IConfigurationData config in configItems) { GrowLabel lbl = new GrowLabel { Anchor = AnchorStyles.Left, Text = AddSpaceBetweenWords(config.Name), Width = labelWidth }; tblMain.RowStyles.Add(new RowStyle(SizeType.Absolute, lbl.Height < height32 ? height32 : lbl.Height)); tblMain.Controls.Add(lbl, 0, tblMain.RowStyles.Count - 1); if (!string.IsNullOrEmpty(config.AvailableValues)) HandleComboBox(tblMain, config); else if (config.Name == AppConfiguration.ScreenshotPreviewSizeName) AddTrackBar(tblMain, config, dpiScale); else if (config.Name == AppConfiguration.TileImageSizeName) AddTrackBar(tblMain, config, dpiScale); else HandleTextBox(tblMain, config); height += height32 + rowPadding; } tblMain.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); tblMain.Height = height + dpiScale.ScaleIntY(8); tabControl.TabPages[0].Controls.Add(tblMain); Height = tblMain.Height + dpiScale.ScaleIntY(108+32); Width = dpiScale.ScaleIntX(452); } private void HandleTextBox(TableLayoutPanel tblMain, IConfigurationData config) { TextBox txt = new TextBox { Anchor = AnchorStyles.Left, Text = config.Value, Width = GetDefaultControlWidth(), Margin = m_controlMargin }; m_configValues.Add(new Tuple(config, txt)); if (config.Name == "GameFileDirectory") HandleGameFileDirectory(tblMain, txt); else if (config.Name == "ScreenshotCaptureDirectories") HandleScreenshotCaptureDirectories(tblMain, txt); else tblMain.Controls.Add(txt, 1, tblMain.RowStyles.Count - 1); } private void HandleComboBox(TableLayoutPanel tblMain, IConfigurationData config) { ComboBox cmb = new ComboBox { Anchor = AnchorStyles.Left, Width = GetDefaultControlWidth(), Margin = m_controlMargin }; string[] items = Util.SplitString(config.AvailableValues); List> cmbDataSource = new List>(); for (int i = 0; i < items.Length - 1; i += 2) cmbDataSource.Add(new Tuple(items[i], items[i + 1])); cmb.ValueMember = "Item1"; cmb.DataSource = cmbDataSource; cmb.BindingContext = new BindingContext(); cmb.SelectedItem = cmbDataSource.FirstOrDefault(x => x.Item2 == config.Value); tblMain.Controls.Add(cmb, 1, tblMain.RowStyles.Count - 1); m_configValues.Add(new Tuple(config, cmb)); } private void AddTrackBar(TableLayoutPanel tblMain, IConfigurationData config, DpiScale dpiScale) { var label = new Label { Width = dpiScale.ScaleIntX(68), Height = dpiScale.ScaleIntY(16), Margin = new Padding(dpiScale.ScaleIntX(0), dpiScale.ScaleIntY(8), 0, 0) }; var trackbar = new TrackBar { Minimum = 100, Maximum = 600, LargeChange = TrackBarStep, SmallChange = TrackBarStep, TickFrequency = TrackBarStep, Value = Math.Max(100, Convert.ToInt32(config.Value)), Width = dpiScale.ScaleIntX(200) }; trackbar.ValueChanged += Trk_ValueChanged; FlowLayoutPanel flp = new FlowLayoutPanel { Dock = DockStyle.Fill }; flp.Controls.Add(trackbar); flp.Controls.Add(label); m_trackBarToLabel[trackbar] = label; tblMain.Controls.Add(flp, 1, tblMain.RowStyles.Count - 1); m_configValues.Add(new Tuple(config, trackbar)); UpdateTrackBar(trackbar); } private void Trk_ValueChanged(object sender, EventArgs e) { UpdateTrackBar((TrackBar)sender); } private void UpdateTrackBar(TrackBar trackBar) { trackBar.ValueChanged -= Trk_ValueChanged; int remainder = trackBar.Value % TrackBarStep; int amount = trackBar.Value / TrackBarStep; trackBar.Value = (amount * TrackBarStep) + (remainder > TrackBarStep / 2 ? TrackBarStep : 0); trackBar.ValueChanged += Trk_ValueChanged; if (trackBar == null || !m_trackBarToLabel.TryGetValue(trackBar, out var label)) return; label.Text = string.Concat("Width: ", trackBar.Value); } private void HandleScreenshotCaptureDirectories(TableLayoutPanel tblMain, TextBox txt) { DpiScale dpiScale = new DpiScale(CreateGraphics()); m_screenshotDirectories = txt; m_screenshotDirectories.Width = dpiScale.ScaleIntX(TextBoxWidth); m_screenshotDirectories.Enabled = false; FlowLayoutPanel flp = new FlowLayoutPanel { Dock = DockStyle.Fill, Margin = new Padding(0) }; flp.Controls.Add(txt); Button changeButton = new Button { Text = "Change..." }; changeButton.Width = dpiScale.ScaleIntX(changeButton.Width); changeButton.Height = dpiScale.ScaleIntY(changeButton.Height); changeButton.Click += ChangeButton_Click; flp.Controls.Add(changeButton); tblMain.Controls.Add(flp, 1, tblMain.RowStyles.Count - 1); } private void HandleGameFileDirectory(TableLayoutPanel tblMain, TextBox txt) { DpiScale dpiScale = new DpiScale(CreateGraphics()); m_gameFileDirectory = txt; m_gameFileDirectory.Width = dpiScale.ScaleIntX(TextBoxWidth); FlowLayoutPanel flp = new FlowLayoutPanel { Dock = DockStyle.Fill, Margin = new Padding(0) }; flp.Controls.Add(m_gameFileDirectory); Button browseButton = new Button { Text = "Browse..." }; browseButton.Width = dpiScale.ScaleIntX(browseButton.Width); browseButton.Height = dpiScale.ScaleIntY(browseButton.Height); browseButton.Click += browseButton_Click; flp.Controls.Add(browseButton); tblMain.Controls.Add(flp, 1, tblMain.RowStyles.Count - 1); } void browseButton_Click(object sender, EventArgs e) { LauncherPath path = new LauncherPath(m_gameFileDirectory.Text); FolderBrowserDialog dialog = new FolderBrowserDialog { SelectedPath = path.GetFullPath() }; if (dialog.ShowDialog(this) == DialogResult.OK) { path = new LauncherPath(dialog.SelectedPath); m_gameFileDirectory.Text = path.GetPossiblyRelativePath(); } } private void ChangeButton_Click(object sender, EventArgs e) { DirectoriesForm form = new DirectoriesForm(); form.SetDirectories(Util.SplitString(m_screenshotDirectories.Text)); form.StartPosition = FormStartPosition.CenterParent; if (form.ShowDialog(this) == DialogResult.OK) m_screenshotDirectories.Text = string.Join(";", form.GetDirectories()); } private void PopulateDefaultSettings(IDataSourceAdapter adapter) { cmbSourcePorts.DataSource = adapter.GetSourcePorts(); cmbIwad.DataSource = Util.GetIWadsDataSource(adapter); cmbSkill.DataSource = Util.GetSkills(); cmbFileManagement.DataSource = Enum.GetValues(typeof(FileManagement)); cmbViewType.DataSource = new [] { "Grid", "Tile Large", "Tile Small" }; cmbTheme.DataSource = new [] { "Default", "Dark", "System" }; cmbSourcePorts.SelectedValue = m_appConfig.GetTypedConfigValue(ConfigType.DefaultSourcePort, typeof(int)); cmbIwad.SelectedValue = m_appConfig.GetTypedConfigValue(ConfigType.DefaultIWad, typeof(int)); cmbSkill.SelectedItem = m_appConfig.GetTypedConfigValue(ConfigType.DefaultSkill, typeof(string)); cmbFileManagement.SelectedIndex = (int)Enum.Parse(typeof(FileManagement), (string)m_appConfig.GetTypedConfigValue(ConfigType.FileManagement, typeof(string))); cmbViewType.SelectedIndex = (int)Enum.Parse(typeof(GameFileViewType), (string)m_appConfig.GetTypedConfigValue(ConfigType.GameFileViewType, typeof(string))); cmbTheme.SelectedIndex = (int)Enum.Parse(typeof(ColorThemeType), (string)m_appConfig.GetTypedConfigValue(ConfigType.ColorThemeType, typeof(string))); } private void btnSave_Click(object sender, EventArgs e) { foreach (Tuple config in m_configValues) { config.Item1.Value = GetValue(config.Item1, config.Item2); m_adapter.UpdateConfiguration(config.Item1); } HandleLaunchSettings(); Close(); } private void HandleLaunchSettings() { string[] configNames = new string[] { ConfigType.DefaultSourcePort.ToString("g"), ConfigType.DefaultIWad.ToString("g"), ConfigType.DefaultSkill.ToString("g"), ConfigType.FileManagement.ToString("g"), ConfigType.GameFileViewType.ToString("g"), ConfigType.VisibleViews.ToString("g"), ConfigType.ColorThemeType.ToString("g"), }; string[] configValues = new string[] { cmbSourcePorts.SelectedItem == null ? null : ((ISourcePortData)cmbSourcePorts.SelectedItem).SourcePortID.ToString(), cmbIwad.SelectedItem == null ? null : ((IIWadData)cmbIwad.SelectedItem).IWadID.ToString(), cmbSkill.SelectedItem?.ToString(), cmbFileManagement.SelectedValue.ToString(), ((GameFileViewType)cmbViewType.SelectedIndex).ToString(), string.Join(";", chkListViews.GetCheckedItems()), ((ColorThemeType)cmbTheme.SelectedIndex).ToString(), }; IEnumerable configuration = m_adapter.GetConfiguration().Where(x => configNames.Contains(x.Name)); for (int i = 0; i < configNames.Length; i++) { string configName = configNames[i]; string configValue = configValues[i]; IConfigurationData config = configuration.FirstOrDefault(x => x.Name == configName); if (configValue != null) { if (config == null) { config = CreateConfig(configName, configValue); m_adapter.InsertConfiguration(config); } else { config.Value = configValue; m_adapter.UpdateConfiguration(config); } } } } private static IConfigurationData CreateConfig(string configName, string configValue) { return new ConfigurationData { Name = configName, Value = configValue, UserCanModify = false, AvailableValues = string.Empty }; } private string GetValue(IConfigurationData config, object value) { if (!string.IsNullOrEmpty(config.AvailableValues) && value is ComboBox cmb && cmb.SelectedItem != null) { Tuple item = cmb.SelectedItem as Tuple; return item.Item2; } else if (value is TrackBar trk) { return trk.Value.ToString(); } else if (value is TextBox txt) { return txt.Text; } return string.Empty; } private void CmbViewType_SelectedIndexChanged(object sender, EventArgs e) { pnlViewRestart.Visible = GameFileViewFactory.IsBaseViewTypeChange(m_appConfig.GameFileViewType, (GameFileViewType)cmbViewType.SelectedIndex); } private void cmbTheme_SelectedIndexChanged(object sender, EventArgs e) { pnlViewRestart.Visible = true; } private void chkListViews_ItemCheck(object sender, CheckListBoxEventArgs e) { pnlViewRestart.Visible = true; } private static string AddSpaceBetweenWords(string item) { for (int i = 0; i < item.Length; i++) { if (char.IsUpper(item[i]) && i != 0) { item = item.Insert(i, " "); i++; } } return item; } } } ================================================ FILE: DoomLauncher/Forms/SettingsForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/SimpleFileSelectForm.Designer.cs ================================================ namespace DoomLauncher { partial class SimpleFileSelectForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SimpleFileSelectForm)); this.btnOK = new System.Windows.Forms.Button(); this.lstFiles = new System.Windows.Forms.ListBox(); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.tblMain.SuspendLayout(); this.SuspendLayout(); // // btnOK // this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.Right; this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(270, 311); this.btnOK.Margin = new System.Windows.Forms.Padding(4); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(100, 28); this.btnOK.TabIndex = 1; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; // // lstFiles // this.lstFiles.Dock = System.Windows.Forms.DockStyle.Fill; this.lstFiles.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lstFiles.FormattingEnabled = true; this.lstFiles.ItemHeight = 20; this.lstFiles.Location = new System.Drawing.Point(4, 4); this.lstFiles.Margin = new System.Windows.Forms.Padding(4); this.lstFiles.Name = "lstFiles"; this.lstFiles.Size = new System.Drawing.Size(366, 297); this.lstFiles.TabIndex = 2; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Controls.Add(this.btnOK, 0, 1); this.tblMain.Controls.Add(this.lstFiles, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.Size = new System.Drawing.Size(374, 345); this.tblMain.TabIndex = 3; // // SimpleFileSelectForm // this.AcceptButton = this.btnOK; this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(374, 345); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "SimpleFileSelectForm"; this.Text = "Select File"; this.tblMain.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Button btnOK; private System.Windows.Forms.ListBox lstFiles; private TableLayoutPanelDB tblMain; } } ================================================ FILE: DoomLauncher/Forms/SimpleFileSelectForm.cs ================================================ using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class SimpleFileSelectForm : Form { public SimpleFileSelectForm() { InitializeComponent(); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public void Initialize(IEnumerable files) { foreach (string file in files) lstFiles.Items.Add(file); if (files.Count() > 0) lstFiles.SelectedItem = files.First(); } public string SelectedFile { get { if (lstFiles.SelectedItems.Count > 0) return (string)lstFiles.SelectedItems[0]; return null; } } } } ================================================ FILE: DoomLauncher/Forms/SimpleFileSelectForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/SourcePortEditForm.Designer.cs ================================================ namespace DoomLauncher { partial class SourcePortEditForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SourcePortEditForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.button1 = new System.Windows.Forms.Button(); this.btnSave = new System.Windows.Forms.Button(); this.groupBox2 = new System.Windows.Forms.GroupBox(); this.sourcePortEdit1 = new DoomLauncher.SourcePortEdit(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.grpAdditionalFiles = new System.Windows.Forms.GroupBox(); this.lblInfo = new System.Windows.Forms.Label(); this.pbInfo = new System.Windows.Forms.PictureBox(); this.ctrlFiles = new DoomLauncher.FilesCtrl(); this.flpInfo = new System.Windows.Forms.FlowLayoutPanel(); this.pbInfo2 = new System.Windows.Forms.PictureBox(); this.table2 = new DoomLauncher.TableLayoutPanelDB(); this.lblReplacementInfo = new System.Windows.Forms.Label(); this.tblReplacementVars = new DoomLauncher.TableLayoutPanelDB(); this.label4 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.tblMain.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.groupBox2.SuspendLayout(); this.grpAdditionalFiles.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo)).BeginInit(); this.flpInfo.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo2)).BeginInit(); this.table2.SuspendLayout(); this.tblReplacementVars.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.flowLayoutPanel1, 0, 4); this.tblMain.Controls.Add(this.groupBox2, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Controls.Add(this.grpAdditionalFiles, 0, 3); this.tblMain.Controls.Add(this.flpInfo, 0, 2); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 5; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 305F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 105F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 308F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.Size = new System.Drawing.Size(534, 822); this.tblMain.TabIndex = 2; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.button1); this.flowLayoutPanel1.Controls.Add(this.btnSave); this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 763); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0, 5, 0, 0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(534, 59); this.flowLayoutPanel1.TabIndex = 1; // // button1 // this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.button1.Location = new System.Drawing.Point(418, 5); this.button1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(112, 35); this.button1.TabIndex = 1; this.button1.Text = "Cancel"; this.button1.UseVisualStyleBackColor = true; // // btnSave // this.btnSave.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnSave.Location = new System.Drawing.Point(298, 5); this.btnSave.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(112, 35); this.btnSave.TabIndex = 0; this.btnSave.Text = "Save"; this.btnSave.UseVisualStyleBackColor = true; this.btnSave.Click += new System.EventHandler(this.btnSave_Click); // // groupBox2 // this.groupBox2.Controls.Add(this.sourcePortEdit1); this.groupBox2.Dock = System.Windows.Forms.DockStyle.Fill; this.groupBox2.Location = new System.Drawing.Point(4, 45); this.groupBox2.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.groupBox2.Name = "groupBox2"; this.groupBox2.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); this.groupBox2.Size = new System.Drawing.Size(526, 295); this.groupBox2.TabIndex = 4; this.groupBox2.TabStop = false; this.groupBox2.Text = "Properties"; // // sourcePortEdit1 // this.sourcePortEdit1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.sourcePortEdit1.Dock = System.Windows.Forms.DockStyle.Fill; this.sourcePortEdit1.ForeColor = System.Drawing.Color.White; this.sourcePortEdit1.Location = new System.Drawing.Point(4, 24); this.sourcePortEdit1.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); this.sourcePortEdit1.Name = "sourcePortEdit1"; this.sourcePortEdit1.Size = new System.Drawing.Size(518, 266); this.sourcePortEdit1.TabIndex = 0; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(534, 40); this.titleBar.TabIndex = 5; this.titleBar.Title = "Source Port"; // // grpAdditionalFiles // this.grpAdditionalFiles.Controls.Add(this.lblInfo); this.grpAdditionalFiles.Controls.Add(this.pbInfo); this.grpAdditionalFiles.Controls.Add(this.ctrlFiles); this.grpAdditionalFiles.Location = new System.Drawing.Point(4, 455); this.grpAdditionalFiles.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.grpAdditionalFiles.Name = "grpAdditionalFiles"; this.grpAdditionalFiles.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); this.grpAdditionalFiles.Size = new System.Drawing.Size(525, 298); this.grpAdditionalFiles.TabIndex = 3; this.grpAdditionalFiles.TabStop = false; this.grpAdditionalFiles.Text = "Additional Files"; // // lblInfo // this.lblInfo.AutoSize = true; this.lblInfo.Location = new System.Drawing.Point(46, 29); this.lblInfo.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblInfo.Name = "lblInfo"; this.lblInfo.Size = new System.Drawing.Size(42, 20); this.lblInfo.TabIndex = 23; this.lblInfo.Text = "label"; // // pbInfo // this.pbInfo.Location = new System.Drawing.Point(14, 29); this.pbInfo.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.pbInfo.Name = "pbInfo"; this.pbInfo.Size = new System.Drawing.Size(24, 25); this.pbInfo.TabIndex = 22; this.pbInfo.TabStop = false; // // ctrlFiles // this.ctrlFiles.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.ctrlFiles.ForeColor = System.Drawing.Color.White; this.ctrlFiles.Location = new System.Drawing.Point(14, 79); this.ctrlFiles.Margin = new System.Windows.Forms.Padding(6, 6, 6, 6); this.ctrlFiles.Name = "ctrlFiles"; this.ctrlFiles.Size = new System.Drawing.Size(498, 211); this.ctrlFiles.TabIndex = 21; // // flpInfo // this.flpInfo.Controls.Add(this.pbInfo2); this.flpInfo.Controls.Add(this.table2); this.flpInfo.Dock = System.Windows.Forms.DockStyle.Fill; this.flpInfo.Location = new System.Drawing.Point(0, 345); this.flpInfo.Margin = new System.Windows.Forms.Padding(0); this.flpInfo.Name = "flpInfo"; this.flpInfo.Size = new System.Drawing.Size(534, 105); this.flpInfo.TabIndex = 6; // // pbInfo2 // this.pbInfo2.Location = new System.Drawing.Point(9, 10); this.pbInfo2.Margin = new System.Windows.Forms.Padding(9, 10, 4, 5); this.pbInfo2.Name = "pbInfo2"; this.pbInfo2.Size = new System.Drawing.Size(24, 25); this.pbInfo2.TabIndex = 24; this.pbInfo2.TabStop = false; // // table2 // this.table2.ColumnCount = 1; this.table2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.table2.Controls.Add(this.lblReplacementInfo, 0, 0); this.table2.Controls.Add(this.tblReplacementVars, 0, 1); this.table2.Location = new System.Drawing.Point(37, 0); this.table2.Margin = new System.Windows.Forms.Padding(0); this.table2.Name = "table2"; this.table2.Padding = new System.Windows.Forms.Padding(0, 0, 0, 5); this.table2.RowCount = 2; this.table2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.table2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.table2.Size = new System.Drawing.Size(497, 100); this.table2.TabIndex = 25; // // lblReplacementInfo // this.lblReplacementInfo.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblReplacementInfo.AutoSize = true; this.lblReplacementInfo.Location = new System.Drawing.Point(4, 10); this.lblReplacementInfo.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblReplacementInfo.Name = "lblReplacementInfo"; this.lblReplacementInfo.Size = new System.Drawing.Size(174, 20); this.lblReplacementInfo.TabIndex = 26; this.lblReplacementInfo.Text = "Replacement Variables"; // // tblReplacementVars // this.tblReplacementVars.ColumnCount = 2; this.tblReplacementVars.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 112F)); this.tblReplacementVars.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblReplacementVars.Controls.Add(this.label4, 1, 1); this.tblReplacementVars.Controls.Add(this.label3, 0, 1); this.tblReplacementVars.Controls.Add(this.label2, 1, 0); this.tblReplacementVars.Controls.Add(this.label1, 0, 0); this.tblReplacementVars.Dock = System.Windows.Forms.DockStyle.Fill; this.tblReplacementVars.Location = new System.Drawing.Point(0, 40); this.tblReplacementVars.Margin = new System.Windows.Forms.Padding(0); this.tblReplacementVars.Name = "tblReplacementVars"; this.tblReplacementVars.RowCount = 2; this.tblReplacementVars.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.tblReplacementVars.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.tblReplacementVars.Size = new System.Drawing.Size(497, 55); this.tblReplacementVars.TabIndex = 0; // // label4 // this.label4.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(115, 35); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(120, 20); this.label4.TabIndex = 3; this.label4.Text = "IWAD file name"; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(3, 35); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(50, 20); this.label3.TabIndex = 2; this.label3.Text = "$iwad"; // // label2 // this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(115, 5); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(218, 20); this.label2.TabIndex = 1; this.label2.Text = "FIle name of the launched file"; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(3, 5); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(78, 20); this.label1.TabIndex = 0; this.label1.Text = "$filename"; // // SourcePortEditForm // this.AcceptButton = this.btnSave; this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.button1; this.ClientSize = new System.Drawing.Size(534, 822); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "SourcePortEditForm"; this.Text = "Source Port"; this.tblMain.ResumeLayout(false); this.flowLayoutPanel1.ResumeLayout(false); this.groupBox2.ResumeLayout(false); this.grpAdditionalFiles.ResumeLayout(false); this.grpAdditionalFiles.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo)).EndInit(); this.flpInfo.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.pbInfo2)).EndInit(); this.table2.ResumeLayout(false); this.table2.PerformLayout(); this.tblReplacementVars.ResumeLayout(false); this.tblReplacementVars.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button button1; private System.Windows.Forms.Button btnSave; private System.Windows.Forms.GroupBox grpAdditionalFiles; private FilesCtrl ctrlFiles; private System.Windows.Forms.GroupBox groupBox2; private SourcePortEdit sourcePortEdit1; private System.Windows.Forms.PictureBox pbInfo; private System.Windows.Forms.Label lblInfo; private Controls.TitleBarControl titleBar; private System.Windows.Forms.FlowLayoutPanel flpInfo; private System.Windows.Forms.PictureBox pbInfo2; private TableLayoutPanelDB table2; private System.Windows.Forms.Label lblReplacementInfo; private TableLayoutPanelDB tblReplacementVars; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label label3; } } ================================================ FILE: DoomLauncher/Forms/SourcePortEditForm.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class SourcePortEditForm : Form { private readonly IDataSourceAdapter m_adapter; private readonly ITabView[] m_additionalFileViews; private readonly SourcePortLaunchType m_type; private ISourcePortData m_sourcePort; public SourcePortEditForm(IDataSourceAdapter adapter, ITabView[] additionalTabViews, SourcePortLaunchType type) { InitializeComponent(); pbInfo.Image = Properties.Resources.bon2b; lblInfo.Text = string.Format("These files will automatically be added when this executable{0} is selected.", Environment.NewLine); pbInfo2.Image = Properties.Resources.bon2b; ctrlFiles.Initialize("GameFileID", "FileName"); ctrlFiles.NewItemNeeded += ctrlFiles_NewItemNeeded; m_adapter = adapter; m_additionalFileViews = additionalTabViews; m_type = type; Text = m_type.ToString("g"); sourcePortEdit1.ShowOptions(type == SourcePortLaunchType.Utility); if (type == SourcePortLaunchType.Utility) { grpAdditionalFiles.Visible = false; flpInfo.Visible = false; tblMain.RowStyles[2].Height = 0; tblMain.RowStyles[3].Height = 0; var dpiScale = new DpiScale(CreateGraphics()); Height = (int)(tblMain.RowStyles[0].Height + tblMain.RowStyles[1].Height + tblMain.RowStyles[4].Height) + dpiScale.ScaleIntY(48); } IEnumerable extensions = GetSourcePortExtensions(type); sourcePortEdit1.SetSupportedExtensions(string.Join(",", extensions)); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } private static IEnumerable GetSourcePortExtensions(SourcePortLaunchType launchType) { IEnumerable extensions = new string[] { ".wad" }; switch (launchType) { case SourcePortLaunchType.SourcePort: extensions = extensions.Union(Util.GetDehackedExtensions().Union(Util.GetSourcePortPkExtensions())); break; case SourcePortLaunchType.Utility: extensions = extensions.Union(Util.GetSourcePortPkExtensions()); break; case SourcePortLaunchType.Doom64: extensions = extensions.Union(Util.GetExtraDoom64Extensions()); break; } return extensions; } private void ctrlFiles_NewItemNeeded(object sender, AdditionalFilesEventArgs e) { using (FileSelectForm fileSelect = new FileSelectForm()) { fileSelect.Initialize(m_adapter, m_additionalFileViews); fileSelect.MultiSelect = true; fileSelect.StartPosition = FormStartPosition.CenterParent; if (fileSelect.ShowDialog(this) == DialogResult.OK) { IGameFile[] selectedFiles = fileSelect.SelectedFiles; if (selectedFiles.Length > 0) { e.NewItems = selectedFiles.Cast().ToList(); } } } } public void SetDataSource(ISourcePortData sourcePort) { m_sourcePort = sourcePort; sourcePortEdit1.SetDataSource(sourcePort); ctrlFiles.SetDataSource(Util.GetAdditionalFiles(m_adapter, sourcePort)); } public void UpdateDataSource(ISourcePortData sourcePort) { m_sourcePort = sourcePort; sourcePortEdit1.UpdateDataSource(sourcePort); sourcePort.SettingsFiles = ctrlFiles.GetAdditionalFilesString(); } private void btnSave_Click(object sender, EventArgs e) { string err = null; string type = GetTypeString(); if (string.IsNullOrEmpty(sourcePortEdit1.SourcePortName)) err = string.Format("Please enter a name for the {0}.", type); if (string.IsNullOrEmpty(sourcePortEdit1.SourcePortExec)) err = string.Format("Please select an executable for the {0}.", type); if (m_sourcePort == null) { IEnumerable data; if (m_type == SourcePortLaunchType.SourcePort) data = m_adapter.GetSourcePorts(); else data = m_adapter.GetUtilities(); if (data.Any(x => x.Executable == sourcePortEdit1.SourcePortExec && x.Directory.GetFullPath() == sourcePortEdit1.GetSourcePortDirectory().GetFullPath()) && PromptUserSourcePortDuplicate()) { DialogResult = DialogResult.None; return; } } if (!string.IsNullOrEmpty(err)) { MessageBox.Show(this, err, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); DialogResult = DialogResult.None; } else { DialogResult = DialogResult.OK; } } private bool PromptUserSourcePortDuplicate() { return MessageBox.Show(this, $"The {GetTypeString()} {sourcePortEdit1.SourcePortExec} already exists. Continue?", "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.Cancel; } private string GetTypeString() { if (m_type == SourcePortLaunchType.SourcePort) return "source port"; else if (m_type == SourcePortLaunchType.Utility) return "utility"; else // Doom64 return "Doom 64 installation"; } } } ================================================ FILE: DoomLauncher/Forms/SourcePortEditForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/SourcePortViewForm.Designer.cs ================================================ namespace DoomLauncher { partial class SourcePortViewForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SourcePortViewForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.dgvSourcePorts = new System.Windows.Forms.DataGridView(); this.ColName = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Executable = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Directory = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.tableLayoutPanelDB1 = new DoomLauncher.TableLayoutPanelDB(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.btnNew = new System.Windows.Forms.Button(); this.btnEdit = new System.Windows.Forms.Button(); this.btnDelete = new System.Windows.Forms.Button(); this.btnNext = new System.Windows.Forms.Button(); this.flpTop = new System.Windows.Forms.FlowLayoutPanel(); this.chkShowArchived = new DoomLauncher.CCheckBox(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.ctrlSearch = new DoomLauncher.SearchControl(); this.tblMain.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.dgvSourcePorts)).BeginInit(); this.tableLayoutPanelDB1.SuspendLayout(); this.flpButtons.SuspendLayout(); this.flpTop.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.dgvSourcePorts, 0, 2); this.tblMain.Controls.Add(this.tableLayoutPanelDB1, 0, 3); this.tblMain.Controls.Add(this.flpTop, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 4; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.Size = new System.Drawing.Size(832, 544); this.tblMain.TabIndex = 0; // // dgvSourcePorts // this.dgvSourcePorts.AllowUserToAddRows = false; this.dgvSourcePorts.AllowUserToDeleteRows = false; dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this.dgvSourcePorts.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1; this.dgvSourcePorts.BackgroundColor = System.Drawing.SystemColors.ControlLightLight; this.dgvSourcePorts.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal; this.dgvSourcePorts.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dgvSourcePorts.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.ColName, this.Executable, this.Directory}); this.dgvSourcePorts.Dock = System.Windows.Forms.DockStyle.Fill; this.dgvSourcePorts.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically; this.dgvSourcePorts.GridColor = System.Drawing.SystemColors.ActiveBorder; this.dgvSourcePorts.Location = new System.Drawing.Point(4, 66); this.dgvSourcePorts.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.dgvSourcePorts.Name = "dgvSourcePorts"; this.dgvSourcePorts.RowHeadersWidth = 51; this.dgvSourcePorts.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; this.dgvSourcePorts.Size = new System.Drawing.Size(824, 435); this.dgvSourcePorts.TabIndex = 0; this.dgvSourcePorts.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvSourcePorts_CellDoubleClick); // // ColName // this.ColName.DataPropertyName = "Name"; this.ColName.HeaderText = "Name"; this.ColName.MinimumWidth = 6; this.ColName.Name = "ColName"; this.ColName.Width = 125; // // Executable // this.Executable.DataPropertyName = "Executable"; this.Executable.HeaderText = "Executable"; this.Executable.MinimumWidth = 6; this.Executable.Name = "Executable"; this.Executable.Width = 125; // // Directory // this.Directory.DataPropertyName = "Directory"; this.Directory.HeaderText = "Directory"; this.Directory.MinimumWidth = 6; this.Directory.Name = "Directory"; this.Directory.Width = 125; // // tableLayoutPanelDB1 // this.tableLayoutPanelDB1.ColumnCount = 2; this.tableLayoutPanelDB1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanelDB1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tableLayoutPanelDB1.Controls.Add(this.flpButtons, 0, 0); this.tableLayoutPanelDB1.Controls.Add(this.btnNext, 1, 0); this.tableLayoutPanelDB1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanelDB1.Location = new System.Drawing.Point(0, 505); this.tableLayoutPanelDB1.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanelDB1.Name = "tableLayoutPanelDB1"; this.tableLayoutPanelDB1.RowCount = 1; this.tableLayoutPanelDB1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanelDB1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tableLayoutPanelDB1.Size = new System.Drawing.Size(832, 39); this.tableLayoutPanelDB1.TabIndex = 1; // // flpButtons // this.flpButtons.Controls.Add(this.btnNew); this.flpButtons.Controls.Add(this.btnEdit); this.flpButtons.Controls.Add(this.btnDelete); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.Location = new System.Drawing.Point(0, 0); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(416, 39); this.flpButtons.TabIndex = 2; // // btnNew // this.btnNew.Location = new System.Drawing.Point(4, 4); this.btnNew.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.btnNew.Name = "btnNew"; this.btnNew.Size = new System.Drawing.Size(100, 28); this.btnNew.TabIndex = 2; this.btnNew.Text = "New"; this.btnNew.UseVisualStyleBackColor = true; this.btnNew.Click += new System.EventHandler(this.btnNew_Click); // // btnEdit // this.btnEdit.Location = new System.Drawing.Point(112, 4); this.btnEdit.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.btnEdit.Name = "btnEdit"; this.btnEdit.Size = new System.Drawing.Size(100, 28); this.btnEdit.TabIndex = 4; this.btnEdit.Text = "Edit"; this.btnEdit.UseVisualStyleBackColor = true; this.btnEdit.Click += new System.EventHandler(this.btnEdit_Click); // // btnDelete // this.btnDelete.Location = new System.Drawing.Point(220, 4); this.btnDelete.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.btnDelete.Name = "btnDelete"; this.btnDelete.Size = new System.Drawing.Size(100, 28); this.btnDelete.TabIndex = 3; this.btnDelete.Text = "Delete"; this.btnDelete.UseVisualStyleBackColor = true; this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click); // // btnNext // this.btnNext.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnNext.Location = new System.Drawing.Point(724, 4); this.btnNext.Margin = new System.Windows.Forms.Padding(4, 4, 8, 4); this.btnNext.Name = "btnNext"; this.btnNext.Size = new System.Drawing.Size(100, 28); this.btnNext.TabIndex = 3; this.btnNext.Text = "Next"; this.btnNext.UseVisualStyleBackColor = true; this.btnNext.Visible = false; this.btnNext.Click += new System.EventHandler(this.btnLaunch_Click); // // flpTop // this.flpTop.Controls.Add(this.ctrlSearch); this.flpTop.Controls.Add(this.chkShowArchived); this.flpTop.Dock = System.Windows.Forms.DockStyle.Fill; this.flpTop.Location = new System.Drawing.Point(3, 32); this.flpTop.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); this.flpTop.Name = "flpTop"; this.flpTop.Size = new System.Drawing.Size(829, 30); this.flpTop.TabIndex = 2; // // chkShowArchived // this.chkShowArchived.Anchor = System.Windows.Forms.AnchorStyles.Left; this.chkShowArchived.AutoSize = true; this.chkShowArchived.Location = new System.Drawing.Point(278, 7); this.chkShowArchived.Margin = new System.Windows.Forms.Padding(10, 6, 3, 3); this.chkShowArchived.Name = "chkShowArchived"; this.chkShowArchived.Size = new System.Drawing.Size(118, 20); this.chkShowArchived.TabIndex = 0; this.chkShowArchived.Text = "Show Archived"; this.chkShowArchived.CheckedChanged += new System.EventHandler(this.chkShowArchived_CheckedChanged); // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(832, 32); this.titleBar.TabIndex = 3; this.titleBar.Title = "Title"; // // ctrlSearch // this.ctrlSearch.Location = new System.Drawing.Point(4, 4); this.ctrlSearch.Margin = new System.Windows.Forms.Padding(4); this.ctrlSearch.Name = "ctrlSearch"; this.ctrlSearch.SearchText = ""; this.ctrlSearch.Size = new System.Drawing.Size(260, 23); this.ctrlSearch.TabIndex = 1; // // SourcePortViewForm // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(832, 544); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.Name = "SourcePortViewForm"; this.Text = "Source Ports"; this.tblMain.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.dgvSourcePorts)).EndInit(); this.tableLayoutPanelDB1.ResumeLayout(false); this.flpButtons.ResumeLayout(false); this.flpTop.ResumeLayout(false); this.flpTop.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.DataGridView dgvSourcePorts; private System.Windows.Forms.DataGridViewTextBoxColumn ColName; private System.Windows.Forms.DataGridViewTextBoxColumn Executable; private System.Windows.Forms.DataGridViewTextBoxColumn Directory; private TableLayoutPanelDB tableLayoutPanelDB1; private System.Windows.Forms.FlowLayoutPanel flpButtons; private System.Windows.Forms.Button btnNew; private System.Windows.Forms.Button btnEdit; private System.Windows.Forms.Button btnDelete; private System.Windows.Forms.Button btnNext; private System.Windows.Forms.FlowLayoutPanel flpTop; private Controls.TitleBarControl titleBar; private SearchControl ctrlSearch; private CCheckBox chkShowArchived; } } ================================================ FILE: DoomLauncher/Forms/SourcePortViewForm.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Forms; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Windows.Forms; using System.Windows.Markup.Localizer; namespace DoomLauncher { public partial class SourcePortViewForm : Form { public event EventHandler SourcePortLaunched; private readonly ITabView[] m_tabViews; private readonly SourcePortLaunchType m_launchType; private readonly IDataSourceAdapter m_adapter; private readonly AppConfiguration m_appConfig; private IEnumerable m_sourcePorts = Array.Empty(); private string m_searchText = string.Empty; public SourcePortViewForm(IDataSourceAdapter adapter, AppConfiguration appConfig, ITabView[] tabViews, SourcePortLaunchType type) { InitializeComponent(); dgvSourcePorts.MultiSelect = false; m_adapter = adapter; m_appConfig = appConfig; m_tabViews = tabViews; m_launchType = type; ctrlSearch.SearchTextChanged += CtrlSearch_SearchTextChanged; ctrlSearch.SetShowDropDown(false); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); ResetData(); dgvSourcePorts.Columns[dgvSourcePorts.Columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; } private void CtrlSearch_SearchTextChanged(object sender, EventArgs e) { m_searchText = ctrlSearch.SearchText; SetSourcePortsData(); } public void DisplayInitSetupButton() { IsInitSetup = true; btnNext.Text = "Next >>"; btnNext.Visible = true; } private bool IsInitSetup { get; set; } public void ShowPlayButton(bool set) { btnNext.Visible = set; } private void ResetData() { bool loadArchived = chkShowArchived.Checked; IEnumerable data; if (m_launchType == SourcePortLaunchType.Utility) { this.Text = titleBar.Title = "Utilities"; data = m_adapter.GetUtilities(loadArchived); } else // SourcePortLaunchType.SourcePort (Doom64 not currently used for this screen) { this.Text = titleBar.Title = "Source Ports"; data = m_adapter.GetSourcePorts(loadArchived); } SetDataSource(data); } private void SetDataSource(IEnumerable sourcePorts) { m_sourcePorts = sourcePorts; SetSourcePortsData(); } private void SetSourcePortsData() { var sourcePorts = m_sourcePorts; if (!string.IsNullOrEmpty(m_searchText)) sourcePorts = m_sourcePorts.Where(x => x.Name.IndexOf(m_searchText, StringComparison.CurrentCultureIgnoreCase) >= 0); dgvSourcePorts.DataSource = (from item in sourcePorts select new { item.SourcePortID, item.Name, item.Executable, Directory = item.Directory.GetPossiblyRelativePath(), SourcePort = item }).ToList(); } private ISourcePortData SelectedItem { get { if (dgvSourcePorts.SelectedRows.Count == 0) return null; return GetSourcePortFromRow(dgvSourcePorts.SelectedRows[0]); } } private ISourcePortData GetSourcePortFromRow(DataGridViewRow row) { object item = row.DataBoundItem; PropertyInfo pi = item.GetType().GetProperty("SourcePort"); return pi.GetValue(item) as ISourcePortData; } private void dgvSourcePorts_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { HandleEdit(); } private void btnEdit_Click(object sender, EventArgs e) { HandleEdit(); } private void HandleEdit() { if (SelectedItem != null) { SourcePortEditForm editForm = new SourcePortEditForm(m_adapter, m_tabViews, m_launchType); ISourcePortData sourcePort = SelectedItem; editForm.SetDataSource(sourcePort); editForm.StartPosition = FormStartPosition.CenterParent; if (editForm.ShowDialog(this) == DialogResult.OK) { editForm.UpdateDataSource(sourcePort); m_adapter.UpdateSourcePort(sourcePort); ResetData(); SelectSourcePort(sourcePort); } } } private void btnNew_Click(object sender, EventArgs e) { SourcePortEditForm editForm = new SourcePortEditForm(m_adapter, m_tabViews, m_launchType); editForm.StartPosition = FormStartPosition.CenterParent; if (editForm.ShowDialog(this) == DialogResult.OK) { SourcePortData sourcePort = new SourcePortData(); editForm.UpdateDataSource(sourcePort); sourcePort.LaunchType = m_launchType; m_adapter.InsertSourcePort(sourcePort); ResetData(); SelectSourcePort(sourcePort); } } private void SelectSourcePort(ISourcePortData sourcePort) { // Source ports are ordered by name so we have to search for it // The identity (SourcePortID) is saved after the insert so just use the name to find it when the ID is zero foreach (DataGridViewRow row in dgvSourcePorts.Rows) { if (sourcePort.SourcePortID > 0) { if (sourcePort.SourcePortID == GetSourcePortFromRow(row).SourcePortID) { row.Selected = true; break; } } else if (sourcePort.Executable == GetSourcePortFromRow(row).Executable) { row.Selected = true; break; } } } private void btnDelete_Click(object sender, EventArgs e) { MessageCheckBox messageBox = new MessageCheckBox("Confirm", GetDeleteConfirm(), "Delete save games, demos, and statistics associated with this port", SystemIcons.Exclamation, MessageBoxButtons.OKCancel); messageBox.SetShowCheckBox(m_launchType == SourcePortLaunchType.SourcePort); if (SelectedItem != null && messageBox.ShowDialog(this) == DialogResult.OK) { int index = dgvSourcePorts.SelectedRows[0].Index; try { if (m_launchType == SourcePortLaunchType.SourcePort) { m_adapter.UpdateGameFiles(GameFileFieldType.SourcePortID, GameFileFieldType.SourcePortID, SelectedItem.SourcePortID, null); if (messageBox.Checked) DeleteSourcePortFiles(); else UnlinkFilesFromSourcePort(); } m_adapter.DeleteSourcePort(SelectedItem); } catch (IOException) { MessageBox.Show(this, "This file appears to be in use and cannot be deleted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } catch (Exception ex) { Util.DisplayUnexpectedException(this, ex); } ResetData(); if (index >= dgvSourcePorts.Rows.Count) index = dgvSourcePorts.Rows.Count - 1; if (dgvSourcePorts.Rows.Count > 0) dgvSourcePorts.Rows[index].Selected = true; } } private static FileType[] GetDeleteFileTypes() { return new FileType[] { FileType.SaveGame, FileType.Demo }; } private void DeleteSourcePortFiles() { var deleteFileTypes = GetDeleteFileTypes(); var files = m_adapter.GetFiles().Where(x => x.SourcePortID == SelectedItem.SourcePortID && deleteFileTypes.Contains(x.FileTypeID)); foreach(var fileType in deleteFileTypes) m_adapter.DeleteFiles(SelectedItem, fileType); m_adapter.DeleteStats(SelectedItem); m_adapter.UpdateFiles(SelectedItem.SourcePortID, -1); //Since we didn't delete screenshots unlink them foreach(var file in files) { try { string deleteFile = Path.Combine(m_appConfig.PathForFileType(file.FileTypeID).GetFullPath(), file.FileName); if (File.Exists(deleteFile)) File.Delete(deleteFile); } catch { //we tried } } } private void UnlinkFilesFromSourcePort() { m_adapter.UpdateFiles(SelectedItem.SourcePortID, -1); var stats = m_adapter.GetStats().Where(x => x.SourcePortID == SelectedItem.SourcePortID); foreach (var stat in stats) { stat.SourcePortID = -1; m_adapter.UpdateStats(stat); } } private string GetDeleteConfirm() { if (m_launchType == SourcePortLaunchType.SourcePort) return "Deleting this source port will orphan demo files and save games associated with it. Are you sure you want to continue?\n\nConsider archiving this port instead!"; else return "Are you sure you want to delete this utility?"; } private void btnLaunch_Click(object sender, EventArgs e) { if (IsInitSetup) Close(); else SourcePortLaunched?.Invoke(this, new EventArgs()); } public ISourcePortData GetSelectedSourcePort() { return SelectedItem; } private void chkShowArchived_CheckedChanged(object sender, EventArgs e) { ResetData(); } } } ================================================ FILE: DoomLauncher/Forms/SourcePortViewForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 True True True AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/SpecificFilesForm.Designer.cs ================================================ namespace DoomLauncher { partial class SpecificFilesForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SpecificFilesForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.btnCancel = new System.Windows.Forms.Button(); this.btnOK = new System.Windows.Forms.Button(); this.clbFiles = new System.Windows.Forms.CheckedListBox(); this.tblInner = new System.Windows.Forms.TableLayoutPanel(); this.btnSearch = new System.Windows.Forms.Button(); this.txtSearch = new System.Windows.Forms.TextBox(); this.pnlLinks = new System.Windows.Forms.Panel(); this.chkPkContents = new DoomLauncher.CCheckBox(); this.chkSupported = new DoomLauncher.CCheckBox(); this.pnl = new System.Windows.Forms.Panel(); this.lblLoading = new System.Windows.Forms.Label(); this.lnkSelect = new System.Windows.Forms.LinkLabel(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.flpButtons.SuspendLayout(); this.tblInner.SuspendLayout(); this.pnlLinks.SuspendLayout(); this.pnl.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.flpButtons, 0, 3); this.tblMain.Controls.Add(this.clbFiles, 0, 2); this.tblMain.Controls.Add(this.tblInner, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 4; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 98F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.Size = new System.Drawing.Size(419, 444); this.tblMain.TabIndex = 0; // // flpButtons // this.flpButtons.Controls.Add(this.btnCancel); this.flpButtons.Controls.Add(this.btnOK); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flpButtons.Location = new System.Drawing.Point(0, 405); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(419, 39); this.flpButtons.TabIndex = 0; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(315, 4); this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(100, 28); this.btnCancel.TabIndex = 0; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // btnOK // this.btnOK.Location = new System.Drawing.Point(207, 4); this.btnOK.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(100, 28); this.btnOK.TabIndex = 1; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; this.btnOK.Click += new System.EventHandler(this.btnOK_Click); // // clbFiles // this.clbFiles.CheckOnClick = true; this.clbFiles.Dock = System.Windows.Forms.DockStyle.Fill; this.clbFiles.FormattingEnabled = true; this.clbFiles.Location = new System.Drawing.Point(4, 134); this.clbFiles.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.clbFiles.Name = "clbFiles"; this.clbFiles.Size = new System.Drawing.Size(411, 267); this.clbFiles.TabIndex = 1; // // tblInner // this.tblInner.ColumnCount = 2; this.tblInner.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInner.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 213F)); this.tblInner.Controls.Add(this.btnSearch, 1, 1); this.tblInner.Controls.Add(this.txtSearch, 0, 1); this.tblInner.Controls.Add(this.pnlLinks, 1, 0); this.tblInner.Controls.Add(this.pnl, 0, 0); this.tblInner.Dock = System.Windows.Forms.DockStyle.Fill; this.tblInner.Location = new System.Drawing.Point(0, 32); this.tblInner.Margin = new System.Windows.Forms.Padding(0); this.tblInner.Name = "tblInner"; this.tblInner.RowCount = 2; this.tblInner.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 62F)); this.tblInner.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInner.Size = new System.Drawing.Size(419, 98); this.tblInner.TabIndex = 5; // // btnSearch // this.btnSearch.Location = new System.Drawing.Point(210, 66); this.btnSearch.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.btnSearch.Name = "btnSearch"; this.btnSearch.Size = new System.Drawing.Size(100, 27); this.btnSearch.TabIndex = 6; this.btnSearch.Text = "Search"; this.btnSearch.UseVisualStyleBackColor = true; this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click); // // txtSearch // this.txtSearch.Dock = System.Windows.Forms.DockStyle.Fill; this.txtSearch.Location = new System.Drawing.Point(4, 66); this.txtSearch.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.txtSearch.Name = "txtSearch"; this.txtSearch.Size = new System.Drawing.Size(198, 22); this.txtSearch.TabIndex = 7; // // pnlLinks // this.pnlLinks.Controls.Add(this.chkPkContents); this.pnlLinks.Controls.Add(this.chkSupported); this.pnlLinks.Dock = System.Windows.Forms.DockStyle.Fill; this.pnlLinks.Location = new System.Drawing.Point(206, 0); this.pnlLinks.Margin = new System.Windows.Forms.Padding(0); this.pnlLinks.Name = "pnlLinks"; this.pnlLinks.Size = new System.Drawing.Size(213, 62); this.pnlLinks.TabIndex = 4; // // chkPkContents // this.chkPkContents.AutoSize = true; this.chkPkContents.Location = new System.Drawing.Point(4, 31); this.chkPkContents.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.chkPkContents.Name = "chkPkContents"; this.chkPkContents.Size = new System.Drawing.Size(142, 20); this.chkPkContents.TabIndex = 4; this.chkPkContents.Text = "Show pk3 Contents"; this.chkPkContents.UseVisualStyleBackColor = true; this.chkPkContents.CheckedChanged += new System.EventHandler(this.chkPkContents_CheckedChanged); // // chkSupported // this.chkSupported.AutoSize = true; this.chkSupported.Location = new System.Drawing.Point(4, 6); this.chkSupported.Margin = new System.Windows.Forms.Padding(32, 0, 0, 0); this.chkSupported.Name = "chkSupported"; this.chkSupported.Size = new System.Drawing.Size(190, 20); this.chkSupported.TabIndex = 3; this.chkSupported.Text = "Supported Extensions Only"; this.chkSupported.UseVisualStyleBackColor = true; this.chkSupported.CheckedChanged += new System.EventHandler(this.chkSupported_CheckedChanged); // // pnl // this.pnl.Controls.Add(this.lblLoading); this.pnl.Controls.Add(this.lnkSelect); this.pnl.Dock = System.Windows.Forms.DockStyle.Fill; this.pnl.Location = new System.Drawing.Point(0, 0); this.pnl.Margin = new System.Windows.Forms.Padding(0); this.pnl.Name = "pnl"; this.pnl.Size = new System.Drawing.Size(206, 62); this.pnl.TabIndex = 8; // // lblLoading // this.lblLoading.AutoSize = true; this.lblLoading.Location = new System.Drawing.Point(4, 32); this.lblLoading.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblLoading.Name = "lblLoading"; this.lblLoading.Size = new System.Drawing.Size(65, 16); this.lblLoading.TabIndex = 3; this.lblLoading.Text = "Loading..."; // // lnkSelect // this.lnkSelect.AutoSize = true; this.lnkSelect.Location = new System.Drawing.Point(4, 7); this.lnkSelect.Margin = new System.Windows.Forms.Padding(4, 7, 4, 0); this.lnkSelect.Name = "lnkSelect"; this.lnkSelect.Size = new System.Drawing.Size(120, 16); this.lnkSelect.TabIndex = 2; this.lnkSelect.TabStop = true; this.lnkSelect.Text = "Select/Unselect All"; this.lnkSelect.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkSelect_LinkClicked); // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(419, 32); this.titleBar.TabIndex = 6; this.titleBar.Title = "Select Files"; // // SpecificFilesForm // this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(419, 444); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.MinimumSize = new System.Drawing.Size(393, 357); this.Name = "SpecificFilesForm"; this.Text = "Select Files"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.SpecificFilesForm_FormClosing); this.tblMain.ResumeLayout(false); this.flpButtons.ResumeLayout(false); this.tblInner.ResumeLayout(false); this.tblInner.PerformLayout(); this.pnlLinks.ResumeLayout(false); this.pnlLinks.PerformLayout(); this.pnl.ResumeLayout(false); this.pnl.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.FlowLayoutPanel flpButtons; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.CheckedListBox clbFiles; private System.Windows.Forms.LinkLabel lnkSelect; private System.Windows.Forms.CheckBox chkSupported; private System.Windows.Forms.Panel pnlLinks; private System.Windows.Forms.CheckBox chkPkContents; private System.Windows.Forms.Button btnSearch; private System.Windows.Forms.TextBox txtSearch; private System.Windows.Forms.TableLayoutPanel tblInner; private System.Windows.Forms.Panel pnl; private System.Windows.Forms.Label lblLoading; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/SpecificFilesForm.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace DoomLauncher { public partial class SpecificFilesForm : Form { public class SpecificFilePath { public string ExtractedFile { get; set; } public string InternalFilePath { get; set; } } private bool m_select, m_autoSelect, m_loading, m_closing; private string[] m_specificFiles = new string[] { }; private string[] m_supportedExtensions = new string[] { }; private List m_gameFiles; private LauncherPath m_directory, m_temp; private readonly List m_filePaths = new List(); private CancellationTokenSource m_ct; private List m_items = new List(); private List m_checkedItems = new List(); public SpecificFilesForm() { InitializeComponent(); AutoCheckSupportedExtensions(true); ShowPkContentsCheckBox(false); lblLoading.Text = string.Empty; txtSearch.PreviewKeyDown += TxtSearch_PreviewKeyDown; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } private void TxtSearch_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) { if (e.KeyCode == Keys.Enter) HandleSearch(); } public void AutoCheckSupportedExtensions(bool set) { m_autoSelect = set; } public void ShowPkContentsCheckBox(bool set) { DpiScale dpiScale = new DpiScale(CreateGraphics()); chkPkContents.Visible = set; tblMain.RowStyles[0].Height = (set ? dpiScale.ScaleIntY(80) : dpiScale.ScaleIntY(24)); } public void Initialize(LauncherPath gameFileDirectory, IEnumerable gameFiles, string[] supportedExtensions, string[] specificFiles) { Initialize(gameFileDirectory, gameFiles, supportedExtensions, specificFiles, null); } public void Initialize(LauncherPath gameFileDirectory, IEnumerable gameFiles, string[] supportedExtensions, string[] specificFiles, LauncherPath tempDirectory) { if (specificFiles != null) m_specificFiles = specificFiles.ToArray(); m_supportedExtensions = supportedExtensions.ToArray(); m_gameFiles = gameFiles.ToList(); m_directory = gameFileDirectory; m_temp = tempDirectory; try { SetGrid(); } catch(Exception ex) { Util.DisplayUnexpectedException(this, ex); } } private async void SetGrid() { if (!m_loading) { chkPkContents.Enabled = btnSearch.Enabled = false; m_loading = true; lblLoading.Text = "Loading..."; clbFiles.Items.Clear(); m_items = new List(); m_checkedItems = new List(); m_ct = new CancellationTokenSource(); await Task.Run(() => SetGridTask()); clbFiles.Items.AddRange(m_items.ToArray()); m_checkedItems.ForEach(x => clbFiles.SetItemChecked(x, true)); if (!m_closing) { lblLoading.Text = string.Empty; m_loading = false; chkPkContents.Enabled = btnSearch.Enabled = true; } } } private void SetGridTask() { m_filePaths.Clear(); foreach (IGameFile gameFile in m_gameFiles) { string path = Path.Combine(m_directory.GetFullPath(), gameFile.FileName); using (IArchiveReader reader = CreateArchiveReader(gameFile, path)) { if (m_ct.IsCancellationRequested) break; try { if (m_specificFiles == null || m_specificFiles.Length == 0) { HandleDefaultSelection(path, reader); continue; } foreach (IArchiveEntry entry in reader.Entries) { if (string.IsNullOrEmpty(entry.Name) || entry.IsDirectory) continue; if (m_ct.IsCancellationRequested) break; HandleAddItem(path, entry.FullName, entry.Name, m_specificFiles.Contains(entry.FullName)); } } catch { //this is laziness, sometimes it throws an exception when the form is closing in the middle of loading. But were closing the form so who cares } } } } private static IArchiveReader CreateArchiveReader(IGameFile gameFile, string path) { // this ignored pk3 files specifically for some reason if (!File.Exists(path) && !Directory.Exists(path)) return ArchiveReader.EmptyArchiveReader; bool isPackagedArchive = ArchiveUtil.ShouldReadPackagedArchive(path); if (gameFile.IsUnmanaged() && !isPackagedArchive) return new FileArchiveReader(path); // TODO IsPk necessary? if (isPackagedArchive) return ArchiveReader.Create(path); if (ArchiveReader.IsPk(Path.GetExtension(path))) return new ZipArchiveReader(path); return ArchiveReader.EmptyArchiveReader; } private void HandleDefaultSelection(string file, IArchiveReader reader) { IEnumerable filteredEntries; if (reader is FileArchiveReader) filteredEntries = reader.Entries; else filteredEntries = reader.Entries.Where(x => !string.IsNullOrEmpty(x.Name) && !x.Name.EndsWith(Path.PathSeparator.ToString()) && m_supportedExtensions.Any(y => y.Equals(Path.GetExtension(x.Name), StringComparison.OrdinalIgnoreCase))) .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase).ToList(); if (chkPkContents.Checked) { IEnumerable pk3Entries = Util.GetEntriesByExtension(reader, Util.GetPkExtenstions()); //should check for MAPINFO (e.g. joyofmapping) foreach (IArchiveEntry entry in pk3Entries) { string extractedFile = Util.ExtractTempFile(m_temp.GetFullPath(), entry); using (IArchiveReader zaInner = ArchiveReader.Create(extractedFile)) HandleDefaultSelection(extractedFile, zaInner); } } foreach (IArchiveEntry entry in reader.Entries) { if (string.IsNullOrEmpty(entry.Name) || entry.IsDirectory) continue; if (m_ct.IsCancellationRequested) break; HandleAddItem(file, entry.FullName, entry.Name, filteredEntries.Contains(entry)); } } private void HandleAddItem(string file, string fullname, string name, bool isChecked) { isChecked = isChecked && m_autoSelect; try { if (chkSupported.Checked) { if (m_supportedExtensions.Any(x => x.Equals(Path.GetExtension(name), StringComparison.OrdinalIgnoreCase))) { if (isChecked) m_checkedItems.Add(m_items.Count); m_items.Add(fullname); m_filePaths.Add(new SpecificFilePath { ExtractedFile = file, InternalFilePath = fullname }); } } else { if (isChecked) m_checkedItems.Add(m_items.Count); m_items.Add(fullname); m_filePaths.Add(new SpecificFilePath { ExtractedFile = file, InternalFilePath = fullname }); } } catch { //i dunno } } public static string[] GetSupportedFiles(string gameFileDirectory, IGameFile gameFile, string[] supportedExtensions) { List files = new List(); string path = Path.Combine(gameFileDirectory, gameFile.FileName); // Directories do not have extensions, always add it // Unmanaged pk3s should not be extracted if (gameFile.IsDirectory() || (gameFile.IsUnmanaged() && !ArchiveUtil.ShouldReadPackagedArchive(gameFile.FileName))) { files.Add(gameFile.FileName); return files.ToArray(); } using (IArchiveReader reader = CreateArchiveReader(gameFile, path)) { foreach (IArchiveEntry entry in reader.Entries) { if (string.IsNullOrEmpty(entry.Name)) continue; if (!supportedExtensions.Any(x => x.Equals(Path.GetExtension(entry.Name), StringComparison.OrdinalIgnoreCase))) continue; files.Add(entry.FullName); } } return files.ToArray(); } public string[] GetSpecificFiles() { return clbFiles.CheckedItems.Cast().ToArray(); } public List GetPathedSpecificFiles() { List ret = new List(); for (int i = 0; i < clbFiles.Items.Count; i++) { if (clbFiles.GetItemChecked(i)) ret.Add(m_filePaths.First(x => x.InternalFilePath.Equals(clbFiles.Items[i]))); } return ret; } private void btnSearch_Click(object sender, EventArgs e) { HandleSearch(); } private void HandleSearch() { if (!string.IsNullOrEmpty(txtSearch.Text)) { var items = m_items.Where(x => x.IndexOf(txtSearch.Text, StringComparison.OrdinalIgnoreCase) != -1); clbFiles.Items.Clear(); clbFiles.Items.AddRange(items.ToArray()); } else { clbFiles.Items.AddRange(m_items.ToArray()); } } private void btnOK_Click(object sender, EventArgs e) { if (chkPkContents.Visible && GetSpecificFiles().Length == 0) //really only need to validate if selecting a file to open w/utility { MessageBox.Show(this, "At least one file must be selected.", "No Files Selected", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { DialogResult = DialogResult.OK; Close(); } } private void SpecificFilesForm_FormClosing(object sender, FormClosingEventArgs e) { if (m_ct != null) { m_closing = true; m_ct.Cancel(); } } private void chkPkContents_CheckedChanged(object sender, EventArgs e) { SetGrid(); } private void lnkSelect_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { for(int i = 0; i < clbFiles.Items.Count; i++) clbFiles.SetItemChecked(i, m_select); m_select = !m_select; } private void chkSupported_CheckedChanged(object sender, EventArgs e) { SetGrid(); } } } ================================================ FILE: DoomLauncher/Forms/SpecificFilesForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/SplashScreen.Designer.cs ================================================ namespace DoomLauncher { partial class SplashScreen { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SplashScreen)); this.SuspendLayout(); // // SplashScreen // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.BackColor = System.Drawing.Color.Magenta; this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage"))); this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; this.ClientSize = new System.Drawing.Size(256, 256); this.ControlBox = false; this.DoubleBuffered = true; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.Name = "SplashScreen"; this.ShowIcon = false; this.ShowInTaskbar = false; this.Text = "SplashScreen"; this.ResumeLayout(false); } #endregion } } ================================================ FILE: DoomLauncher/Forms/SplashScreen.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public partial class SplashScreen : Form { public SplashScreen() { SetStyle(ControlStyles.SupportsTransparentBackColor, true); BackColor = Color.Transparent; TransparencyKey = Color.Magenta; InitializeComponent(); } } } ================================================ FILE: DoomLauncher/Forms/SplashScreen.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 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 ================================================ FILE: DoomLauncher/Forms/StatsInfo.Designer.cs ================================================ namespace DoomLauncher { partial class StatsInfo { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(StatsInfo)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.btnOK = new System.Windows.Forms.Button(); this.tblInfoOuter = new System.Windows.Forms.TableLayoutPanel(); this.tblInner = new System.Windows.Forms.TableLayoutPanel(); this.label6 = new System.Windows.Forms.Label(); this.lblBoom = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.lblZdoom = new System.Windows.Forms.Label(); this.lblChocolate = new System.Windows.Forms.Label(); this.tblInfo = new System.Windows.Forms.TableLayoutPanel(); this.label4 = new System.Windows.Forms.Label(); this.pbInfo1 = new System.Windows.Forms.PictureBox(); this.label3 = new System.Windows.Forms.Label(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.tblInfoOuter.SuspendLayout(); this.tblInner.SuspendLayout(); this.tblInfo.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo1)).BeginInit(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.btnOK, 0, 2); this.tblMain.Controls.Add(this.tblInfoOuter, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.Size = new System.Drawing.Size(400, 441); this.tblMain.TabIndex = 0; // // btnOK // this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.Right; this.btnOK.Location = new System.Drawing.Point(322, 413); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(75, 23); this.btnOK.TabIndex = 0; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; this.btnOK.Click += new System.EventHandler(this.btnOK_Click); // // tblInfoOuter // this.tblInfoOuter.ColumnCount = 1; this.tblInfoOuter.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInfoOuter.Controls.Add(this.tblInner, 0, 1); this.tblInfoOuter.Controls.Add(this.tblInfo, 0, 0); this.tblInfoOuter.Dock = System.Windows.Forms.DockStyle.Fill; this.tblInfoOuter.Location = new System.Drawing.Point(0, 26); this.tblInfoOuter.Margin = new System.Windows.Forms.Padding(0); this.tblInfoOuter.Name = "tblInfoOuter"; this.tblInfoOuter.RowCount = 2; this.tblInfoOuter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 80F)); this.tblInfoOuter.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInfoOuter.Size = new System.Drawing.Size(400, 383); this.tblInfoOuter.TabIndex = 1; // // tblInner // this.tblInner.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.Single; this.tblInner.ColumnCount = 2; this.tblInner.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F)); this.tblInner.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInner.Controls.Add(this.label6, 0, 2); this.tblInner.Controls.Add(this.lblBoom, 1, 1); this.tblInner.Controls.Add(this.label2, 0, 1); this.tblInner.Controls.Add(this.label1, 0, 0); this.tblInner.Controls.Add(this.lblZdoom, 1, 0); this.tblInner.Controls.Add(this.lblChocolate, 1, 2); this.tblInner.Dock = System.Windows.Forms.DockStyle.Fill; this.tblInner.Location = new System.Drawing.Point(3, 83); this.tblInner.Name = "tblInner"; this.tblInner.RowCount = 3; this.tblInner.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 40F)); this.tblInner.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 30F)); this.tblInner.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 30F)); this.tblInner.Size = new System.Drawing.Size(394, 297); this.tblInner.TabIndex = 2; // // label6 // this.label6.AutoSize = true; this.label6.Location = new System.Drawing.Point(4, 207); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(92, 39); this.label6.TabIndex = 6; this.label6.Text = "Chocolate Doom, CNDoom, CRL, Inter-Doom"; // // lblBoom // this.lblBoom.AutoSize = true; this.lblBoom.Location = new System.Drawing.Point(105, 119); this.lblBoom.Name = "lblBoom"; this.lblBoom.Size = new System.Drawing.Size(0, 13); this.lblBoom.TabIndex = 3; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(4, 119); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(74, 52); this.label2.TabIndex = 2; this.label2.Text = "Crispy Doom, PrBoom+, DSDA-Doom, Nyan Doom, Helion, Woof!"; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(4, 1); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(42, 13); this.label1.TabIndex = 0; this.label1.Text = "ZDoom"; // // lblZdoom // this.lblZdoom.AutoSize = true; this.lblZdoom.Location = new System.Drawing.Point(105, 1); this.lblZdoom.Name = "lblZdoom"; this.lblZdoom.Size = new System.Drawing.Size(0, 13); this.lblZdoom.TabIndex = 1; // // lblChocolate // this.lblChocolate.AutoSize = true; this.lblChocolate.Location = new System.Drawing.Point(105, 207); this.lblChocolate.Name = "lblChocolate"; this.lblChocolate.Size = new System.Drawing.Size(0, 13); this.lblChocolate.TabIndex = 7; // // tblInfo // this.tblInfo.ColumnCount = 2; this.tblInfo.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblInfo.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblInfo.Controls.Add(this.label4, 1, 1); this.tblInfo.Controls.Add(this.pbInfo1, 0, 0); this.tblInfo.Controls.Add(this.label3, 1, 0); this.tblInfo.Dock = System.Windows.Forms.DockStyle.Fill; this.tblInfo.Location = new System.Drawing.Point(0, 0); this.tblInfo.Margin = new System.Windows.Forms.Padding(0); this.tblInfo.Name = "tblInfo"; this.tblInfo.RowCount = 2; this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblInfo.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tblInfo.Size = new System.Drawing.Size(400, 80); this.tblInfo.TabIndex = 3; // // label4 // this.label4.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(35, 53); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(262, 13); this.label4.TabIndex = 3; this.label4.Text = "Statistics recording is supported for the following ports:"; // // pbInfo1 // this.pbInfo1.Dock = System.Windows.Forms.DockStyle.Fill; this.pbInfo1.InitialImage = null; this.pbInfo1.Location = new System.Drawing.Point(0, 0); this.pbInfo1.Margin = new System.Windows.Forms.Padding(0); this.pbInfo1.Name = "pbInfo1"; this.pbInfo1.Size = new System.Drawing.Size(32, 40); this.pbInfo1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; this.pbInfo1.TabIndex = 0; this.pbInfo1.TabStop = false; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(35, 7); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(361, 26); this.label3.TabIndex = 2; this.label3.Text = "The \'Save Statistics\' option will become available when a supported source port i" + "s selected"; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(400, 26); this.titleBar.TabIndex = 2; this.titleBar.Title = "Save Statistics"; // // StatsInfo // this.AcceptButton = this.btnOK; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(400, 441); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "StatsInfo"; this.Text = "Save Statistics"; this.tblMain.ResumeLayout(false); this.tblInfoOuter.ResumeLayout(false); this.tblInner.ResumeLayout(false); this.tblInner.PerformLayout(); this.tblInfo.ResumeLayout(false); this.tblInfo.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.pbInfo1)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.TableLayoutPanel tblInfoOuter; private System.Windows.Forms.TableLayoutPanel tblInner; private System.Windows.Forms.Label lblBoom; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label lblZdoom; private System.Windows.Forms.TableLayoutPanel tblInfo; private System.Windows.Forms.PictureBox pbInfo1; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label lblChocolate; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/StatsInfo.cs ================================================ using System; using System.Windows.Forms; namespace DoomLauncher { public partial class StatsInfo : Form { public StatsInfo() { InitializeComponent(); pbInfo1.Image = DoomLauncher.Properties.Resources.bon2b; lblZdoom.Text = "For all ZDoom based ports. Uses save games to parse statistics. This means statistics cannot be read for the last level of an episode. " + "Items and skill level are not available for ZDoom, Zandronum, and GZDoom pre-3.5.0. Statistics will be recorded when the game is saved or when an auto save is generated."; lblBoom.Text = "Uses the -levelstat parameter and parses the generated levelstat.txt. All statistics will be recorded when the game has exited."; lblChocolate.Text = "Uses the -statdump parameter and parses the generated stats.txt. All statistics will be recorded when the game has exited. ExM8 maps are only generated by CNDoom."; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } private void btnOK_Click(object sender, EventArgs e) { Close(); } } } ================================================ FILE: DoomLauncher/Forms/StatsInfo.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/SyncStatusForm.Designer.cs ================================================ namespace DoomLauncher { partial class SyncStatusForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SyncStatusForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.dgvFiles = new DoomLauncher.CDataGridView(); this.FileName = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.Selected = new System.Windows.Forms.DataGridViewCheckBoxColumn(); this.btnSelectAll = new System.Windows.Forms.Button(); this.flpBottom = new System.Windows.Forms.FlowLayoutPanel(); this.btnContinue = new System.Windows.Forms.Button(); this.cmbOptions = new System.Windows.Forms.ComboBox(); this.lblHeader = new System.Windows.Forms.Label(); this.tblMain.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.dgvFiles)).BeginInit(); this.flpBottom.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.dgvFiles, 0, 1); this.tblMain.Controls.Add(this.btnSelectAll, 0, 2); this.tblMain.Controls.Add(this.flpBottom, 0, 3); this.tblMain.Controls.Add(this.lblHeader, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 4; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.Size = new System.Drawing.Size(365, 311); this.tblMain.TabIndex = 0; // // dgvFiles // this.dgvFiles.AllowUserToAddRows = false; this.dgvFiles.AllowUserToDeleteRows = false; this.dgvFiles.AllowUserToOrderColumns = true; dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this.dgvFiles.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1; this.dgvFiles.BackgroundColor = System.Drawing.SystemColors.ControlLightLight; this.dgvFiles.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.SingleHorizontal; this.dgvFiles.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.dgvFiles.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.FileName, this.Selected}); this.dgvFiles.Dock = System.Windows.Forms.DockStyle.Fill; this.dgvFiles.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter; this.dgvFiles.GridColor = System.Drawing.SystemColors.ActiveBorder; this.dgvFiles.Location = new System.Drawing.Point(3, 35); this.dgvFiles.Name = "dgvFiles"; this.dgvFiles.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; this.dgvFiles.Size = new System.Drawing.Size(359, 209); this.dgvFiles.TabIndex = 2; // // FileName // this.FileName.DataPropertyName = "FileName"; this.FileName.HeaderText = "FileName"; this.FileName.Name = "FileName"; this.FileName.ReadOnly = true; // // Selected // this.Selected.DataPropertyName = "Selected"; this.Selected.HeaderText = ""; this.Selected.Name = "Selected"; // // btnSelectAll // this.btnSelectAll.Anchor = System.Windows.Forms.AnchorStyles.Right; this.btnSelectAll.Location = new System.Drawing.Point(287, 251); this.btnSelectAll.Name = "btnSelectAll"; this.btnSelectAll.Size = new System.Drawing.Size(75, 23); this.btnSelectAll.TabIndex = 3; this.btnSelectAll.Text = "Select All"; this.btnSelectAll.UseVisualStyleBackColor = true; this.btnSelectAll.Click += new System.EventHandler(this.btnSelectAll_Click); // // flpBottom // this.flpBottom.Controls.Add(this.btnContinue); this.flpBottom.Controls.Add(this.cmbOptions); this.flpBottom.Dock = System.Windows.Forms.DockStyle.Fill; this.flpBottom.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flpBottom.Location = new System.Drawing.Point(0, 279); this.flpBottom.Margin = new System.Windows.Forms.Padding(0); this.flpBottom.Name = "flpBottom"; this.flpBottom.Size = new System.Drawing.Size(365, 32); this.flpBottom.TabIndex = 4; // // btnContinue // this.btnContinue.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnContinue.Location = new System.Drawing.Point(287, 3); this.btnContinue.Name = "btnContinue"; this.btnContinue.Size = new System.Drawing.Size(75, 23); this.btnContinue.TabIndex = 1; this.btnContinue.Text = "Continue"; this.btnContinue.UseVisualStyleBackColor = true; // // cmbOptions // this.cmbOptions.Anchor = System.Windows.Forms.AnchorStyles.None; this.cmbOptions.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbOptions.FormattingEnabled = true; this.cmbOptions.Location = new System.Drawing.Point(157, 4); this.cmbOptions.Margin = new System.Windows.Forms.Padding(3, 3, 6, 3); this.cmbOptions.Name = "cmbOptions"; this.cmbOptions.Size = new System.Drawing.Size(121, 21); this.cmbOptions.TabIndex = 0; // // lblHeader // this.lblHeader.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lblHeader.AutoSize = true; this.lblHeader.Location = new System.Drawing.Point(3, 9); this.lblHeader.Name = "lblHeader"; this.lblHeader.Size = new System.Drawing.Size(13, 13); this.lblHeader.TabIndex = 5; this.lblHeader.Text = " "; // // SyncStatusForm // this.AcceptButton = this.btnContinue; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(365, 311); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "SyncStatusForm"; this.Text = "SyncStatusFile"; this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.dgvFiles)).EndInit(); this.flpBottom.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private CDataGridView dgvFiles; private System.Windows.Forms.DataGridViewTextBoxColumn FileName; private System.Windows.Forms.DataGridViewCheckBoxColumn Selected; private System.Windows.Forms.Button btnSelectAll; private System.Windows.Forms.FlowLayoutPanel flpBottom; private System.Windows.Forms.ComboBox cmbOptions; private System.Windows.Forms.Button btnContinue; private System.Windows.Forms.Label lblHeader; } } ================================================ FILE: DoomLauncher/Forms/SyncStatusForm.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class SyncStatusForm : Form { public SyncStatusForm() { InitializeComponent(); dgvFiles.Columns[dgvFiles.Columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public void SetHeaderText(string text) { lblHeader.Text = text; } public void SetData(IEnumerable files, IEnumerable dropDownOptions) { List syncFiles = files.Select(x => new SyncFileData(x)).OrderBy(x => x.FileName).ToList(); dgvFiles.DataSource = syncFiles; cmbOptions.DataSource = dropDownOptions.ToList(); } public List GetSelectedFiles() { List syncFiles = dgvFiles.DataSource as List; return syncFiles.Where(x => x.Selected).Select(x => x.FileName).ToList(); } public int SelectedOptionIndex { get { return cmbOptions.SelectedIndex; } } private void btnSelectAll_Click(object sender, EventArgs e) { List syncFiles = dgvFiles.DataSource as List; if (syncFiles.Count > 0) { bool select = !syncFiles[0].Selected; foreach (SyncFileData file in syncFiles) file.Selected = select; dgvFiles.DataSource = null; dgvFiles.DataSource = syncFiles; dgvFiles.Columns[dgvFiles.Columns.Count - 1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; dgvFiles.Columns[dgvFiles.Columns.Count - 1].HeaderText = string.Empty; } } } } ================================================ FILE: DoomLauncher/Forms/SyncStatusForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 True True AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/TagEditForm.Designer.cs ================================================ namespace DoomLauncher { partial class TagEditForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TagEditForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.btnCancel = new System.Windows.Forms.Button(); this.btnOK = new System.Windows.Forms.Button(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.flowLayoutPanel1, 0, 2); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 32F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.Size = new System.Drawing.Size(382, 277); this.tblMain.TabIndex = 0; // // flowLayoutPanel1 // this.flowLayoutPanel1.Controls.Add(this.btnCancel); this.flowLayoutPanel1.Controls.Add(this.btnOK); this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 238); this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.flowLayoutPanel1.Name = "flowLayoutPanel1"; this.flowLayoutPanel1.Size = new System.Drawing.Size(382, 39); this.flowLayoutPanel1.TabIndex = 0; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(278, 4); this.btnCancel.Margin = new System.Windows.Forms.Padding(4); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(100, 28); this.btnCancel.TabIndex = 0; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // btnOK // this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(170, 4); this.btnOK.Margin = new System.Windows.Forms.Padding(4); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(100, 28); this.btnOK.TabIndex = 1; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(3, 2); this.titleBar.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); this.titleBar.Name = "titleBar"; this.titleBar.RememberNormalSize = true; this.titleBar.Size = new System.Drawing.Size(376, 28); this.titleBar.TabIndex = 1; this.titleBar.Title = "Tag"; // // TagEditForm // this.AcceptButton = this.btnOK; this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(382, 277); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "TagEditForm"; this.ShowInTaskbar = false; this.Text = "Tag"; this.tblMain.ResumeLayout(false); this.flowLayoutPanel1.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnOK; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/TagEditForm.cs ================================================ using System.Windows.Forms; namespace DoomLauncher { public partial class TagEditForm : Form { private TagEdit m_tagEdit; public TagEditForm() { InitializeComponent(); m_tagEdit = new TagEdit(); m_tagEdit.Dock = DockStyle.Fill; tblMain.Controls.Add(m_tagEdit, 0, 1); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public TagEdit TagEditControl { get { return m_tagEdit; } } } } ================================================ FILE: DoomLauncher/Forms/TagEditForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/TagForm.Designer.cs ================================================ namespace DoomLauncher { partial class TagForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TagForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(0); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 2; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Size = new System.Drawing.Size(936, 678); this.tblMain.TabIndex = 0; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(936, 40); this.titleBar.TabIndex = 0; this.titleBar.Title = "Manage Tags"; // // TagForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(936, 678); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "TagForm"; this.ShowInTaskbar = false; this.Text = "Manage Tags"; this.tblMain.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/TagForm.cs ================================================ using DoomLauncher.Interfaces; using System.Windows.Forms; namespace DoomLauncher { public partial class TagForm : Form { private readonly TagControl m_tabCtrl; public TagForm() { InitializeComponent(); m_tabCtrl = new TagControl(); m_tabCtrl.Dock = DockStyle.Fill; tblMain.Controls.Add(m_tabCtrl, 0, 1); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public void Init(IDataSourceAdapter adapter) { m_tabCtrl.Init(adapter); } public TagControl TagControl { get { return m_tabCtrl; } } } } ================================================ FILE: DoomLauncher/Forms/TagForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/TagSelectForm.Designer.cs ================================================ namespace DoomLauncher { partial class TagSelectForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TagSelectForm)); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.btnCancel = new System.Windows.Forms.Button(); this.btnOK = new System.Windows.Forms.Button(); this.tagSelectControl = new DoomLauncher.Controls.TagSelectControl(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.flpButtons.SuspendLayout(); this.SuspendLayout(); // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.flpButtons, 0, 2); this.tblMain.Controls.Add(this.tagSelectControl, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblMain.Size = new System.Drawing.Size(486, 925); this.tblMain.TabIndex = 1; // // flpButtons // this.flpButtons.Controls.Add(this.btnCancel); this.flpButtons.Controls.Add(this.btnOK); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flpButtons.Location = new System.Drawing.Point(0, 876); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(486, 49); this.flpButtons.TabIndex = 3; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(370, 5); this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(112, 35); this.btnCancel.TabIndex = 3; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; // // btnOK // this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(250, 5); this.btnOK.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(112, 35); this.btnOK.TabIndex = 2; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; // // tagSelectControl // this.tagSelectControl.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.tagSelectControl.Dock = System.Windows.Forms.DockStyle.Fill; this.tagSelectControl.ForeColor = System.Drawing.Color.White; this.tagSelectControl.Location = new System.Drawing.Point(6, 48); this.tagSelectControl.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); this.tagSelectControl.Name = "tagSelectControl"; this.tagSelectControl.Size = new System.Drawing.Size(474, 820); this.tagSelectControl.TabIndex = 4; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(3, 3); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(480, 34); this.titleBar.TabIndex = 5; this.titleBar.Title = "Select Tags"; // // TagSelectForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(486, 925); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "TagSelectForm"; this.Text = "Select Tags"; this.tblMain.ResumeLayout(false); this.flpButtons.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.FlowLayoutPanel flpButtons; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnOK; private Controls.TagSelectControl tagSelectControl; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/TagSelectForm.cs ================================================ using DoomLauncher.Controls; using System; using System.Windows.Forms; namespace DoomLauncher { public partial class TagSelectForm : Form { public TagSelectForm() { InitializeComponent(); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public TagSelectControl TagSelectControl => tagSelectControl; protected override void OnShown(EventArgs e) { // DataGridViews make no sense sometimes, the checks are being cleared between setting/show so reset the DataSource here base.OnShown(e); TagSelectControl.SetCheckedTags(); } } } ================================================ FILE: DoomLauncher/Forms/TagSelectForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/TextBoxForm.Designer.cs ================================================ namespace DoomLauncher { partial class TextBoxForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TextBoxForm)); this.tblContainer = new System.Windows.Forms.TableLayoutPanel(); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.txtText = new System.Windows.Forms.TextBox(); this.lblHeader = new DoomLauncher.GrowLabel(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.btnCancel = new System.Windows.Forms.Button(); this.btnOK = new System.Windows.Forms.Button(); this.lnk = new System.Windows.Forms.LinkLabel(); this.chk = new DoomLauncher.CCheckBox(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblContainer.SuspendLayout(); this.tblMain.SuspendLayout(); this.flpButtons.SuspendLayout(); this.SuspendLayout(); // // tblContainer // this.tblContainer.ColumnCount = 1; this.tblContainer.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblContainer.Controls.Add(this.tblMain, 0, 1); this.tblContainer.Controls.Add(this.titleBar, 0, 0); this.tblContainer.Dock = System.Windows.Forms.DockStyle.Fill; this.tblContainer.Location = new System.Drawing.Point(0, 0); this.tblContainer.Margin = new System.Windows.Forms.Padding(0); this.tblContainer.Name = "tblContainer"; this.tblContainer.RowCount = 2; this.tblContainer.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblContainer.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblContainer.Size = new System.Drawing.Size(800, 450); this.tblContainer.TabIndex = 2; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.txtText, 0, 3); this.tblMain.Controls.Add(this.lblHeader, 0, 0); this.tblMain.Controls.Add(this.flpButtons, 0, 4); this.tblMain.Controls.Add(this.lnk, 0, 2); this.tblMain.Controls.Add(this.chk, 0, 1); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(4, 45); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 5; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblMain.Size = new System.Drawing.Size(792, 400); this.tblMain.TabIndex = 0; // // txtText // this.txtText.Dock = System.Windows.Forms.DockStyle.Fill; this.txtText.Location = new System.Drawing.Point(4, 152); this.txtText.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtText.Multiline = true; this.txtText.Name = "txtText"; this.txtText.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.txtText.Size = new System.Drawing.Size(784, 194); this.txtText.TabIndex = 0; // // lblHeader // this.lblHeader.AutoSize = true; this.lblHeader.IsPath = false; this.lblHeader.Location = new System.Drawing.Point(4, 5); this.lblHeader.Margin = new System.Windows.Forms.Padding(4, 5, 0, 0); this.lblHeader.Name = "lblHeader"; this.lblHeader.Size = new System.Drawing.Size(39, 20); this.lblHeader.TabIndex = 2; this.lblHeader.Text = "Text"; // // flpButtons // this.flpButtons.Controls.Add(this.btnCancel); this.flpButtons.Controls.Add(this.btnOK); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flpButtons.Location = new System.Drawing.Point(0, 351); this.flpButtons.Margin = new System.Windows.Forms.Padding(0); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(792, 49); this.flpButtons.TabIndex = 3; // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(676, 5); this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(112, 35); this.btnCancel.TabIndex = 3; this.btnCancel.Text = "Cancel"; this.btnCancel.UseVisualStyleBackColor = true; this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); // // btnOK // this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnOK.Location = new System.Drawing.Point(556, 5); this.btnOK.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(112, 35); this.btnOK.TabIndex = 2; this.btnOK.Text = "OK"; this.btnOK.UseVisualStyleBackColor = true; this.btnOK.Click += new System.EventHandler(this.btnOK_Click_1); // // lnk // this.lnk.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lnk.AutoSize = true; this.lnk.Location = new System.Drawing.Point(4, 112); this.lnk.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lnk.Name = "lnk"; this.lnk.Size = new System.Drawing.Size(38, 20); this.lnk.TabIndex = 4; this.lnk.TabStop = true; this.lnk.Text = "Link"; this.lnk.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnk_LinkClicked); // // chk // this.chk.Anchor = System.Windows.Forms.AnchorStyles.Left; this.chk.AutoSize = true; this.chk.Location = new System.Drawing.Point(4, 61); this.chk.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chk.Name = "chk"; this.chk.Size = new System.Drawing.Size(107, 24); this.chk.TabIndex = 5; this.chk.Text = "CheckBox"; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.CanClose = true; this.titleBar.ControlBox = true; this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(800, 40); this.titleBar.TabIndex = 1; this.titleBar.Title = "Title"; // // TextBoxForm // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(800, 450); this.Controls.Add(this.tblContainer); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "TextBoxForm"; this.Text = "TextBoxForm2"; this.tblContainer.ResumeLayout(false); this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.flpButtons.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tblContainer; private TableLayoutPanelDB tblMain; private System.Windows.Forms.TextBox txtText; private GrowLabel lblHeader; private System.Windows.Forms.FlowLayoutPanel flpButtons; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnOK; private System.Windows.Forms.LinkLabel lnk; private CCheckBox chk; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/TextBoxForm.cs ================================================ using System; using System.Diagnostics; using System.Windows.Forms; namespace DoomLauncher { public partial class TextBoxForm : Form { private static readonly int s_headerRow = 0; private static readonly int s_checkBoxRow = 1; private static readonly int s_linkRow = 2; private string m_url; const int TitleBarHeight = 40; public TextBoxForm() : this(true, MessageBoxButtons.OK) { } public TextBoxForm(bool multiline, MessageBoxButtons buttons) { InitializeComponent(); DpiScale dpiScale = new DpiScale(CreateGraphics()); if (buttons != MessageBoxButtons.OK && buttons != MessageBoxButtons.OKCancel) throw new NotSupportedException(buttons.ToString() + " not supported"); btnCancel.Visible = buttons == MessageBoxButtons.OKCancel; HeaderText = string.Empty; txtText.Multiline = multiline; if (!multiline) { Height = dpiScale.ScaleIntY(100 + TitleBarHeight); Width = dpiScale.ScaleIntX(300); } tblMain.RowStyles[s_checkBoxRow].Height = 0; tblMain.RowStyles[s_linkRow].Height = 0; lnk.Visible = false; chk.Visible = false; AcceptButton = btnOK; CancelButton = btnCancel; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public string Title { get { return titleBar.Title; } set { titleBar.Title = value; Text = value; } } public void SetCheckBox(string text) { DpiScale dpiScale = new DpiScale(CreateGraphics()); chk.Visible = true; chk.Text = text; tblMain.RowStyles[s_checkBoxRow].Height = dpiScale.ScaleIntY(32); Height += dpiScale.ScaleIntY(32); } public void SetLink(string text, string url) { DpiScale dpiScale = new DpiScale(CreateGraphics()); lnk.Visible = true; lnk.Text = text; m_url = url; tblMain.RowStyles[s_linkRow].Height = dpiScale.ScaleIntY(32); Height += dpiScale.ScaleIntY(32); } public void SetMaxLength(int length) { txtText.MaxLength = length; } public void SelectDisplayText(int start, int length) { txtText.Select(start, length); } public string DisplayText { get { return txtText.Text; } set { txtText.Text = value; } } public bool CheckBoxChecked { get { return chk.Checked; } set { chk.Checked = value; } } public void AppendText(string text) { txtText.Text += text; } public string HeaderText { get { return lblHeader.Text; } set { lblHeader.Text = value; tblMain.RowStyles[s_headerRow].Height = lblHeader.Height + 3; } } private void lnk_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { Process.Start(m_url); } private void btnOK_Click_1(object sender, EventArgs e) { DialogResult = DialogResult.OK; Close(); } private void btnCancel_Click(object sender, EventArgs e) { DialogResult = DialogResult.Cancel; Close(); } } } ================================================ FILE: DoomLauncher/Forms/TextBoxForm.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/TxtGenerator.Designer.cs ================================================ namespace DoomLauncher { partial class TxtGenerator { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TxtGenerator)); this.btnGenerate = new System.Windows.Forms.Button(); this.btnCancel = new System.Windows.Forms.Button(); this.tblMain = new DoomLauncher.TableLayoutPanelDB(); this.flpButtons = new System.Windows.Forms.FlowLayoutPanel(); this.tabControl = new DoomLauncher.CTabControl(); this.tabPage1 = new System.Windows.Forms.TabPage(); this.tblLayout1 = new DoomLauncher.TableLayoutPanelDB(); this.cmbPrimaryPurpose = new System.Windows.Forms.ComboBox(); this.txtAdditionalCredits = new System.Windows.Forms.TextBox(); this.txtDescription = new System.Windows.Forms.TextBox(); this.txtMiscAuthor = new System.Windows.Forms.TextBox(); this.txtOtherFiles = new System.Windows.Forms.TextBox(); this.txtEmail = new System.Windows.Forms.TextBox(); this.txtAuthor = new System.Windows.Forms.TextBox(); this.txtFilename = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.label7 = new System.Windows.Forms.Label(); this.label8 = new System.Windows.Forms.Label(); this.label9 = new System.Windows.Forms.Label(); this.txtTile = new System.Windows.Forms.TextBox(); this.dtRelease = new System.Windows.Forms.DateTimePicker(); this.label35 = new System.Windows.Forms.Label(); this.cmbEngine = new System.Windows.Forms.ComboBox(); this.label34 = new System.Windows.Forms.Label(); this.tabPage2 = new System.Windows.Forms.TabPage(); this.tblLayout2 = new System.Windows.Forms.TableLayoutPanel(); this.chkGraphics = new System.Windows.Forms.CheckBox(); this.chkMusic = new System.Windows.Forms.CheckBox(); this.label12 = new System.Windows.Forms.Label(); this.label10 = new System.Windows.Forms.Label(); this.numLevels = new System.Windows.Forms.NumericUpDown(); this.label11 = new System.Windows.Forms.Label(); this.chkSounds = new System.Windows.Forms.CheckBox(); this.label13 = new System.Windows.Forms.Label(); this.label14 = new System.Windows.Forms.Label(); this.label15 = new System.Windows.Forms.Label(); this.label16 = new System.Windows.Forms.Label(); this.label17 = new System.Windows.Forms.Label(); this.chkDehacked = new System.Windows.Forms.CheckBox(); this.chkDemos = new System.Windows.Forms.CheckBox(); this.chkOther = new System.Windows.Forms.CheckBox(); this.txtOtherFilesRequired = new System.Windows.Forms.TextBox(); this.tabPage3 = new System.Windows.Forms.TabPage(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.txtOtherGameStyles = new System.Windows.Forms.TextBox(); this.cmbDeathmatch = new System.Windows.Forms.ComboBox(); this.cmbCoop = new System.Windows.Forms.ComboBox(); this.cmbSingle = new System.Windows.Forms.ComboBox(); this.label18 = new System.Windows.Forms.Label(); this.label19 = new System.Windows.Forms.Label(); this.label20 = new System.Windows.Forms.Label(); this.label21 = new System.Windows.Forms.Label(); this.label22 = new System.Windows.Forms.Label(); this.label23 = new System.Windows.Forms.Label(); this.label24 = new System.Windows.Forms.Label(); this.cmbGame = new System.Windows.Forms.ComboBox(); this.txtMaps = new System.Windows.Forms.TextBox(); this.chkDifficulty = new System.Windows.Forms.CheckBox(); this.tabPage4 = new System.Windows.Forms.TabPage(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); this.label26 = new System.Windows.Forms.Label(); this.label30 = new System.Windows.Forms.Label(); this.label29 = new System.Windows.Forms.Label(); this.label28 = new System.Windows.Forms.Label(); this.label25 = new System.Windows.Forms.Label(); this.label27 = new System.Windows.Forms.Label(); this.cmbBase = new System.Windows.Forms.ComboBox(); this.txtBuildTime = new System.Windows.Forms.TextBox(); this.txtEditorsUsed = new System.Windows.Forms.TextBox(); this.txtKnownBugs = new System.Windows.Forms.TextBox(); this.txtMayNotRun = new System.Windows.Forms.TextBox(); this.txtTestedWith = new System.Windows.Forms.TextBox(); this.tabPage5 = new System.Windows.Forms.TabPage(); this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); this.label31 = new System.Windows.Forms.Label(); this.cmbPermission = new System.Windows.Forms.ComboBox(); this.label32 = new System.Windows.Forms.Label(); this.label33 = new System.Windows.Forms.Label(); this.txtWebSites = new System.Windows.Forms.TextBox(); this.txtFtpSites = new System.Windows.Forms.TextBox(); this.titleBar = new DoomLauncher.Controls.TitleBarControl(); this.tblMain.SuspendLayout(); this.flpButtons.SuspendLayout(); this.tabControl.SuspendLayout(); this.tabPage1.SuspendLayout(); this.tblLayout1.SuspendLayout(); this.tabPage2.SuspendLayout(); this.tblLayout2.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.numLevels)).BeginInit(); this.tabPage3.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout(); this.tabPage4.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout(); this.tabPage5.SuspendLayout(); this.tableLayoutPanel3.SuspendLayout(); this.SuspendLayout(); // // btnGenerate // this.btnGenerate.Location = new System.Drawing.Point(541, 5); this.btnGenerate.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnGenerate.Name = "btnGenerate"; this.btnGenerate.Size = new System.Drawing.Size(112, 35); this.btnGenerate.TabIndex = 1; this.btnGenerate.Text = "Generate"; this.btnGenerate.UseVisualStyleBackColor = true; this.btnGenerate.Click += new System.EventHandler(this.btnOK_Click); // // btnCancel // this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnCancel.Location = new System.Drawing.Point(661, 5); this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(112, 35); this.btnCancel.TabIndex = 0; this.btnCancel.Text = "Close"; this.btnCancel.UseVisualStyleBackColor = true; // // tblMain // this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.flpButtons, 0, 2); this.tblMain.Controls.Add(this.tabControl, 0, 1); this.tblMain.Controls.Add(this.titleBar, 0, 0); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 3; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 62F)); this.tblMain.Size = new System.Drawing.Size(785, 779); this.tblMain.TabIndex = 0; // // flpButtons // this.flpButtons.Controls.Add(this.btnCancel); this.flpButtons.Controls.Add(this.btnGenerate); this.flpButtons.Dock = System.Windows.Forms.DockStyle.Fill; this.flpButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; this.flpButtons.Location = new System.Drawing.Point(4, 722); this.flpButtons.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.flpButtons.Name = "flpButtons"; this.flpButtons.Size = new System.Drawing.Size(777, 52); this.flpButtons.TabIndex = 1; // // tabControl // this.tabControl.Controls.Add(this.tabPage1); this.tabControl.Controls.Add(this.tabPage2); this.tabControl.Controls.Add(this.tabPage3); this.tabControl.Controls.Add(this.tabPage4); this.tabControl.Controls.Add(this.tabPage5); this.tabControl.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControl.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed; this.tabControl.Location = new System.Drawing.Point(4, 45); this.tabControl.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabControl.Multiline = true; this.tabControl.Name = "tabControl"; this.tabControl.SelectedIndex = 0; this.tabControl.Size = new System.Drawing.Size(777, 667); this.tabControl.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; this.tabControl.TabIndex = 2; // // tabPage1 // this.tabPage1.Controls.Add(this.tblLayout1); this.tabPage1.Location = new System.Drawing.Point(4, 29); this.tabPage1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabPage1.Name = "tabPage1"; this.tabPage1.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabPage1.Size = new System.Drawing.Size(769, 634); this.tabPage1.TabIndex = 0; this.tabPage1.Text = "General"; this.tabPage1.UseVisualStyleBackColor = true; // // tblLayout1 // this.tblLayout1.ColumnCount = 2; this.tblLayout1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 240F)); this.tblLayout1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblLayout1.Controls.Add(this.cmbPrimaryPurpose, 1, 1); this.tblLayout1.Controls.Add(this.txtAdditionalCredits, 1, 10); this.tblLayout1.Controls.Add(this.txtDescription, 1, 9); this.tblLayout1.Controls.Add(this.txtMiscAuthor, 1, 8); this.tblLayout1.Controls.Add(this.txtOtherFiles, 1, 7); this.tblLayout1.Controls.Add(this.txtEmail, 1, 6); this.tblLayout1.Controls.Add(this.txtAuthor, 1, 5); this.tblLayout1.Controls.Add(this.txtFilename, 1, 3); this.tblLayout1.Controls.Add(this.label1, 0, 2); this.tblLayout1.Controls.Add(this.label2, 0, 3); this.tblLayout1.Controls.Add(this.label3, 0, 4); this.tblLayout1.Controls.Add(this.label4, 0, 5); this.tblLayout1.Controls.Add(this.label5, 0, 6); this.tblLayout1.Controls.Add(this.label6, 0, 7); this.tblLayout1.Controls.Add(this.label7, 0, 8); this.tblLayout1.Controls.Add(this.label8, 0, 9); this.tblLayout1.Controls.Add(this.label9, 0, 10); this.tblLayout1.Controls.Add(this.txtTile, 1, 2); this.tblLayout1.Controls.Add(this.dtRelease, 1, 4); this.tblLayout1.Controls.Add(this.label35, 0, 1); this.tblLayout1.Controls.Add(this.cmbEngine, 1, 0); this.tblLayout1.Controls.Add(this.label34, 0, 0); this.tblLayout1.Dock = System.Windows.Forms.DockStyle.Fill; this.tblLayout1.Location = new System.Drawing.Point(4, 5); this.tblLayout1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblLayout1.Name = "tblLayout1"; this.tblLayout1.RowCount = 13; this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 246F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout1.Size = new System.Drawing.Size(761, 624); this.tblLayout1.TabIndex = 1; // // cmbPrimaryPurpose // this.cmbPrimaryPurpose.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbPrimaryPurpose.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbPrimaryPurpose.FormattingEnabled = true; this.cmbPrimaryPurpose.Items.AddRange(new object[] { "Single+Coop play", "Deathmatch", "No levels included"}); this.cmbPrimaryPurpose.Location = new System.Drawing.Point(244, 42); this.cmbPrimaryPurpose.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbPrimaryPurpose.Name = "cmbPrimaryPurpose"; this.cmbPrimaryPurpose.Size = new System.Drawing.Size(513, 28); this.cmbPrimaryPurpose.TabIndex = 22; // // txtAdditionalCredits // this.txtAdditionalCredits.Dock = System.Windows.Forms.DockStyle.Fill; this.txtAdditionalCredits.Location = new System.Drawing.Point(244, 584); this.txtAdditionalCredits.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtAdditionalCredits.Name = "txtAdditionalCredits"; this.txtAdditionalCredits.Size = new System.Drawing.Size(513, 26); this.txtAdditionalCredits.TabIndex = 17; // // txtDescription // this.txtDescription.Dock = System.Windows.Forms.DockStyle.Fill; this.txtDescription.Location = new System.Drawing.Point(244, 338); this.txtDescription.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtDescription.Multiline = true; this.txtDescription.Name = "txtDescription"; this.txtDescription.Size = new System.Drawing.Size(513, 236); this.txtDescription.TabIndex = 16; // // txtMiscAuthor // this.txtMiscAuthor.Dock = System.Windows.Forms.DockStyle.Fill; this.txtMiscAuthor.Location = new System.Drawing.Point(244, 301); this.txtMiscAuthor.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtMiscAuthor.Name = "txtMiscAuthor"; this.txtMiscAuthor.Size = new System.Drawing.Size(513, 26); this.txtMiscAuthor.TabIndex = 15; // // txtOtherFiles // this.txtOtherFiles.Dock = System.Windows.Forms.DockStyle.Fill; this.txtOtherFiles.Location = new System.Drawing.Point(244, 264); this.txtOtherFiles.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtOtherFiles.Name = "txtOtherFiles"; this.txtOtherFiles.Size = new System.Drawing.Size(513, 26); this.txtOtherFiles.TabIndex = 14; // // txtEmail // this.txtEmail.Dock = System.Windows.Forms.DockStyle.Fill; this.txtEmail.Location = new System.Drawing.Point(244, 227); this.txtEmail.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtEmail.Name = "txtEmail"; this.txtEmail.Size = new System.Drawing.Size(513, 26); this.txtEmail.TabIndex = 13; // // txtAuthor // this.txtAuthor.Dock = System.Windows.Forms.DockStyle.Fill; this.txtAuthor.Location = new System.Drawing.Point(244, 190); this.txtAuthor.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtAuthor.Name = "txtAuthor"; this.txtAuthor.Size = new System.Drawing.Size(513, 26); this.txtAuthor.TabIndex = 12; // // txtFilename // this.txtFilename.Dock = System.Windows.Forms.DockStyle.Fill; this.txtFilename.Location = new System.Drawing.Point(244, 116); this.txtFilename.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtFilename.Name = "txtFilename"; this.txtFilename.Size = new System.Drawing.Size(513, 26); this.txtFilename.TabIndex = 10; // // label1 // this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(4, 82); this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(38, 20); this.label1.TabIndex = 0; this.label1.Text = "Title"; // // label2 // this.label2.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(4, 119); this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(74, 20); this.label2.TabIndex = 1; this.label2.Text = "Filename"; // // label3 // this.label3.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(4, 156); this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(107, 20); this.label3.TabIndex = 2; this.label3.Text = "Release Date"; // // label4 // this.label4.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(4, 193); this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(57, 20); this.label4.TabIndex = 3; this.label4.Text = "Author"; // // label5 // this.label5.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label5.AutoSize = true; this.label5.Location = new System.Drawing.Point(4, 230); this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(111, 20); this.label5.TabIndex = 4; this.label5.Text = "Email Address"; // // label6 // this.label6.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label6.AutoSize = true; this.label6.Location = new System.Drawing.Point(4, 267); this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(160, 20); this.label6.TabIndex = 5; this.label6.Text = "Other Files By Author"; // // label7 // this.label7.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label7.AutoSize = true; this.label7.Location = new System.Drawing.Point(4, 304); this.label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(129, 20); this.label7.TabIndex = 6; this.label7.Text = "Misc. Author Info"; // // label8 // this.label8.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label8.AutoSize = true; this.label8.Location = new System.Drawing.Point(4, 446); this.label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(89, 20); this.label8.TabIndex = 7; this.label8.Text = "Description"; // // label9 // this.label9.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label9.AutoSize = true; this.label9.Location = new System.Drawing.Point(4, 587); this.label9.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label9.Name = "label9"; this.label9.Size = new System.Drawing.Size(151, 20); this.label9.TabIndex = 8; this.label9.Text = "Additional Credits to"; // // txtTile // this.txtTile.Dock = System.Windows.Forms.DockStyle.Fill; this.txtTile.Location = new System.Drawing.Point(244, 79); this.txtTile.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtTile.Name = "txtTile"; this.txtTile.Size = new System.Drawing.Size(513, 26); this.txtTile.TabIndex = 9; // // dtRelease // this.dtRelease.Dock = System.Windows.Forms.DockStyle.Fill; this.dtRelease.Location = new System.Drawing.Point(244, 153); this.dtRelease.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.dtRelease.Name = "dtRelease"; this.dtRelease.Size = new System.Drawing.Size(513, 26); this.dtRelease.TabIndex = 18; // // label35 // this.label35.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label35.AutoSize = true; this.label35.Location = new System.Drawing.Point(4, 45); this.label35.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label35.Name = "label35"; this.label35.Size = new System.Drawing.Size(124, 20); this.label35.TabIndex = 20; this.label35.Text = "Primary Purpose"; // // cmbEngine // this.cmbEngine.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbEngine.FormattingEnabled = true; this.cmbEngine.Location = new System.Drawing.Point(244, 5); this.cmbEngine.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbEngine.Name = "cmbEngine"; this.cmbEngine.Size = new System.Drawing.Size(513, 28); this.cmbEngine.TabIndex = 21; // // label34 // this.label34.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label34.AutoSize = true; this.label34.Location = new System.Drawing.Point(4, 8); this.label34.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label34.Name = "label34"; this.label34.Size = new System.Drawing.Size(203, 20); this.label34.TabIndex = 23; this.label34.Text = "Advanced Engine Required"; // // tabPage2 // this.tabPage2.Controls.Add(this.tblLayout2); this.tabPage2.Location = new System.Drawing.Point(4, 29); this.tabPage2.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabPage2.Name = "tabPage2"; this.tabPage2.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabPage2.Size = new System.Drawing.Size(769, 634); this.tabPage2.TabIndex = 1; this.tabPage2.Text = "Included"; this.tabPage2.UseVisualStyleBackColor = true; // // tblLayout2 // this.tblLayout2.ColumnCount = 2; this.tblLayout2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 240F)); this.tblLayout2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblLayout2.Controls.Add(this.chkGraphics, 1, 3); this.tblLayout2.Controls.Add(this.chkMusic, 1, 2); this.tblLayout2.Controls.Add(this.label12, 0, 2); this.tblLayout2.Controls.Add(this.label10, 0, 0); this.tblLayout2.Controls.Add(this.numLevels, 1, 0); this.tblLayout2.Controls.Add(this.label11, 0, 1); this.tblLayout2.Controls.Add(this.chkSounds, 1, 1); this.tblLayout2.Controls.Add(this.label13, 0, 3); this.tblLayout2.Controls.Add(this.label14, 0, 4); this.tblLayout2.Controls.Add(this.label15, 0, 5); this.tblLayout2.Controls.Add(this.label16, 0, 6); this.tblLayout2.Controls.Add(this.label17, 0, 7); this.tblLayout2.Controls.Add(this.chkDehacked, 1, 4); this.tblLayout2.Controls.Add(this.chkDemos, 1, 5); this.tblLayout2.Controls.Add(this.chkOther, 1, 6); this.tblLayout2.Controls.Add(this.txtOtherFilesRequired, 1, 7); this.tblLayout2.Dock = System.Windows.Forms.DockStyle.Fill; this.tblLayout2.Location = new System.Drawing.Point(4, 5); this.tblLayout2.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tblLayout2.Name = "tblLayout2"; this.tblLayout2.RowCount = 11; this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 215F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tblLayout2.Size = new System.Drawing.Size(761, 624); this.tblLayout2.TabIndex = 0; // // chkGraphics // this.chkGraphics.AutoSize = true; this.chkGraphics.Location = new System.Drawing.Point(244, 116); this.chkGraphics.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chkGraphics.Name = "chkGraphics"; this.chkGraphics.Size = new System.Drawing.Size(96, 24); this.chkGraphics.TabIndex = 31; this.chkGraphics.Text = "Included"; this.chkGraphics.UseVisualStyleBackColor = true; // // chkMusic // this.chkMusic.AutoSize = true; this.chkMusic.Location = new System.Drawing.Point(244, 79); this.chkMusic.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chkMusic.Name = "chkMusic"; this.chkMusic.Size = new System.Drawing.Size(96, 24); this.chkMusic.TabIndex = 30; this.chkMusic.Text = "Included"; this.chkMusic.UseVisualStyleBackColor = true; // // label12 // this.label12.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label12.AutoSize = true; this.label12.Location = new System.Drawing.Point(4, 82); this.label12.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label12.Name = "label12"; this.label12.Size = new System.Drawing.Size(50, 20); this.label12.TabIndex = 24; this.label12.Text = "Music"; // // label10 // this.label10.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label10.AutoSize = true; this.label10.Location = new System.Drawing.Point(4, 8); this.label10.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label10.Name = "label10"; this.label10.Size = new System.Drawing.Size(89, 20); this.label10.TabIndex = 20; this.label10.Text = "New Levels"; // // numLevels // this.numLevels.Location = new System.Drawing.Point(244, 5); this.numLevels.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.numLevels.Name = "numLevels"; this.numLevels.Size = new System.Drawing.Size(62, 26); this.numLevels.TabIndex = 21; // // label11 // this.label11.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label11.AutoSize = true; this.label11.Location = new System.Drawing.Point(4, 45); this.label11.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label11.Name = "label11"; this.label11.Size = new System.Drawing.Size(64, 20); this.label11.TabIndex = 22; this.label11.Text = "Sounds"; // // chkSounds // this.chkSounds.AutoSize = true; this.chkSounds.Location = new System.Drawing.Point(244, 42); this.chkSounds.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chkSounds.Name = "chkSounds"; this.chkSounds.Size = new System.Drawing.Size(96, 24); this.chkSounds.TabIndex = 23; this.chkSounds.Text = "Included"; this.chkSounds.UseVisualStyleBackColor = true; // // label13 // this.label13.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label13.AutoSize = true; this.label13.Location = new System.Drawing.Point(4, 119); this.label13.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label13.Name = "label13"; this.label13.Size = new System.Drawing.Size(73, 20); this.label13.TabIndex = 25; this.label13.Text = "Graphics"; // // label14 // this.label14.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label14.AutoSize = true; this.label14.Location = new System.Drawing.Point(4, 156); this.label14.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label14.Name = "label14"; this.label14.Size = new System.Drawing.Size(164, 20); this.label14.TabIndex = 26; this.label14.Text = "Dehacked/BEX Patch"; this.label14.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // label15 // this.label15.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label15.AutoSize = true; this.label15.Location = new System.Drawing.Point(4, 193); this.label15.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label15.Name = "label15"; this.label15.Size = new System.Drawing.Size(60, 20); this.label15.TabIndex = 27; this.label15.Text = "Demos"; this.label15.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // label16 // this.label16.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label16.AutoSize = true; this.label16.Location = new System.Drawing.Point(4, 230); this.label16.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label16.Name = "label16"; this.label16.Size = new System.Drawing.Size(49, 20); this.label16.TabIndex = 28; this.label16.Text = "Other"; this.label16.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // label17 // this.label17.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label17.AutoSize = true; this.label17.Location = new System.Drawing.Point(4, 356); this.label17.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label17.Name = "label17"; this.label17.Size = new System.Drawing.Size(155, 20); this.label17.TabIndex = 29; this.label17.Text = "Other Files Required"; this.label17.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // chkDehacked // this.chkDehacked.AutoSize = true; this.chkDehacked.Location = new System.Drawing.Point(244, 153); this.chkDehacked.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chkDehacked.Name = "chkDehacked"; this.chkDehacked.Size = new System.Drawing.Size(96, 24); this.chkDehacked.TabIndex = 32; this.chkDehacked.Text = "Included"; this.chkDehacked.UseVisualStyleBackColor = true; // // chkDemos // this.chkDemos.AutoSize = true; this.chkDemos.Location = new System.Drawing.Point(244, 190); this.chkDemos.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chkDemos.Name = "chkDemos"; this.chkDemos.Size = new System.Drawing.Size(96, 24); this.chkDemos.TabIndex = 33; this.chkDemos.Text = "Included"; this.chkDemos.UseVisualStyleBackColor = true; // // chkOther // this.chkOther.AutoSize = true; this.chkOther.Location = new System.Drawing.Point(244, 227); this.chkOther.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chkOther.Name = "chkOther"; this.chkOther.Size = new System.Drawing.Size(96, 24); this.chkOther.TabIndex = 34; this.chkOther.Text = "Included"; this.chkOther.UseVisualStyleBackColor = true; // // txtOtherFilesRequired // this.txtOtherFilesRequired.Dock = System.Windows.Forms.DockStyle.Fill; this.txtOtherFilesRequired.Location = new System.Drawing.Point(244, 264); this.txtOtherFilesRequired.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtOtherFilesRequired.Multiline = true; this.txtOtherFilesRequired.Name = "txtOtherFilesRequired"; this.txtOtherFilesRequired.Size = new System.Drawing.Size(513, 205); this.txtOtherFilesRequired.TabIndex = 35; // // tabPage3 // this.tabPage3.Controls.Add(this.tableLayoutPanel1); this.tabPage3.Location = new System.Drawing.Point(4, 29); this.tabPage3.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabPage3.Name = "tabPage3"; this.tabPage3.Size = new System.Drawing.Size(769, 634); this.tabPage3.TabIndex = 2; this.tabPage3.Text = "Play Info"; this.tabPage3.UseVisualStyleBackColor = true; // // tableLayoutPanel1 // this.tableLayoutPanel1.ColumnCount = 2; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 240F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.Controls.Add(this.txtOtherGameStyles, 1, 5); this.tableLayoutPanel1.Controls.Add(this.cmbDeathmatch, 1, 4); this.tableLayoutPanel1.Controls.Add(this.cmbCoop, 1, 3); this.tableLayoutPanel1.Controls.Add(this.cmbSingle, 1, 2); this.tableLayoutPanel1.Controls.Add(this.label18, 0, 2); this.tableLayoutPanel1.Controls.Add(this.label19, 0, 0); this.tableLayoutPanel1.Controls.Add(this.label20, 0, 1); this.tableLayoutPanel1.Controls.Add(this.label21, 0, 3); this.tableLayoutPanel1.Controls.Add(this.label22, 0, 4); this.tableLayoutPanel1.Controls.Add(this.label23, 0, 5); this.tableLayoutPanel1.Controls.Add(this.label24, 0, 6); this.tableLayoutPanel1.Controls.Add(this.cmbGame, 1, 0); this.tableLayoutPanel1.Controls.Add(this.txtMaps, 1, 1); this.tableLayoutPanel1.Controls.Add(this.chkDifficulty, 1, 6); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 11; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 215F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel1.Size = new System.Drawing.Size(769, 634); this.tableLayoutPanel1.TabIndex = 1; // // txtOtherGameStyles // this.txtOtherGameStyles.Dock = System.Windows.Forms.DockStyle.Fill; this.txtOtherGameStyles.Location = new System.Drawing.Point(244, 190); this.txtOtherGameStyles.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtOtherGameStyles.Name = "txtOtherGameStyles"; this.txtOtherGameStyles.Size = new System.Drawing.Size(521, 26); this.txtOtherGameStyles.TabIndex = 41; // // cmbDeathmatch // this.cmbDeathmatch.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbDeathmatch.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbDeathmatch.FormattingEnabled = true; this.cmbDeathmatch.Location = new System.Drawing.Point(244, 153); this.cmbDeathmatch.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbDeathmatch.Name = "cmbDeathmatch"; this.cmbDeathmatch.Size = new System.Drawing.Size(521, 28); this.cmbDeathmatch.TabIndex = 40; // // cmbCoop // this.cmbCoop.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbCoop.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbCoop.FormattingEnabled = true; this.cmbCoop.Location = new System.Drawing.Point(244, 116); this.cmbCoop.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbCoop.Name = "cmbCoop"; this.cmbCoop.Size = new System.Drawing.Size(521, 28); this.cmbCoop.TabIndex = 39; // // cmbSingle // this.cmbSingle.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbSingle.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbSingle.FormattingEnabled = true; this.cmbSingle.Location = new System.Drawing.Point(244, 79); this.cmbSingle.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbSingle.Name = "cmbSingle"; this.cmbSingle.Size = new System.Drawing.Size(521, 28); this.cmbSingle.TabIndex = 38; // // label18 // this.label18.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label18.AutoSize = true; this.label18.Location = new System.Drawing.Point(4, 82); this.label18.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label18.Name = "label18"; this.label18.Size = new System.Drawing.Size(100, 20); this.label18.TabIndex = 24; this.label18.Text = "Single Player"; // // label19 // this.label19.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label19.AutoSize = true; this.label19.Location = new System.Drawing.Point(4, 8); this.label19.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label19.Name = "label19"; this.label19.Size = new System.Drawing.Size(53, 20); this.label19.TabIndex = 20; this.label19.Text = "Game"; // // label20 // this.label20.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label20.AutoSize = true; this.label20.Location = new System.Drawing.Point(4, 45); this.label20.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label20.Name = "label20"; this.label20.Size = new System.Drawing.Size(48, 20); this.label20.TabIndex = 22; this.label20.Text = "Maps"; // // label21 // this.label21.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label21.AutoSize = true; this.label21.Location = new System.Drawing.Point(4, 119); this.label21.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label21.Name = "label21"; this.label21.Size = new System.Drawing.Size(168, 20); this.label21.TabIndex = 25; this.label21.Text = "Cooperative 2-4 Player"; // // label22 // this.label22.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label22.AutoSize = true; this.label22.Location = new System.Drawing.Point(4, 156); this.label22.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label22.Name = "label22"; this.label22.Size = new System.Drawing.Size(171, 20); this.label22.TabIndex = 26; this.label22.Text = "Deathmatch 2-4 Player"; this.label22.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // label23 // this.label23.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label23.AutoSize = true; this.label23.Location = new System.Drawing.Point(4, 193); this.label23.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label23.Name = "label23"; this.label23.Size = new System.Drawing.Size(137, 20); this.label23.TabIndex = 27; this.label23.Text = "Other game styles"; this.label23.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // label24 // this.label24.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label24.AutoSize = true; this.label24.Location = new System.Drawing.Point(4, 230); this.label24.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label24.Name = "label24"; this.label24.Size = new System.Drawing.Size(132, 20); this.label24.TabIndex = 28; this.label24.Text = "Difficulty Settings"; this.label24.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // cmbGame // this.cmbGame.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbGame.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbGame.FormattingEnabled = true; this.cmbGame.Location = new System.Drawing.Point(244, 5); this.cmbGame.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbGame.Name = "cmbGame"; this.cmbGame.Size = new System.Drawing.Size(521, 28); this.cmbGame.TabIndex = 36; // // txtMaps // this.txtMaps.Dock = System.Windows.Forms.DockStyle.Fill; this.txtMaps.Location = new System.Drawing.Point(244, 42); this.txtMaps.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtMaps.Name = "txtMaps"; this.txtMaps.Size = new System.Drawing.Size(521, 26); this.txtMaps.TabIndex = 37; // // chkDifficulty // this.chkDifficulty.AutoSize = true; this.chkDifficulty.Location = new System.Drawing.Point(244, 227); this.chkDifficulty.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.chkDifficulty.Name = "chkDifficulty"; this.chkDifficulty.Size = new System.Drawing.Size(63, 24); this.chkDifficulty.TabIndex = 42; this.chkDifficulty.Text = "Yes"; this.chkDifficulty.UseVisualStyleBackColor = true; // // tabPage4 // this.tabPage4.Controls.Add(this.tableLayoutPanel2); this.tabPage4.Location = new System.Drawing.Point(4, 29); this.tabPage4.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabPage4.Name = "tabPage4"; this.tabPage4.Size = new System.Drawing.Size(769, 634); this.tabPage4.TabIndex = 3; this.tabPage4.Text = "Construction"; this.tabPage4.UseVisualStyleBackColor = true; // // tableLayoutPanel2 // this.tableLayoutPanel2.ColumnCount = 2; this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 240F)); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel2.Controls.Add(this.label26, 0, 0); this.tableLayoutPanel2.Controls.Add(this.label30, 0, 6); this.tableLayoutPanel2.Controls.Add(this.label29, 0, 5); this.tableLayoutPanel2.Controls.Add(this.label28, 0, 4); this.tableLayoutPanel2.Controls.Add(this.label25, 0, 3); this.tableLayoutPanel2.Controls.Add(this.label27, 0, 2); this.tableLayoutPanel2.Controls.Add(this.cmbBase, 1, 0); this.tableLayoutPanel2.Controls.Add(this.txtBuildTime, 1, 2); this.tableLayoutPanel2.Controls.Add(this.txtEditorsUsed, 1, 3); this.tableLayoutPanel2.Controls.Add(this.txtKnownBugs, 1, 4); this.tableLayoutPanel2.Controls.Add(this.txtMayNotRun, 1, 5); this.tableLayoutPanel2.Controls.Add(this.txtTestedWith, 1, 6); this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tableLayoutPanel2.Name = "tableLayoutPanel2"; this.tableLayoutPanel2.RowCount = 11; this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 215F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel2.Size = new System.Drawing.Size(769, 634); this.tableLayoutPanel2.TabIndex = 2; // // label26 // this.label26.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label26.AutoSize = true; this.label26.Location = new System.Drawing.Point(4, 8); this.label26.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label26.Name = "label26"; this.label26.Size = new System.Drawing.Size(46, 20); this.label26.TabIndex = 20; this.label26.Text = "Base"; // // label30 // this.label30.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label30.AutoSize = true; this.label30.Location = new System.Drawing.Point(4, 230); this.label30.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label30.Name = "label30"; this.label30.Size = new System.Drawing.Size(94, 20); this.label30.TabIndex = 27; this.label30.Text = "Tested With"; this.label30.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // label29 // this.label29.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label29.AutoSize = true; this.label29.Location = new System.Drawing.Point(4, 193); this.label29.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label29.Name = "label29"; this.label29.Size = new System.Drawing.Size(137, 20); this.label29.TabIndex = 26; this.label29.Text = "May Not Run With"; this.label29.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // // label28 // this.label28.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label28.AutoSize = true; this.label28.Location = new System.Drawing.Point(4, 156); this.label28.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label28.Name = "label28"; this.label28.Size = new System.Drawing.Size(98, 20); this.label28.TabIndex = 25; this.label28.Text = "Known Bugs"; // // label25 // this.label25.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label25.AutoSize = true; this.label25.Location = new System.Drawing.Point(4, 119); this.label25.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label25.Name = "label25"; this.label25.Size = new System.Drawing.Size(108, 20); this.label25.TabIndex = 24; this.label25.Text = "Editor(s) used"; // // label27 // this.label27.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label27.AutoSize = true; this.label27.Location = new System.Drawing.Point(4, 82); this.label27.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label27.Name = "label27"; this.label27.Size = new System.Drawing.Size(82, 20); this.label27.TabIndex = 22; this.label27.Text = "Build Time"; // // cmbBase // this.cmbBase.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbBase.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbBase.FormattingEnabled = true; this.cmbBase.Items.AddRange(new object[] { "New from scratch", "Modified"}); this.cmbBase.Location = new System.Drawing.Point(244, 5); this.cmbBase.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbBase.Name = "cmbBase"; this.cmbBase.Size = new System.Drawing.Size(521, 28); this.cmbBase.TabIndex = 28; // // txtBuildTime // this.txtBuildTime.Dock = System.Windows.Forms.DockStyle.Fill; this.txtBuildTime.Location = new System.Drawing.Point(244, 79); this.txtBuildTime.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtBuildTime.Name = "txtBuildTime"; this.txtBuildTime.Size = new System.Drawing.Size(521, 26); this.txtBuildTime.TabIndex = 29; // // txtEditorsUsed // this.txtEditorsUsed.Dock = System.Windows.Forms.DockStyle.Fill; this.txtEditorsUsed.Location = new System.Drawing.Point(244, 116); this.txtEditorsUsed.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtEditorsUsed.Name = "txtEditorsUsed"; this.txtEditorsUsed.Size = new System.Drawing.Size(521, 26); this.txtEditorsUsed.TabIndex = 30; // // txtKnownBugs // this.txtKnownBugs.Dock = System.Windows.Forms.DockStyle.Fill; this.txtKnownBugs.Location = new System.Drawing.Point(244, 153); this.txtKnownBugs.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtKnownBugs.Name = "txtKnownBugs"; this.txtKnownBugs.Size = new System.Drawing.Size(521, 26); this.txtKnownBugs.TabIndex = 31; // // txtMayNotRun // this.txtMayNotRun.Dock = System.Windows.Forms.DockStyle.Fill; this.txtMayNotRun.Location = new System.Drawing.Point(244, 190); this.txtMayNotRun.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtMayNotRun.Name = "txtMayNotRun"; this.txtMayNotRun.Size = new System.Drawing.Size(521, 26); this.txtMayNotRun.TabIndex = 32; // // txtTestedWith // this.txtTestedWith.Dock = System.Windows.Forms.DockStyle.Fill; this.txtTestedWith.Location = new System.Drawing.Point(244, 227); this.txtTestedWith.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtTestedWith.Name = "txtTestedWith"; this.txtTestedWith.Size = new System.Drawing.Size(521, 26); this.txtTestedWith.TabIndex = 33; // // tabPage5 // this.tabPage5.Controls.Add(this.tableLayoutPanel3); this.tabPage5.Location = new System.Drawing.Point(4, 29); this.tabPage5.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tabPage5.Name = "tabPage5"; this.tabPage5.Size = new System.Drawing.Size(769, 634); this.tabPage5.TabIndex = 4; this.tabPage5.Text = "Permissions"; this.tabPage5.UseVisualStyleBackColor = true; // // tableLayoutPanel3 // this.tableLayoutPanel3.ColumnCount = 2; this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 240F)); this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel3.Controls.Add(this.label31, 0, 0); this.tableLayoutPanel3.Controls.Add(this.cmbPermission, 1, 0); this.tableLayoutPanel3.Controls.Add(this.label32, 0, 1); this.tableLayoutPanel3.Controls.Add(this.label33, 0, 2); this.tableLayoutPanel3.Controls.Add(this.txtWebSites, 1, 1); this.tableLayoutPanel3.Controls.Add(this.txtFtpSites, 1, 2); this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel3.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.tableLayoutPanel3.Name = "tableLayoutPanel3"; this.tableLayoutPanel3.RowCount = 11; this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 215F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel3.Size = new System.Drawing.Size(769, 634); this.tableLayoutPanel3.TabIndex = 3; // // label31 // this.label31.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label31.AutoSize = true; this.label31.Location = new System.Drawing.Point(4, 8); this.label31.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label31.Name = "label31"; this.label31.Size = new System.Drawing.Size(86, 20); this.label31.TabIndex = 20; this.label31.Text = "Permission"; // // cmbPermission // this.cmbPermission.Dock = System.Windows.Forms.DockStyle.Fill; this.cmbPermission.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cmbPermission.FormattingEnabled = true; this.cmbPermission.Items.AddRange(new object[] { "May distribute", "May not distribute"}); this.cmbPermission.Location = new System.Drawing.Point(244, 5); this.cmbPermission.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.cmbPermission.Name = "cmbPermission"; this.cmbPermission.Size = new System.Drawing.Size(521, 28); this.cmbPermission.TabIndex = 28; // // label32 // this.label32.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label32.AutoSize = true; this.label32.Location = new System.Drawing.Point(4, 45); this.label32.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label32.Name = "label32"; this.label32.Size = new System.Drawing.Size(79, 20); this.label32.TabIndex = 29; this.label32.Text = "Web sites"; // // label33 // this.label33.Anchor = System.Windows.Forms.AnchorStyles.Left; this.label33.AutoSize = true; this.label33.Location = new System.Drawing.Point(4, 82); this.label33.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label33.Name = "label33"; this.label33.Size = new System.Drawing.Size(75, 20); this.label33.TabIndex = 30; this.label33.Text = "FTP sites"; // // txtWebSites // this.txtWebSites.Dock = System.Windows.Forms.DockStyle.Fill; this.txtWebSites.Location = new System.Drawing.Point(244, 42); this.txtWebSites.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtWebSites.Name = "txtWebSites"; this.txtWebSites.Size = new System.Drawing.Size(521, 26); this.txtWebSites.TabIndex = 31; // // txtFtpSites // this.txtFtpSites.Dock = System.Windows.Forms.DockStyle.Fill; this.txtFtpSites.Location = new System.Drawing.Point(244, 79); this.txtFtpSites.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.txtFtpSites.Name = "txtFtpSites"; this.txtFtpSites.Size = new System.Drawing.Size(521, 26); this.txtFtpSites.TabIndex = 32; // // titleBar // this.titleBar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(54))))); this.titleBar.Dock = System.Windows.Forms.DockStyle.Fill; this.titleBar.ForeColor = System.Drawing.Color.White; this.titleBar.Location = new System.Drawing.Point(0, 0); this.titleBar.Margin = new System.Windows.Forms.Padding(0); this.titleBar.Name = "titleBar"; this.titleBar.Size = new System.Drawing.Size(785, 40); this.titleBar.TabIndex = 3; this.titleBar.Title = "Text File Generator"; // // TxtGenerator // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; this.ClientSize = new System.Drawing.Size(785, 779); this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.Name = "TxtGenerator"; this.Text = "Text File Generator"; this.tblMain.ResumeLayout(false); this.flpButtons.ResumeLayout(false); this.tabControl.ResumeLayout(false); this.tabPage1.ResumeLayout(false); this.tblLayout1.ResumeLayout(false); this.tblLayout1.PerformLayout(); this.tabPage2.ResumeLayout(false); this.tblLayout2.ResumeLayout(false); this.tblLayout2.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.numLevels)).EndInit(); this.tabPage3.ResumeLayout(false); this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.PerformLayout(); this.tabPage4.ResumeLayout(false); this.tableLayoutPanel2.ResumeLayout(false); this.tableLayoutPanel2.PerformLayout(); this.tabPage5.ResumeLayout(false); this.tableLayoutPanel3.ResumeLayout(false); this.tableLayoutPanel3.PerformLayout(); this.ResumeLayout(false); } #endregion private TableLayoutPanelDB tblMain; private System.Windows.Forms.FlowLayoutPanel flpButtons; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnGenerate; private DoomLauncher.CTabControl tabControl; private System.Windows.Forms.TabPage tabPage1; private TableLayoutPanelDB tblLayout1; private System.Windows.Forms.TextBox txtAdditionalCredits; private System.Windows.Forms.TextBox txtDescription; private System.Windows.Forms.TextBox txtMiscAuthor; private System.Windows.Forms.TextBox txtOtherFiles; private System.Windows.Forms.TextBox txtEmail; private System.Windows.Forms.TextBox txtAuthor; private System.Windows.Forms.TextBox txtFilename; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label label5; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label label7; private System.Windows.Forms.Label label8; private System.Windows.Forms.Label label9; private System.Windows.Forms.TextBox txtTile; private System.Windows.Forms.DateTimePicker dtRelease; private System.Windows.Forms.TabPage tabPage2; private System.Windows.Forms.TabPage tabPage3; private System.Windows.Forms.TabPage tabPage4; private System.Windows.Forms.TabPage tabPage5; private System.Windows.Forms.TableLayoutPanel tblLayout2; private System.Windows.Forms.Label label10; private System.Windows.Forms.NumericUpDown numLevels; private System.Windows.Forms.Label label11; private System.Windows.Forms.CheckBox chkSounds; private System.Windows.Forms.Label label12; private System.Windows.Forms.Label label13; private System.Windows.Forms.Label label14; private System.Windows.Forms.Label label15; private System.Windows.Forms.Label label16; private System.Windows.Forms.Label label17; private System.Windows.Forms.CheckBox chkGraphics; private System.Windows.Forms.CheckBox chkMusic; private System.Windows.Forms.CheckBox chkDehacked; private System.Windows.Forms.CheckBox chkDemos; private System.Windows.Forms.CheckBox chkOther; private System.Windows.Forms.TextBox txtOtherFilesRequired; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.Label label18; private System.Windows.Forms.Label label19; private System.Windows.Forms.Label label20; private System.Windows.Forms.Label label21; private System.Windows.Forms.Label label22; private System.Windows.Forms.Label label23; private System.Windows.Forms.Label label24; private System.Windows.Forms.ComboBox cmbGame; private System.Windows.Forms.TextBox txtMaps; private System.Windows.Forms.ComboBox cmbDeathmatch; private System.Windows.Forms.ComboBox cmbCoop; private System.Windows.Forms.ComboBox cmbSingle; private System.Windows.Forms.TextBox txtOtherGameStyles; private System.Windows.Forms.CheckBox chkDifficulty; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; private System.Windows.Forms.Label label25; private System.Windows.Forms.Label label26; private System.Windows.Forms.Label label27; private System.Windows.Forms.Label label28; private System.Windows.Forms.Label label29; private System.Windows.Forms.Label label30; private System.Windows.Forms.ComboBox cmbBase; private System.Windows.Forms.TextBox txtBuildTime; private System.Windows.Forms.TextBox txtEditorsUsed; private System.Windows.Forms.TextBox txtKnownBugs; private System.Windows.Forms.TextBox txtMayNotRun; private System.Windows.Forms.TextBox txtTestedWith; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; private System.Windows.Forms.Label label31; private System.Windows.Forms.ComboBox cmbPermission; private System.Windows.Forms.Label label32; private System.Windows.Forms.Label label33; private System.Windows.Forms.TextBox txtWebSites; private System.Windows.Forms.TextBox txtFtpSites; private System.Windows.Forms.Label label35; private System.Windows.Forms.ComboBox cmbPrimaryPurpose; private System.Windows.Forms.ComboBox cmbEngine; private System.Windows.Forms.Label label34; private Controls.TitleBarControl titleBar; } } ================================================ FILE: DoomLauncher/Forms/TxtGenerator.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class TxtGenerator : Form { private IDataSourceAdapter m_adapter; public TxtGenerator() { InitializeComponent(); PopulatePlayInfoCombo(cmbSingle); PopulatePlayInfoCombo(cmbCoop); PopulatePlayInfoCombo(cmbDeathmatch); cmbSingle.SelectedIndex = cmbCoop.SelectedIndex = cmbDeathmatch.SelectedIndex = cmbBase.SelectedIndex = cmbPermission.SelectedIndex = cmbPrimaryPurpose.SelectedIndex = 0; dtRelease.Format = DateTimePickerFormat.Custom; dtRelease.CustomFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); } public void SetData(IDataSourceAdapter adapter) { SetData(adapter, null); } public void SetData(IDataSourceAdapter adapter, IGameFile gameFile) { m_adapter = adapter; cmbEngine.ValueMember = "SourcePortID"; cmbEngine.DisplayMember = "Name"; List sourcePorts = adapter.GetSourcePorts().ToList(); SourcePortData noPort = new SourcePortData(); noPort.SourcePortID = 0; noPort.Name = "N/A"; sourcePorts.Insert(0, noPort); cmbEngine.DataSource = sourcePorts; cmbGame.ValueMember = "IWadID"; cmbGame.DisplayMember = "Name"; cmbGame.DataSource = GetIwads(adapter); cmbGame.SelectedIndex = 0; if (gameFile != null) { if (gameFile.SourcePortID.HasValue) cmbEngine.SelectedValue = gameFile.SourcePortID.Value; txtTile.Text = gameFile.Title; txtFilename.Text = gameFile.FileName; if (gameFile.ReleaseDate.HasValue) dtRelease.Value = gameFile.ReleaseDate.Value; txtAuthor.Text = gameFile.Author; txtDescription.Text = gameFile.Description; if (gameFile.MapCount.HasValue) numLevels.Value = gameFile.MapCount.Value; if (gameFile.Map != null) txtMaps.Text = GetMapString(gameFile.Map); if (gameFile.IWadID.HasValue) cmbGame.SelectedValue = gameFile.IWadID.Value; } } private static IEnumerable GetIwads(IDataSourceAdapter adapter) { List iwads = new List(); IWadData data = new IWadData(); data.IWadID = -1; data.Name = "N/A"; iwads.Add(data); iwads.AddRange(adapter.GetIWads()); return iwads; } private string CreateTextFile() { string textFile = @"=========================================================================== Advanced engine needed : {34} Primary purpose : {35} =========================================================================== Title : {0} Filename : {1} Release date : {2} Author : {3} Email Address : {4} Other Files By Author : {5} Misc. Author Info : {6} Description : {7} Additional Credits to : {8} =========================================================================== * What is included * New levels : {9} Sounds : {10} Music : {11} Graphics : {12} Dehacked/BEX Patch : {13} Demos : {14} Other : {15} Other files required : {16} * Play Information * Game : {17} Map # : {18} Single Player : {19} Cooperative 2-4 Player : {20} Deathmatch 2-4 Player : {21} Other game styles : {22} Difficulty Settings : {23} * Construction * Base : {24} Build Time : {25} Editor(s) used : {26} Known Bugs : {27} May Not Run With : {28} Tested With : {29} * Copyright / Permissions * Authors {30} use the contents of this file as a base for modification or reuse. Permissions have been obtained from original authors for any of their resources modified or included in this file. {31} * Where to get the file that this text file describes * The Usual: ftp://archives.3dgamers.com/pub/idgames/ and mirrors Web sites: {32} FTP sites: {33}"; string may = @"You MAY not distribute this file in any format."; string maynot = @"You MAY distribute this file, provided you include this text file, with no modifications. You may distribute this file in any electronic format (BBS, Diskette, CD, etc) as long as you include this file intact. I have received permission from the original authors of any modified or included content in this file to allow further distribution."; return string.Format(textFile, txtTile.Text, txtFilename.Text, dtRelease.Value.ToString(CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern), txtAuthor.Text, txtEmail.Text, txtOtherFiles.Text, txtMiscAuthor.Text, txtDescription.Text, txtAdditionalCredits.Text, Convert.ToInt32(numLevels.Value), GetIncluded(chkSounds), GetIncluded(chkMusic), GetIncluded(chkGraphics), GetIncluded(chkDehacked), GetIncluded(chkDemos), GetIncluded(chkOther), txtOtherFilesRequired.Text, GetGameName(), txtMaps.Text, cmbSingle.SelectedItem.ToString(), cmbCoop.SelectedItem.ToString(), cmbDeathmatch.SelectedItem.ToString(), txtOtherGameStyles.Text, GetIncluded(chkDifficulty), GetBase(cmbBase), txtBuildTime.Text, txtEditorsUsed.Text, txtKnownBugs.Text, txtMayNotRun.Text, txtTestedWith.Text, cmbPermission.SelectedIndex == 0 ? "MAY" : "may NOT", cmbPermission.SelectedIndex == 0 ? may : maynot, txtWebSites.Text, txtFtpSites.Text, ((ISourcePortData)cmbEngine.SelectedItem).Name, cmbPrimaryPurpose.SelectedItem.ToString()); } private string GetGameName() { IIWadData iwad = cmbGame.SelectedItem as IIWadData; string ext = Path.GetExtension(iwad.Name); if (!string.IsNullOrEmpty(ext)) return iwad.Name.Replace(ext, string.Empty); else return iwad.Name; } private static string GetMapString(string mapString) { string[] maps = mapString.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (maps.Length == 1) { return maps.First(); } else if (maps.Length > 1) { List> mapLookup = new List>(); foreach (string map in maps) { int mapDigit = 0; string numberString = new string(map.Where(x => Char.IsDigit(x)).ToArray()); if (int.TryParse(numberString, out mapDigit)) mapLookup.Add(new Tuple(mapDigit, map)); } if (mapLookup.Count > 0) { int min = mapLookup.Min(x => x.Item1); int max = mapLookup.Max(x => x.Item1); return string.Format("{0}-{1}", mapLookup.First(x => x.Item1 == min).Item2, mapLookup.First(x => x.Item1 == max).Item2); } } return string.Empty; } private static string GetBase(ComboBox cmbBase) { if (cmbBase.SelectedIndex == 0) return cmbBase.SelectedItem.ToString(); return string.Format("{0} (-)", cmbBase.SelectedItem.ToString()); } private static string GetIncluded(CheckBox chk) { return chk.Checked ? "Yes" : "No"; } private static void PopulatePlayInfoCombo(ComboBox cmb) { cmb.Items.Add("Designed for"); cmb.Items.Add("Player starts only"); cmb.Items.Add("No"); } private void btnOK_Click(object sender, EventArgs e) { if (cmbGame.SelectedItem == null) { MessageBox.Show(this, "A game must be selected.", "Game", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } string textFile = CreateTextFile(); string filename = Path.Combine(DataCache.Instance.AppConfiguration.TempDirectory.GetFullPath(), "outgen.txt"); try { File.WriteAllText(filename, textFile); Process.Start(filename); } catch(Exception ex) { Util.DisplayUnexpectedException(this, ex); } } } } ================================================ FILE: DoomLauncher/Forms/TxtGenerator.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/Forms/Welcome.Designer.cs ================================================ namespace DoomLauncher.Forms { partial class Welcome { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Welcome)); this.btnContinue = new System.Windows.Forms.Button(); this.tblMain = new System.Windows.Forms.TableLayoutPanel(); this.lblWelcome = new System.Windows.Forms.Label(); this.lnkHelp = new System.Windows.Forms.LinkLabel(); this.lblInfo = new DoomLauncher.GrowLabel(); this.tblMain.SuspendLayout(); this.SuspendLayout(); // // btnContinue // this.btnContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.btnContinue.DialogResult = System.Windows.Forms.DialogResult.OK; this.btnContinue.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btnContinue.Location = new System.Drawing.Point(381, 385); this.btnContinue.Margin = new System.Windows.Forms.Padding(4); this.btnContinue.Name = "btnContinue"; this.btnContinue.Size = new System.Drawing.Size(100, 28); this.btnContinue.TabIndex = 4; this.btnContinue.Text = "Continue"; this.btnContinue.UseVisualStyleBackColor = true; // // tblMain // this.tblMain.BackgroundImage = global::DoomLauncher.Properties.Resources.DoomLauncherTile; this.tblMain.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; this.tblMain.ColumnCount = 1; this.tblMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.Controls.Add(this.lblWelcome, 0, 0); this.tblMain.Controls.Add(this.lblInfo, 0, 1); this.tblMain.Controls.Add(this.lnkHelp, 0, 2); this.tblMain.Controls.Add(this.btnContinue, 0, 4); this.tblMain.Dock = System.Windows.Forms.DockStyle.Fill; this.tblMain.Location = new System.Drawing.Point(0, 0); this.tblMain.Margin = new System.Windows.Forms.Padding(4); this.tblMain.Name = "tblMain"; this.tblMain.RowCount = 5; this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 246F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 79F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 49F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 39F)); this.tblMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tblMain.Size = new System.Drawing.Size(485, 420); this.tblMain.TabIndex = 3; // // lblWelcome // this.lblWelcome.AutoSize = true; this.lblWelcome.BackColor = System.Drawing.Color.Transparent; this.lblWelcome.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lblWelcome.ForeColor = System.Drawing.Color.White; this.lblWelcome.Location = new System.Drawing.Point(8, 7); this.lblWelcome.Margin = new System.Windows.Forms.Padding(8, 7, 4, 0); this.lblWelcome.Name = "lblWelcome"; this.lblWelcome.Size = new System.Drawing.Size(284, 25); this.lblWelcome.TabIndex = 0; this.lblWelcome.Text = "Welcome to Doom Launcher"; // // lnkHelp // this.lnkHelp.Anchor = System.Windows.Forms.AnchorStyles.Left; this.lnkHelp.AutoSize = true; this.lnkHelp.BackColor = System.Drawing.Color.Transparent; this.lnkHelp.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lnkHelp.LinkColor = System.Drawing.Color.White; this.lnkHelp.Location = new System.Drawing.Point(4, 337); this.lnkHelp.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lnkHelp.Name = "lnkHelp"; this.lnkHelp.Size = new System.Drawing.Size(329, 25); this.lnkHelp.TabIndex = 3; this.lnkHelp.TabStop = true; this.lnkHelp.Text = "Click here to view the help document"; this.lnkHelp.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkHelp_LinkClicked); // // lblInfo // this.lblInfo.AutoSize = true; this.lblInfo.BackColor = System.Drawing.Color.Transparent; this.lblInfo.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lblInfo.ForeColor = System.Drawing.Color.White; this.lblInfo.IsPath = false; this.lblInfo.Location = new System.Drawing.Point(4, 246); this.lblInfo.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.lblInfo.Name = "lblInfo"; this.lblInfo.Size = new System.Drawing.Size(476, 50); this.lblInfo.TabIndex = 2; this.lblInfo.Text = "If this is your first time using Doom Launcher it is recommended to view the help" + " document."; // // Welcome // this.AcceptButton = this.btnContinue; this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(485, 420); this.ControlBox = false; this.Controls.Add(this.tblMain); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Margin = new System.Windows.Forms.Padding(4); this.Name = "Welcome"; this.Text = "Welcome"; this.tblMain.ResumeLayout(false); this.tblMain.PerformLayout(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Label lblWelcome; private GrowLabel lblInfo; private System.Windows.Forms.TableLayoutPanel tblMain; private System.Windows.Forms.LinkLabel lnkHelp; private System.Windows.Forms.Button btnContinue; } } ================================================ FILE: DoomLauncher/Forms/Welcome.cs ================================================ using System.Diagnostics; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher.Forms { public partial class Welcome : Form { public Welcome() { InitializeComponent(); Stylizer.Stylize(this, DesignMode, StylizerOptions.SetupTitleBar); lblInfo.BackColor = Color.Transparent; lblWelcome.BackColor = Color.Transparent; lblInfo.ForeColor = Color.White; lblWelcome.ForeColor = Color.White; lnkHelp.LinkColor = Color.White; } private void lnkHelp_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { try { Process.Start("Help.pdf"); } catch { MessageBox.Show(this, "Could not open the help file. Did you copy all the files to your Doom Launcher folder correctly?", "Help Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } } ================================================ FILE: DoomLauncher/Forms/Welcome.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AAABAAMAEBAAAAEAIABoBAAANgAAACAgAAABACAAqBAAAJ4EAABAQAAAAQAgAChCAABGFQAAKAAAABAA AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUrNJUFCA3yDREX6n18e+iEhIPodnZ16Hh4 d+h4eHfoeHh36Hh4d+h2dnXohISD6Hx8eugLDRHqCxAX8iw1QZQ+S1r/KzI6/xMZIv9nZmb/m5qY/4qK iv+Kion/ioqK/4qKiv+Kion/i4uL/5qZmP9paWj/AwQG/xMXG/8XGyH/MjhA6T5CS/8lLTX/BwoN/6Oj ov/Nzcz/tra2/7i4t/+4uLf/tra2/8zMy/+lpaT/Fhwj/xsfJP8gJSr/GBsg6UFGTeg0NTf/Dw4O/ygr LP/Y2dj/yMjG/8LCwf/BwcD/wcHA/8LCwf/GxcT/2djX/zlCTf8iKjX/Ki82/xseIehESU/oPkFF/yQk Jv8aHiL/pKOj/+Xk4v+/v77/zc3N/83Nzf++vr7/5eXj/6Khof8eIyr/LDQ+/yguNv8gIyfoQ0VH6EtS XP8+RE//GyEp/ygpKv/08/D/5+bl/76+v/+/wMH/6eno//T08/8qLTD/Dg8U/zc/SP9BTFn/KzE46EhK TuhaZHD/Sllq/01VX/8aISj/WmFo/9TV1f+xsK7/tLKx/8zMzP9SWF7/HSEn/yktNP9OYnX/ZniI/yow OOhGSE3ocXmC/5Ccpv84RlX/OT5C/xwnL/8fKDH/bnV+/1JXXP8eJCv/Iygu/y0uMP9BS1X/nqew/0BF Tv8kKjHoUFRb6GdveP/08/H/mp6j/xMaIv8rMDX/SVBX/xokMP8uNj//QklQ/x8lLf8dICX/Z2pt/9rY 1v8pLjL/KCwx6FNZX+hYYWz/3Nzb//////+IiIv/n6Ch//z6+P9OUlj/uru9/+zs6/95fYP/kJCR/4aF g//JzM7/NzxE/yoqLehbY2zoXmVt/8XGxv+3uLf/7Ozq/83Nzv+nqaz//////7i5uf+6urz//Pr3/8vM zP/a2tn/yMrL/zQ4P/8lJyjoXmNr6GRobf/AwMD/gYKD/+Pj4v+foKL/c3V4//z8+v+SlJb/goSI/+Pj 4v/g4N///////8zKyf8wMTP/SFBb6F1jauhmZ2n/xsfH/4WFhf/s6+r/qamp/3h5ev/7+vn/mZyg/4+S lv/p6en/6+vp///////Q0ND/Kiwt/05YYehna3Ppa25x/+ro5f/l4+L/6urp/+Lg4P/l5OP/5+bm/9zc 3f/q6ur/6+rp/+no5//l5eX/7+7t/0BGTP8xNDjpeHt//4OFif+tsLf/ur7C/5OZoP+Oj5D/t7m8/4uR mf+rsrn/ub7D/42QlP+do6n/gYaN/5eZmv9cXF3/S09W/46TmJOIio/yf4eS6oKLleiFiY/onKi16ION muh7gYnogYyY6HV6geh9g4vogIiS6ISMlOhxd3/qeHuB8mVobJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAACAAAABAAAAAAQAgAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAACQsNw4DAgSqDxIX8RUbJOsLERfoDRIY6GZmZeiCgoHod3Z26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26IKB gOhoaWnoCgsO6A0RGOgOEBXrEhcd8CgyPqkyOUQNQ1Jirh8kK/8TFRr/FBoh/wsRGf8RFBn/kI+O/5KS kf+GhoX/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eHh/+Hh4f/h4eH/4eH h/+GhoX/kpOR/4+OjP8RExX/ERcg/w0NDf8iKjT/JCo0/zA4Q6xKWGn4YXCC/xUZHv8lLDT/KzdE/wAA Cv9xcnP/4d/d/7a2tf+xsbD/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Ozsv+zs7L/s7Oy/7Oz sv+zs7L/sbGw/7W1tP/i4uH/bm1r/wAACP8cHiH/GR0j/yEpM/8WGiH/GSAn9zpDT+taZ3f/FBYa/z9F Uf9baXr/AAUL/z9ESv/d3Nr/uLi3/7a3tv+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5uP+5ubj/ubm4/7m5 uP+5ubj/ubm4/7m5uP+2trb/uLm4/9jW1P87PD3/BwsV/xkZG/8cHiL/ICMo/xcZHf8RExXrOUBJ6EFL V/8cHSH/Ki81/01aaf8aHB3/HSMr/xUXGf9iYmL/2trZ/7m5uf+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/ubm5/9vb2v9kY2P/ExMV/yEoLv8pLzn/ISYr/x8jJ/8xPEf/Iykw/xca IOg7PkToPUdQ/yYqMP8cHR//Ji85/yAhIf8bISf/AAAB/5+fnv/X1tb/u7u7/729vf+9vb3/vb29/729 vf+9vb3/vb29/729vf+9vb3/vb29/729vf+7u7v/1tXU/5+env8QGyX/LTZA/yMlKP8kJyr/LDI5/yow OP8hJiz/HiIo6Dg7P+g2OkD/KSsv/yQlJ/8hJCn/ICAg/xQYHP8qLTH/ycnJ/8jIx/+/v77/wMC//8DA v//AwL//wMC//8DAv//AwL//wMC//8DAv//AwL//wMC//8DAv//ExMP/zs3M/0FKU/8fKTT/LDI7/zQ7 RP8rLzf/JScq/yEkKv8ZGx7oREhO6EdOV/8kJCT/JCMj/yEiJP8iISL/ERER/zE2Of/a29v/zs7M/8DA wP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/wMDA/83NzP/a19T/NzxD/yY1 RP81PEf/KzA3/zdBTP8tMjj/KC42/xwfIuhFSlHoTFNb/ywwM/8rKy3/LC4y/ykqK/8dIST/MTY7/8XD wv/S0tH/w8PD/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/Dw8P/0dDQ/8bF w/81PUb/JCs0/yYoLP8+SlX/QEhT/y40Pf8qLjP/ICIm6D0/Q+g4Oj3/OkFK/zI3O/8uLzL/LTI3/y8z Of8tMDX/ERIU/7Ozsv/g39//xcXE/8fHxv/Hx8b/x8fH/8bGxf/GxsX/x8fH/8fHxv/Hx8b/xcXE/+Df 3/+0srL/Exce/y40O/8pKy3/O0ZS/z1FTv8qLzf/MThE/y40O/8gIiXoPj4/6ERIUf85P0T/MTY9/zEz Of81OkP/NDo//zY7Qf8AAAT/gYB+//r6+f/BwcH/ysrJ/8nJyf/FxcX/19fX/9fX1//FxcX/ycnJ/8rK yf/BwsH/+/r5/4GDhf8AAAD/Kykq/zM7RP8wNj3/LjI7/zU+Sf81PEX/MThB/yQpLuhAQULoTVRb/0FH Tv9CS1b/OUJO/zpCTP9AR0//OT9G/yQnKv8cGRb/4uLh/+no5//Dw8P/ysrK/9DQz/+6urr/urq6/9DQ zv/Kysn/w8PE/+/u7f/Y2Nf/JSsx/yYmKP8xOD//LC80/yotNP9KV2T/VmRx/1dldP9AS1j/Jiwz6EJC ROhSWmH/NTs//1hjcP9MVmH/SVNe/0xXZP8zNjz/P0VM/x8rNv9FTFL/5uXk//z6+P/Y2Nj/9vXz/1VY XP9WWV3/+Pf2/93d3P/9/Pr/2dnZ/ztDTP8dIyv/PkNL/yswNv8lJiv/SlZk/2B1h/9xhZf/aXiJ/zxI U/8kJizoRUZH6FBXX/9IUFv/UFxq/0xXY/9ianX/V2Z0/zI3O/8+RE3/SlVh/x4oMv8xOUD/m56f/9na 2f/r6uf/vr28/7++vf/n5eT/09TU/4yPkf8gJSn/ICgx/0JHTf80ODz/Kiko/0NOWf9SaHz/WGp9/4KP nv9PXm7/Mz1I/ycsMOhISEroSElN/05YY/9ygI7/TFhj/0xaaf93hpf/P0tX/z1BRv80MzL/Q0tT/zVE Uf8MEhn/ODxB/1BXX/9ZW17/Z21z/0ZGSP8oKi3/Fh4n/y0wNf9CSE7/Oj5D/zc5PP80NTj/P1Bi/0hY Z/+an6P/WGVy/zU/TP8/SFL/KjA26FBTWehOUVX/RlFc/296hf/V1dT/f4iS/0pabf9GUFz/QEJF/z1B Rf8/SFH/QUhP/z9ESv8wNTv/R1Zo/0dUY/8oKi//NT9J/z9GTv9AREn/PT9D/z5DS/87PkL/OTs8/ywz Ov9PW2n/2NjY/8zLyv8dJjD/QEdR/zIzOP8pLjboV1tg6FdcYP9LVWD/QUxW/97d2v//////fICE/xAV HP9ATFn/Rk9X/zw+Qf8/R1H/ISoz/z1ETP9xfYz/TlZe/0JFSf9ET1n/PEpY/z9DSP8/Qkf/Q0lR/zI1 OP84Oj7/Exgh/3R3e///////q6mp/yIoMP9HUFr/Mzg+/yswNuhNTU/oZGlx/1Zhb/9HUlz/xsXE//Dw 7////vv/paiq/xkeJf8nKi//KSww/yguNf98f4P/Mjc+/y0yN/9QVl3/LjZB/zc5O/9scHP/HiMp/z9K Vf81PUX/Jigr/0tNUf8vMjf/Xl9h//////+2uLn/KC40/z9ESv84PkT/Kisv6FBQUOhmbHX/T1pn/0VO Wv/Ozs3/7Ovr/9ra2f//////zsvK/zY8Qv8eIyn/v769///////Gxsf/LC4w/yEpNf9ARk7/zczK//// //+goaL/Higz/y4zO//Gxcb/eXp5/2JiY/9xcXH//////7u+v/8rMzv/QkVL/zk8Qv8uMDPoWlxh6Fxk bf9FS1P/RE5Z/9HR0P/v7u3/+fn5/+Hh4f//////l5iZ/7i5uv//////+/r6///////Hx8f/WmBm/9zd 3f///////Pv7//////+mp6j/nJ2e//////9nZ2f/paWl/3t8fP//////v8HD/zVATf9GSVH/ODxC/yss LuhianPoXWZw/0lQV/9FT1r/19bV/+7u7f+lpqb/4uLh/+np6P+enp//6ejo//Hx8f+Wlpj/sbKy//7+ /v+2uLj//v78/62ur/+oqKr/8/Py/+Tj4v+qqan/8/Tz/4WHiP/f4N//l5ma//n6+P/AwcL/MztH/0hN VP86Ozz/MjU46GZscuhpcnz/SE5X/0RNWP/Z2Nb//v///yUmKf+XmJn//////5eXl//u7u3/0tTX/wEH Dv+1tbX//f39/52foP//////lpaY/xofJv/29vf/2NfW/5+goP/u7u7/tLa3/+7u7//Q0dH/+vr5/76/ wP8tMDX/Q0ZJ/zY4O/8uMDLoY2dt6Gxyev9ITVT/QkdN/9va2P//////NTc5/5KTlP//////mJiY//Tz 8v/Ky83/GB4l/8XFxf/4+fj/mJma//////+rq6z/Ki80//Dx8f/e3t7/oqKi//Pz8v/n5+b/5+fn/+3t 7P//////vb69/yUkI/9RWGH/S1Rf/y4yNuhiZ2/oZ2xz/0lNUv9CRkz/397c//////87PD7/mJiY//// //+Zmpv/8/Pz/9DQ0P8dHiH/xMTF//v6+v+YmZn//////62wsv8vMjf/8/Pz/9/f3/+jo6P/9/f3/+3t 7f/p6ej/6Ojo///////AwcH/JiUk/09TWv97iZj/S1Re6GZrdOhqcHf/R0lL/0VKUf/k5OT//////0BB Q/+cnJz//////5qbm//08/P/09PU/yMkJv/Gxsf/+/v7/5mZmf//////tbm9/ztBSP/z8vL/4OHi/6Wm p//5+fj/6+vq/+rq6f/q6un//////8HBwv8yNDf/Ozk4/1deaf94hpboZm516G1yd/9MTU7/R0pN/+nq 7P//////IyMl/5GRkv//////pKSl//39/f/Ky8v/AAIG/8DBwf//////mpqb//////+usrb/Ji42//b0 8//l5eb/qaqr//r6+f/t7ez//f37//Dw7///////w8TG/zQ1OP9MUFT/P0VM/1FaY+hscXnoa252/1RY Xv9BQD7/6erq//////9sbW3/s7Oz//z7+/+ysrH/+fn4/9ra2v9maGv/19fY//X19f+dnp//+fr5/8PE x/9rbnP/+fn3/+Tk5P+rq63//f39//Pz8//h4eL/7u7u///////Dw8T/NDc8/19qdv9FSEn/LCoq6Hh/ iOheXmH/aHJ+/0ZFRf/z8e/////////////////////9/769vf/+/vz/////////////////9/b1/66v sP////7/////////////////7ezr/7a2tv///////////2xvcv/d3t///////9HQzv8/RlD/Wl1j/0ZJ UP8+Q0roe4OO611bWv9fZW3/d4GO/8HAv/++vLn/yMbG/9DS0//FzNH/Zm11/6eqrf/My8r/w8G//9XY 2v+2ur7/Ymhw/8PIzf/e4uX/0NLU/8/R0v+ZnaP/h4mL/9LS0//a3+P/Q0hR/5aYnP/MzMz/t7m6/1NV V/9LS0//W2Fr/0NITut+ho/4cXFx/2BgYv9tc3r/bnqI/15odv9ITlb/Z3J//4KRof9ueYX/PkJH/zg2 NP9dZnL/TVNb/1xlcv9kbnv/S1Zh/2x5iP9kbnv/XGd0/0ZKUP9gZ3H/SU9V/2VxgP96hpf/QkJF/0NF Sv9VXWb/YGFi/1VVVf9VWFz/RUdM+IqRmKuHiYz/gIKI/3V3fP98gIb/kpup/5Seq/9/gYX/bGpp/3t/ gP+EjJX/lqGu/52tvv95fYX/fH+H/4qUnv+EjZf/e4GI/3qBiv9+gor/hIyU/4SLlf98hIz/eX2C/5Gc qf91eoH/eX6E/3yDjf9xcnT/a2pr/19iZ/9iaXKqk5qjC5KVnaWIipHvjJSf7IaLkuh/f4LoqLTD6Jmh rOiTmZ7ooqix6L7L2ei/zNzoqLPA6I+YouiIjJLohImR6J2puOiSmqPoiZGa6IOEh+iPkpjoiY6V6Jii seiGiI3oiZCW6Jqms+iIjZPohYiO6ImQmuyJkZvvb3B0pWJlaAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAABAAAAAgAAAAAEA IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5SloAFRgeAA4OEiUNDhObDhEW4RAUGe8bIiroFRwk6A4T GegOEhjoFRwk6AoPF+gbGhroZWRk6IKCgeh2dnXod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3 duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh3d3bod3d26Hd3duh2dnXogoKB6GRkY+gcHR3oDxYe6A8S F+gMCw3oFx0l6BIYIegOEBboCwsN7xQaIuAaJC6aIik0JCwyPAAyOkYANERSABMYH0cPEBX1DAsN/w8S Gf8RFBz/FRsj/xcdJf8VGiL/FBkg/wcME/8VGiD/lZWU/5+gnv+CgoL/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aG hf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4aGhf+GhoX/hoaF/4KC gf+fn57/l5iY/xIUGf8AAAD/Fxog/x0mMv8REhn/Dg0O/xAUGf8iLDr/KzQ//yszPvMqMTtELDM9AEBN Xh8qMz7/HiUw/xARFf8dHyT/GyAq/xskL/8ZICj/HCQv/yMqM/8OGCH/GRwh/728vP+3t7b/r66u/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+vr/+vr6//r6+v/6+v r/+vr6//r6+v/6+vr/+vr6//uLi3/7y8vP8YGRv/BQUG/yQtOP8fJjP/GRwg/xQSEf8fJjD/PEtc/zE6 R/8iJzD/NDxG/jQ8RxpDT2CjW2p7/ztHWP8jKTP/IiUs/yQqNP8aIyz/GyIq/x8oM/8dJzD/Fx0m/wAA Cv99fX//19XT/7GxsP+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0 s/+0tLP/tLSz/7S0s/+0tLP/tLSz/7S0s/+0tLP/sbGw/9jY1/95eXn/AAAA/x8kK/8qNUT/GiAn/xMT E/8VFRb/LTlJ/0JTZf8ZHSf/EBIY/zM+Sv8uNT+eJDE95mV1iv9icYX/Nj9K/w8SGP8hKDD/MDhC/xsg KP8dJC//KDVB/x0fJf8DBQ7/Oz9D/9XU0f+1tbX/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2 tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7a2tv+2trb/tra2/7W1tf/V1dX/NTQz/wAA AP8rNkT/Iikx/xQVFf8SDw//GRwh/zJDVv8mLzv/EA0S/ygzP/8qNT//FRkh5CYsNu5SYnP/bnyM/0tZ af8UGiL/EhAO/zM8R/8mLzr/LzlF/ztIWf8eJi//FRof/xohKf+zs7H/xcXE/7a2tf+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3 tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7e3tv+3t7b/t7e2/7a2 tf/FxcX/t7e2/woJCP8SGiT/JzA7/xQTEv8XGBr/Fhcb/x8mL/8rN0P/FhUW/yApMf8xPkz/GR0l/w4R Fu4vMzvoPklX/11pev9KVmT/GiMt/w0LDP8vNT7/Tlpq/01XZv9HVWb/Hyct/xQUFf8CChX/k5SV/93c 2P+6urn/vb28/7q6uf+3t7b/uLi4/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4t/+4uLf/uLi3/7i4 uP+3t7b/urq5/729vP+6urj/4ODf/4uLif8AAAD/KDE9/xwbHf8bHiT/HB8l/xgaHf8iJjD/ISQo/xgZ Gv8nMj//Gxsd/xMTF/8ZHiToOUJM6DE5Q/9LWWr/OkRR/x4kLf8TEhX/Jysy/1Nfbv9WYXH/RlJh/xca Gv8aGx3/CBEc/1ZaYP/q5+X/y8rJ/8vLyv+6urr/tra2/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5uf+5ubn/ubm5/7m5 uf+5ubn/ubm5/7m5uf+5ubn/t7e2/7q6uv/Kysn/zMzM/+vp6P9FREP/BQsS/yMkKf8fISr/JCo0/xob G/8ZGRr/IiQp/x0eIf8cIij/Gxsd/xgYF/8eIyf/Gx4j6EVQW+gtMTr/OUZT/zxIVv8mLDT/FBIR/yEj J/88SFX/WWV1/1Bbav8TExL/GBga/yYuOf8dJCv/OUBH/zQ0Nf8iIiL/iYqL/9bW1f+5ubj/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/urq6/7q6 uv+6urr/urq6/7q6uv+6urr/urq6/7q6uv+6urr/ubm4/9jY1/+Hh4f/HyAg/zc3Of8uLCv/FRog/yMq L/8hJCv/KDE+/x8gI/8YGBf/FRMR/x8jJ/81RFT/MTlD/yQoLv8pLzr/GBod/xAPEeg7QkvoMjQ7/zI9 Rv84RVP/ISUt/xUTEv8eHh//JzE6/0FMWf9FT1z/Gxwe/xUUE/8lLTb/ICcu/wYJDP8BCBD/AAAA/62t rP/R0ND/ubm5/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7 uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7u7uv+7u7r/u7u6/7q6uf/Ly8v/ubi3/wIE B/8AAAH/DRMa/yYxPP8lKS3/LDM6/ykxPP8pLzf/Iysz/ycwPP8iKTD/LjlC/zVDUP8xPkz/Ki43/x0h Kf8ZHyfoQUlU6DIzN/8sMTn/M0JP/ykxPP8VFRb/GxgX/yQpMf8tOEP/JjA5/x4fIP8bGhn/ICUr/ygy PP8dHBv/AAED/3Fzdv/i4eD/uLi5/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8vf+8vL3/vLy9/7y8 vf+9vL3/tre2/+jn5v90dHb/AAUN/0BOW/8/TFj/LTI3/yAhJf8oLzn/KCwx/x4dHP8vOEH/KzM6/ykt Nf8xO0n/Ghsc/ycrMv8tN0L/HCIo6ERMVeg2OTz/KS80/zA9S/8qMjr/HyIl/x0cG/8nKjH/JC02/yQr M/8hIyT/Hh4d/yAjKP8oMDv/GRod/w0MC/++vr7/1NPT/7u7uv++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+vf++vr3/vr69/76+ vf++vr3/vr69/76+vf++vr3/vr69/7y8u//Qz8//urm5/x0kLP8nMz7/LDE5/ykuNf8mKzT/Jigp/xsY F/8mJyv/KSos/ywyO/8rLzX/Ki40/ycsMv8gIib/KDA3/xsfI+g6PUPoOj1E/yswNv8tNkP/JCYr/yYn KP8oKiz/Jiku/yIlK/8iJSr/ICIj/yAhIf8jJin/KjE6/xMWG/8fICD/v7+//8vLyv++vr3/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/ vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/v7++/7+/vv+/v77/xMTD/8rJx/9AR0//KDVC/zY+ R/8pLzf/LDE4/yEfIf8vOEH/ND1H/yotNP8wNj//HRoY/yIkJ/8xPEr/Hx4e/x4dHf8aHSLoPkdR6DIw MP8sMDb/KS84/yYpL/8eHR//JCUm/ycqLP8kJSr/IyUo/yAhIv8fHx7/IiMj/ygtM/8QERL/LTE1/8fH yP/Jysn/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DA wP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8TE xP/R0M7/Q0lQ/x0pNP87Q03/NT5G/yUsNf8wOkT/P01a/zM4Qv83RFD/NUBM/x4bG/8uND3/Ky82/yco Kv8kKzP/HCAm6DxAQ+g2ODz/Ki83/ygtMv8qLDD/IyQk/yIiIv8jJCb/JCYo/yMmKP8gIiT/ICAg/yMi Iv8jJSj/ExQW/z5CR//Ly8v/ycnI/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LCwf/CwsH/wsLB/8LC wf/CwsH/wsLB/8LCwf/GxsX/1dTT/zg5Pv8UGyb/QE1a/y46Rv8wOkf/PkhW/zM4Qf8nKS//KzI7/ysw OP8vNDz/LjI5/yYoLf8sOEb/KC41/xUVFOg5OzvoWWJq/0RRYP8oLzf/JiYl/ygoKP8mJif/JCQm/yUm J/8jJCb/IiMk/yMiIv8pKSz/JiYm/xIVF/87QUb/zMzK/8nJyP/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PD wv/Dw8L/w8PC/8PDwv/Dw8L/w8PC/8PDwv/Dw8L/x8jH/9HQzv80NDj/Hio2/zhFUf81Pkv/PkdR/y4z Ov8eHh//JSkt/ztHVP80OkT/NTxF/y82Pv83RFL/MThB/x0aGP8dICLoOz1B6EBESP9UXmv/Q01Y/x4d HP8vMzn/KCoq/ykqKv8oKCn/JCQj/ykqLP8mJST/KCkq/y8zOP8XGyD/Mzc7/97d3P/e3t3/yMjH/8HB wf/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TE xP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/ExMT/xMTE/8TExP/BwcH/x8jH/93d3P/f3tz/PUNJ/xkh Kf8uNkL/NDxG/y0yOP8iISL/Jiku/0dVZP9OW2n/P0hT/zA2Pv8oLDL/KCgq/0BJVP8mKjH/IyUo6D5B Reg8Qk3/Kywx/y8wMf8wOUH/Ky80/yckIf8zNjv/MTU9/y8xNf8vNDn/Ky8z/yUkIf8zOkD/Iis1/zIx Mf/Ix8j/xMXE/8bGx//Fxcb/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TE xf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xMTF/8TExf/ExMX/xcXG/8fG xv/Ew8P/yMjJ/zg7QP8dJC//OUFM/zI2O/8gHx//JCky/0ZVaP9RYG//NzxF/ygrMv8wOEL/MTQ5/yks MP8tMDb/LjQ8/x0gJOg7OzzoSVFZ/ysvNP8gHx7/PUdU/0FLVv8vMzf/LjA0/yoqLf81OUD/LC4w/yot MP8zOED/LC0w/zA4Pv8rLjL/IiQm/wUHC/+ZmJf/5uXk/8TEw//GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bGxf/GxsX/xsbF/8bG xf/GxsX/xMTE/+bl5P+Yl5f/BgkN/ygsMf8tND3/NDxI/zU7QP8pKCb/JSsz/0JSZP9SX2z/LTA0/yMm Kv85Qk3/N0BN/zxIVv9CT17/MjhA/yUmJ/8eICLoOzo76EVITf8uLzH/Ky81/y41PP85PkT/QEtY/ygq Kv8pKif/Mjc9/y4wM/8lKCf/NT9K/zAxNf8tLjD/MjpB/yUnKf8AAAD/d3h3//Dv7//ExMT/yMjI/8jI x//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jIx//IyMf/yMjH/8jI x//IyMf/yMjH/8jIx//IyMf/yMjI/8TExf/v7ez/e31//wABBv8lKjH/MzxG/zI1Of8oJyX/Jyox/zxM XP9KVV//MC4w/yksMf89Slf/NDtG/y4yPP8uND3/MDI6/zlASf8vNz7/Hh8f6Do5OOhAQ0n/Kist/zM5 Qf8zNz3/KSor/z1FUP81OkP/Kiwt/zc7Qv8xNDr/LjE2/zZCTf80OT7/LCsr/zhCTP81OT//CQkH/0tN Tv/s7Oz/xsbG/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jI yf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/IyMn/yMjJ/8jIyf/Ix8j/6eno/0xPVP8OEBP/LTE1/y0v Mf8nJST/Jyku/zdEU/86Q03/Kysu/yswOP8+S1r/NDlC/y0zPf80Pkr/PERP/zI0OP84QU3/MzxF/x8g I+hFSU3oPkBD/y0tMP8/SVf/Nj1D/y0sK/87RE3/OEBM/zAyOv87QUj/MzY+/zU4P/9BS1f/NjxA/y4t Lf81Pkn/NzxD/yUjI/8OEBH/wcHB/+Pj4//Gxsb/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rK yf/Jycn/yMjH/8jIx//Jycn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/Kysn/ysrJ/8rKyf/FxcX/6Ofm/7m7 u/8NERb/Jyco/yoqK/8nJST/Jygr/zVATf85Q0v/JSUm/ystN/85RVH/NDxF/zQ6Rf87RlP/QktW/zg+ R/9BTl3/R1Rj/yYmKP8kKjDoRkpP6ENDQv8zNzz/RlJf/1FdaP8rKiz/PURM/z5LVv8yOkP/PURP/zQ7 RP81O0L/RlJd/zc9Qv8wMTP/QktV/zY8Qv8yMTD/AgIE/2FhYP////7/x8fG/8vLyv/Ly8r/y8vK/8vL yv/Ly8r/y8vK/8vLy//IyMf/zs7O/+Tk4//k5OP/zs7N/8jIx//Ly8v/y8vK/8vLyv/Ly8r/y8vK/8vL y//Kysn/zMzL//n39P9WW2H/ERgf/zQ1Nf8sKyr/Kios/zU9Rv82QEn/KSkq/y0uMv80Pkv/P0ZR/0pS W/9ATlz/V2Ft/0tVXf9HVmb/UF5u/zU8Q/8vN0H/KzM76D8+PuhBQED/Oj9F/0BKVP9CRkz/PUNM/0lT Xv9WYm7/NT9J/z1FTv85Qk3/OkFM/05aZf86Qkv/NDQ6/z1CR/82PEb/Ly8t/zAvL/8AAAD/paWk//38 +//DxMX/zMzM/8vLzP/Ly8z/y8vM/8vLzP/Ly8z/z8/P/8rKyf+zsrH/srCx/8nJyP/Pz9D/y8vL/8vL zP/Ly8z/y8vM/8zMzP/Kysv/xsfI///+/P+QkJL/CRAZ/0JKUv84Oz//MDM2/zQ6Qv8zPEf/Ki4x/yop LP8yN0L/NkBN/09XY/9bann/W2x8/15pdv9XZ3j/Wml6/0pUYf9OWmb/OUVQ/yEjKuhDRUnoQD8+/z5F Tv9HUl3/Kigm/zM5Pv9MWWj/aXOA/0VPWv80PUf/QEhT/0JKVP9ZY2//RFFf/zIzNv83O0D/OUBL/y8u Lf8zNjf/JzA4/yQsNf/NzMz/+vj2/8jIyf/LzMv/zs7O/87Ozf/Ozs3/y8vL/+vr6f+qq6r/BAgN/wkO Ff+sraz/6uno/8vLy//Ozs3/zs7N/87Ozv/Jycn/0NDQ/////v+rrq//GCEq/y0zOv84O0D/NDQ2/zY8 Rv8yOkT/KS0z/yoqLP8uMDn/ND5K/0lUYP9WZHT/WG6A/2R1hf9ldYX/cX+Q/1hldv9SYG//T11t/y82 Pv8iIyjoSUxQ6D89Pf9BSVP/UF5t/zQ1N/8wMjP/T11q/2Fpdv9JUl7/TFZg/1BZY/9LVmD/V2Nw/0hW Zv8yNDj/Oj9D/zxBSP86RFH/Slxs/1Zlc/8lLjf/GSAm/7W2t/////7/39/e/8nKyf/Nzc3/z8/P/87O zv/i4uH/w8PE/w8WH/8UGyH/xsbH/+Lh4f/Ozs7/zs7O/8zMy//MzMv/6+vp//37+f+Kiov/GCMt/zY/ Sf8/REv/NjtA/zg8Qf82O0P/LDA3/ywvMP8tLTH/MjpG/0ZTX/9cann/XW+A/1htfv9meIn/anyL/219 jv9fboD/P01a/z5KV/8xNDn/IiQo6EFBQuhCREb/QEdQ/1hjbv88RFD/NThA/05ebP9lb3z/Ulpl/1Fb Zv9QWmf/XWVx/2Fsev9LW2r/Mzg+/zo+Qv80NDn/R1dm/0VRXv8xNj7/QEtT/zE8Rv8UHCT/dXh8/9rZ 1//7+vf/6ejo/9XV1f/Ozs7/zs7O/+Pj4/+6urv/t7e3/+Li4v/Ozs//0dHQ/97e3v/w7+3/9PPz/7/B w/9OUVP/Cg0S/zhATP9ASFH/ODtC/zg/Rf84PUL/NDc9/y4yM/8xMjP/MDQ9/z5MWv9XaHf/XW6B/11v gP9jdIf/eYaY/3KCkf9ndof/QU1a/z9LWv9ET1r/LTE3/yUoLuhBQkPoSkxQ/z9DSf84O0L/RE5b/1hi bf9XYnD/T11s/1hha/9YXmn/Vl1p/1deav9seIf/VGd2/zQ8QP83OT3/PUNM/0BJUP8zMjT/PkJK/0JN Wf9GTVb/PUZQ/xIdJ/8rMjn/fYGE/72/wP/g397/+Pf1/93d2v+dnZ3/vLy7/769vf+cnJv/3t3b//T0 9P/R0ND/qayv/2Zrb/8XHST/EBUY/zxARf8+RU3/O0JI/zg6Pf85PkX/NjlA/zM0Nv8yNDX/MTE0/zhD Tv9NXGv/Wml8/2Fxgv9IW2z/YW19/4CMmf9odYb/U2Bu/ztIVv9MWWf/OURO/zI7R/8sMDboRUZG6E5Q U/9BREn/MjM2/0NKU/9ZY27/Ym99/1JhcP86SVj/U11n/1llbv9hbHn/cn+O/1xtfv85Qkr/PEBF/0RO W/83Nzn/QERI/0NMVf80Njv/Nz1E/0dSXv9ASVP/LDI7/w8TGf8dHyP/SVBV/2pwdf+EiIz/hISG/4eF g/+LjI3/jpGW/3J0d/9PT0//Kikp/x0mMP8jLTT/NDY6/zg6Pf88Qkj/OUBG/zo/Rf85PD//PUFE/zxD S/8yNTb/MjEx/zEzOf9DUF3/WGl4/2Z3iP85SVj/QE1Z/7y/wv9jbnj/Pkxc/0VUZP86Qk3/XGl4/0lV Yf85Qkr/KCwy6EdHSOhLTE3/P0FI/0NITP9ITlf/Ulxp/1Nkcf98hZL/hI2V/yY3Rv9NXGn/b3yN/36L m/9nd4f/P0tY/0BHUf9GT1r/PD9E/z9FS/8zMzX/MjM1/0ZTXv9FUl3/RExX/0FMVv81OkL/MTAx/y44 Q/8oNkT/FRsn/xIUGf8dJS3/HCIp/ykuNf8jLjv/EhER/ygnJv8+REz/R1Jc/zo8Qv81Njf/QkhP/z5E Sv84PUP/O0BF/zg5O/9AR0//NztB/zAwMP8yNz//R1Vk/2NxgP80RFT/PEZR/+Hh4v/AwcL/GCQw/05f b/9IVmb/LS0w/0xVYP9JV2X/NT1F/ycqLuhOUFLoS05S/zo7P/9FSVH/SU5V/1djbv9VZXT/b3mC//j0 8P+hpqn/PEhX/2h2h/9/jJz/aXeH/0NPXP9CSlP/SVFa/z1AQ/82NTP/MzM0/0VQW/9DT1j/OUBH/0RN VP89R1D/LzA0/zxARf9ESlL/RlFd/0dQXf9fbHr/ZniK/zw+Qv9ISk3/U19s/zxCSf8xMC7/P0RL/0RL Vf89QEb/MzM0/0JKUv9ARkr/ODxD/zxARf83OTz/O0BF/zs/Qf8xMTL/OUNM/0tZaP8mMT7/WV5k/+jn 5P//////f4SH/yEsOP9MVWD/Q0xY/zM1Of87QEf/SFBY/zE1O/8iJCnoVlpg6FNbZv86PED/R0pP/0pQ WP9WYW3/VWFu/0xWYP/S0tP//////8jIyP9bZW7/Slhn/3F/j/9GTFb/QUlS/zs/Q/82MzH/QEJG/0dT Xv9JWGL/Nzk8/zxARv88Qkj/NDM1/zs+RP9ETFX/PD9C/1djcP98i5z/doea/0xTW/8xLCn/SExR/0VM VP9VY3D/UVhh/zU6P/9IUFj/PD5B/zY1Nv9DTFb/PUJI/zs/Q/86PkP/OTs9/z9ESP86Ozz/MTEy/zpF UP9DUF7/i46S//n28v/n5+X/8vDv/42Qk/8aHyT/PkBF/0JNV/9EUFz/Q0dL/zIvL/8uND7/MDlC6FNW WehkanH/QURL/0tQV/9KUFj/Tlpn/1VicP8yOkH/ysvK/+Xk5P/z8e//4N7d/2dveP8mMj//Nzc4/0NI Tf9RWmb/TFZi/0RRXv9DUVv/Njg6/zIyNP9GUFr/SVJc/0VOV/9KV2P/Oz5F/ztARP9seYn/aHOB/1xo dv9ARkz/PkBF/0hPVv9CSVH/Qk1V/2x8jf9cZnP/P0ZO/zs+QP86Ojz/QUhR/z1BSf88P0T/PEFH/zk7 PP88QUf/QERJ/zs6PP82OUD/JC44/8vMzP/39vX/z8/Q//j39/+Jio3/EhIV/z9HUP9OXW3/SlBZ/zcz NP86RVL/P0pU/y4wNOhNUFboV1ha/0dLUv9PV2D/S1FY/01WYP9baXf/Lzc9/8nJyP/q6un/0dHR/+vq 6P/+/fv/d3h5/wgKDf81PEX/WGVy/0lXY/8/SFT/Ojw//zQxL/8+Q03/R1Bb/1JcZv81PET/QElT/0NH TP9ARUv/W2h3/1Zfaf9IUlv/PEJJ/0JHTP9QWmX/RE5X/z9DR/8+R1D/YG+A/z1BR/9BQ0f/PD9B/0FJ Uf9DSFD/Oz9D/zxBR/86Oz3/Oz5C/0ZJUP9BRUv/Njg9/xATGf+KjI7/+/r5/9TV1f/5+fn/iYiI/w8R Fv9KWmn/UVdh/zU1N/9AS1f/SFRf/zM1N/8lJiroWVtg6E1MTf9LUVf/X2hy/0tPWP9JT1b/XW16/zM7 Qf/Hxsb/7ezr/9nZ2P/U1dX/4eDg//////+trq//Iywy/x8lLv9GS1P/PT9C/zc0M/8+RUz/PEBE/z9B Rf81QEr/LDE3/y8xNf8pLTH/VVxk/0pSWv88Ozv/TFVg/0hQWf9BRkn/PEBI/1FZYv8uLi//MTc8/ykz Pf87PUL/Q0hP/z9DSP9FT1n/RExU/z1AQ/9GS1H/KSkq/yYoLf9ER0v/PUBF/z9FS/8cISj/lZWU//r6 +f/W1tb/+vr4/4mJif8kLzv/VFxk/zg4Of8/R1D/SFRg/z4/Qv8vMTf/LjM56FBQUOhOUFH/T1Rd/2lw ev9WYGz/TlZk/19ufP80PUb/xcTE/+/v7v/Z2tr/29vb/9jY2P/Z2dn//////9nZ2v84OTr/DQ0O/z49 Pv9DTFb/QEdQ/z8+PP8UGB3/KTA4/9HS0//V1dX/KSos/yUtN/9OUVX/PD1B/0tRWf9LU1v/WGFt/0xS W/8PERT/SUtM/9fY2P+Hioz/DxMY/zk/Rv9FTFT/RlNf/0JKVP9DREf/Iigv/zY3OP9sb3L/HiEl/3d5 ff+CgoT/BQgM/5yeof/6+vn/2NjY//r59/+Sl5v/Iykx/zw6Ov8/REn/RElS/0JHTv8+Q0v/OT1D/yst MuhPT07oT1FS/09UXv9haXX/Tllk/1FaZv9ZZ3f/NTxE/8XExP/x8fD/2trb/9zc3P/c3Nz/3Nzc/9XV 1f/49/f/9fX0/19eX/8LEBf/QktV/0VJUP8ICAz/P0JG/9zd3f////7//Pv7/+Ti4v9MUln/FBYZ/0hL T/9GSlD/SE5Y/0tSXP8VGiD/WVlY//Pz8//29vX//////6qqqf8gIyf/Iyw0/1hlcf9PVl7/EhUZ/zE0 Of/j4uP/vb/A/wAAAP+QkJD/t7i5/wAAAP+am5v//f39/9nZ2f/8/Pv/kpOV/x8oMf9DTVf/R0xU/0NH Tf9DRkr/RUlQ/zY4Ov8sLTDoUFBQ6FNVWf9cZG3/VWBr/0NHTf9RV2D/UV5s/y01O//Mzc3/8/Hv/9zc 2//d3dz/3d3c/93d3P/e3t3/19fX/+3t7P//////l5iY/zg7P/8KERn/cXN1/////v/29vT/19fW/9jY 1v/29vX//f39/2xrbf8OEBH/SlFY/1BaZ/8cIij/hoaI///////s7Oz/2NjX/93d3P//////1NTU/zI5 P/8tND3/KzA2/09SVf/v7+///////5STkv8AAAD/s7S1/9/e3v8AAAD/mJiX/////v/a29n//Pz6/5WX mf8nMDn/TVNa/0lNU/9FSlH/QUNI/0JGTP85PkP/LC4v6FBPUOhbW13/WWFt/05XZv9ERkj/UFZf/1Je a/8wNz//ysvL//Pz8v/c3N3/3t7f/93d3f/e3t7/3t7f/9/f3//a2tv/6urq//////88P0L/hIeJ//// ///p6ej/2dnZ/9/f3//e3t7/2NjY/+rq6v//////ioqL/zA3QP9LVF3/tLW3///////j4+P/2dna/97e 3v/e3t7/2dnZ///+/v/s7e3/QkVK/1VZXf//////7+7u//39/f+amZn/AAAA/9HS1P/+/fv/Fxgb/4+Q kf//////29vc//78+v+VmJ3/KjdF/1FcaP9CRUr/Q0ZM/0VIUP9GTVf/Oj1D/y0uL+haXmXoU1db/1Jc av9QXGn/QkJF/1JYYf9RXm3/MztE/8zMzP/08/L/3t7f/9zc3f/z8/P/5eXl/93d3f/g4OD/3d3d//39 /P+ampv/WVpc///////h4eH/3d3d/+Dg4P/a2tr/9/f4//Py8//W1tb/4uLi//////92e37/l5ue//// ///d3dz/2dnZ//X19f/s7Oz/29vb/+Dg4P/Z2dr/9PTz//T09P9zdXb/1dTU/+Xk5P//////kZKS/xob G//n5uX//////zs8Pf99fn///////9zc3f/+/fz/lpme/y04RP9VYG7/SU5W/0JDRv9GSlL/R01U/zc3 Nv8sLS/oYGVr6F9mcf9UYW3/UVxo/0ZFRv9UXGT/Tltn/zM5P//Nzs7/9vX0/+Dh4P/j4+P/w8PD//j4 9//s7Ov/3d3c/+Dg3//+/v7/mpub/3Bwcv//////3t7e/+Hh4P/f397//////8LCw/+trq//7+/v/93e 3f//////hYeI/5udnv/+/fz/39/e/+bm5f+4uLn/6Ojo//f39v/b29v/4uLh/+Hh4f/6+vr/V1dX/7a2 tf/09fT//////4WGh/9AQUP//v79//////9sb3D/d3l5///////e3t7//////5aYmv8kKC7/TVNc/0xS XP9JTVP/REVI/z9AQf81ODn/Kiwu6FtbXehoc37/VWFt/09WX/9FREX/VmFs/09caP8wNDv/zMvM//j3 9//g4OD//////1JTVP9WVlf/9fX1/+fn5//f39///v7+/5+foP9nZ2j//////+Hh4f/f39//+vr5/72+ wP8EBAb/hYeG///////f3t///////32Agv+WmZv////+/9/f3///////XWBj/ystMf/d3d7/9PPz/9/g 4P/l5eX/8PDw/15dXv/AwcH/8fHx//////+DhIb/bG9x///////9/Pz/nZ+g/4SGiP//////39/f//// //+Wl5r/KDE8/0xTXv9NU1z/R0lM/0VHSf9MUFX/R01V/zg+ROhYV1jobHZ//2Fwfv9QV2D/R0hN/1lk cP9OW2f/MjY9/83Nzf/5+ff/4ODg//////93eHn/AAAA/3l7ff//////4eHh//////+foKD/aGlp//// ///g4OD/+Pj4/8bHyf8YHCP/EBUb/6aoqP//////3+Dg//////+Bg4X/mZye///////f39///////4WG if8AAQf/WV9m//b19P/p6Oj/5eXl//Hx8P9dXl//wcLC//Lx8f//////i4yO/5aYmf//////7u7u/8PE xf+dn6D/9/f2/+Dg4f//////lpWW/yktNP9QV2D/SEtP/0RFRv9IS0//SUtO/zo8Pf80NzroWVla6Gpx ev9qdoP/Ulpi/0ZFRv9WX2v/TVpm/zI2Ov/Pzs3/+vr5/+Li4v//////e3t9/wgKDP9oamz//////+Tk 5P//////oaCg/2lpav//////4eHh//////+nqKv/JC02/ygvNf+goJ7//////+Hh4f//////foCC/5ea m///////4eHh//////+Ehof/Fxke/09WXv/m5uX/8O/u/+fn5v/y8vL/Xl9f/8LDwv/19fT/+vn5/6an qP+8vb7/9fT0/+Pj4//j4+P/0NDR/+fm5v/j4+P//////5iYmv8fICL/RUZG/0VFRf9JTVH/Q0ZM/0JG Sv84ODv/KSgn6FdYWuhgaHD/bHaC/1ddZP9EQ0T/V2Bs/0pSXf8yMzX/z8/P//r6+//j4+P//////3p7 ff8GBwf/dXZ3///////k5OT//////6Giov9qamv//////+Li4v//////s7O1/y42P/8mKjD/oJ+e//// ///i4uL//////3p8ff+TlJX//////+Li4v//////hYeI/xUYHP9XXWT/7e3t/+7t7f/o6Oj/8vLy/2Bg Yf/Dw8P/9/f2/+np6f/R0tL/39/f/+fn5//k5OT/6Ojo/+7u7v/j4+P/5OTj//////+YmZn/ICIl/0ZI S/9HSk7/TVVg/1BYYv9GTFP/ODpA/zEzOOhZWVvoZW10/2Zvef9XXGX/SEhJ/1hgav9ITlj/MzI0/9DR 0P/8/Pz/5ubl//////97e3z/BwkK/3R1dv//////5+fn//////+io6P/bGxt///////k5OT//////7S0 tf8sMDf/JSow/6Khof//////5OTk//////96enz/kJGS///////k5eT//////4WGiP8XGx//VVpf/+zs 6//w8fH/6urq//Pz8/9iY2T/xsXG//n4+P/k5OT/7Ozs/+rq6v/m5ub/5+fn/+fn5//n5+f/5+fn/+Xl 5f//////mZqZ/yAhIf9ISUr/S05U/1dib/9mcYD/XWZz/0lTXf86QEboW1xg6GhtdP9cZG3/WF1l/0pK Sv9WW2H/S1Jb/zIzNf/R0tH//v79/+jo5///////fX6A/wgICv9zdHT//////+np6P//////pKWl/25v cP//////5ubl//////+1tbX/LC4x/yUpLf+jo6L//////+bm5f//////fX5//5KTlP//////5ufm//// //+KjpL/Fxgb/1JUV//w8PH/8vLw/+zs6//09PT/ZGVl/8jIyP/6+vn/5+fn/+np6f/p6ej/6Ojo/+np 6P/p6ej/6eno/+np6P/m5uf//////5ydnf8mJyr/SUlL/0RFSP9faXb/kqCx/3yFk/9SXGf/QEhR6F1g ZehkaXH/W2Vv/1daYf9NTlH/TlNX/0xUXv84ODv/0tLS//7+/f/o6Oj//////3+BhP8KCw3/cnJy//// ///p6en//////6Wlpv9vb3H//////+fn5///////tLOz/y4vMf8nKiz/pKSj///////m5ub//////35/ f/+TlJT//////+fn5///////j5WY/xsgJv9RU1X/8fDw//Ly8v/s7Oz/9PT0/2JjY//IyMj/+/r6/+jo 6P/p6en/6enp/+np6f/p6en/6enp/+np6f/p6en/5+fo//////+dnp//Jigq/0tLTP9APj3/TVJa/4mY qv+Rnqz/WGFu/0ZPWuhfY2zoaG10/19oc/9UVlz/U1VY/0tOUP9NVl//PD9E/9PT0v//////6urq//// //+AgYH/DhAS/3N0c///////6+vr//////+lpqb/bW1u///////o6On//////7W1tv8vMDP/KSos/6al pf//////5+fo//////99fX3/kZKS///////o6Oj//////5OXnP8gJzD/UVNX/+/v7v/19PX/7e3u//X1 9f9jY2T/ycjI//z8/P/p6en/6urq/+rq6v/q6ur/6urq/+rq6v/q6ur/6urq/+jo6P//////nZ2d/yQk Jv9MTU7/S0tL/0FAQP9VXmj/l6a5/4yaqv9ibHjoY2dw6GRna/9ncX3/VVlf/1VXW/9MS0z/TFJZ/0BF TP/V1NX//////+zs6///////f39//xESFf92dnf//////+3t7P//////pqeo/3R0dP//////6enp//// //+6u77/MjQ4/ygpKv+oqan//////+jp6P//////f4CB/5KTlP//////6enp//////+Vm6D/KTA4/1xf Y//x8PD/9fT0/+7u7v/29/f/Z2lr/8rKyf/9/f3/6+vq/+zs6//s7Ov/7Ozr/+zs6//s7Ov/7Ozr/+zs 6//q6ur//////6CgoP8qLTP/TlBS/09TWf9PUlb/QT8//2Fte/+Pn7D/eoWR6GpzfuheXmD/aXN//15j af9VV1n/U1RV/0ZIS/9CSVD/2NjZ/////v/t7ez//////359fv8SEhT/eHl7///////u7u3//////6Sk pf+BgYL//////+rq6f//////t7e3/zI0Nv8sLzL/qaqr///////q6un//////4CBgf+Tk5T//////+vr 6v//////mZ+k/zA6Q/9aXWL/8vHx//b29v/w7+//9vb2/21wdP/Nzs7//f38/+zs6//t7ez/7e3s/+3t 7P/s7Oz/7e3t/+3t7P/t7ez/7Ozs//////+ioqP/LC40/1FUWf9QVFj/UFBT/0tOVv9SX27/Xmt7/2h0 gOhvfInoXl9i/19lbP9mbXj/UlJT/1teYP9FRET/QkVK/9vd3///////7u7u//////9/f4D/EhIT/3t8 ff//////7+/v//////+kpKT/j4+Q///////r6+v//////7e4t/8yNTz/LC0y/6usrP//////6+vr//// //+CgoT/lJSV///////t7e3//////5qeo/83QEr/Wl5i//Py8v/4+Pj/8vLy//b19f9qbG7/zs/Q//7+ /v/t7e3/7u7u/+7u7v/v7+//8/Pz/+3t7f/u7u7/7u7u/+3t7f//////oqOl/y0vNf9SVFb/TU1O/0xQ WP9ic4P/Zmx0/z9BQ/9OWGTodoKQ6GBhZf9WWVz/bnmH/1dYXP9QUVH/SUpM/0JCQ//d3d7///////Dw 7///////ZmZn/wAAAP9fX1////////Hx8f//////pqam/5iYmP//////7e3s//////+ura3/Fhke/xET GP+fn6D//////+zs6///////g4SG/5WWlv//////7u7u//////98f4P/DBMd/0ZKUP/z8vD/+/v6//Pz 8v/4+Pj/aWpr/83Nzv//////7u7u//Dw7//u7u3/9/f3//z8+//u7+7/7+/v/+/v7//u7u7//////6Kk pf8qKy7/TU5O/09SWf9icoL/Ympx/0hFQ/80Mi7/PkJI6IWQn+hhZGj/UE9Q/2x1gv9sc37/R0ZG/0lK TP9EQ0T/3t3d///////w8O//9/f3/7/Av/+RkZD/u7u7//f29//w8PD//////6mpqf+enZ7//////+/v 7v/19fX/4ODg/729vv+9v8D/3d3e//b29f/u7u3//////4aHif+WmJn//////+/v7v/29/f/xcbI/5ud n/+ztbf/8fHx//X19P/09PL/+fn5/29ydf/Pz8///////+/v7v/w8O//+vr6/9HS0/+anJz//v7+//Dw 7//w8PD/7u/u//////+kpKT/Ki0x/1BSWv9jcH3/bHR8/0pIR/9LTE//QkJF/zU2OOiUoK/oX2Jo/1RS VP9aX2b/fYmY/1pcYP9CQUD/RUZI/97e3v//////8PDw/+7u7v/////////////////u7u7/8PDw//// //+qqan/oqGh///////w8PD/7u7u//n5+v////////////v7+//u7u7/7+/v//////+HiIn/lZeY//// ///w8PD/7u7u//////////////////Hx8f/v7+//9fX1//j49/9ub3L/0NHS///////w8PD/7+/w//// //+nqaz/QERJ///////39/f/8fHx//Dw8P//////q6yu/zU5QP9dZ3T/aHJ8/1FOTv9SVVz/WWFq/0hJ T/82NTboi5im6Gltc/9UU1X/U1NT/2x3g/+AjJv/REND/0A9Pf/e39////////Dw8P/y8vL/8PDw/+/w 7//w8fD/8vLy//Dw8P//////uLe3/6qqqv//////8PDw//Hx8f/x8fH/8fHx//Hx8f/x8fH/8vLy/+/v 7///////mpye/6eoq///////8PDw//Ly8v/x8fH/8PDw//Hw8P/x8fH/8vLx//f39///////bW1t/8XG xv//////8PDw//Ly8v//////l5mc/yYsM//a3Nz///////Dw8P/w8PD//////52dnf8pLDH/aHF8/1ZW V/9RUlX/Wl9q/1pjbv9WYm7/P0VK6ISPneh1eoL/Tk9Q/1ZWV/9WWFr/e4mX/295hv9FREX/+vj1//// /////////////////////////////////////////////4WMkv9ianH/7/Dw//////////////////// ////////////////////////+fj4/1tia/+IjpX///////////////////////////////////////// ////////zs/R/19gYf/8+/n//////////////////////2hrbv8eJzD/srS4//////////////////// ///Kycj/PkRK/1pdY/9RT1D/Vllh/1leZ/9iZ3D/WV5n/0ZOV+h7hpXofYSO/09PT/9XWl3/WFlZ/1hc Yf9vfI3/iJOe/62usf+cmpr/p6Wj/6inp/+lpKP/qqmo/6yvs/+4vcP/trq//3V9hP90gI7/U1xo/1NZ X/+qr7P/q6qq/6moqP+pq6v/qaus/6mqrP+5vsP/wMbL/2NmbP9TW2X/UFtn/4OKkv+/xcz/v8PI/7W4 vP+1ub7/s7a6/6Kjo/+xtLj/rLK6/z1FUP9hZGn/uLy//6eoqf+vs7j/zNLY/8LJ0f9MUVn/S1Fa/3V8 hP+npqb/rK2u/62usv+qrK3/r7Cx/3d9g/9ISUr/UFFR/1NWXf9wdn//c3mC/1JTWf86Ojzpd4GN732D jP9VVFP/VldY/19iaP9cXGD/XGBm/3qFkv9seYf/U1xk/zE1PP85P0j/S1Rf/z9FTP80Nzz/Tlto/4SW qP9seof/Zmty/2Rlav9PVFv/PkZP/zEvL/8sKSb/RUpR/1FbaP8sLS//SVNf/1xodf9WX2z/am93/3+K lv83P0b/SVZj/3GDlf9mc4H/WWJw/05ZZ/9QXGj/RU1X/0lRW/9gZmz/WmFp/0ZRXf80ODz/MDI1/1hl c/+Hmq//Z256/2Fmbf9aY23/LS0t/zM0Nv88Q03/Njo+/z1BR/9eY2v/VlRU/1JUVv9YXGH/am1y/2Jl b/9KTFD/PT0/7oCJleZ8hY3/XV1d/1dXWP9bXWD/X2Np/1paWv9eYGX/dYCO/4+erv+Yqbz/eoiX/2pz fv93hJP/iJan/5mmtf+cqbj/oa6+/4SPnf9vdXz/YGhz/11iaf9WU07/U1RT/5GkuP+Bj5//XGBn/2Bk bP9iZW3/g5Kk/1tfZ/95gIz/Z3N//09SVv9zf43/i5ir/3N8hf90fYb/eIKQ/3uJmf9WWV3/WFZS/15i aP+Mna//cXuH/1FQUf9MT1H/eomb/46cr/9XXGP/Y2pz/1pcYP9YWVr/ZnB8/15iaP9fZm//XmJo/1RS U/9UVln/WFpe/1NVWv9XV1v/UlVZ/0VKUeOCipCeh5Gb/3Fwcv9dXF3/XF9l/15jbP9fYmn/XF1e/2Jm bP9pcoD/dX6N/42crf90f4v/WVpc/250e/9tc3r/U1NX/1ZWWP94foT/jpml/4qaqv9YX2b/S0pJ/3aF l/+ZrcT/dICL/212gf9gZGz/XWRw/3iBjP98ipj/ZnF+/3qLnP9ocn3/VFNX/3R9iP9wfYr/W15l/2Nm bf9xeob/eIiY/1lfZv9QTk7/d4GN/4ydr/9RVFv/W2Bm/298iv+RoLX/Zm97/1pfZ/9YWF3/X2Vu/2No b/9ja3b/Y2x5/1xeYv9XVlT/WFhY/1lZWP9WVVb/WFxg/2FreP9ha3achImMGZOcqPuGiYz/cXFx/2dr cv9manT/ZWl2/2Roc/9gYWb/am1y/2NlbP9gZG7/l6a7/4mWpP9WVlX/TUlG/1VSUv9UVFL/TUxI/0pG Qv9veIL/hpms/4OSov+nuM//jZqs/257iP9ye4b/cXZ//2Jrdv9fYmn/Z2lt/3KEk/9zgY//dnyF/3qE j/9cYGT/ZG55/2Jocv9iZWv/YmRo/3V+if+Flqb/Zm97/1FPUf93hZT/aHJ8/2FiZv9naWz/g4+f/3J/ jv9hanX/X2Vs/253gv9laW7/X2Vs/257if9aWVr/XV1g/15fYv9cXF7/a293/3F4hf9dYGb6Y2luGaCq ugCdqLc9jpOZ8IaFiP+ChIv/foGJ/3yEjf9+hY//foSP/3h6f/9/f4D/cnN1/5Sfrv+zwdL/laGw/4yS mf+EiY//fICG/4GFi/+Hj5n/rrzK/73L2/+Zo7D/usfV/6y4x/+Dj5v/goyW/4+Wov+Aho7/fIOM/3R0 df92eoP/l6a6/4mSnf+AhY7/g4yX/36JlP98hI3/eXp+/3p8gv92eHz/g4WK/5SerP96gIn/dXqC/56t wP97f4j/aGVh/32Ch/+Mmaf/i5ej/4+cqf91eX//dnp//4CIkv99gYr/dHd+/32Gkf92fIX/gImV/3yD i/9qaWrvV1dbPFxfZACaprkAmaWzAJCWniCJiYyWi42V3IiOle6JkJrpjJOe6I2Vn+iMlKDohYaL6IaJ jOiYo7Doq7fF6KCsuuiRmqboqrTB6L7I1ejCzNvowMzd6MzX5ejP3OnooKm06Ki0w+jI1OPonqe06JKc p+iTmqTok52n6IeMk+iHjJToiJCb6J+sveidqrjok52p6JCVneifqbTojZWg6IWGieiLjJDok5eg6ISG ieiBf4LomqGn6JOcp+ilscLokJil6H59f+iFiY7ol6Sx6Jegq+iTmqLokJag6ISFieiOk5vogYOK6I6V n+iWoK7pj5mj7oySmdx1dHWWZmdpH2dqcQBhZGkAwAAAAAAAAAOAAAAAAAAAAQAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABwAAAAAAA AAM= ================================================ FILE: DoomLauncher/GameFileView/GameFileViewFactory.cs ================================================ using System.Windows.Forms; namespace DoomLauncher { public class GameFileViewFactory { public bool IsUsingColumnView => DefaultType == GameFileViewType.GridView; public bool IsUsingTileView => DefaultType != GameFileViewType.GridView; public GameFileViewType DefaultType { get; private set; } private static readonly Padding TileMargin = new Padding(8, 8, 8, 8); private readonly ToolTipDisplayHandler m_toolTipDisplayHandler; public GameFileViewFactory(MainForm form, GameFileViewType defaultType) { DefaultType = defaultType; m_toolTipDisplayHandler = new ToolTipDisplayHandler(form); } public static bool IsBaseViewTypeChange(GameFileViewType type, GameFileViewType other) { if (type == GameFileViewType.GridView && other != GameFileViewType.GridView) return true; if (other == GameFileViewType.GridView && type != GameFileViewType.GridView) return true; return false; } public void UpdateDefaultType(GameFileViewType defaultType) { DefaultType = defaultType; } public static ColumnField[] DefaultColumnTextFields { get { return new ColumnField[] { new ColumnField("FileNameNoPath", "File"), new ColumnField("LastDirectory", "Directory"), new ColumnField("Title", "Title"), new ColumnField("Author", "Author"), new ColumnField("ReleaseDate", "Release Date"), new ColumnField("MapCount", "Maps"), new ColumnField("Comments", "Comments"), new ColumnField("Rating", "Rating"), new ColumnField("Downloaded", "Downloaded"), new ColumnField("LastPlayed", "Last Played"), }; } } public IGameFileView CreateGameFileView() { IGameFileView view; switch (DefaultType) { case GameFileViewType.GridView: view = new GameFileViewControl(); break; case GameFileViewType.TileView: case GameFileViewType.TileViewCondensed: view = new GameFileTileViewControl(); break; default: view = new GameFileViewControl(); break; } m_toolTipDisplayHandler.RegisterView(view); return view; } public GameFileTileBase CreateTile() { if (DefaultType == GameFileViewType.TileView) return new GameFileTileExpanded { Margin = TileMargin }; else if (DefaultType == GameFileViewType.TileViewCondensed) return new GameFileTile { Margin = TileMargin }; return null; } public static IGameFileView CreateGameFileViewGrid() { return new GameFileViewControl(); } } } ================================================ FILE: DoomLauncher/GameFileView/GameFileViewType.cs ================================================ namespace DoomLauncher { public enum GameFileViewType { GridView, TileView, TileViewCondensed } } ================================================ FILE: DoomLauncher/GameStores/AutomaticGameStoreCheck.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; namespace DoomLauncher.GameStores { public class AutomaticGameStoreCheck { private IDataSourceAdapter m_dataSourceAdapter; private Func m_loadGameStoreFiles; private List m_installedGameFiles; public AutomaticGameStoreCheck(Func loadGameStoreFiles, IDataSourceAdapter dataSourceAdapter) { m_dataSourceAdapter = dataSourceAdapter; m_loadGameStoreFiles = loadGameStoreFiles; } public delegate Task AsyncWadInstaller(List wads); public delegate Task AsyncDoom64Installer(string doom64Exe); public async Task LoadGamesFromGameStores(AsyncWadInstaller addIwadToGame, AsyncWadInstaller addPwadToGame, AsyncDoom64Installer addDoom64ExeToGame) { var gameStoreFiles = m_loadGameStoreFiles(); m_installedGameFiles = m_dataSourceAdapter.GetGameFileNames().Select(Path.GetFileName).ToList(); var iwadsFromStore = gameStoreFiles.InstalledIWads.Where(NotInCollection).ToList(); if (iwadsFromStore.Count > 0) { await addIwadToGame(iwadsFromStore); } var pwadsFromStore = gameStoreFiles.InstalledPWads.Where(NotInCollection).ToList(); if (pwadsFromStore.Count > 0) { await addPwadToGame(pwadsFromStore); } var doom64ExeFromStore = gameStoreFiles.InstalledDoom64Exe; if (doom64ExeFromStore != null) { await addDoom64ExeToGame(doom64ExeFromStore); } } private bool NotInCollection(string file) { var justTheFileName = Path.GetFileName(file); var justTheFileNameZip = Path.GetFileNameWithoutExtension(file) + ".zip"; return !m_installedGameFiles.Exists(x => x.Equals(justTheFileName) || x.Equals(justTheFileNameZip)); } } } ================================================ FILE: DoomLauncher/GameStores/GameStoreFiles.cs ================================================ using System.Collections.Generic; using System.IO; using System.Linq; namespace DoomLauncher.GameStores { public class GameStoreFiles { public List InstalledIWads { get; } public List InstalledPWads { get; } public string InstalledDoom64Exe { get; } public static readonly GameStoreFiles EMPTY = new GameStoreFiles(new List(), new List(), null); public GameStoreFiles(List installedIWads, List installedPWads, string installedDoom64Exe) { InstalledIWads = new List(installedIWads); InstalledPWads = new List(installedPWads); InstalledDoom64Exe = installedDoom64Exe; } public GameStoreFiles Combine(GameStoreFiles other) => new GameStoreFiles( installedIWads: CombineListsIgnoreRepeats(InstalledIWads, other.InstalledIWads), installedPWads: CombineListsIgnoreRepeats(InstalledPWads, other.InstalledPWads), installedDoom64Exe: InstalledDoom64Exe ?? other.InstalledDoom64Exe); private static bool HasSameFileName(string file1, string file2) => Path.GetFileName(file1).Equals(Path.GetFileName(file2)); private static List CombineListsIgnoreRepeats(List existingItems, List newItems) { var resultList = new List(existingItems); foreach (var newItem in newItems) { if (!resultList.Exists(existingItem => HasSameFileName(newItem, existingItem))) resultList.Add(newItem); } return resultList; } public override bool Equals(object obj) { if (obj is GameStoreFiles) { GameStoreFiles other = (GameStoreFiles)obj; return InstalledIWads.SequenceEqual(other.InstalledIWads) && InstalledPWads.SequenceEqual(other.InstalledPWads) && (InstalledDoom64Exe != null && InstalledDoom64Exe.Equals(other.InstalledDoom64Exe)) || InstalledDoom64Exe == other.InstalledDoom64Exe; } return false; } public override int GetHashCode() { return (InstalledIWads, InstalledPWads, InstalledDoom64Exe).GetHashCode(); } } } ================================================ FILE: DoomLauncher/GameStores/StoreGame.cs ================================================ using System.Collections.Generic; namespace DoomLauncher.GameStores { public class StoreGame { public readonly static StoreGame ULTIMATE_DOOM = new StoreGame(2280, 1317223010, "DOOM + DOOM II", new List { @"rerelease\doom.wad", @"rerelease\doom2.wad", @"rerelease\plutonia.wad", @"rerelease\tnt.wad", @"base\doom.wad", @"base\doom2\doom2.wad", @"base\plutonia\plutonia.wad", @"base\tnt\tnt.wad" }, new List { @"rerelease\id1.wad", @"rerelease\nerve.wad", @"rerelease\masterlevels.wad", @"rerelease\sigil.wad", @"rerelease\sigil2.wad" }, null); public readonly static StoreGame HERETIC_PLUS_HEXEN = new StoreGame(3286930, 1572667751, "Heretic + Hexen", new List { @"base\heretic\heretic.wad", @"base\hexen\hexen.wad" }, new List { @"base\hexendk\hexdd.wad" }, null); public readonly static StoreGame DOOM2 = new StoreGame(2300, null, "DOOM II", new List { "base\\doom2.wad" }, new List(), null); public readonly static StoreGame FINAL_DOOM = new StoreGame(2290, null, "Final DOOM", new List { "base\\plutonia.wad", "base\\tnt.wad" }, new List(), null); public readonly static StoreGame HERETIC = new StoreGame(2390, 1290366318, "Heretic: Shadow of the Serpent Riders", new List { "base\\heretic.wad" }, new List(), null); public readonly static StoreGame HEXEN = new StoreGame(2360, 1247951670, "Hexen: Beyond Heretic", new List { "base\\hexen.wad" }, new List(), null); public readonly static StoreGame STRIFE = new StoreGame(317040, 1432899949, "Strife: Veteran Edition", new List { "strife1.wad" }, new List(), null); public readonly static StoreGame DOOM64 = new StoreGame(1148590, 1456611261, "Doom 64", new List { "doom64.wad" }, new List(), "doom64_x64.exe"); public static readonly List GAMES_IN_PRIORITY_ORDER = new List() { ULTIMATE_DOOM, DOOM2, FINAL_DOOM, HERETIC_PLUS_HEXEN, HERETIC, HEXEN, STRIFE, DOOM64 }; public int SteamId { get; } public int? GogId { get; } public string Name { get; } public List ExpectedIWadFiles { get; } public List ExpectedPWadFiles { get; } public string ExpectedDoom64Exe { get; } private StoreGame(int steamId, int? gogId, string name, List expectedIWadFiles, List expectedPwadFiles, string expectedDoom64Exe) { SteamId = steamId; GogId = gogId; Name = name; ExpectedIWadFiles = expectedIWadFiles; ExpectedPWadFiles = expectedPwadFiles; ExpectedDoom64Exe = expectedDoom64Exe; } public override bool Equals(object obj) { if (obj is StoreGame) { StoreGame other = (StoreGame)obj; return other.SteamId == SteamId; } return false; } public override int GetHashCode() { return SteamId; } } } ================================================ FILE: DoomLauncher/GameStores/StoreGameLoader.cs ================================================ using Microsoft.Win32; using System; using System.IO; using System.Collections.Generic; using System.Linq; namespace DoomLauncher.GameStores { public static class StoreGameLoader { public delegate string GameFinder(StoreGame game); // null if not found public static GameStoreFiles LoadAllStoreGamesFromRegistry() { var gameFinders = new List { GetSteamGameFolder, GetGogGameFolder }; return LoadAllStoreGames(gameFinders); } private static string GetSteamGameFolder(StoreGame game) { var steamKey = $@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App {game.SteamId}"; return Registry.GetValue(steamKey, "InstallLocation", null)?.ToString(); } private static string GetGogGameFolder(StoreGame game) { var gogKey = $@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\GOG.com\Games\{game.GogId}"; return Registry.GetValue(gogKey, "path", null)?.ToString(); } public static GameStoreFiles LoadAllStoreGames(List gameFinders) { var gameStoreFiles = from game in StoreGame.GAMES_IN_PRIORITY_ORDER from finder in gameFinders select LoadStoreGame(game, finder(game)); return gameStoreFiles.Aggregate(GameStoreFiles.EMPTY, (a, b) => a.Combine(b)); } public static GameStoreFiles LoadStoreGame(StoreGame game, string gamePath) { // Expected IWad/PWad files are allowed to not exist; Id Software has changed the directory structure // a few times, and it's fine if we can't find something. We'll take what we can get. if (gamePath == null || !Directory.Exists(gamePath)) return GameStoreFiles.EMPTY; List installedIwads = (from iwad in game.ExpectedIWadFiles let absolutePath = Path.Combine(gamePath, iwad) where File.Exists(absolutePath) select absolutePath).ToList(); List installedPwads = (from pwad in game.ExpectedPWadFiles let absolutePath = Path.Combine(gamePath, pwad) where File.Exists(absolutePath) select absolutePath).ToList(); string installedDoom64Exe = null; if (game.ExpectedDoom64Exe != null) { var absolutePath = Path.Combine(gamePath, game.ExpectedDoom64Exe); if (File.Exists(absolutePath)) installedDoom64Exe = absolutePath; } return new GameStoreFiles(installedIwads, installedPwads, installedDoom64Exe); } } } ================================================ FILE: DoomLauncher/Handlers/AddFileType.cs ================================================ namespace DoomLauncher { enum AddFileType { GameFile, IWad } } ================================================ FILE: DoomLauncher/Handlers/ArchiveUtil.cs ================================================ using DoomLauncher.Archive.Rar; using SevenZip; using System; using System.IO; using System.IO.Compression; using System.Linq; namespace DoomLauncher { internal class ArchiveUtil { public static readonly string[] Exenstions = new string[] { ".7z", ".rar" }; private static readonly string[] CoreExtensions = new string[] { ".zip", ".7z", ".rar" }; public static bool IsTransformableToZip(string extension) => Exenstions.Contains(extension, StringComparer.OrdinalIgnoreCase); // If this is an archive that packages files together that Doom Launcher should read the contents of (".zip", ".7z", ".rar") public static bool ShouldReadPackagedArchive(string file) => CoreExtensions.Contains(Path.GetExtension(file), StringComparer.OrdinalIgnoreCase); public static bool CreateZipFrom(FileInfo fi, string tempDirectory, out FileInfo fileInfo) { fileInfo = null; try { if (fi.Extension.Equals(".7z", StringComparison.OrdinalIgnoreCase)) fileInfo = CreateZipFromSevenZip(fi, tempDirectory); else if (fi.Extension.Equals(".rar", StringComparison.OrdinalIgnoreCase)) fileInfo = CreateZipFromRar(fi, tempDirectory); if (fileInfo != null) return true; } catch { } return false; } private static FileInfo CreateZipFromSevenZip(FileInfo fi, string tempDirectory) { SevenZipBase.SetLibraryPath(Path.Combine(Util.GetInteropDirectory(), "7z.dll")); using (SevenZipExtractor extractor = new SevenZipExtractor(fi.FullName)) { DirectoryInfo dir = Directory.CreateDirectory(Path.Combine(tempDirectory, Guid.NewGuid().ToString())); extractor.ExtractArchive(dir.FullName); string zipFile = Path.Combine(tempDirectory, fi.Name.Replace(fi.Extension, ".zip")); ZipFile.CreateFromDirectory(dir.FullName, zipFile); Directory.Delete(dir.FullName, true); return new FileInfo(zipFile); } } private static FileInfo CreateZipFromRar(FileInfo fi, string tempDirectory) { using (RarArchiveReader archive = new RarArchiveReader(fi.FullName)) { DirectoryInfo dir = Directory.CreateDirectory(Path.Combine(tempDirectory, Guid.NewGuid().ToString())); var directoryEntries = archive.Entries.Where(entry => entry.IsDirectory); foreach (var dirEntry in directoryEntries) Directory.CreateDirectory(Path.Combine(dir.FullName, dirEntry.FullName)); foreach (var entry in archive.Entries) { if (entry.IsDirectory) continue; try { string path = dir.FullName; if (entry.FullName.Contains(Path.DirectorySeparatorChar) || entry.FullName.Contains(Path.AltDirectorySeparatorChar)) path = Path.Combine(dir.FullName, GetSubDirectoryPath(entry)); entry.ExtractToFile(Path.Combine(path, entry.Name), true); } catch { } } string zipFile = Path.Combine(tempDirectory, fi.Name.Replace(fi.Extension, ".zip")); if (File.Exists(zipFile)) File.Delete(zipFile); ZipFile.CreateFromDirectory(dir.FullName, zipFile); Directory.Delete(dir.FullName, true); return new FileInfo(zipFile); } } private static string GetSubDirectoryPath(IArchiveEntry entry) { int index; if (entry.FullName.Contains(Path.DirectorySeparatorChar)) index = entry.FullName.LastIndexOf(Path.DirectorySeparatorChar); else index = entry.FullName.LastIndexOf(Path.AltDirectorySeparatorChar); if (index == -1) return string.Empty; return entry.FullName.Substring(0, index); } } } ================================================ FILE: DoomLauncher/Handlers/AutoCompleteCombo.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Windows.Forms; namespace DoomLauncher.Handlers { public static class AutoCompleteCombo { public static void SetAutoCompleteCustomSource(ComboBox cmb, IEnumerable datasource, Type dataType, string property) { AutoCompleteStringCollection collection = new AutoCompleteStringCollection(); if (dataType != null) { PropertyInfo pi = dataType.GetProperty(property); collection.AddRange(datasource.Select(x => (string)pi.GetValue(x)).ToArray()); } else { collection.AddRange(datasource.Cast().ToArray()); } cmb.DataSource = datasource; cmb.AutoCompleteSource = AutoCompleteSource.CustomSource; cmb.AutoCompleteMode = AutoCompleteMode.SuggestAppend; cmb.AutoCompleteCustomSource = collection; cmb.DropDown += Cmb_DropDown; if (!datasource.Any()) cmb.Text = string.Empty; } private static void Cmb_DropDown(object sender, EventArgs e) { ((ComboBox)sender).PreviewKeyDown += Cmb_PreviewKeyDown; } private static void Cmb_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) { ComboBox cmb = (ComboBox)sender; cmb.PreviewKeyDown -= Cmb_PreviewKeyDown; if (cmb.DroppedDown) cmb.Focus(); } } } ================================================ FILE: DoomLauncher/Handlers/DemoHandler.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.IO; namespace DoomLauncher { class DemoHandler { public DemoHandler(IDataSourceAdapter adapter, LauncherPath demoDirectory) { DataAdapter = adapter; DemoDirectory = demoDirectory; } public IEnumerable HandleNewDemo(ISourcePortData sourcePort, IGameFile gameFile, string demoFile, string descriptionText) { List ret = new List(); FileInfo fi = new FileInfo(demoFile); if (gameFile.GameFileID.HasValue && fi.Exists) { fi.CopyTo(Path.Combine(DemoDirectory.GetFullPath(), fi.Name)); FileData file = new FileData(); file.FileName = fi.Name; file.GameFileID = gameFile.GameFileID.Value; file.SourcePortID = sourcePort.SourcePortID; file.FileTypeID = FileType.Demo; file.Description = descriptionText; DataAdapter.InsertFile(file); } return ret; } public IDataSourceAdapter DataAdapter { get; set; } public LauncherPath DemoDirectory { get; set; } } } ================================================ FILE: DoomLauncher/Handlers/DownloadHandler.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; namespace DoomLauncher { class DownloadHandler { private readonly List m_currentDownloads = new List(); public DownloadHandler(LauncherPath downloadDirectory, DownloadView view) { DownloadDirectory = downloadDirectory; DownloadView = view; view.DownloadCancelled += view_DownloadCancelled; } public bool IsDownloading(IGameFileDownloadable dlItem) { return m_currentDownloads.Contains(dlItem); } void view_DownloadCancelled(object sender, EventArgs e) { var cancelled = DownloadView.GetCancelledDownloads(); foreach (object obj in cancelled) { IGameFileDownloadable dlItem = obj as IGameFileDownloadable; if (dlItem != null) { dlItem.Cancel(); m_currentDownloads.Remove(dlItem); } } } public void Download(IGameFileDataSourceAdapter adapter, IGameFileDownloadable dlItem) { if (dlItem != null && !IsDownloading(dlItem)) { try { m_currentDownloads.Add(dlItem); dlItem.DownloadProgressChanged += dlItem_DownloadProgressChanged; dlItem.DownloadCompleted += dlItem_DownloadCompleted; if (DownloadView != null) DownloadView.AddDownload(dlItem, dlItem.FileName); dlItem.Download(adapter, Path.Combine(DownloadDirectory.GetFullPath(), dlItem.FileName)); } catch { //failed, nothing to do } } } private DateTime m_dtLastDowanloadUpdate = DateTime.Now; void dlItem_DownloadProgressChanged(object sender, System.Net.DownloadProgressChangedEventArgs e) { IGameFileDownloadable dlItem = sender as IGameFileDownloadable; if (DownloadView != null && dlItem != null && (e.ProgressPercentage == 100 || DateTime.Now.Subtract(m_dtLastDowanloadUpdate).TotalMilliseconds > 400)) { m_dtLastDowanloadUpdate = DateTime.Now; DownloadView.UpdateDownload(sender, e.ProgressPercentage); DownloadView.UpdateDownload(sender, string.Format("{0} - {1}/{2}MB", dlItem.FileName, Math.Round(e.BytesReceived / 1024.0 / 1024.0, 1), Math.Round(e.TotalBytesToReceive / 1024.0 / 1024.0, 1))); } } void dlItem_DownloadCompleted(object sender, AsyncCompletedEventArgs e) { IGameFileDownloadable dlItem = sender as IGameFileDownloadable; if (DownloadView != null && dlItem != null) { DownloadView.UpdateDownload(sender, string.Format("{0} ({1})", dlItem.FileName, e.Cancelled ? "Cancelled" : "Complete")); m_currentDownloads.Remove(dlItem); } } public DownloadView DownloadView { get; set; } public LauncherPath DownloadDirectory { get; set; } } } ================================================ FILE: DoomLauncher/Handlers/FIleAddResults.cs ================================================ using System.Collections.Generic; using System.Linq; namespace DoomLauncher { public class FileError { public string FileName { get; set; } public string Error { get; set; } } class FileAddResults { public List NewFiles = new List(); public List ReplacedFiles = new List(); public List Errors = new List(); public IEnumerable GetAllFiles() => NewFiles.Union(ReplacedFiles); } } ================================================ FILE: DoomLauncher/Handlers/FileLoadHandler.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Linq; namespace DoomLauncher.Handlers { public class FileLoadHandler { private enum AddFilesType { SourcePort, IWAD } private List m_iwadAdditionalFiles; private List m_sourcePortAdditionalFiles; private List m_saveAdditionalFiles = new List(); private List m_currentFiles = new List(); private List m_currentNewFiles = new List(); private readonly IDataSourceAdapter m_adapter; private readonly IGameFile m_gameFile; private readonly IGameProfile m_gameProfile; private IGameFile m_selectedIWad; private ISourcePortData m_selectedSourcePort; public FileLoadHandler(IDataSourceAdapter adapter, IGameFile gameFile) : this (adapter, gameFile, (GameFile)gameFile) { } public FileLoadHandler(IDataSourceAdapter adapter, IGameFile gameFile, IGameProfile gameProfile) { m_adapter = adapter; m_gameFile = gameFile; m_gameProfile = gameProfile; // Load gameProfile's "SettingsFiles" GameFiles from the database, // add gameFile, and set them to m_currentFiles and m_saveCurrentFiles. SetAdditionalFiles(Util.GetAdditionalFiles(m_adapter, gameProfile)); // If gameProfile has an associated IWAD, load the IWAD GameFile, load its "SettingsFiles" GameFiles, // removing those that are in its "SettingsFilesSourcePort" GameFiles. // // Otherwise load the gameProfile's "SettingsFilesIWAD" GameFiles. m_iwadAdditionalFiles = GetIWadFilesFromGameFile(gameProfile); // Load the "SettingsFilesSourcePort" GameFiles of gameProfile. m_sourcePortAdditionalFiles = Util.GetSourcePortAdditionalFiles(m_adapter, gameProfile); } private List GetIWadFilesFromGameFile(IGameProfile gameFile) { if (gameFile.IWadID.HasValue) { var gameFileIwad = m_adapter.GetGameFileIWads().FirstOrDefault(x => x.IWadID == gameFile.IWadID.Value); if (gameFileIwad != null) return GetAdditionalFiles(AddFilesType.IWAD, gameFileIwad, null); } return Util.GetIWadAdditionalFiles(m_adapter, gameFile); } public bool IsIWadFile(IGameFile gameFile) { return m_iwadAdditionalFiles.Contains(gameFile); } public bool IsSourcePortFile(IGameFile gameFile) { return m_sourcePortAdditionalFiles.Contains(gameFile); } public List GetCurrentAdditionalFiles() { return m_currentFiles.ToList(); } public List GetCurrentAdditionalNewFiles() { return m_currentNewFiles.ToList(); } public List GetIWadFiles() { return m_iwadAdditionalFiles.ToList(); } public List GetSourcePortFiles() { return m_sourcePortAdditionalFiles.ToList(); } // Reset m_currentFiles and m_saveAdditionalFiles back to the state they were in after construction. public void Reset() { SetAdditionalFiles(m_saveAdditionalFiles); } private void SetAdditionalFiles(IEnumerable gameFiles) { //In pervious versions you were not able to control the order the current file. If it doesn't exist in the list add it. if (!gameFiles.Contains(m_gameFile)) { List setFiles = new List(); setFiles.Add(m_gameFile); setFiles.AddRange(gameFiles); gameFiles = setFiles.Distinct(); } m_currentFiles = gameFiles.ToList(); m_saveAdditionalFiles = gameFiles.ToList(); } // Recalculate m_currentFiles, m_iwadPortAdditionalFiles and m_sourcePortAdditionalFiles // based on the given IWAD and SourcePort, also using the IWAD, SourcePort and results // from the previous CalculateAdditionalFiles() call. // // Also records the difference from the existing m_currentFiles, and records the two // parameters for next time. public void CalculateAdditionalFiles(IGameFile iwad, ISourcePortData sourcePort) { // if iwad is provided, load the "SettingsFile" GameFiles from sourcePort into // m_sourcePortAdditionalFiles, and unless the iwad is m_gameFile, // load the iwad's "SettingsFile" GameFiles into m_iwadPortAdditionalFiles, subtracting the iwad's "SettingsFileSourcePort" GameFiles. // Exclude m_gameFile from both. // // Otherwise clear m_iwadPortAdditionalFiles and m_sourcePortAdditionalFiles. SetExtraAdditionalFilesFromSettings(iwad, sourcePort); IGameFile lastIwad = m_selectedIWad; ISourcePortData lastSourcePort = m_selectedSourcePort; if (lastIwad == null) lastIwad = iwad; if (lastSourcePort == null) lastSourcePort = sourcePort; List gameFiles = m_currentFiles; List originalList = gameFiles.ToList(); // Get the "SettingsFile" GameFiles from iwad, subtracting its // "SettingsFileSourcePort" GameFiles, adding the "SettingsFile" GameFiles // from sourcePort and removing m_gameFile List newTypeFiles = GetAdditionalFiles(iwad, sourcePort); List oldTypeFiles = GetAdditionalFiles(lastIwad, lastSourcePort); gameFiles.RemoveAll(x => oldTypeFiles.Contains(x)); gameFiles.AddRange(newTypeFiles); // Any files that are still around from before should come first gameFiles = SortByOriginal(gameFiles, originalList); // If iwad has "SettingsFile" GameFiles that are not in "SettingsFileSourcePort", then // make sure these come first gameFiles = CheckIwadFileOrder(gameFiles, iwad, sourcePort); // Remember the calculated game files, and which ones are new m_currentFiles = gameFiles.Distinct().ToList(); m_currentNewFiles = gameFiles.Except(originalList).ToList(); // Remember the inputs for next time m_selectedIWad = iwad; m_selectedSourcePort = sourcePort; } private List CheckIwadFileOrder(List gameFiles, IGameFile iwad, ISourcePortData sourcePort) { var iwadAdditionalFiles = GetAdditionalFiles(AddFilesType.IWAD, iwad, sourcePort, removeIwad: false); if (iwadAdditionalFiles.Count == 0) return gameFiles; if (iwadAdditionalFiles[0].GameFileID == iwad.GameFileID) return gameFiles; gameFiles.RemoveAll(x => iwadAdditionalFiles.Contains(x)); return GetAdditionalFiles(AddFilesType.IWAD, iwad, sourcePort).Union(gameFiles).ToList(); } private void SetExtraAdditionalFilesFromSettings(IGameFile iwad, ISourcePortData sourcePort) { m_iwadAdditionalFiles.Clear(); m_sourcePortAdditionalFiles.Clear(); if (iwad != null) { if (!iwad.Equals(m_gameFile)) m_iwadAdditionalFiles = GetAdditionalFiles(AddFilesType.IWAD, iwad, sourcePort); m_sourcePortAdditionalFiles = GetAdditionalFiles(AddFilesType.SourcePort, iwad, sourcePort); m_iwadAdditionalFiles.Remove(m_gameFile); m_sourcePortAdditionalFiles.Remove(m_gameFile); } } private List GetAdditionalFiles(IGameFile gameIwad, ISourcePortData sourcePort) { var iwadExclude = Util.GetSourcePortAdditionalFiles(m_adapter, (GameFile)gameIwad); return GetAdditionalFiles(AddFilesType.IWAD, gameIwad, sourcePort).Except(iwadExclude) .Union(GetAdditionalFiles(AddFilesType.SourcePort, gameIwad, sourcePort)) .Except(new IGameFile[] { m_gameFile }).ToList(); } private List GetAdditionalFiles(AddFilesType type, IGameFile gameIwad, ISourcePortData sourcePort, bool removeIwad = true) { switch (type) { case AddFilesType.IWAD: if (gameIwad != null) { var ret = Util.GetAdditionalFiles(m_adapter, (GameFile)gameIwad).Except(Util.GetSourcePortAdditionalFiles(m_adapter, (GameFile)gameIwad)).ToList(); if (removeIwad) ret.Remove(gameIwad); return ret; } break; case AddFilesType.SourcePort: if (sourcePort != null) return Util.GetAdditionalFiles(m_adapter, sourcePort); break; } return new List(); } private List SortByOriginal(List gameFiles, List originalList) { List sortedList = new List(); foreach (var gameFile in originalList) { if (gameFiles.Contains(gameFile)) sortedList.Add(gameFile); } sortedList.AddRange(gameFiles.Except(sortedList)); return sortedList; } } } ================================================ FILE: DoomLauncher/Handlers/FileLoadHandlerLegacy.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Linq; namespace DoomLauncher.Handlers { public class FileLoadHandlerLegacy { private enum AddFilesType { SourcePort, IWAD } private List m_iwadAdditionalFiles = new List(); private List m_sourcePortAdditionalFiles = new List(); private List m_saveAdditionalFiles = new List(); private List m_currentFiles = new List(); private List m_currentNewFiles = new List(); private readonly IDataSourceAdapter m_adapter; private readonly IGameFile m_gameFile; private IGameFile m_selectedIWad; private ISourcePortData m_selectedSourcePort; public FileLoadHandlerLegacy(IDataSourceAdapter adapter, IGameFile gameFile) { m_adapter = adapter; m_gameFile = gameFile; SetAdditionalFiles(Util.GetAdditionalFiles(m_adapter, (GameFile)gameFile)); } public bool IsIWadFile(IGameFile gameFile) { return m_iwadAdditionalFiles.Contains(gameFile); } public bool IsSourcePortFile(IGameFile gameFile) { return m_sourcePortAdditionalFiles.Contains(gameFile); } public List GetCurrentAdditionalFiles() { return m_currentFiles.ToList(); } public List GetCurrentAdditionalNewFiles() { return m_currentNewFiles.ToList(); } public List GetIWadFiles() { return m_iwadAdditionalFiles.ToList(); } public List GetSourcePortFiles() { return m_sourcePortAdditionalFiles.ToList(); } public void Reset() { SetAdditionalFiles(m_saveAdditionalFiles); } private void SetAdditionalFiles(IEnumerable gameFiles) { //In pervious versions you were not able to control the order the current file. If it doesn't exist in the list add it. if (!gameFiles.Contains(m_gameFile)) { List setFiles = new List(); setFiles.Add(m_gameFile); setFiles.AddRange(gameFiles); gameFiles = setFiles.Distinct(); } m_currentFiles = gameFiles.ToList(); m_saveAdditionalFiles = gameFiles.ToList(); } public void CalculateAdditionalFiles(IGameFile iwad, ISourcePortData sourcePort) { SetExtraAdditionalFilesFromSettings(iwad, sourcePort); IGameFile lastIwad = m_selectedIWad; ISourcePortData lastSourcePort = m_selectedSourcePort; if (lastIwad == null) lastIwad = iwad; if (lastSourcePort == null) lastSourcePort = sourcePort; List gameFiles = m_currentFiles; List originalList = gameFiles.ToList(); List newTypeFiles = GetAdditionalFiles(iwad, sourcePort); List oldTypeFiles = GetAdditionalFiles(lastIwad, lastSourcePort); gameFiles.RemoveAll(x => oldTypeFiles.Contains(x)); gameFiles.AddRange(newTypeFiles); gameFiles = SortByOriginal(gameFiles, originalList); m_currentFiles = gameFiles.Distinct().ToList(); m_currentNewFiles = gameFiles.Except(originalList).ToList(); m_selectedIWad = iwad; m_selectedSourcePort = sourcePort; } private void SetExtraAdditionalFilesFromSettings(IGameFile iwad, ISourcePortData sourcePort) { m_iwadAdditionalFiles.Clear(); m_sourcePortAdditionalFiles.Clear(); if (iwad != null) { if (!iwad.Equals(m_gameFile)) m_iwadAdditionalFiles = GetAdditionalFiles(AddFilesType.IWAD, iwad, sourcePort); m_sourcePortAdditionalFiles = GetAdditionalFiles(AddFilesType.SourcePort, iwad, sourcePort); } } private List GetAdditionalFiles(IGameFile gameIwad, ISourcePortData sourcePort) { IEnumerable exclude = new IGameFile[] { m_gameFile }; return GetAdditionalFiles(AddFilesType.IWAD, gameIwad, sourcePort) .Union(GetAdditionalFiles(AddFilesType.SourcePort, gameIwad, sourcePort)) .Except(exclude).ToList(); } private List GetAdditionalFiles(AddFilesType type, IGameFile gameIwad, ISourcePortData sourcePort) { switch (type) { case AddFilesType.IWAD: if (gameIwad != null) return Util.GetAdditionalFiles(m_adapter, (GameFile)gameIwad); break; case AddFilesType.SourcePort: if (sourcePort != null) return Util.GetAdditionalFiles(m_adapter, sourcePort); break; } return new List(); } private List SortByOriginal(List gameFiles, List originalList) { List sortedList = new List(); foreach (var gameFile in originalList) { if (gameFiles.Contains(gameFile)) sortedList.Add(gameFile); } sortedList.AddRange(gameFiles.Except(sortedList)); return sortedList; } } } ================================================ FILE: DoomLauncher/Handlers/FileMapComparer.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; namespace DoomLauncher.Handlers { public class FileMapComparer : IComparer { public int Compare(IFileData x, IFileData y) { bool xMap = string.IsNullOrEmpty(x.Map); bool yMap = string.IsNullOrEmpty(y.Map); if (!xMap && yMap) return 0; if (xMap && !yMap) return -1; if (!xMap && yMap) return 1; return x.Map.CompareTo(y.Map); } } } ================================================ FILE: DoomLauncher/Handlers/GameFileTileManager.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { class GameFileTileManager { public event EventHandler TilesRecreated; public static GameFileTileManager Instance { get; private set; } = new GameFileTileManager(); public List Tiles = new List(); public List OldTiles = new List(); public FlowLayoutPanelDB TileLayout = new FlowLayoutPanelDB(); public int MaxItems => DataCache.Instance.AppConfiguration.ItemsPerPage; private GameFileViewFactory m_factory; public void Init(GameFileViewFactory factory) { m_factory = factory; TileLayout.Margin = new Padding(0); TileLayout.AutoScroll = true; TileLayout.CaptureArrowKeys = true; ResetLayout(); } private void ResetLayout() { OldTiles.AddRange(Tiles); TileLayout.SuspendLayout(); if (m_factory.IsUsingTileView) { GameFileTileBase testTile = m_factory.CreateTile(); if (Tiles.Count == 0 || Tiles[0].GetType() != testTile.GetType() || Tiles[0].ImageWidth != testTile.ImageWidth) RecreateLayout(); else UpdateLayout(); } TileLayout.ResumeLayout(); TilesRecreated?.Invoke(this, EventArgs.Empty); OldTiles.Clear(); } private void UpdateLayout() { if (Tiles.Count > MaxItems) { Tiles = Tiles.Take(MaxItems).ToList(); while (TileLayout.Controls.Count > MaxItems) TileLayout.Controls.RemoveAt(TileLayout.Controls.Count - 1); } else { int addCount = MaxItems - Tiles.Count; for (int i = 0; i < addCount; i++) { GameFileTileBase tile = m_factory.CreateTile(); Tiles.Add(tile); TileLayout.Controls.Add(tile); } } } private void RecreateLayout() { TileLayout.Controls.Clear(); Tiles.Clear(); for (int i = 0; i < MaxItems; i++) { GameFileTileBase tile = m_factory.CreateTile(); Tiles.Add(tile); TileLayout.Controls.Add(tile); } } public void SetMaxItems(int max) { DataCache.Instance.AppConfiguration.ItemsPerPage = max; ResetLayout(); } } } ================================================ FILE: DoomLauncher/Handlers/GameProfileUtil.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; namespace DoomLauncher.Handlers { public static class GameProfileUtil { public static IList GetAllProfiles(IDataSourceAdapter adapter, GameFile gameFile, out IList globalProfiles) { globalProfiles = adapter.GetGlobalGameProfiles().OrderBy(x => x.Name).ToList(); if (!gameFile.GameFileID.HasValue) return Array.Empty(); List profiles = globalProfiles.ToList(); profiles.Add(gameFile); profiles.AddRange(adapter.GetGameProfiles(gameFile.GameFileID.Value).OrderBy(x => x.Name)); return profiles; } public static bool IsValidProfileName(IDataSourceAdapter adapter, IGameFile gameFile, IList globalProfiles, int gameProfileId, string name, out string error) { error = null; if (string.IsNullOrEmpty(name)) { error = "A name must be entered."; return false; } if (NameExists(adapter, gameFile, globalProfiles, gameProfileId, name)) { error = "This profile name already exists."; return false; } return true; } public static bool NameExists(IDataSourceAdapter adapter, IGameFile gameFile, IList globalProfiles, int excludeGameProfileId, string name) { if (gameFile.GameFileID.HasValue) { var gameProfiles = adapter.GetGameProfiles(gameFile.GameFileID.Value) .Where(x => x.GameProfileID != excludeGameProfileId); if (FindProfileName(gameProfiles, name)) return true; } return FindProfileName(globalProfiles, name); } private static bool FindProfileName(IEnumerable profiles, string name) => name.Equals(GameFile.DefaultProfileName, StringComparison.OrdinalIgnoreCase) || profiles.Any(x => x.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase)); } } ================================================ FILE: DoomLauncher/Handlers/ImageExtensions.cs ================================================ using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IO; namespace DoomLauncher { internal static class ImageExtensions { public static bool TryFromFile(string path, out Image image) { image = null; try { if (!File.Exists(path)) return false; image = Image.FromFile(path); return true; } catch { } return false; } public static Image FromFileOrDefault(string path) { if (TryFromFile(path, out var image)) return image; return new Bitmap(1, 1, PixelFormat.Format32bppRgb); } public static Image FixedSize(this Image imgPhoto, int width, int height, Color backColor) { int sourceWidth = imgPhoto.Width; int sourceHeight = imgPhoto.Height; int sourceX = 0; int sourceY = 0; int destX = 0; int destY = 0; float nPercent; float nPercentW = width / (float)sourceWidth; float nPercentH = height / (float)sourceHeight; if (nPercentH < nPercentW) { nPercent = nPercentH; destX = Convert.ToInt16((width - (sourceWidth * nPercent)) / 2); } else { nPercent = nPercentW; destY = Convert.ToInt16((height - (sourceHeight * nPercent)) / 2); } int destWidth = (int)(sourceWidth * nPercent); int destHeight = (int)(sourceHeight * nPercent); Bitmap bmPhoto = new Bitmap(width, height, PixelFormat.Format32bppPArgb); bmPhoto.SetResolution(imgPhoto.HorizontalResolution, imgPhoto.VerticalResolution); Graphics grPhoto = Graphics.FromImage(bmPhoto); grPhoto.Clear(backColor); grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic; grPhoto.DrawImage(imgPhoto, new Rectangle(destX, destY, destWidth, destHeight), new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), GraphicsUnit.Pixel); grPhoto.Dispose(); return bmPhoto; } public static Image Rotate(this Image image, float angle) { Bitmap bm = image as Bitmap; Matrix matrixOrigin = new Matrix(); matrixOrigin.Rotate(angle); PointF[] points = { new PointF(0, 0), new PointF(bm.Width, 0), new PointF(bm.Width, bm.Height), new PointF(0, bm.Height), }; matrixOrigin.TransformPoints(points); GetPointBounds(points, out float xMin, out float xMax, out float yMin, out float yMax); int width = (int)Math.Round(xMax - xMin); int height = (int)Math.Round(yMax - yMin); Bitmap result = new Bitmap(width, height); Matrix matrixCenter = new Matrix(); matrixCenter.RotateAt(angle, new PointF(width / 2f, height / 2f)); using (Graphics gr = Graphics.FromImage(result)) { gr.InterpolationMode = InterpolationMode.High; gr.Clear(bm.GetPixel(0, 0)); gr.Transform = matrixCenter; int x = (width - bm.Width) / 2; int y = (height - bm.Height) / 2; gr.DrawImage(bm, x, y); } return result; } public static Image Resize(this Image image, int width, int height, InterpolationMode interpolationMode) { 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.InterpolationMode = interpolationMode; graphics.CompositingMode = CompositingMode.SourceCopy; graphics.CompositingQuality = CompositingQuality.HighQuality; 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; } public static Image ScaleDoomImage(this Image image) { // Check for Doom's aspect ratio and force to 1.33 like the original so the image doesn't look distored. if (image.Width / (float)image.Height != 1.6f) return image; int multiplier = image.Width / 320; return image.Resize(640 * multiplier, 480 * multiplier, InterpolationMode.NearestNeighbor); } private static void GetPointBounds(PointF[] points, out float xmin, out float xmax, out float ymin, out float ymax) { xmin = points[0].X; xmax = xmin; ymin = points[0].Y; ymax = ymin; foreach (PointF point in points) { if (xmin > point.X) xmin = point.X; if (xmax < point.X) xmax = point.X; if (ymin > point.Y) ymin = point.Y; if (ymax < point.Y) ymax = point.Y; } } } } ================================================ FILE: DoomLauncher/Handlers/InvalidFile.cs ================================================ namespace DoomLauncher { public class InvalidFile { public InvalidFile(string filename, string reason) { FileName = filename; Reason = reason; } public string FileName { get; } public string Reason { get; } } } ================================================ FILE: DoomLauncher/Handlers/LaunchData.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Linq; namespace DoomLauncher { class LaunchData { public LaunchData(IGameFile gameFile, IGameProfile gameProfile, IEnumerable additionalGameFiles) { GameFile = gameFile; GameProfile = gameProfile; if (additionalGameFiles == null) AdditionalGameFiles = new List(); else AdditionalGameFiles = additionalGameFiles.ToList(); Success = true; } public LaunchData(string errorTitle, string errorDescritpion) { Success = false; ErrorTitle = errorTitle; ErrorDescription = errorDescritpion; } public bool Success { get; set; } public string ErrorTitle { get; set; } public string ErrorDescription { get; set; } public IGameFile GameFile { get; set; } public IGameProfile GameProfile { get; set; } public List AdditionalGameFiles { get; set; } } } ================================================ FILE: DoomLauncher/Handlers/MenuConstants.cs ================================================ namespace DoomLauncher { public static class MenuConstants { public static readonly string Rename = "Rename..."; public static readonly string Utility = "Utility"; public static readonly string Resync = "Resync"; } } ================================================ FILE: DoomLauncher/Handlers/NativeMethods.cs ================================================ using System; using System.Runtime.InteropServices; namespace DoomLauncher { public class NativeMethods { public enum GWL { ExStyle = -20 } public enum LWA { ColorKey = 0x1, Alpha = 0x2 } [DllImport("user32.dll", EntryPoint = "GetWindowLong")] private static extern int GetWindowLongNative(IntPtr hWnd, GWL nIndex); [DllImport("user32.dll", EntryPoint = "SetWindowLong")] private static extern int SetWindowLongNative(IntPtr hWnd, GWL nIndex, int dwNewLong); [DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")] private static extern bool SetLayeredWindowAttributesNative(IntPtr hWnd, int crKey, int alpha, LWA dwFlags); [DllImport("user32.dll", EntryPoint= "MoveWindow", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, ExactSpelling = true, SetLastError = true)] private static extern void MoveWindowNative(IntPtr hwnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); #pragma warning disable S4200 public static int GetWindowLong(IntPtr hWnd, GWL nIndex) => GetWindowLongNative(hWnd, nIndex); public static int SetWindowLong(IntPtr hWnd, GWL nIndex, int dwNewLong) => SetWindowLongNative(hWnd, nIndex, dwNewLong); public static bool SetLayeredWindowAttributes(IntPtr hWnd, int crKey, int alpha, LWA dwFlags) => SetLayeredWindowAttributesNative(hWnd, crKey, alpha, dwFlags); public static void MoveWindow(IntPtr hwnd, int X, int Y, int nWidth, int nHeight, bool bRepaint) => MoveWindowNative(hwnd, X, Y, nWidth, nHeight, bRepaint); } } ================================================ FILE: DoomLauncher/Handlers/NewFileDetector.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace DoomLauncher { public class NewFileDetector : INewFileDetector { public NewFileDetector(string[] extensions, string directory) { Directory = directory; Extenstions = extensions; ScanSubDirectories = false; } public NewFileDetector(string[] extensions, string directory, bool scanSubDirectories) { Directory = directory; Extenstions = extensions; ScanSubDirectories = scanSubDirectories; } public void StartDetection() { BaseFiles = GetFiles(Directory); } public string[] GetNewFiles() { if (BaseFiles != null && Directory != null) { string[] baseFiles = BaseFiles.Select(x => x.FullName).ToArray(); string[] currentFiles = GetFiles(Directory).Select(x => x.FullName).ToArray(); return currentFiles.Except(baseFiles).ToArray(); } return new string[] { }; } public string[] GetModifiedFiles() { if (BaseFiles != null && Directory != null) { FileInfo[] currentFiles = GetFiles(Directory); var joinedFiles = from file in BaseFiles join currentFile in currentFiles on file.FullName equals currentFile.FullName select new { OriginalFile = file, CurrentFile = currentFile }; return (from file in joinedFiles where file.OriginalFile.LastWriteTime != file.CurrentFile.LastWriteTime || file.OriginalFile.Length != file.CurrentFile.Length select file.CurrentFile.FullName).ToArray(); } return new string[] { }; } public string[] GetDeletedFiles() { if (BaseFiles != null && Directory != null) { string[] baseFiles = BaseFiles.Select(x => x.FullName).ToArray(); string[] currentFiles = GetFiles(Directory).Select(x => x.FullName).ToArray(); return baseFiles.Except(currentFiles).ToArray(); } return new string[] { }; } private FileInfo[] GetFiles(string directory) { try { if (ScanSubDirectories) { List files = new List(); DirectoryInfo dir = new DirectoryInfo(directory); DirectoryInfo[] subDirs = dir.GetDirectories(); files.AddRange(GetFilesBase(directory)); Array.ForEach(subDirs, x => files.AddRange(GetFiles(x.FullName))); return files.ToArray(); } else { return GetFilesBase(directory); } } catch { return new FileInfo[] { }; } } private FileInfo[] GetFilesBase(string directory) { try { DirectoryInfo di = new DirectoryInfo(directory); return di.GetFiles().Where(x => Extenstions.Contains(x.Extension)).ToArray(); } catch { return new FileInfo[] { }; } } public bool ScanSubDirectories { get; set; } public string Directory { get; set; } private FileInfo[] BaseFiles { get; set; } public string[] Extenstions { get; set; } } } ================================================ FILE: DoomLauncher/Handlers/PathExtensions.cs ================================================ using System.IO; namespace DoomLauncher.Handlers { public static class PathExtensions { // Path.IsRootedPath incorrectly allows path starting with directory separated chars to be counted as rooted. // .NET Framework does not have this function so it is copied from .NET core source public static bool IsPartiallyQualified(string path) { if (path.Length < 2) { // It isn't fixed, it must be relative. There is no way to specify a fixed // path with one character (or less). return true; } if (IsDirectorySeparator(path[0])) { // There is no valid way to specify a relative path with two initial slashes or // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\ return !(path[1] == '?' || IsDirectorySeparator(path[1])); } // The only way to specify a fixed path that doesn't begin with two slashes // is the drive, colon, slash format- i.e. C:\ return !((path.Length >= 3) && (path[1] == Path.VolumeSeparatorChar) && IsDirectorySeparator(path[2]) // To match old behavior we'll check the drive character for validity as the path is technically // not qualified if you don't have a valid drive. "=:\" is the "=" file's default data stream. && IsValidDriveChar(path[0])); } internal static bool IsDirectorySeparator(char c) { return c == Path.DirectorySeparatorChar || c == Path.AltDirectorySeparatorChar; } internal static bool IsValidDriveChar(char value) { return ((value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z')); } } } ================================================ FILE: DoomLauncher/Handlers/SaveGameHandler.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.SaveGame; using DoomLauncher.SourcePort; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace DoomLauncher { class SaveGameHandler { public SaveGameHandler(IDataSourceAdapter adapter, LauncherPath savegameDirectory) { DataSourceAdapter = adapter; SaveGameDirectory = savegameDirectory; } public IEnumerable HandleNewSaveGames(ISourcePortData sourcePort, IGameFile gameFile, string[] files) { List ret = new List(); if (gameFile != null && gameFile.GameFileID.HasValue) { foreach (string file in files) { try { FileInfo fi = new FileInfo(file); string fileName = Guid.NewGuid().ToString() + fi.Extension; fi.CopyTo(Path.Combine(SaveGameDirectory.GetFullPath(), fileName)); FileData fileData = new FileData { Description = GetSaveGameName(sourcePort, fi), OriginalFileName = fi.Name, FileName = fileName, GameFileID = gameFile.GameFileID.Value, SourcePortID = sourcePort.SourcePortID, FileTypeID = FileType.SaveGame }; DataSourceAdapter.InsertFile(fileData); ret.Add(fileData); } catch { //failed, nothing to do } } } return ret; } private static string GetSaveGameName(ISourcePortData sourcePort, FileInfo fi) { ISaveGameReader reader = CreateSaveGameReader(sourcePort, fi); if (reader != null) return reader.GetName(); return fi.Name; } private static ISaveGameReader CreateSaveGameReader(ISourcePortData sourcePort, FileInfo fi) { return sourcePort.GetFlavor().CreateSaveGameReader(fi); } public void HandleUpdateSaveGames(ISourcePortData sourcePort, IGameFile gameFile, IFileData[] files) { foreach (IFileData file in files) { FileInfo fi = new FileInfo(Path.Combine(sourcePort.GetReadSavePath().GetFullPath(), file.OriginalFileName)); if (fi.Exists) { if (file.DateCreated == fi.LastWriteTime) continue; try { fi.CopyTo(Path.Combine(SaveGameDirectory.GetFullPath(), file.FileName), true); } catch { //failed, nothing to do } //check to see if the save name changed string saveName = GetSaveGameName(sourcePort, fi); if (saveName != file.Description) file.Description = saveName; file.DateCreated = fi.LastWriteTime; DataSourceAdapter.UpdateFile(file); } } } public void HandleDeleteSaveGames(string[] deletedFiles, IFileData[] previousFiles) { foreach (var file in deletedFiles) { FileInfo fi = new FileInfo(file); IFileData saveFile = previousFiles.FirstOrDefault(x => x.OriginalFileName == fi.Name); if (saveFile != null) DataSourceAdapter.DeleteFile(saveFile); } } public void CopySaveGamesToSourcePort(ISourcePortData sourcePort, IFileData[] files) { foreach (IFileData file in files) { string savePath = sourcePort.GetReadSavePath().GetFullPath(); string fileName = Path.Combine(sourcePort.GetReadSavePath().GetFullPath(), file.OriginalFileName); FileInfo fiFrom = new FileInfo(Path.Combine(SaveGameDirectory.GetFullPath(), file.FileName)); try { if (fiFrom.Exists) { DirectoryInfo di = new DirectoryInfo(Path.Combine(savePath, file.OriginalFilePath)); if (!di.Exists) di.Create(); fiFrom.CopyTo(fileName, true); } } catch { //failed, nothing to do } } } public LauncherPath SaveGameDirectory { get; set; } public IDataSourceAdapter DataSourceAdapter { get; set; } } } ================================================ FILE: DoomLauncher/Handlers/ScreenshotHandler.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; namespace DoomLauncher { public static class ScreenshotHandler { public static bool InsertScreenshot(IGameFile gameFile, MemoryStream imageStream, IEnumerable existingScreenshots, out IFileData fileData) { fileData = null; if (gameFile == null || !gameFile.GameFileID.HasValue) return false; if (existingScreenshots == null) existingScreenshots = DataCache.Instance.DataSourceAdapter.GetFiles(gameFile, FileType.Screenshot); try { string fileName = Guid.NewGuid().ToString() + ".png"; string path = Path.Combine(DataCache.Instance.AppConfiguration.ScreenshotDirectory.GetFullPath(), fileName); using (FileStream fs = new FileStream(path, FileMode.Create)) imageStream.WriteTo(fs); fileData = new FileData { FileName = fileName, GameFileID = gameFile.GameFileID.Value, SourcePortID = -1, FileTypeID = FileType.Screenshot, FileOrder = 0 }; DataCache.Instance.DataSourceAdapter.InsertFile(fileData); int order = 1; foreach (IFileData file in existingScreenshots) { file.FileOrder = order++; DataCache.Instance.DataSourceAdapter.UpdateFile(file); } ThumbnailManager.UpdateThumbnail(gameFile); } catch { //failed, nothing to do } return true; } public static IEnumerable HandleNewScreenshots(ISourcePortData sourcePort, IGameFile gameFile, string[] files) { List ret = new List(); if (gameFile == null || !gameFile.GameFileID.HasValue) return ret; foreach (string file in files) { try { FileInfo fi = new FileInfo(file); string fileName = Guid.NewGuid().ToString() + fi.Extension; fi.CopyTo(Path.Combine(DataCache.Instance.AppConfiguration.ScreenshotDirectory.GetFullPath(), fileName)); FileData fileData = new FileData { FileName = fileName, GameFileID = gameFile.GameFileID.Value, SourcePortID = sourcePort.SourcePortID, FileTypeID = FileType.Screenshot, FileOrder = short.MaxValue, }; DataCache.Instance.DataSourceAdapter.InsertFile(fileData); ret.Add(fileData); if (DataCache.Instance.AppConfiguration.DeleteScreenshotsAfterImport) File.Delete(file); } catch { //failed, nothing to do } } return ret; } public static bool FindScreenshot(IEnumerable screenshots, Image image, out MemoryStream imageStream) { // Create png image in memory and use the size to compare to existing screenshot file sizes. // This method should be accurate enough to determine if an existing screenshot is the titlepic. // This method only works with titlepics pull by Doom Laucher, existing user generated screenshots from source ports will not match. imageStream = null; long fileSize = 0; try { imageStream = new MemoryStream(); image.Save(imageStream, ImageFormat.Png); fileSize = imageStream.Length; } catch { } if (!screenshots.Any()) return false; foreach (IFileData screenshot in screenshots) { try { FileInfo fi = new FileInfo(Path.Combine(DataCache.Instance.AppConfiguration.ScreenshotDirectory.GetFullPath(), screenshot.FileName)); if (fi.Length == fileSize) return true; } catch { } } return false; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/Doom64SyncAction.cs ================================================ using DoomLauncher.Interfaces; using System.Text.RegularExpressions; namespace DoomLauncher.Handlers.Sync { public class Doom64SyncAction : ISyncAction { private static readonly Regex ClassTypeRegex = new Regex(@"classtype\s*="); // Only Doom64 files have classtype in the MAPINFO private readonly IDataSourceAdapter m_database; public Doom64SyncAction(IDataSourceAdapter database) { m_database = database; } public SyncResult ApplyToGameFile(IGameFile file, IArchiveReader reader, string[] mapInfoData) { var isDoom64 = IsDoom64Wad(mapInfoData); file.IsDoom64 = isDoom64; if (isDoom64) { var doom64Iwad = m_database.GetGameFile("doom64.zip"); if (doom64Iwad != null) { file.IWadID = doom64Iwad.IWadID; } } return SyncResult.EMPTY; } private bool IsDoom64Wad(string[] mapInfoData) { foreach (string data in mapInfoData) { MatchCollection matches = ClassTypeRegex.Matches(data); if (matches.Count > 0) return true; } return false; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/Doom64TitlePicSyncAction.cs ================================================ using DoomLauncher.Interfaces; using System.Drawing; using System.IO; using System.Linq; namespace DoomLauncher.Handlers.Sync { public class Doom64TitlePicSyncAction : ISyncAction { public SyncResult ApplyToGameFile(IGameFile gameFile, IArchiveReader reader, string[] mapInfoData) { var entry = reader.Entries.FirstOrDefault(e => e.FullName.EndsWith("Doom64_HiRes.png")); if (entry != null) { var image = Image.FromStream(new MemoryStream(entry.ReadEntry())); return SyncResult.TitlePic(gameFile, image); } return SyncResult.EMPTY; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/GameConfSyncAction.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Web.Script.Serialization; namespace DoomLauncher.Handlers.Sync { public class GameConfSyncAction : ISyncAction { private readonly IDataSourceAdapter m_database; public GameConfSyncAction(IDataSourceAdapter database) { m_database = database; } public SyncResult ApplyToGameFile(IGameFile gameFile, IArchiveReader reader, string[] mapInfoData) { var entry = reader.Entries.FirstOrDefault(e => e.Name.ToLower().Equals("gameconf")); if (entry != null) { var json = entry.ReadString(Encoding.UTF7); try { var serializer = new JavaScriptSerializer(); var gameConfData = serializer.Deserialize>(json); var data = (Dictionary)gameConfData["data"]; var title = data.ContainsKey("title") ? data["title"] as string : null; var author = data.ContainsKey("author") ? data["author"] as string : null; var description = data.ContainsKey("description") ? data["description"] as string : null; var iwad = data.ContainsKey("iwad") ? data["iwad"] as string : null; if (!string.IsNullOrEmpty(title)) gameFile.Title = title; if (!string.IsNullOrEmpty(author)) gameFile.Author = author; if (!string.IsNullOrEmpty(description)) gameFile.Description = description; if (!string.IsNullOrEmpty(iwad)) { var gameName = Path.GetFileNameWithoutExtension(iwad.ToLower()); var iwadFile = m_database.GetIWads().FirstOrDefault(x => Path.GetFileNameWithoutExtension(x.Name.ToLower()).Equals(gameName)); if (iwadFile != null) gameFile.IWadID = iwadFile.IWadID; } } catch { } } return SyncResult.EMPTY; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/GameInfoSyncAction.cs ================================================ using DoomLauncher.Interfaces; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace DoomLauncher.Handlers.Sync { /// /// https://zdoom.org/wiki/GAMEINFO /// public class GameInfoSyncAction : ISyncAction { private static readonly Regex STARTUPTITLE_REGEX = new Regex(@"\s*STARTUPTITLE\s*=\s*"".*"""); //@"\s*{0}\s*:[^=]*"; private static readonly Regex SUBTRACT_REGEX = new Regex(@"\s*STARTUPTITLE\s*=\s*"); public SyncResult ApplyToGameFile(IGameFile gameFile, IArchiveReader reader, string[] mapInfoData) { // Normally it's GAMEINFO, but I've seen GAMEINFO.txt in the wild. var entry = reader.Entries.FirstOrDefault(x => x.Name.ToLower().StartsWith("gameinfo")); if (entry != null) { var text = entry.ReadString(Encoding.UTF7); Match m = STARTUPTITLE_REGEX.Match(text); if (m.Success) { var fullStatement = m.Value; var assignment = SUBTRACT_REGEX.Match(fullStatement); if (assignment.Success) { var title = fullStatement.Substring(assignment.Length + 1, fullStatement.Length - assignment.Length - 2).Trim(); if (!string.IsNullOrEmpty(title)) gameFile.Title = title; } } } return SyncResult.EMPTY; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/ISyncAction.cs ================================================ using DoomLauncher.Interfaces; namespace DoomLauncher.Handlers.Sync { public interface ISyncAction { SyncResult ApplyToGameFile(IGameFile file, IArchiveReader reader, string[] mapInfoData); } class EmptySyncAction : ISyncAction { public SyncResult ApplyToGameFile(IGameFile gameFile, IArchiveReader reader, string[] mapInfoData) { return SyncResult.EMPTY; } } public static class SyncActionExtensions { public static ISyncAction OnlyIf(this ISyncAction action, bool condition) => condition ? action : new EmptySyncAction(); } } ================================================ FILE: DoomLauncher/Handlers/Sync/IWadTitlesSyncAction.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DoomLauncher.Handlers.Sync { public class IWadTitlesSyncAction : ISyncAction { public SyncResult ApplyToGameFile(IGameFile gameFile, IArchiveReader reader, string[] mapInfoData) { IWadInfo info = IWadInfo.GetIWadInfo(gameFile.FileName); if (info != null) gameFile.Title = info.Title; return SyncResult.EMPTY; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/IdGamesTextFileParser.cs ================================================ using System; using System.Globalization; using System.Linq; using System.Text.RegularExpressions; namespace DoomLauncher.Handlers.Sync { public class IdGamesTextFileParser { private static readonly string s_fullRegex = @"\s*{0}\s*:[^\r\n]*"; private static readonly string s_fullRegexDescription = @"\s*{0}\s*:[^=]*"; private static readonly string s_regex = @"\s*{0}\s*:\s*"; private readonly string[] m_dateParseFormats; public IdGamesTextFileParser(string[] dateParseFormats) { m_dateParseFormats = dateParseFormats.ToArray(); } public IdGamesTextInfo Parse(string text) { var title = FindValue(text, "Title", s_fullRegex, false); var author = FindValue(text, "Authors*", s_fullRegex, false); var releaseDate = ParseReleaseDate(text); var description = FindValue(text, "Description", s_fullRegexDescription, false).Replace("\r\n", "\n"); var game = FindValue(text, "Game", s_fullRegex, false); return new IdGamesTextInfo(title, author, releaseDate, description, game); } private DateTime? ParseReleaseDate(string text) { string[] dateItems = new string[] { "Date Finished", "Release date" }; string date = string.Empty; foreach (string item in dateItems) { date = FindValue(text, item, s_fullRegex, true).Replace("th", string.Empty).Trim(); if (!string.IsNullOrEmpty(date)) break; } if (!string.IsNullOrEmpty(date)) { DateTime dt; date = date.Replace(".", "/"); if (ParseDate1(date, m_dateParseFormats, out dt)) return dt; else if (ParseDate2(date, m_dateParseFormats, out dt)) return dt; else if (ParseDate3(date, m_dateParseFormats, out dt)) return dt; } return null; } private bool ParseDate1(string date, string[] dateParseFormats, out DateTime dt) { if (DateTime.TryParse(date, CultureInfo.InvariantCulture, DateTimeStyles.None, out dt)) { return true; } else if (DateTime.TryParseExact(date, dateParseFormats, CultureInfo.InvariantCulture, DateTimeStyles.None, out dt)) return true; return false; } private bool ParseDate2(string date, string[] dateParseFormats, out DateTime dt) { if (date.Length > 0 && !char.IsNumber(date.Last())) date = date.Substring(0, date.Length - 1); if (date.Length > 0 && !char.IsNumber(date.First())) date = date.Substring(1); return ParseDate1(date, dateParseFormats, out dt); } private bool ParseDate3(string date, string[] dateParseFormats, out DateTime dt) { while (date.Length > 0 && !char.IsNumber(date.Last())) date = date.Substring(0, date.Length - 1); while (date.Length > 0 && !char.IsNumber(date.First())) date = date.Substring(1); return ParseDate1(date, dateParseFormats, out dt); } private string FindValue(string text, string category, string regexFull, bool ignoreCase) { Match m = new Regex(string.Format(regexFull, category), RegexOptions.IgnoreCase).Match(text); if (m.Success) return m.Value.Substring((new Regex(string.Format(s_regex, category), RegexOptions.IgnoreCase).Match(m.Value).Value.Length)); return string.Empty; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/IdGamesTextInfo.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace DoomLauncher.Handlers.Sync { public class IdGamesTextInfo { public static readonly IdGamesTextInfo EMPTY = new IdGamesTextInfo(null, null, null, null, null); public IdGamesTextInfo(string title, string author, DateTime? releaseDate, string description, string game) { Title = title ?? ""; Author = author ?? ""; ReleaseDate = releaseDate; Description = description ?? ""; Game = game ?? ""; } public int QualityScore { get { var fields = new List() { Title, Author, ReleaseDate, Description, Game }; return fields.Where(x => x != null && !(x is string s && string.IsNullOrWhiteSpace(s))).Count(); } } // Preserve the first non null element public IdGamesTextInfo Combine(IdGamesTextInfo other) => new IdGamesTextInfo( !string.IsNullOrWhiteSpace(Title) ? Title : other.Title, !string.IsNullOrWhiteSpace(Author) ? Author : other.Author, ReleaseDate ?? other.ReleaseDate, !string.IsNullOrWhiteSpace(Description) ? Description : other.Description, !string.IsNullOrWhiteSpace(Game) ? Game : other.Game); public string Title { get; } public string Author { get; } public DateTime? ReleaseDate { get; } public string Description { get; } public string Game { get; } public override bool Equals(object obj) { return obj != null && obj is IdGamesTextInfo info && (Title, Author, ReleaseDate, Description, Game).Equals( (info.Title, info.Author, info.ReleaseDate, info.Description, info.Game)); } public override int GetHashCode() => (Title, Author, ReleaseDate, Description, Game).GetHashCode(); } } ================================================ FILE: DoomLauncher/Handlers/Sync/KnownWadsSyncAction.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Linq; namespace DoomLauncher.Handlers.Sync { public class KnownWadsSyncAction : ISyncAction { private readonly IDataSourceAdapter m_database; public KnownWadsSyncAction(IDataSourceAdapter database) { m_database = database; } public SyncResult ApplyToGameFile(IGameFile file, IArchiveReader reader, string[] mapInfoData) { var baseName = file.FileNameBase.ToLower(); switch (baseName) { case "hexdd": file.Title = "Hexen: Deathkings of the Dark Citadel"; file.IWadID = m_database.GetIWads().FirstOrDefault(iwad => iwad.FileNameBase.Equals("hexen", StringComparison.OrdinalIgnoreCase))?.IWadID; break; }; return SyncResult.EMPTY; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/MapInfoUtil.cs ================================================ using System; using System.Linq; using System.Text; namespace DoomLauncher.Handlers.Sync { public static class MapInfoUtil { private static readonly string[] MapInfoNames = new string[] { "mapinfo", "zmapinfo" }; private static readonly string[] MapInfoSubNames = new string[] { "mapinfo.", "zmapinfo." }; public static string[] GetMapInfoData(IArchiveReader reader) { var mapInfoEntries = reader.Entries.Where(IsEntryMapInfo).ToArray(); return GetArchiveEntryData(mapInfoEntries.ToArray()); } private static string[] GetArchiveEntryData(params IArchiveEntry[] entries) { string[] data = new string[entries.Length]; for (int i = 0; i < entries.Length; i++) { try { data[i] = Encoding.UTF8.GetString(entries[i].ReadEntry()); } catch { data[i] = string.Empty; } } return data; } private static bool IsEntryMapInfo(IArchiveEntry entry) { try { string entryName = entry.GetNameWithoutExtension(); foreach (string name in MapInfoNames) { if (entryName.Equals(name, StringComparison.OrdinalIgnoreCase)) return true; } foreach (string name in MapInfoSubNames) { if (entryName.StartsWith(name, StringComparison.OrdinalIgnoreCase)) return true; } return false; } catch (ArgumentException) { return false; } } } } ================================================ FILE: DoomLauncher/Handlers/Sync/MapStringSyncAction.cs ================================================ using DoomLauncher.Archive; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Web.UI.WebControls; namespace DoomLauncher.Handlers.Sync { public class MapStringSyncAction : ISyncAction { private readonly LauncherPath m_tempDirectory; private static readonly Regex MapRegex = new Regex(@"\s*map\s+\w+", RegexOptions.IgnoreCase); private static readonly Regex IncludeRegex = new Regex(@"\s*include\s+(\S+)", RegexOptions.IgnoreCase); public MapStringSyncAction(LauncherPath tempDirectory) { m_tempDirectory = tempDirectory; } public SyncResult ApplyToGameFile(IGameFile gameFile, IArchiveReader reader, string[] mapInfoData) { var maps = GetMaps(reader, mapInfoData); var mapString = string.Join(", ", maps.ToArray()); gameFile.Map = mapString; if (!string.IsNullOrEmpty(gameFile.Map)) gameFile.MapCount = gameFile.Map.Count(x => x == ',') + 1; return SyncResult.EMPTY; } private List GetMaps(IArchiveReader reader, string[] mapInfoData) { List maps = new List(); if (mapInfoData.Length > 0) { foreach (var mapInfo in mapInfoData) maps.AddRange(MapStringFromMapInfo(reader, mapInfo)); } else { maps.AddRange(MapStringFromGameFileWads(reader)); } return maps.Distinct().ToList(); } private List MapStringFromMapInfo(IArchiveReader reader, string mapInfoData) { return MapStringFromMapInfo(reader, mapInfoData, new HashSet(StringComparer.OrdinalIgnoreCase)); } private List MapStringFromMapInfo(IArchiveReader reader, string mapInfoData, ISet includedFiles) { List maps = new List(); maps.AddRange(ParseMapInfoInclude(reader, mapInfoData, includedFiles)); maps.AddRange(GetMapStringFromMapInfo(mapInfoData)); return maps; } private List ParseMapInfoInclude(IArchiveReader reader, string mapinfo, ISet includedFiles) { List maps = new List(); var includeFiles = new HashSet(includedFiles); // copy the set to avoid modifying the original MatchCollection matches = IncludeRegex.Matches(mapinfo); foreach (Match match in matches) { if (match.Groups.Count < 2) continue; string file = match.Groups[1].Value.Trim(); if (includeFiles.Contains(file)) continue; includeFiles.Add(file); var entry = reader.Entries.FirstOrDefault(x => x.FullName.Equals(file, StringComparison.OrdinalIgnoreCase)); if (entry == null) continue; string[] entryData = GetArchiveEntryData(entry); if (entryData.Length > 0) maps.AddRange(MapStringFromMapInfo(reader, entryData[0], includeFiles)); // Recurse } return maps; } private List GetMapStringFromMapInfo(string mapinfo) { MatchCollection matches = MapRegex.Matches(mapinfo); return matches.Cast().Select(x => x.Value.Trim().Substring(3).Trim()).ToList(); } private List MapStringFromGameFileWads(IArchiveReader reader) => GetIWadFilenames(reader).SelectMany(Util.GetMapStringFromWad).ToList(); private List GetIWadFilenames(IArchiveReader reader) { if (reader is WadArchiveReader wadArchive) { return new List() { wadArchive.Filename }; } else if (reader is RecursiveArchiveReader recursiveReader && recursiveReader.Root is WadArchiveReader wadArchive2) { return new List() { wadArchive2.Filename }; } else { IEnumerable wadEntries = GetEntriesByExtension(reader, ".wad"); return wadEntries.Select(entry => Util.ExtractTempFile(m_tempDirectory.GetFullPath(), entry)).ToList(); } } private static string[] GetArchiveEntryData(params IArchiveEntry[] entries) { string[] data = new string[entries.Length]; for (int i = 0; i < entries.Length; i++) { try { data[i] = Encoding.UTF8.GetString(entries[i].ReadEntry()); } catch { data[i] = string.Empty; } } return data; } private static IEnumerable GetEntriesByExtension(IArchiveReader reader, string ext) { return reader.Entries.Where(x => x.Name.Contains('.') && Path.GetExtension(x.Name).Equals(ext, StringComparison.OrdinalIgnoreCase)); } } } ================================================ FILE: DoomLauncher/Handlers/Sync/StartupImageSyncAction.cs ================================================ using DoomLauncher.Interfaces; using System.Drawing; using System.IO; using System.Linq; namespace DoomLauncher.Handlers.Sync { /// /// https://zdoom.org/wiki/Startup_lumps /// Planar image reading code adapted from https://github.com/ZDoom/gzdoom/blob/5e35ebc8fe698f86c8b0c4c98774bc397f30e7d4/src/common/textures/formats/startuptexture.cpp /// public class StartupImageSyncAction : ISyncAction { private static readonly int WIDTH = 640; private static readonly int HEIGHT = 480; private static readonly int PALETTE_LENGTH = 48; // 16 colors, 3 bytes each (RGB) private static readonly int PIXELS_LENGTH = WIDTH * HEIGHT / 2; // 4 bits per pixel private static readonly int STARTUP_LENGTH = PALETTE_LENGTH + PIXELS_LENGTH; public SyncResult ApplyToGameFile(IGameFile gameFile, IArchiveReader reader, string[] mapInfoData) { var entry = reader.Entries.FirstOrDefault(e => e.Name.ToLower().StartsWith("startup") // STARTUP, startup.dat, startup.png, startup.lmp etc && !e.Name.ToLower().EndsWith(".wad") // Not an image! && !e.Name.ToLower().StartsWith("startup0")); // Ignore Strife startup image if (entry != null) { byte[] entryBytes = entry.ReadEntry(); try { Image pngImage = new Bitmap(new MemoryStream(entryBytes), true); return SyncResult.TitlePic(gameFile, pngImage); } catch (System.Exception) { // OK it's not a PNG... let's try planar image } if (entry.Length != STARTUP_LENGTH) { return SyncResult.FailedTitlePicFile(gameFile); } Color[] palette = ReadPalette(entryBytes); Image image = ReadPlanarPalettedImage(palette, entryBytes); return SyncResult.TitlePic(gameFile, image); } return SyncResult.EMPTY; } // Each pixel is an index into the 16-length palette array. // However, this is a planar image, meaning that each index is split apart into bits, // rather than being stored as a single byte. // All the first bits of all the indices come first, then all the second bits, etc. private Image ReadPlanarPalettedImage(Color[] palette, byte[] entryBytes) { Bitmap image = new Bitmap(640, 480); int planeSize = WIDTH * HEIGHT / 8; for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH / 8; x++) { int offset = PALETTE_LENGTH + y * (WIDTH / 8) + x; byte p0 = entryBytes[offset]; byte p1 = entryBytes[offset + planeSize]; byte p2 = entryBytes[offset + 2 * planeSize]; byte p3 = entryBytes[offset + 3 * planeSize]; for (int bit = 0; bit < 8; bit++) { int mask = 1 << (7 - bit); int colorIndex = ((p0 & mask) != 0 ? 1 : 0) | ((p1 & mask) != 0 ? 2 : 0) | ((p2 & mask) != 0 ? 4 : 0) | ((p3 & mask) != 0 ? 8 : 0); image.SetPixel(x * 8 + bit, y, palette[colorIndex]); } } } return image; } private Color[] ReadPalette(byte[] entryBytes) { Color[] palette = new Color[16]; for (int i = 0; i < 16; i++) { int r = entryBytes[i * 3 + 0] * 255 / 63; int g = entryBytes[i * 3 + 1] * 255 / 63; int b = entryBytes[i * 3 + 2] * 255 / 63; palette[i] = Color.FromArgb(r, g, b); } return palette; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/SyncResult.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Drawing; using System.Linq; namespace DoomLauncher.Handlers.Sync { public class SyncResult { public static readonly SyncResult EMPTY = new SyncResult(new List(), new List(), new List(), new Dictionary(), new List()); public List AddedGameFiles { get; } public List UpdatedGameFiles { get; } public List InvalidFiles { get; } public Dictionary TitlePics { get; } public List FailedTitlePicFiles { get; } public bool Failed => InvalidFiles.Count > 0; public List AddedOrUpdatedFiles { get => AddedGameFiles.Union(UpdatedGameFiles).ToList(); } private SyncResult(List addedGameFiles, List updatedGameFiles, List invalidFiles, Dictionary titlePics, List failedTitlePicFiles) { AddedGameFiles = new List(addedGameFiles); UpdatedGameFiles = new List(updatedGameFiles); InvalidFiles = new List(invalidFiles); TitlePics = titlePics; FailedTitlePicFiles = new List(failedTitlePicFiles); } public static SyncResult operator +(SyncResult a, SyncResult b) => a.Combine(b); public SyncResult Combine(SyncResult other) { return new SyncResult( addedGameFiles: CombineLists(AddedGameFiles, other.AddedGameFiles), updatedGameFiles: CombineLists(UpdatedGameFiles, other.UpdatedGameFiles), invalidFiles: CombineLists(InvalidFiles, other.InvalidFiles), titlePics: CombineDictionariesKeepLatest(TitlePics, other.TitlePics), failedTitlePicFiles: CombineLists(FailedTitlePicFiles, other.FailedTitlePicFiles)); } public bool GetTitlePic(IGameFile gameFile, out Image image) => TitlePics.TryGetValue(gameFile, out image); public static SyncResult AddedGameFile(IGameFile gameFile) { return new SyncResult( addedGameFiles: new List() { gameFile }, updatedGameFiles: new List(), invalidFiles: new List(), titlePics: new Dictionary(), failedTitlePicFiles: new List()); } public static SyncResult UpdatedGameFile(IGameFile gameFile) { return new SyncResult( addedGameFiles: new List(), updatedGameFiles: new List() { gameFile }, invalidFiles: new List(), titlePics: new Dictionary(), failedTitlePicFiles: new List()); } public static SyncResult InvalidFile(string filename, string reason) { return new SyncResult( addedGameFiles: new List(), updatedGameFiles: new List(), invalidFiles: new List() { new InvalidFile(filename, reason) }, titlePics: new Dictionary(), failedTitlePicFiles: new List()); } public static SyncResult TitlePic(IGameFile gameFile, Image image) { return new SyncResult( addedGameFiles: new List(), updatedGameFiles: new List() , invalidFiles: new List(), titlePics: new Dictionary() { { gameFile, image } }, failedTitlePicFiles: new List()); } public static SyncResult FailedTitlePicFile(IGameFile gameFile) { return new SyncResult( addedGameFiles: new List(), updatedGameFiles: new List(), invalidFiles: new List(), titlePics: new Dictionary(), failedTitlePicFiles: new List() { gameFile }); } private static List CombineLists(List ourList, List otherList) { var newList = new List(ourList.Count + otherList.Count); newList.AddRange(ourList); newList.AddRange(otherList); return newList.Distinct().ToList(); } // Keep the existing key/values private static Dictionary CombineDictionariesKeepLatest(Dictionary ourDict, Dictionary otherDict) { var combinedDictionary = new Dictionary(otherDict); foreach (var key in ourDict.Keys) { if (!combinedDictionary.ContainsKey(key)) combinedDictionary[key] = ourDict[key]; } return combinedDictionary; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/TextFileSyncAction.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Linq; using System.Text; using System.IO; namespace DoomLauncher.Handlers.Sync { public class TextFileSyncAction : ISyncAction { private readonly Func m_parseTextFile; public TextFileSyncAction(Func parseTextFile) { m_parseTextFile = parseTextFile; } public SyncResult ApplyToGameFile(IGameFile gameFile, IArchiveReader reader, string[] mapInfoData) { var textInfos = from entry in reader.Entries where isTxtFile(entry.FullName) || entry.FullName.ToLower().Equals("wadinfo") let info = m_parseTextFile(entry.ReadString(Encoding.UTF7)) orderby info.QualityScore descending select info; var bestInfo = textInfos.Aggregate(IdGamesTextInfo.EMPTY, (a, b) => a.Combine(b)); if (!string.IsNullOrEmpty(bestInfo.Title)) gameFile.Title = bestInfo.Title; if (!string.IsNullOrEmpty(bestInfo.Author)) gameFile.Author = bestInfo.Author; if (bestInfo.ReleaseDate != null) gameFile.ReleaseDate = bestInfo.ReleaseDate; if (!string.IsNullOrEmpty(bestInfo.Description)) gameFile.Description = bestInfo.Description; if (string.IsNullOrWhiteSpace(gameFile.Title)) gameFile.Title = GetUserFriendlyFilename(gameFile.FileNameNoPath); return SyncResult.EMPTY; } private string GetUserFriendlyFilename(string filename) { var words = Path.GetFileNameWithoutExtension(filename).Replace("_", " ").Replace("-", " ").Split(); var capitalisedWords = words.Select(word => string.Concat(word[0].ToString().ToUpper(), word.Substring(1))); return string.Join(" ", capitalisedWords.ToArray()); } private bool isTxtFile(string filename) { try { return Path.GetExtension(filename).Equals(".txt", StringComparison.OrdinalIgnoreCase); } catch (Exception) { return false; // Path.GetExtension is a bit of a stickler, but we just need yes or no. } } } } ================================================ FILE: DoomLauncher/Handlers/Sync/TitlePicSyncAction.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Text.RegularExpressions; using WadReader; namespace DoomLauncher.Handlers.Sync { public class TitlePicSyncAction : ISyncAction { private const string DoomTitlepicName = "TITLEPIC"; private const string HereticHexenTitlepicName = "TITLE"; private static readonly Regex TitlePageRegex = new Regex(@"titlepage\s*=\s*""([^""]*)"""); private readonly Palette m_doomPalette; private readonly Palette m_hexenPalette; private readonly Palette m_hereticPalette; private readonly IDataSourceAdapter m_database; public TitlePicSyncAction(IDataSourceAdapter database, Palette doomPalette, Palette hexenPalette, Palette hereticPalette) { m_doomPalette = doomPalette; m_hexenPalette = hexenPalette; m_hereticPalette = hereticPalette; m_database = database; } public SyncResult ApplyToGameFile(IGameFile file, IArchiveReader reader, string[] mapInfoData) { string titlepicName = DoomTitlepicName; if (GetTitlepicNameFromMapInfo(mapInfoData, out string newTitlepicName)) titlepicName = newTitlepicName; Palette palette = null; if (!TitlePicUtil.GetEntry(reader, titlepicName, out IArchiveEntry entry)) { if (TitlePicUtil.GetEntry(reader, HereticHexenTitlepicName, out entry)) { var iwadFileName = m_database.GetIWads().FirstOrDefault(iw => iw.IWadID == file.IWadID)?.FileNameBase; if (iwadFileName != null && iwadFileName.Equals("hexen", StringComparison.OrdinalIgnoreCase)) palette = m_hexenPalette; else palette = m_hereticPalette; } else { return SyncResult.EMPTY; } } palette = palette ?? GetPaletteOrDefault(file, reader); if (!TitlePicUtil.ConvertToImage(entry.ReadEntry(), palette, out Image image)) { return SyncResult.FailedTitlePicFile(file); } return SyncResult.TitlePic(file, image); } private bool GetTitlepicNameFromMapInfo(string[] mapInfoData, out string newTitlepicName) { newTitlepicName = string.Empty; foreach (string data in mapInfoData) { Match match = TitlePageRegex.Match(data); if (!match.Success) continue; newTitlepicName = match.Groups[1].Value; return true; } return false; } private Palette GetPaletteOrDefault(IGameFile gameFile, IArchiveReader reader) { if (!TitlePicUtil.FindPalette(reader, out IArchiveEntry paletteEntry)) { return m_doomPalette; } Palette palette = Palette.From(paletteEntry.ReadEntry()); if (palette != null) return palette; return m_doomPalette; } } } ================================================ FILE: DoomLauncher/Handlers/Sync/TitlePicUtil.cs ================================================ using System; using System.Drawing; using System.IO; using System.Linq; using WadReader; namespace DoomLauncher.Handlers.Sync { public static class TitlePicUtil { public static bool FindPalette(IArchiveReader archive, out IArchiveEntry entry) => GetEntry(archive, "PLAYPAL", out entry); public static bool GetEntry(IArchiveReader archive, string name, out IArchiveEntry entry) { try { if (archive.EntriesHaveExtensions) entry = archive.Entries.FirstOrDefault(x => Path.GetFileNameWithoutExtension(x.Name).Equals(name, StringComparison.OrdinalIgnoreCase)); else entry = archive.Entries.FirstOrDefault(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); } catch (Exception) { entry = null; return false; } return entry != null; } public static bool ConvertToImage(byte[] data, Palette palette, out Image image) { try { if (IsPng(data) || IsJpg(data) || IsBmp(data)) { image = new Bitmap(new MemoryStream(data), true); return true; } DoomImage doomImage; if (PaletteReaders.LikelyFlat(data)) doomImage = PaletteReaders.ReadFlat(data); else doomImage = PaletteReaders.ReadColumn(data); if (palette != null && doomImage != null) doomImage = doomImage.PaletteToArgb(palette); image = doomImage?.Bitmap; return image != null; } catch { image = null; return false; } } private static bool IsPng(byte[] data) { return data.Length > 8 && data[0] == 137 && data[1] == 'P' && data[2] == 'N' && data[3] == 'G'; } private static bool IsJpg(byte[] data) { return data.Length > 10 && data[0] == 0xFF && data[1] == 0xD8; } private static bool IsBmp(byte[] data) { return data.Length > 14 && data[0] == 'B' && data[1] == 'M'; } } } ================================================ FILE: DoomLauncher/Handlers/SyncFileData.cs ================================================ namespace DoomLauncher { class SyncFileData { public SyncFileData(string filename) { FileName = filename; } public string FileName { get; set; } public bool Selected { get; set; } } } ================================================ FILE: DoomLauncher/Handlers/SyncFileOption.cs ================================================ namespace DoomLauncher { enum SyncFileOption { None, Add, Delete } } ================================================ FILE: DoomLauncher/Handlers/SyncLibraryHandler.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; using DoomLauncher.Handlers.Sync; using DoomLauncher.Archive; using DoomLauncher.Config; namespace DoomLauncher { public class SyncLibraryHandler { public delegate void SyncProgressHandler(SyncProgressEvent e); public event SyncProgressHandler SyncFileChanged; public delegate void GameFileDataNeededHandler(GameFileDataNeededEvent e); public event GameFileDataNeededHandler GameFileDataNeeded; private readonly IGameFileDataSourceAdapter m_dbDataSource; private readonly IGameFileDataSourceAdapter m_syncDataSource; private readonly FileManagement m_fileManagement; private readonly List m_syncActions; private readonly IDirectoriesConfiguration m_directories; public SyncLibraryHandler(IGameFileDataSourceAdapter dbDataSource, IGameFileDataSourceAdapter syncDataSource, IDirectoriesConfiguration directories, FileManagement fileManagement, List syncActions) { m_dbDataSource = dbDataSource; m_syncDataSource = syncDataSource; m_syncActions = syncActions; m_fileManagement = fileManagement; m_directories = directories; } public SyncResult SyncManyFiles(string[] files) { int syncFileCurrent = 0; SyncResult result = SyncResult.EMPTY; foreach (string fileName in files) { SyncFileChanged?.Invoke(new SyncProgressEvent(fileName, syncFileCurrent, files.Length)); result += SyncFile(fileName); syncFileCurrent++; } return result; } public SyncResult SyncFile(string fileName) { SyncResult resultSoFar = PrepareGameFileForUpdate(fileName, out var existingFile, out var fileToUpdate); if (resultSoFar.Failed) return resultSoFar; GameFileDataNeeded?.Invoke(new GameFileDataNeededEvent(fileToUpdate)); try { using (IArchiveReader reader = new RecursiveArchiveReader(CreateRootArchiveReader(fileToUpdate), CreateBranchArchiveReader)) { resultSoFar += PopulateGameFileFromArchive(fileToUpdate, reader); } } catch (IOException) { fileToUpdate.Map = string.Empty; resultSoFar += SyncResult.InvalidFile(fileName, "File is in use/Not found"); } catch (InvalidDataException) { fileToUpdate.Map = string.Empty; resultSoFar += SyncResult.InvalidFile(fileName, "Zip archive invalid or contained an improper pk3"); } catch (Exception ex) { fileToUpdate.Map = string.Empty; var errorMsg = string.Concat("Unexpected exception - ", ex.Message, ex.StackTrace); resultSoFar += SyncResult.InvalidFile(fileName, errorMsg); } resultSoFar += Upsert(existingFile, fileToUpdate); return resultSoFar; } private SyncResult PrepareGameFileForUpdate(string fileName, out IGameFile existingFile, out IGameFile fileToUpdate) { var resultSoFar = SyncResult.EMPTY; fileToUpdate = m_syncDataSource.GetGameFile(fileName); existingFile = m_dbDataSource.GetGameFile(fileName); // If we've already got a copy in the DB, modify that one if (existingFile != null) fileToUpdate = existingFile; if (fileToUpdate == null) { // Delete the file from managed storage try { FileInfo fileDelete = new FileInfo(Path.Combine(m_directories.GameFileDirectory.GetFullPath(), fileName)); if (fileDelete.Exists) fileDelete.Delete(); } catch { //delete failed, just keep going } return SyncResult.InvalidFile(fileName, "Not found"); } if (m_fileManagement == FileManagement.Unmanaged) fileToUpdate.FileName = LauncherPath.GetRelativePath(fileName); fileToUpdate.Downloaded = existingFile == null ? DateTime.Now : existingFile.Downloaded; return SyncResult.EMPTY; } private SyncResult Upsert(IGameFile existingFile, IGameFile fileToUpdate) { if (existingFile == null) { m_dbDataSource.InsertGameFile(fileToUpdate); IGameFile gameFile = m_dbDataSource.GetGameFile(fileToUpdate.FileName); return (gameFile != null) ? SyncResult.AddedGameFile(gameFile) : SyncResult.EMPTY; } else { m_dbDataSource.UpdateGameFile(fileToUpdate, Util.DefaultGameFileUpdateFields); return SyncResult.UpdatedGameFile(fileToUpdate); } } private SyncResult PopulateGameFileFromArchive(IGameFile gameFile, IArchiveReader reader) { string[] mapInfoData = MapInfoUtil.GetMapInfoData(reader); var syncResults = m_syncActions.Select(frag => frag.ApplyToGameFile(gameFile, reader, mapInfoData)); return syncResults.Aggregate(SyncResult.EMPTY, (a, b) => a + b); } private IArchiveReader CreateRootArchiveReader(IGameFile file) { if (m_fileManagement == FileManagement.Unmanaged) return ArchiveReader.Create(new LauncherPath(file.FileName).GetFullPath()); return ArchiveReader.Create(Path.Combine(m_directories.GameFileDirectory.GetFullPath(), file.FileName)); } private IArchiveReader CreateBranchArchiveReader(IArchiveEntry entry) { if (IsRecursiveEntry(entry)) { string extractedFile = Util.ExtractTempFile(m_directories.TempDirectory.GetFullPath(), entry); return ArchiveReader.Create(extractedFile); } else { return null; } } private bool IsRecursiveEntry(IArchiveEntry entry) { List recursiveExtensions = new List(Util.GetReadablePkExtensions()).Append(".wad").ToList(); return entry.Name.Contains('.') && recursiveExtensions.Exists(ext => ext.Equals(Path.GetExtension(entry.Name), StringComparison.OrdinalIgnoreCase)); } public class SyncProgressEvent { public string CurrentSyncFileName { get; } public int SyncFileCurrent { get; } public int SyncFileCount { get; } public SyncProgressEvent(string currentSyncFileName, int syncFileCurrent, int syncFileCount) { CurrentSyncFileName = currentSyncFileName; SyncFileCurrent = syncFileCurrent; SyncFileCount = syncFileCount; } } public class GameFileDataNeededEvent { public IGameFile CurrentGameFile { get; } public GameFileDataNeededEvent(IGameFile currentGameFile) { CurrentGameFile = currentGameFile; } } } } ================================================ FILE: DoomLauncher/Handlers/TabHandler.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Windows.Forms; namespace DoomLauncher { class TabHandler { private readonly Dictionary m_tabLookup = new Dictionary(); private readonly List m_tabs = new List(); private class TabItem { public ITabView TabView { get; set; } public TabPage TabPage { get; set; } } public TabHandler(CTabControl tabControl) { TabControl = tabControl; } public void SetTabs(IEnumerable tabs) { m_tabLookup.Clear(); foreach (ITabView tab in tabs) AddTab(tab); } private TabPage CreateTabPage(ITabView tab) { TabPage page = new TabPage(tab.Title); page.Name = tab.Key.ToString(); if (tab is Control ctrl) { page.Controls.Add(ctrl); ctrl.Dock = DockStyle.Fill; } return page; } public void AddTab(ITabView tab) { TabPage page = CreateTabPage(tab); m_tabLookup.Add(tab.GameFileViewControl, new TabItem { TabView = tab, TabPage = page }); m_tabs.Add(tab); TabControl.TabPages.Add(page); } public void InsertTab(int index, ITabView tab) { TabPage page = CreateTabPage(tab); TabControl.TabPages.Insert(index, page); m_tabLookup.Add(tab.GameFileViewControl, new TabItem { TabView = tab, TabPage = page }); m_tabs.Insert(index, tab); } public void RemoveTab(ITabView tab) { if (m_tabLookup.ContainsKey(tab.GameFileViewControl)) { TabItem item = m_tabLookup[tab.GameFileViewControl]; m_tabLookup.Remove(tab.GameFileViewControl); m_tabs.Remove(item.TabView); TabControl.TabPages.Remove(item.TabPage); } } public int GetTabIndex(ITabView tab) { int index = 0; foreach (var item in m_tabLookup.Values) { if (item.TabView == tab) return index; index++; } return -1; } public void SetTabIndex(int index, ITabView tab) { if (m_tabLookup.ContainsKey(tab.GameFileViewControl)) { TabItem item = m_tabLookup[tab.GameFileViewControl]; TabControl.TabPages.Remove(item.TabPage); if (index < TabControl.TabPages.Count) TabControl.TabPages.Insert(index, item.TabPage); else TabControl.TabPages.Add(item.TabPage); } } public void UpdateTabTitle(ITabView tab, string text) { tab.Title = text; if (m_tabLookup.ContainsKey(tab.GameFileViewControl)) { TabItem item = m_tabLookup[tab.GameFileViewControl]; item.TabPage.Text = text; } } public ITabView TabViewForControl(IGameFileView ctrl) { if (m_tabLookup.ContainsKey(ctrl)) return m_tabLookup[ctrl].TabView; return null; } public ITabView TabViewForTag(ITagData tag) { foreach (var item in m_tabLookup) { if (item.Value.TabView is TagTabView tagTabView && tagTabView.TagDataSource.TagID == tag.TagID) return item.Value.TabView; } return null; } public bool SelectTabView(ITabView view) { foreach (var item in m_tabLookup) { if (item.Value.TabView == view) { TabControl.SelectedTab = item.Value.TabPage; return true; } } return false; } public void SelectTabFromKey(string key) { foreach (var item in m_tabLookup) { if (item.Value.TabView.Key.Equals(key)) { TabControl.SelectedTab = item.Value.TabPage; break; } } } public CTabControl TabControl { get; private set; } public ITabView[] TabViews { get { return m_tabs.ToArray(); } } } } ================================================ FILE: DoomLauncher/Handlers/TabKeys.cs ================================================ namespace DoomLauncher { public static class TabKeys { public static readonly string RecentKey = "Recent"; public static readonly string LocalKey = "Local"; public static readonly string IWadsKey = "IWads"; public static readonly string IdGamesKey = "Id Games"; public static readonly string UntaggedKey = "Untagged"; public static string[] KeyNames => new string[] { RecentKey, LocalKey, UntaggedKey, IWadsKey, IdGamesKey }; } } ================================================ FILE: DoomLauncher/Handlers/ThumbnailManager.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; namespace DoomLauncher { public static class ThumbnailManager { public static List IWads = new List(); public static readonly Dictionary IWadTileImages = new Dictionary(); public static void SetIWads(List iwads) { IWads = iwads; IWadTileImages.Clear(); foreach (var iwad in iwads) { if (!iwad.IWadID.HasValue || !IWadInfo.TryGetIWadInfo(iwad.FileName, out var iwadInfo) || string.IsNullOrEmpty(iwadInfo.TileImage) || !File.Exists(iwadInfo.TileImage)) continue; IWadTileImages[iwad.IWadID.Value] = new FileData() { GameFileID = iwad.GameFileID.Value, FileName = iwadInfo.TileImage, FileTypeID = FileType.TileImage, SourcePortID = 0 }; } } public static void UpdateThumbnail(IGameFile gameFile) { bool delete = false; var screenshot = DataCache.Instance.DataSourceAdapter.GetFiles(gameFile, FileType.Screenshot).FirstOrDefault(); var thumbnail = DataCache.Instance.DataSourceAdapter.GetFiles(gameFile, FileType.Thumbnail).FirstOrDefault(); // All screenshots for this game file were deleted if (thumbnail != null && screenshot == null) delete = true; // The first screenshot was changed if (thumbnail != null && screenshot != null && thumbnail.SourcePortID != screenshot.FileID) delete = true; if (delete) { string file = Path.Combine(DataCache.Instance.AppConfiguration.ThumbnailDirectory.GetFullPath(), thumbnail.FileName); try { if (File.Exists(file)) File.Delete(file); } catch (IOException) { // File is in use, insert to delete on next startup DataCache.Instance.DataSourceAdapter.InsertCleanupFile(new CleanupFile() { FileName = file }); } DataCache.Instance.DataSourceAdapter.DeleteFile(thumbnail); } GetOrCreateThumbnail(gameFile); } // Returns or creates a new thumbnail and inserts into database if it doesn't exist // Will search screenshots and thumbnails if provided, otherwise will check from database public static IFileData GetOrCreateThumbnail(IGameFile gameFile, IEnumerable screenshots = null, IEnumerable thumbnails = null, bool checkIWad = true) { if (thumbnails == null) thumbnails = DataCache.Instance.DataSourceAdapter.GetFiles(gameFile, FileType.Thumbnail); var thumbnail = thumbnails.FirstOrDefault(x => x.GameFileID == gameFile.GameFileID.Value); if (thumbnail != null) return thumbnail; if (screenshots == null) screenshots = DataCache.Instance.DataSourceAdapter.GetFiles(gameFile, FileType.Screenshot); var screenshot = screenshots.FirstOrDefault(x => x.GameFileID == gameFile.GameFileID.Value); if (screenshot != null) { if (!TryCreateThumbnail(screenshot, out var thumbnailFile)) return null; // Store the FileID of the screenshot in SourcePortID so we can use it to keep track of screenshot re-ordering / deletes FileData fileData = new FileData() { GameFileID = gameFile.GameFileID.Value, FileName = thumbnailFile, FileTypeID = FileType.Thumbnail, SourcePortID = screenshot.FileID.Value }; DataCache.Instance.DataSourceAdapter.InsertFile(fileData); return fileData; } if (checkIWad && gameFile.IWadID.HasValue) { var iwad = IWads.FirstOrDefault(x => x.IWadID == gameFile.IWadID.Value); if (iwad == null) return null; if (iwad.GameFileID.HasValue && iwad.GameFileID == gameFile.GameFileID) { var iwadThumbnail = GetOrCreateThumbnail(iwad, checkIWad: false); if (iwadThumbnail != null) return iwadThumbnail; } if (IWadTileImages.TryGetValue(gameFile.IWadID.Value, out var fileData)) return fileData; } return null; } private static bool TryCreateThumbnail(IFileData screenshot, out string filename) { filename = string.Empty; try { var config = DataCache.Instance.AppConfiguration; string file = Path.Combine(config.ScreenshotDirectory.GetFullPath(), screenshot.FileName); if (!File.Exists(file)) return false; const int ThumbnailSize = 300; using (Image image = Image.FromFile(file)) { using (Image thumb = image.FixedSize(ThumbnailSize, GameFileTile.GetImageHeight(ThumbnailSize), Color.Black)) { filename = Guid.NewGuid().ToString() + ".png"; thumb.Save(Path.Combine(config.ThumbnailDirectory.GetFullPath(), filename), ImageFormat.Png); return true; } } } catch { return false; } } } } ================================================ FILE: DoomLauncher/Handlers/ToolTipDisplayHandler.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Drawing; using System.Timers; using System.Windows.Forms; namespace DoomLauncher { enum ToolTipState { Waiting, BeforeShow, Showing } class ToolTipDisplayHandler { private readonly System.Timers.Timer m_toolTipTimer; private readonly ToolTip m_toolTip; private readonly MainForm m_form; private IGameFile m_gameFile; private Point m_showPoint = new Point(); private Point m_lastPollPoint = new Point(); private ToolTipState m_state = ToolTipState.Waiting; private const int TimerDelay = 500; private const int Range = 16; public ToolTipDisplayHandler(MainForm form) { m_form = form; m_toolTip = new ToolTip(); m_toolTip.BackColor = ColorTheme.Current.TextBoxBackground; m_toolTip.ForeColor = ColorTheme.Current.Text; m_toolTip.OwnerDraw = true; m_toolTip.Draw += ToolTip_Draw; m_toolTipTimer = new System.Timers.Timer(TimerDelay); m_toolTipTimer.Elapsed += ToolTipTimer_Elapsed; } public void RegisterView(IGameFileView view) { view.GameFileEnter += View_GameFileEnter; view.GameFileLeave += View_GameFileLeave; view.SelectionChange += View_SelectionChange; view.ItemClick += View_ItemClick; } private void View_ItemClick(object sender, EventArgs e) { ResetToolTipAndClear(); } private void View_SelectionChange(object sender, EventArgs e) { if (m_state == ToolTipState.Showing) ResetToolTipAndClear(); } private void ToolTipTimer_Elapsed(object sender, ElapsedEventArgs e) { m_lastPollPoint = GetMouseLocation(); if (m_state == ToolTipState.Showing) { if (!MouseMoveInRange(m_showPoint)) ResetToolTipAndClear(); } else if (m_state == ToolTipState.Waiting) { if (MouseMoveInRange(m_lastPollPoint) && m_form.ShouldShowToolTip && m_form.InvokeRequired) m_form.Invoke(new Action(ShowToolTip)); } } private void ShowToolTip() { if (m_gameFile == null) return; if (!DataCache.Instance.AppConfiguration.ShowTooltip) return; ToolTipHandler toolTipHandler = new ToolTipHandler(); m_showPoint = GetMouseLocation(); m_toolTip.Show(toolTipHandler.GetToolTipText(m_form.Font, m_gameFile), m_form, m_showPoint.X, m_showPoint.Y, 32767); m_state = ToolTipState.Showing; } private Point GetMouseLocation() { return new Point(Cursor.Position.X - m_form.Location.X + 1, Cursor.Position.Y - m_form.Location.Y + 16); } private void View_GameFileEnter(object sender, GameFileEventArgs e) { if (m_gameFile != e.GameFile) { m_toolTip.Hide(m_form); m_gameFile = e.GameFile; m_toolTipTimer.Interval = TimerDelay; m_toolTipTimer.Start(); m_state = ToolTipState.Waiting; } } private void View_GameFileLeave(object sender, GameFileEventArgs e) { ResetToolTipAndClear(); m_gameFile = null; } private void ResetToolTipAndClear() { if (m_form.InvokeRequired) { m_form.Invoke(new Action(ResetToolTipAndClear)); } else { m_toolTip.Hide(m_form); m_state = ToolTipState.BeforeShow; m_gameFile = null; m_showPoint = new Point(); } } private bool MouseMoveInRange(in Point pt) { Rectangle rect = new Rectangle(pt, new Size(Range, Range)); rect.Offset(-Range / 2, -Range / 2); return rect.Contains(GetMouseLocation()); } private void ToolTip_Draw(object sender, DrawToolTipEventArgs e) { e.DrawBackground(); e.DrawBorder(); e.DrawText(TextFormatFlags.WordBreak); } } } ================================================ FILE: DoomLauncher/Handlers/ToolTipGroup.cs ================================================ using System.Collections.Generic; using System.Windows.Forms; namespace DoomLauncher.Handlers { public class ToolTipGroup { private readonly List<(ToolTip, Control)> m_toolTips = new List<(ToolTip, Control)>(); public void SetToolTip(Control control, string caption) { var tooltip = new ToolTip(); tooltip.SetToolTip(control, " " + caption); tooltip.BackColor = ColorTheme.Current.TextBoxBackground; tooltip.ForeColor = ColorTheme.Current.Text; tooltip.OwnerDraw = true; tooltip.Draw += ToolTip_Draw; tooltip.Popup += ToolTip_Popup; m_toolTips.Add((tooltip, control)); } private void ToolTip_Popup(object sender, PopupEventArgs e) { foreach (var item in m_toolTips) { if (ReferenceEquals(sender, item.Item1)) continue; item.Item1.Hide(item.Item2); } } private static void ToolTip_Draw(object sender, DrawToolTipEventArgs e) { e.DrawBackground(); e.DrawBorder(); e.DrawText(TextFormatFlags.WordBreak); } } } ================================================ FILE: DoomLauncher/Handlers/ToolTipHandler.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Drawing; using System.Text; using System.Windows.Forms; namespace DoomLauncher { class ToolTipHandler { public string GetToolTipText(Font font, IGameFile item) { StringBuilder sb = new StringBuilder(); if (!string.IsNullOrEmpty(item.FileName)) { sb.Append("File: "); sb.Append(item.FileName); sb.Append(Environment.NewLine); sb.Append(Environment.NewLine); } sb.Append("Title: "); if (item.Title != null) sb.Append(item.Title); sb.Append(Environment.NewLine); sb.Append(Environment.NewLine); sb.Append("Author: "); if (item.Author != null) sb.Append(item.Author); sb.Append(Environment.NewLine); sb.Append(Environment.NewLine); sb.Append("Release Date: "); if (item.ReleaseDate.HasValue) sb.Append(item.ReleaseDate.Value.ToShortDateString()); else sb.Append("N/A"); sb.Append(Environment.NewLine); sb.Append(Environment.NewLine); int maxWidth = 800, maxLines = 26, lineCount = 0; try { sb.Append("Description: "); sb.Append(FormatDescritionToWidth(font, item.Description, maxWidth, maxLines, out lineCount)); maxLines -= lineCount; } catch { sb.Append(item.Description); } if (!string.IsNullOrEmpty(item.Comments) && maxLines > 0) { sb.Append(Environment.NewLine); sb.Append(Environment.NewLine); try { sb.Append("Comments: "); sb.Append(FormatDescritionToWidth(font, item.Comments, maxWidth, maxLines, out lineCount)); } catch { sb.Append(item.Comments); } } return sb.ToString(); } private string FormatDescritionToWidth(Font font, string description, int maxWidth, int maxLines, out int lineCount) { string[] lines = description.Split(new char[] { '\n' }); StringBuilder sb = new StringBuilder(); lineCount = 0; foreach (string line in lines) { if (string.IsNullOrEmpty(line)) { lineCount++; sb.Append(Environment.NewLine); continue; } string formatLine = line; int width = 0; if (!string.IsNullOrEmpty(formatLine)) width = TextRenderer.MeasureText(formatLine, font).Width; while (width > maxWidth) { string sub = TruncateLine(font, formatLine, maxWidth); sb.Append(sub.TrimStart()); sb.Append(Environment.NewLine); if (++lineCount > maxLines) break; formatLine = formatLine.Replace(sub, string.Empty); width = TextRenderer.MeasureText(formatLine, font).Width; } if (lineCount < maxLines) { sb.Append(formatLine.TrimStart()); sb.Append(Environment.NewLine); } if (++lineCount > maxLines) break; } string ret = sb.Replace("\r", string.Empty).ToString(); if (ret.Length > 1) { while (ret.Contains("\n\n\n")) ret = ret.Replace("\n\n\n", "\n\n"); while (ret.Length > 1 && ret[ret.Length - 1] == '\n') ret = ret.Substring(0, ret.Length - 1); } return ret; } private string TruncateLine(Font font, string line, int maxWidth) { for (int i = 1; i < line.Length; i++) { int test = TextRenderer.MeasureText(line.Substring(0, i), font).Width; if (test > maxWidth) { while (i > 0 && line[i] != ' ') i--; if (i == 0) return line.Substring(0, i); return line.Substring(0, i); } } return line; } } } ================================================ FILE: DoomLauncher/Handlers/Util.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using WadReader; namespace DoomLauncher { public static class Util { public static IEnumerable TableToStructure(DataTable dt, Type type) { List ret = new List(); object convertedObj; PropertyInfo[] properties = type.GetProperties().Where(x => x.GetSetMethod() != null && x.GetGetMethod() != null).ToArray(); foreach (DataRow dr in dt.Rows) { object obj = Activator.CreateInstance(type); foreach (PropertyInfo pi in properties) { Type pType = pi.PropertyType; if (pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>)) pType = pType.GetGenericArguments()[0]; if (dt.Columns.Contains(pi.Name) && ChangeType(dr[pi.Name].ToString(), pType, out convertedObj)) pi.SetValue(obj, convertedObj, null); } ret.Add(obj); } return ret; } public static bool ChangeType(string obj, Type t, out object convertedObj) { convertedObj = null; if (obj == null) return false; if (obj.GetType() == typeof(string) && t == typeof(string)) { convertedObj = obj; return true; } else if (obj.GetType() == typeof(string) && t == typeof(bool) && (obj == "0" || obj == "1")) { if (obj == "0") convertedObj = false; else convertedObj = true; return true; } else if (t.BaseType == typeof(Enum)) { convertedObj = Convert.ToInt32(obj); return true; } MethodInfo method = t.GetMethod("TryParse", new[] { typeof(string), Type.GetType(string.Format("{0}&", t.FullName)) }); if (method != null) { object[] args = new object[] { obj, convertedObj }; if ((bool)method.Invoke(null, args)) { convertedObj = args[1]; return true; } } return false; } public static List GetMapStringFromWad(string file) { List maps = new List(); try { FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); WadFileReader wadReader = new WadFileReader(fs); if (wadReader.WadType != WadType.Unknown) { var mapLumps = WadFileReader.GetMapMarkerLumps(wadReader.ReadLumps()).OrderBy(x => x.Name).ToArray(); fs.Close(); foreach (var mapLump in mapLumps) maps.Add(mapLump.Name); } else { fs.Close(); } } catch (Exception ex) { DisplayUnexpectedException(null, ex); } return maps; } public static void DisplayUnexpectedException(Form form, Exception ex) { #if DEBUG throw ex; #else if (form == null) { DisplayException(form, ex); return; } if (form.InvokeRequired) form.Invoke(new Action(DisplayUnexpectedException), form, ex); else DisplayException(form, ex); #endif } private static void DisplayException(Form form, Exception ex) { if (form != null && form.InvokeRequired) { form.Invoke(new Action(DisplayException), new object[] { form, ex }); } else { TextBoxForm txt = new TextBoxForm(); txt.Title = "Unexpected Error"; txt.HeaderText = "An unexpected error occurred. Please submit the error report by clicking the link below. The report has been copied to your clipboard." + Environment.NewLine; txt.DisplayText = ex.ToString(); txt.SetLink("Click here to submit", GitHubRepository); Clipboard.SetText(txt.DisplayText); if (form == null) { txt.ShowDialog(); } else { txt.StartPosition = FormStartPosition.CenterParent; txt.ShowDialog(form); } } } public static string GitHubRepository => $"https://github.com/{GitHubUser}/{GitHubRepositoryName}"; public static string GitHubUser => "nstlaurent"; public static string GitHubRepositoryName => "DoomLauncher"; public static string DoomworldThread => "http://www.doomworld.com/vb/doom-general/69346-doom-launcher-doom-frontend-database/"; public static string Realm667Thread => "http://realm667.com/index.php/en/kunena/doom-launcher"; public static void SetDefaultSearchFields(SearchControl ctrlSearch) { string[] filters = new string[] { "Title", "Author", "Filename", "Description", }; ctrlSearch.SetSearchFilters(filters); ctrlSearch.SetSearchFilter(filters[0], true); ctrlSearch.SetSearchFilter(filters[1], true); ctrlSearch.SetSearchFilter(filters[2], true); } public static GameFileSearchField[] SearchFieldsFromSearchCtrl(SearchControl ctrlSearch) { string[] items = ctrlSearch.GetSelectedSearchFilters(); List ret = new List(); GameFileFieldType type; foreach (string item in items) { if (Enum.TryParse(item, out type)) { ret.Add(new GameFileSearchField(type, GameFileSearchOp.Like, ctrlSearch.SearchText)); } } return ret.ToArray(); } public static List GetSourcePortsData(IDataSourceAdapter adapter) { List sourcePorts = adapter.GetSourcePorts().ToList(); SourcePortData noPort = new SourcePortData(); noPort.Name = "N/A"; noPort.SourcePortID = -1; sourcePorts.Insert(0, noPort); return sourcePorts; } public static string[] GetSkills() { return new string[] { "1", "2", "3", "4", "5" }; } public static string GetTimePlayedString(int minutes) { List items = new List(); TimeSpan ts = new TimeSpan(0, minutes, 0); if (ts.Days > 0) items.Add(TimeString(ts.Days, "Day")); if (ts.Hours > 0) items.Add(TimeString(ts.Hours, "Hour")); items.Add(TimeString(ts.Minutes, "Minute")); return string.Join(", ", items.ToArray()); } private static string TimeString(int time, string type) { return string.Concat(time.ToString(), " ", type, time == 1 ? string.Empty : "s"); } public static List GetAdditionalFiles(IDataSourceAdapter adapter, IGameProfile gameFile) { if (gameFile != null && !string.IsNullOrEmpty(gameFile.SettingsFiles)) return GetAdditionalFiles(adapter, gameFile.SettingsFiles); return new List(); } public static List GetIWadAdditionalFiles(IDataSourceAdapter adapter, IGameProfile gameFile) { if (gameFile != null && !string.IsNullOrEmpty(gameFile.SettingsFilesIWAD)) return GetAdditionalFiles(adapter, gameFile.SettingsFilesIWAD); return new List(); } public static List GetSourcePortAdditionalFiles(IDataSourceAdapter adapter, IGameProfile gameFile) { if (gameFile != null && !string.IsNullOrEmpty(gameFile.SettingsFilesSourcePort)) return GetAdditionalFiles(adapter, gameFile.SettingsFilesSourcePort); return new List(); } public static List GetAdditionalFiles(IDataSourceAdapter adapter, ISourcePortData sourcePort) { return GetAdditionalFiles(adapter, sourcePort.SettingsFiles); } private static List GetAdditionalFiles(IDataSourceAdapter adapter, string property) { string[] fileNames = Util.SplitString(property); List gameFiles = new List(); Array.ForEach(fileNames, x => gameFiles.Add(adapter.GetGameFile(x))); return gameFiles.Where(x => x != null).ToList(); } [Conditional("DEBUG")] public static void ThrowDebugException(string msg) { throw new Exception(msg); } public static IEnumerable GetEntriesByExtension(IArchiveReader reader, string[] extensions) { List entries = new List(); foreach (var ext in extensions) { entries.AddRange(reader.Entries .Where(x => x.Name.Contains('.') && Path.GetExtension(x.Name).Equals(ext, StringComparison.OrdinalIgnoreCase))); } return entries; } public static string[] GetPkExtenstions() { return new string[] { ".pk3", ".ipk3", ".pk7", ".zip" }; } public static string[] GetReadablePkExtensions() { return new string[] { ".pk3", ".ipk3", ".pke", ".zip" }; } public static string[] GetDehackedExtensions() { return new string[] { ".deh", ".bex" }; } public static string[] GetSourcePortPkExtensions() { return new string[] { ".pk3", ".ipk3", ".pk7", ".pke"}; } public static string[] GetExtraDoom64Extensions() { return new string[] { ".kpf" }; } public static GameFileFieldType[] DefaultGameFileUpdateFields { get { return new GameFileFieldType[] { GameFileFieldType.Author, GameFileFieldType.Title, GameFileFieldType.Description, GameFileFieldType.Downloaded, GameFileFieldType.LastPlayed, GameFileFieldType.ReleaseDate, GameFileFieldType.Comments, GameFileFieldType.Rating, GameFileFieldType.Map, GameFileFieldType.MapCount, GameFileFieldType.IWadID }; } } //Takes a file 'MAP01.wad' and makes it 'MAP01_GUID.wad'. //Checks if file with prefix MAP01 exists with same file length and returns that file (same file). //Otherwise a new file is extracted and returned. public static string ExtractTempFile(string tempDirectory, IArchiveEntry entry) { // The file is a regular file and not an archive - return the FulName if (!entry.ExtractRequired) return entry.FullName; string ext = Path.GetExtension(entry.Name); string file = entry.Name.Replace(ext, string.Empty) + "_"; string[] searchFiles = Directory.GetFiles(tempDirectory, file + "*"); string matchingFile = searchFiles.FirstOrDefault(x => new FileInfo(x).Length == entry.Length); if (matchingFile == null) { string extractFile = Path.Combine(tempDirectory, string.Concat(file, Guid.NewGuid().ToString(), ext)); entry.ExtractToFile(extractFile); return extractFile; } return matchingFile; } public static List GetIWadsDataSource(IDataSourceAdapter adapter) { List iwads = adapter.GetIWads().ToList(); iwads.ForEach(x => x.FileName = Path.GetFileNameWithoutExtension(x.FileName)); return iwads; } public static string CleanDescription(string description) { string[] items = description.Split(new char[] { '\n' }); StringBuilder sb = new StringBuilder(); foreach (string item in items) { string text = Regex.Replace(item, @"\s+", " "); if (text.StartsWith(" ")) text = text.Substring(1); sb.Append(text); sb.Append(Environment.NewLine); } return sb.ToString(); } //returns the first position after the magicID is found, else returns -1 public static long ReadAfter(MemoryStream ms, byte[] magicID) { long position = ms.Position; byte[] check = new byte[magicID.Length]; while (ms.Position + magicID.Length < ms.Length) { ms.Read(check, 0, check.Length); if (magicID.SequenceEqual(check)) return ms.Position; ms.Position = ++position; } return -1; } public static int GetPreviewScreenshotWidth(int value) { if (value > 0) return 200 + (40 * value); else return 200 + (10 * value); } public static string[] SplitString(string value) { if (!string.IsNullOrEmpty(value)) return value.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); else return new string[] { }; } public static string GetExecutableNoPath() => AppDomain.CurrentDomain.FriendlyName; public static string GetClippedEllipsesText(Graphics g, Font f, string text, SizeF layout) { int charactersFitted, linesFilled; g.MeasureString(text, f, layout, StringFormat.GenericDefault, out charactersFitted, out linesFilled); if (charactersFitted != text.Length && charactersFitted > 3) return text.Substring(0, charactersFitted - 3) + "..."; return text.Substring(0, charactersFitted); } static public SizeF MeasureDisplayString(this Graphics graphics, string text, Font font) { if (text.Length == 0) return new SizeF(0, 0); StringFormat format = new StringFormat(); RectangleF rect = new RectangleF(0, 0, 1000, 1000); CharacterRange[] ranges = { new CharacterRange(0, text.Length) }; format.SetMeasurableCharacterRanges(ranges); Region[] regions = graphics.MeasureCharacterRanges(text, font, rect, format); rect = regions[0].GetBounds(graphics); rect.Inflate(2, 2); return rect.Size; } [DllImport("user32.dll")] static extern IntPtr WindowFromPoint(WinPoint Point); [StructLayout(LayoutKind.Sequential)] public struct WinPoint { public int X; public int Y; public WinPoint(int x, int y) { X = x; Y = y; } } public static bool IsVisibleAtPoint(this Control control, Point windowPoint) { var hwnd = WindowFromPoint(new WinPoint(windowPoint.X, windowPoint.Y)); var other = Control.FromChildHandle(hwnd); if (other == null) return false; if (control == other || control.Contains(other)) return true; return false; } public static IEnumerable GetChildElements(this Control control) where T : class { foreach (Control subControl in control.Controls) { if (subControl is T subControlType) yield return subControlType; if (subControl.HasChildren) { foreach (T child in GetChildElements(subControl)) yield return child; } } } public static bool IsDirectory(string path) { if (!File.Exists(path) && !Directory.Exists(path)) return false; FileAttributes attr = File.GetAttributes(path); return (attr & FileAttributes.Directory) == FileAttributes.Directory; } public static string ReadString(this IArchiveEntry entry, Encoding encoding) => encoding.GetString(ReadEntry(entry)); public static byte[] ReadEntry(this IArchiveEntry entry) { byte[] data = new byte[entry.Length]; entry.Read(data, 0, data.Length); return data; } public static string GetDescription(this Enum value) { FieldInfo fi = value.GetType().GetField(value.ToString()); if (fi.GetCustomAttributes(typeof(DescriptionAttribute), false) is DescriptionAttribute[] attributes && attributes.Any()) return attributes.First().Description; return value.ToString(); } public static string GetInteropDirectory() { if (Environment.Is64BitOperatingSystem) return "x64"; return "x86"; } private static readonly SolidBrush RectangleBrush = new SolidBrush(Color.FromArgb(128, Color.Black)); public static void DrawImageTitleBar(string title, Rectangle paintRect, PaintEventArgs e, Brush textBrush, Font font) { if (string.IsNullOrEmpty(title)) return; DpiScale dpiScale = new DpiScale(e.Graphics); int padX = dpiScale.ScaleIntX(3); int padY = dpiScale.ScaleIntY(2); title = GetClippedEllipsesText(e.Graphics, font, title, new SizeF(paintRect.Width, font.Height)); SizeF size = e.Graphics.MeasureString(title, font); RectangleF rect = new RectangleF(0, paintRect.Height - size.Height - padY, e.ClipRectangle.Width, size.Height + padY); e.Graphics.FillRectangle(RectangleBrush, rect); e.Graphics.DrawString(title, font, textBrush, new PointF(padX, paintRect.Height - size.Height - padY)); } } } ================================================ FILE: DoomLauncher/Handlers/UtilityHandler.cs ================================================ using DoomLauncher.Adapters.Launch; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.Windows.Forms; using static DoomLauncher.SpecificFilesForm; namespace DoomLauncher { class UtilityHandler { private readonly IWin32Window m_parent; private readonly ISourcePortData m_utility; private readonly AppConfiguration m_config; public UtilityHandler(IWin32Window parent, AppConfiguration config, ISourcePortData utility) { m_parent = parent; m_config = config; m_utility = utility; } public bool RunUtility(IGameFile gameFile) { List files; if (gameFile.IsUnmanaged()) { files = new List { new SpecificFilePath() { ExtractedFile = gameFile.FileName, InternalFilePath = gameFile.FileName } }; } else if (!GetUserSelectedFiles(gameFile, out files)) { return true; } var features = new List { new UtilityFilesLaunchFeature(files), new SourcePortExtraParametersLaunchFeature() }; GameLauncher launcher = new GameLauncher(m_config, features); var launchParameters = launcher.GetLaunchParameters(gameFile, Array.Empty(), m_utility, false); if (launchParameters.Failed) return false; try { Process.Start(m_utility.GetFullExecutablePath(), launchParameters.LaunchString); } catch { return false; } return true; } private bool GetUserSelectedFiles(IGameFile gameFile, out List files) { SpecificFilesForm form = new SpecificFilesForm(); form.AutoCheckSupportedExtensions(false); form.ShowPkContentsCheckBox(true); form.Initialize(m_config.GameFileDirectory, new IGameFile[] { gameFile }, SourcePortData.GetSupportedExtensions(m_utility), new string[] { }, m_config.TempDirectory); form.StartPosition = FormStartPosition.CenterParent; if (form.ShowDialog(m_parent) != DialogResult.OK) { files = new List(); return false; } files = form.GetPathedSpecificFiles(); return true; } } } ================================================ FILE: DoomLauncher/Handlers/VersionHandler.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Handlers; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Data.Common; using System.Diagnostics; using System.IO; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public class VersionHandler { private readonly IDataSourceAdapter m_adapter; private readonly AppConfiguration m_appConfig; private bool m_restartRequired; public event EventHandler UpdateProgress; public event EventHandler UpdateComplete; public int ProgressPercent { get; private set; } public VersionHandler(DataAccess access, IDataSourceAdapter adapter, AppConfiguration appConfig) { DataAccess = access; m_adapter = adapter; m_appConfig = appConfig; } public bool UpdateRequired() { AppVersion version = GetVersion(); var latestVersion = Enum.GetValues(typeof(AppVersion)).Cast().Last(); if (version == AppVersion.Unknown || version < latestVersion) return true; return false; } private void ExecuteUpdate(Action update, AppVersion version) { if (GetVersion() < version || version == AppVersion.Unknown) { update(); if (version != AppVersion.Unknown) WriteVersion(version); } } public VersionUpdateResults HandleVersionUpdate() { m_restartRequired = false; if (UpdateRequired()) { ExecuteUpdate(Pre_0_9_2, AppVersion.Unknown); ExecuteUpdate(Pre_1_0_0, AppVersion.Version_1_0_0); ExecuteUpdate(Pre_1_1_0, AppVersion.Version_1_1_0); ExecuteUpdate(Pre_2_1_0, AppVersion.Version_2_1_0); ExecuteUpdate(Pre_2_2_0, AppVersion.Version_2_2_0); ExecuteUpdate(Pre_2_2_1, AppVersion.Version_2_2_1); ExecuteUpdate(Pre_2_3_0, AppVersion.Version_2_3_0); ExecuteUpdate(Pre_2_4_0, AppVersion.Version_2_4_0); ExecuteUpdate(Pre_2_4_1, AppVersion.Version_2_4_1); ExecuteUpdate(Pre_2_6_0, AppVersion.Version_2_6_0); ExecuteUpdate(Pre_2_6_3, AppVersion.Version_2_6_3); ExecuteUpdate(Pre_2_6_3_1, AppVersion.Version_2_6_3_1); ExecuteUpdate(Pre_2_6_3_2, AppVersion.Version_2_6_3_2); ExecuteUpdate(Pre_2_6_4_1, AppVersion.Version_2_6_4_1); ExecuteUpdate(Pre_2_6_4_1_Update, AppVersion.Version_2_6_4_1_Update1); ExecuteUpdate(Pre_2_7_0_0, AppVersion.Version_2_7_0_0); ExecuteUpdate(Pre_2_8_0_0, AppVersion.Version_2_8_0_0); ExecuteUpdate(Pre_2_8_0_0_1, AppVersion.Version_2_8_0_0_1); ExecuteUpdate(Pre_Version_3_1_0, AppVersion.Version_3_1_0); ExecuteUpdate(Pre_Version_3_2_0, AppVersion.Version_3_2_0); ExecuteUpdate(Pre_Version_3_2_0_Update1, AppVersion.Version_3_2_0_Update1); ExecuteUpdate(Pre_Version_3_2_0_Update2, AppVersion.Version_3_2_0_Update2); ExecuteUpdate(Pre_Version_3_2_0_Update3, AppVersion.Version_3_2_0_Update3); ExecuteUpdate(Pre_Version_3_3_0, AppVersion.Version_3_3_0); ExecuteUpdate(Pre_Version_3_3_1, AppVersion.Version_3_3_1); ExecuteUpdate(Pre_Version_3_3_2, AppVersion.Version_3_3_2); ExecuteUpdate(Pre_Version_3_4_0, AppVersion.Version_3_4_0); ExecuteUpdate(Pre_Version_3_4_0_Update1, AppVersion.Version_3_4_0_Update1); ExecuteUpdate(Pre_Version_3_5_2, AppVersion.Version_3_5_2); ExecuteUpdate(Pre_Version_3_5_2_Update1, AppVersion.Version_3_5_2_Update1); ExecuteUpdate(Pre_Version_3_5_2_Update2, AppVersion.Version_3_5_2_Update2); ExecuteUpdate(Pre_Version_3_5_3_Update1, AppVersion.Version_3_5_3_Update1); ExecuteUpdate(Pre_Version_3_5_3_Update2, AppVersion.Version_3_5_3_Update2); ExecuteUpdate(Pre_Version_3_5_3_Update3, AppVersion.Version_3_5_3_Update3); ExecuteUpdate(Pre_Version_3_7_0, AppVersion.Version_3_7_0); ExecuteUpdate(Pre_Version_3_7_0_Update1, AppVersion.Version_3_7_0_Update1); ExecuteUpdate(Pre_Version_3_7_4, AppVersion.Version_3_7_4); ExecuteUpdate(Pre_Version_3_7_7, AppVersion.Version_3_7_7); } return new VersionUpdateResults(m_restartRequired); } private AppVersion GetVersion() { IConfigurationData config = m_adapter.GetConfiguration().FirstOrDefault(x => x.Name == "Version"); if (config != null) return (AppVersion)Convert.ToInt32(config.Value); return AppVersion.Unknown; } private void WriteVersion(AppVersion version) { IConfigurationData config = m_adapter.GetConfiguration().FirstOrDefault(x => x.Name == "Version"); if (config != null) { config.Value = Convert.ToInt32(version).ToString(); m_adapter.UpdateConfiguration(config); } else { ConfigurationData newConfig = new ConfigurationData(); newConfig.Name = "Version"; newConfig.UserCanModify = false; newConfig.Value = Convert.ToInt32(version).ToString(); m_adapter.InsertConfiguration(newConfig); } } public void Pre_1_0_0() { DataTable dt = DataAccess.ExecuteSelect("pragma table_info(GameFiles);").Tables[0]; if (!dt.Select("name = 'SettingsMap'").Any()) { string query = @"alter table GameFiles add column 'SettingsMap' TEXT; alter table GameFiles add column 'SettingsSkill' TEXT; alter table GameFiles add column 'SettingsExtraParams' TEXT; alter table GameFiles add column 'SettingsFiles' TEXT;"; DataAccess.ExecuteNonQuery(query); } dt = DataAccess.ExecuteSelect("select * from Configuration where Name = 'ColumnConfig'").Tables[0]; if (dt.Rows.Count == 0) { string query = @"insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('SplitTopBottom', '475', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('SplitLeftRight', '680', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('AppWidth', '1024', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('AppHeight', '768', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('AppX', '0', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('AppY', '0', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('WindowState', 'Normal', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('ColumnConfig', '', '', 0);"; DataAccess.ExecuteNonQuery(query); } } public void Pre_0_9_2() { DataTable dt = DataAccess.ExecuteSelect("select name from sqlite_master where type='table' and name='Configuration';").Tables[0]; if (dt.Rows.Count == 0) { string query = @"CREATE TABLE 'Configuration' ( 'ConfigID' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 'Name' TEXT NOT NULL, 'Value' TEXT NOT NULL, 'AvailableValues' TEXT NOT NULL, 'UserCanModify' INTEGER);"; DataAccess.ExecuteNonQuery(query); query = string.Format(@"insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('IdGamesUrl', 'http://www.doomworld.com/idgames/', '', 1); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('ApiPage', 'api/api.php', '', 1); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('MirrorUrl', 'ftp://mancubus.net/pub/idgames/', 'Germany;ftp://ftp.fu-berlin.de/pc/games/idgames/;Idaho;ftp://mirrors.syringanetworks.net/idgames/;Greece;ftp://ftp.ntua.gr/pub/vendors/idgames/;Texas;ftp://mancubus.net/pub/idgames/;New York;http://youfailit.net/pub/idgames/;Florida;http://www.gamers.org/pub/idgames/;', 1); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('ScreenshotCaptureDirectories', '', '', 1); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('DateParseFormats', 'dd/M/yy;dd/MM/yyyy;dd MMMM yyyy;', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('CleanTemp', 'true', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('GameFileDirectory', '{0}', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('ScreenshotDirectory', '{1}', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('TempDirectory', '{2}', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('GameWadDirectory', '{3}', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('DemoDirectory', '{4}', '', 0); insert into Configuration (Name, Value, AvailableValues, UserCanModify) values('SaveGameDirectory', '{5}', '', 0);", ConfigurationManager.AppSettings["GameFileDirectory"], ConfigurationManager.AppSettings["ScreenshotDirectory"], ConfigurationManager.AppSettings["TempDirectory"], ConfigurationManager.AppSettings["GameWadDirectory"], ConfigurationManager.AppSettings["DemoDirectory"], ConfigurationManager.AppSettings["GameFileDirectory"] + "SaveGames\\"); DataAccess.ExecuteNonQuery(query); DirectoryInfo di = new DirectoryInfo(Path.Combine(LauncherPath.GetDataDirectory(), ConfigurationManager.AppSettings["GameFileDirectory"], "SaveGames")); if (!di.Exists) di.Create(); } dt = DataAccess.ExecuteSelect("pragma table_info(Files);").Tables[0]; if (!dt.Select("name = 'OriginalFileName'").Any()) { string query = @"alter table Files add column 'OriginalFileName' TEXT; alter table Files add column 'OriginalFilePath' TEXT;"; DataAccess.ExecuteNonQuery(query); } } public void Pre_1_1_0() { DataTable dt = DataAccess.ExecuteSelect("select name from sqlite_master where type='table' and name='Tags';").Tables[0]; //tag table update if (dt.Rows.Count == 0) { string query = @"CREATE TABLE 'Tags' ( 'TagID' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 'Name' TEXT NOT NULL, 'HasTab' INTEGER NOT NULL);"; DataAccess.ExecuteSelect(query); query = @"CREATE TABLE 'TagMapping' ( 'FileID' INTEGER NOT NULL, 'TagID' INTEGER NOT NULL, PRIMARY KEY(FileID,TagID));"; DataAccess.ExecuteSelect(query); } dt = DataAccess.ExecuteSelect("pragma table_info(Tags);").Tables[0]; if (!dt.Select("name = 'Color'").Any()) { DataAccess.ExecuteNonQuery("alter table Tags add column 'HasColor' int;"); DataAccess.ExecuteNonQuery("alter table Tags add column 'Color' int;"); } dt = DataAccess.ExecuteSelect("pragma table_info(GameFiles);").Tables[0]; //GameFile map count update if (!dt.Select("name = 'MapCount'").Any()) { CreateDatabaseBackup(); DataAccess.ExecuteNonQuery("alter table GameFiles add column 'MapCount' int;"); GameFileGetOptions options = new GameFileGetOptions(); options.SelectFields = new GameFileFieldType[] { GameFileFieldType.GameFileID, GameFileFieldType.Map }; IEnumerable gameFiles = m_adapter.GetGameFiles(options); GameFileFieldType[] updateFields = new GameFileFieldType[] { GameFileFieldType.MapCount }; float total = gameFiles.Count(); int count = 0; foreach (IGameFile gameFile in gameFiles) { if (UpdateProgress != null) { ProgressPercent = Convert.ToInt32(count / total * 100); UpdateProgress(this, new EventArgs()); } if (gameFile.Map != null) { gameFile.MapCount = gameFile.Map.Count(x => x == ',') + 1; m_adapter.UpdateGameFile(gameFile, updateFields); } count++; } } if (!dt.Select("name = 'SettingsSpecificFiles'").Any()) { DataAccess.ExecuteNonQuery("alter table GameFiles add column 'SettingsSpecificFiles' TEXT;"); } WriteVersion(AppVersion.Version_1_1_0); UpdateComplete?.Invoke(this, new EventArgs()); } [Conditional("RELEASE")] private static void CreateDatabaseBackup() { FileInfo fi = new FileInfo(DbDataSourceAdapter.DatabaseFileName); fi.CopyTo(string.Format("{0}_{1}.sqlite.bak", DbDataSourceAdapter.DatabaseFileName, Guid.NewGuid().ToString())); } private void Pre_2_1_0() { DataTable dt = DataAccess.ExecuteSelect("pragma table_info(Files);").Tables[0]; if (!dt.Select("name = 'FileOrder'").Any()) { DataAccess.ExecuteNonQuery("alter table Files add column 'FileOrder' int;"); DataAccess.ExecuteNonQuery("update Files set FileOrder = 2"); } dt = DataAccess.ExecuteSelect("pragma table_info(GameFiles);").Tables[0]; if (!dt.Select("name = 'MinutesPlayed'").Any()) { DataAccess.ExecuteNonQuery("alter table GameFiles add column 'MinutesPlayed' int;"); DataAccess.ExecuteNonQuery("update GameFiles set MinutesPlayed = 0"); } } private void Pre_2_2_0() { DataTable dt = DataAccess.ExecuteSelect("select name from sqlite_master where type='table' and name='Stats';").Tables[0]; if (dt.Rows.Count == 0) { DataAccess.ExecuteNonQuery("update Configuration set UserCanModify = 1 where Name = 'GameFileDirectory'"); string query = @"CREATE TABLE 'Stats' ( 'StatID' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 'GameFileID' INTEGER NOT NULL, 'KillCount' INTEGER NOT NULL, 'TotalKills' INTEGER NOT NULL, 'SecretCount' INTEGER NOT NULL, 'TotalSecrets' INTEGER NOT NULL, 'LevelTime' REAL NOT NULL, 'ItemCount' INTEGER NOT NULL, 'TotalItems' INTEGER NOT NULL, 'SourcePortID' INTEGER NOT NULL, 'MapName' TEXT NOT NULL, 'RecordTime' TEXT NOT NULL);"; DataAccess.ExecuteNonQuery(query); } } private void Pre_2_2_1() { IEnumerable sourcePorts = m_adapter.GetSourcePorts(); foreach (ISourcePortData sourcePort in sourcePorts) { if (sourcePort.SupportedExtensions.Contains(".pk3")) sourcePort.SupportedExtensions = sourcePort.SupportedExtensions.Replace(".pk3", ".pk3,.pk7"); List parameters = new List(); parameters.Add(DataAccess.DbAdapter.CreateParameter("ext", sourcePort.SupportedExtensions)); parameters.Add(DataAccess.DbAdapter.CreateParameter("SourcePortID", sourcePort.SourcePortID)); DataAccess.ExecuteNonQuery("update SourcePorts set SupportedExtensions = @ext where SourcePortID = @SourcePortID", parameters); } } private void Pre_2_3_0() { IEnumerable stats = m_adapter.GetStats(); HashSet statSet = new HashSet(); foreach (IStatsData stat in stats) { if (!statSet.Contains(stat)) statSet.Add(stat); else m_adapter.DeleteStats(stat.StatID); } } private void Pre_2_4_0() { DataTable dt = DataAccess.ExecuteSelect("pragma table_info(IWads);").Tables[0]; if (!dt.Select("name = 'GameFileID'").Any()) { DataAccess.ExecuteNonQuery("alter table IWads add column 'GameFileID' int;"); IEnumerable iwads = m_adapter.GetIWads(); IEnumerable gameFiles = m_adapter.GetGameFiles(); foreach (IIWadData iwad in iwads) { IGameFile find = gameFiles.FirstOrDefault(x => x.FileName.ToLower() == iwad.FileName.ToLower().Replace(".wad", ".zip")); if (find != null) { iwad.GameFileID = find.GameFileID; m_adapter.UpdateIWad(iwad); } } } UpdateSourcePortsTable(); //There was a bug in previous versions that would set MapCount to 1 when there were no maps found DataAccess.ExecuteNonQuery("update GameFiles set MapCount = null where Map is null or length(Map) = 0"); } private void Pre_2_4_1() { string[] saveExts = new string[] { "*.zds", "*.dsg", "*.esg" }; foreach (string ext in saveExts) { string[] files = Directory.GetFiles(m_appConfig.DemoDirectory.GetFullPath(), ext); foreach (string file in files) { FileInfo fi = new FileInfo(file); FileInfo fiTo = new FileInfo(Path.Combine(m_appConfig.SaveGameDirectory.GetFullPath(), fi.Name)); if (fiTo.Exists) fiTo.Delete(); fi.MoveTo(fi.FullName); } } } private void Pre_2_6_0() { UpdateSourcePortsTable(); DataAccess.ExecuteNonQuery(string.Format("update SourcePorts set LaunchType = {0}", (int)SourcePortLaunchType.SourcePort)); DataAccess.ExecuteNonQuery("update SourcePorts set FileOption = '-file'"); DataTable dt = DataAccess.ExecuteSelect("pragma table_info(GameFiles);").Tables[0]; if (!dt.Select("name = 'SettingsStat'").Any()) { DataAccess.ExecuteNonQuery("alter table GameFiles add column 'SettingsStat' INTEGER;"); DataAccess.ExecuteNonQuery("update GameFiles set SettingsStat = 1"); } } private void Pre_2_6_3() { UpdateSourcePortsTable(); } private void Pre_2_6_3_1() { DataTable dt = DataAccess.ExecuteSelect("pragma table_info(GameFiles);").Tables[0]; if (!dt.Select("name = 'SettingsFilesSourcePort'").Any()) DataAccess.ExecuteNonQuery("alter table GameFiles add column 'SettingsFilesSourcePort' TEXT;"); if (!dt.Select("name = 'SettingsFilesIWAD'").Any()) DataAccess.ExecuteNonQuery("alter table GameFiles add column 'SettingsFilesIWAD' TEXT;"); UpdateSourcePortsTable(); var gameFiles = m_adapter.GetGameFiles(); var ports = m_adapter.GetSourcePorts().ToDictionary(x => x.SourcePortID, x => x); var iwads = m_adapter.GetIWads(); var gameFileIwads = m_adapter.GetGameFileIWads().ToDictionary(x => iwads.First(y => y.GameFileID == x.GameFileID.Value).IWadID, x => x); foreach (var gameFile in gameFiles) { if (!string.IsNullOrEmpty(gameFile.SettingsFiles)) { var files = Util.GetAdditionalFiles(m_adapter, (GameFile)gameFile).Select(x => x.FileName); FileLoadHandlerLegacy filehandler = new FileLoadHandlerLegacy(m_adapter, gameFile); filehandler.CalculateAdditionalFiles(GetDictionaryData(gameFile.IWadID, gameFileIwads), GetDictionaryData(gameFile.SourcePortID, ports)); var sourcePortFiles = filehandler.GetSourcePortFiles().Select(x => x.FileName).Where(x => files.Contains(x)); var iwadFiles = filehandler.GetIWadFiles().Select(x => x.FileName).Where(x => files.Contains(x)).Except(sourcePortFiles); gameFile.SettingsFilesSourcePort = string.Join(";", sourcePortFiles.ToArray()); gameFile.SettingsFilesIWAD = string.Join(";", iwadFiles.ToArray()); m_adapter.UpdateGameFile(gameFile); } } } private void Pre_2_6_3_2() { DataAccess.ExecuteNonQuery("update SourcePorts set FileOption = '-file' where LaunchType = 0"); } private void Pre_2_6_4_1() { ConfigurationData config = new ConfigurationData() { Name = "ScreenshotPreviewSize", Value = "0", UserCanModify = true }; m_adapter.InsertConfiguration(config); } private void Pre_2_6_4_1_Update() { var sourcePorts = m_adapter.GetSourcePorts(); var stats = m_adapter.GetStats(); foreach (var stat in stats) { if (!sourcePorts.Any(x => x.SourcePortID == stat.SourcePortID)) { stat.SourcePortID = -1; m_adapter.UpdateStats(stat); } } } private void Pre_2_7_0_0() { DataTable dt = DataAccess.ExecuteSelect("select name from sqlite_master where type='table' and name='GameProfiles';").Tables[0]; if (dt.Rows.Count == 0) { string query = @"CREATE TABLE 'GameProfiles' ( 'GameProfileID' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 'GameFileID' INTEGER NOT NULL, 'SourcePortID' INTEGER NOT NULL, 'IWadID' INTEGER NOT NULL, 'Name' TEXT NOT NULL, 'SettingsMap' TEXT, 'SettingsSkill' TEXT, 'SettingsExtraParams' TEXT, 'SettingsFiles' TEXT, 'SettingsFilesSourcePort' TEXT, 'SettingsFilesIWAD' TEXT, 'SettingsSpecificFiles' TEXT, 'SettingsStat' INTEGER, 'SettingsSaved' INTEGER);"; DataAccess.ExecuteNonQuery(query); } dt = DataAccess.ExecuteSelect("pragma table_info(GameFiles);").Tables[0]; if (!dt.Select("name = 'SettingsGameProfileID'").Any()) DataAccess.ExecuteNonQuery(@"alter table GameFiles add column 'SettingsGameProfileID' INTEGER;"); if (!dt.Select("name = 'SettingsSaved'").Any()) { DataAccess.ExecuteNonQuery(@"alter table GameFiles add column 'SettingsSaved' INTEGER;"); DataAccess.ExecuteNonQuery("update GameFiles set SettingsSaved = 1 where SourcePortID is not null"); DataAccess.ExecuteNonQuery("update GameFiles set SettingsSaved = 0 where SourcePortID is null"); } } private void Pre_2_8_0_0() { ConfigurationData config = new ConfigurationData() { Name = "FileManagement", Value = FileManagement.Managed.ToString(), UserCanModify = false, }; m_adapter.InsertConfiguration(config); } private void Pre_2_8_0_0_1() { m_adapter.InsertConfiguration(new ConfigurationData() { Name = "GameFileViewType", Value = GameFileViewType.TileView.ToString(), UserCanModify = false, }); m_adapter.InsertConfiguration(new ConfigurationData() { Name = "ItemsPerPage", Value = "30", UserCanModify = false, }); } private void Pre_Version_3_1_0() { m_adapter.InsertConfiguration(new ConfigurationData() { Name = "DeleteScreenshotsAfterImport", Value = "false", UserCanModify = true, AvailableValues = "Yes;true;No;false" }); } private void Pre_Version_3_2_0() { m_adapter.InsertConfiguration(new ConfigurationData() { Name = "LastSelectedTabIndex", Value = "0", UserCanModify = false, }); DataTable dt = DataAccess.ExecuteSelect("pragma table_info(Tags);").Tables[0]; if (!dt.Select("name = 'ExcludeFromOtherTabs'").Any()) { DataAccess.ExecuteNonQuery(@"alter table Tags add column 'ExcludeFromOtherTabs' INTEGER;"); DataAccess.ExecuteNonQuery("update Tags set ExcludeFromOtherTabs = 0"); } } private void Pre_Version_3_2_0_Update1() { DataTable dt = DataAccess.ExecuteSelect("pragma table_info(Tags);").Tables[0]; if (!dt.Select("name = 'Favorite'").Any()) { DataAccess.ExecuteNonQuery(@"alter table Tags add column 'Favorite' INTEGER;"); DataAccess.ExecuteNonQuery("update Tags set Favorite = 0"); } m_adapter.InsertConfiguration(new ConfigurationData() { Name = "SplitTagSelect", Value = "300", UserCanModify = false, }); m_adapter.InsertConfiguration(new ConfigurationData() { Name = "TagSelectPinned", Value = "False", UserCanModify = false, }); m_adapter.InsertConfiguration(new ConfigurationData() { Name = "ShowTabHeaders", Value = "true", UserCanModify = true, AvailableValues = "Yes;true;No;false" }); } private void Pre_Version_3_2_0_Update2() { m_adapter.InsertConfiguration(new ConfigurationData() { Name = "CopySaveFiles", Value = "true", UserCanModify = true, AvailableValues = "Yes;true;No;false" }); UpdateSourcePortsTable(); } private void Pre_Version_3_2_0_Update3() { IEnumerable sourcePorts = m_adapter.GetSourcePorts(); foreach (ISourcePortData sourcePort in sourcePorts) { if (!sourcePort.SupportedExtensions.Contains(".pk3")) continue; sourcePort.SupportedExtensions = sourcePort.SupportedExtensions.Replace(".pk3", ".pk3,.ipk3"); List parameters = new List { DataAccess.DbAdapter.CreateParameter("ext", sourcePort.SupportedExtensions), DataAccess.DbAdapter.CreateParameter("SourcePortID", sourcePort.SourcePortID) }; DataAccess.ExecuteNonQuery("update SourcePorts set SupportedExtensions = @ext where SourcePortID = @SourcePortID", parameters); } } private void Pre_Version_3_3_0() { DataTable dt = DataAccess.ExecuteSelect("pragma table_info(GameFiles);").Tables[0]; if (!dt.Select("name = 'SettingsLoadLatestSave'").Any()) { DataAccess.ExecuteNonQuery(@"alter table GameFiles add column 'SettingsLoadLatestSave' INTEGER;"); DataAccess.ExecuteNonQuery("update GameFiles set SettingsLoadLatestSave = 0"); } dt = DataAccess.ExecuteSelect("pragma table_info(GameProfiles);").Tables[0]; if (!dt.Select("name = 'SettingsLoadLatestSave'").Any()) { DataAccess.ExecuteNonQuery(@"alter table GameProfiles add column 'SettingsLoadLatestSave' INTEGER;"); DataAccess.ExecuteNonQuery("update GameProfiles set SettingsLoadLatestSave = 0"); } } private void Pre_Version_3_3_1() { string query = @"update SourcePorts set SupportedExtensions = @SupportedExtensions where SourcePortID = @sourcePortID"; var sourcePorts = m_adapter.GetSourcePorts(); foreach (var sourcePort in sourcePorts) { if (sourcePort.SupportedExtensions.Contains(".pke")) continue; sourcePort.SupportedExtensions += ",.pke"; List parameters = new List { DataAccess.DbAdapter.CreateParameter("SupportedExtensions", sourcePort.SupportedExtensions), DataAccess.DbAdapter.CreateParameter("sourcePortID", sourcePort.SourcePortID) }; DataAccess.ExecuteNonQuery(query, parameters); } } private void Pre_Version_3_3_2() { DataTable dt = DataAccess.ExecuteSelect("select name from sqlite_master where type='table' and name='CleanupFiles';").Tables[0]; if (dt.Rows.Count == 0) { string query = @"CREATE TABLE 'CleanupFiles' ( 'CleanupFileID' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 'FileName' TEXT NOT NULL);"; DataAccess.ExecuteNonQuery(query); } string urls = "Germany;https://www.quaddicted.com/files/idgames/;Idaho;ftp://mirrors.syringanetworks.net/idgames/;New York;https://youfailit.net/pub/idgames/;Sweden;https://ftpmirror1.infania.net/pub/idgames/;Virginia;http://www.gamers.org/pub/idgames/;"; UpdateMirrorUrls(urls, "New York"); } private void Pre_Version_3_4_0() { m_adapter.InsertConfiguration(new ConfigurationData() { Name = "AllowMultiplePlaySessions", Value = "false", UserCanModify = true, AvailableValues = "Yes;true;No;false" }); m_adapter.InsertConfiguration(new ConfigurationData() { Name = "AutomaticallyPullTitlpic", Value = "true", UserCanModify = true, AvailableValues = "Yes;true;No;false" }); } private void Pre_Version_3_4_0_Update1() { var columnConfig = DataCache.Instance.GetColumnConfig().ToList(); var columnGroups = columnConfig.GroupBy(x => x.Parent); List newColumns = new List(columnConfig.Count * 2); // Insert LastDirectory after FileNameNoPath foreach (var set in columnGroups) { if (set.Any(x => x.Column.Equals("LastDirectory", StringComparison.OrdinalIgnoreCase))) continue; List sortColumns = set.ToList(); for (int i = 0; i < sortColumns.Count; i++) { var col = sortColumns[i]; newColumns.Add(col); if (!col.Column.Equals("FileNameNoPath", StringComparison.OrdinalIgnoreCase)) continue; newColumns.Add(new ColumnConfig() { Parent = set.Key, Column = "LastDirectory", Width = 100, Sort = SortOrder.None }); } newColumns.AddRange(sortColumns); } string data = DataCache.SerializeColumnConfig(newColumns); DataCache.Instance.UpdateConfig(DataCache.Instance.DataSourceAdapter.GetConfiguration(), AppConfiguration.ColumnConfigName, data); } private void Pre_Version_3_5_2() { m_adapter.InsertConfiguration(new ConfigurationData() { Name = "VisibleViews", Value = string.Join(";", TabKeys.KeyNames.Except(new string[] { TabKeys.LocalKey })), UserCanModify = false, }); } private void Pre_Version_3_5_2_Update1() { DataTable dt = DataAccess.ExecuteSelect("pragma table_info(Stats);").Tables[0]; if (!dt.Select("name = 'Skill'").Any()) DataAccess.ExecuteNonQuery(@"alter table Stats add column 'Skill' INTEGER;"); } private void Pre_Version_3_5_2_Update2() { UpdateSourcePortsTable(); } private void Pre_Version_3_5_3_Update1() { DataTable dt = DataAccess.ExecuteSelect("pragma table_info(GameFiles);").Tables[0]; if (!dt.Select("name = 'SettingsExtraParamsOnly'").Any()) { string query = @"alter table GameFiles add column 'SettingsExtraParamsOnly' INTEGER;"; DataAccess.ExecuteNonQuery(query); } dt = DataAccess.ExecuteSelect("pragma table_info(GameProfiles);").Tables[0]; if (!dt.Select("name = 'SettingsExtraParamsOnly'").Any()) { string query = @"alter table GameProfiles add column 'SettingsExtraParamsOnly' INTEGER;"; DataAccess.ExecuteNonQuery(query); } } private void Pre_Version_3_5_3_Update2() { DataTable dt = DataAccess.ExecuteSelect("pragma table_info(Files);").Tables[0]; if (!dt.Select("name = 'UserTitle'").Any()) { string query = @"alter table Files add column 'UserTitle' TEXT;"; DataAccess.ExecuteNonQuery(query); } if (!dt.Select("name = 'UserDescription'").Any()) { string query = @"alter table Files add column 'UserDescription' TEXT;"; DataAccess.ExecuteNonQuery(query); } if (!dt.Select("name = 'Map'").Any()) { string query = @"alter table Files add column 'Map' TEXT;"; DataAccess.ExecuteNonQuery(query); } } private void Pre_Version_3_5_3_Update3() { m_adapter.InsertConfiguration(new ConfigurationData() { Name = "ShowPlayDialog", Value = "true", UserCanModify = true, AvailableValues = "Yes;true;No;false" }); } private void Pre_Version_3_7_0() { m_adapter.InsertConfiguration(new ConfigurationData() { Name = "ColorThemeType", Value = "Dark", UserCanModify = false, }); } private void Pre_Version_3_7_0_Update1() { m_restartRequired = true; m_adapter.InsertConfiguration(new ConfigurationData() { Name = "ImportScreenshots", Value = "true", UserCanModify = true, AvailableValues = "Yes;true;No;false" }); } private void Pre_Version_3_7_4() { m_adapter.InsertConfiguration(new ConfigurationData() { Name = AppConfiguration.ShowTooltipName, Value = "true", UserCanModify = true, AvailableValues = "Yes;true;No;false" }); m_adapter.InsertConfiguration(new ConfigurationData() { Name = AppConfiguration.TileImageSizeName, Value = "300", UserCanModify = true, }); var screenshotWidth = m_adapter.GetConfiguration().FirstOrDefault(x => x.Name == AppConfiguration.ScreenshotPreviewSizeName); if (screenshotWidth != null) { screenshotWidth.Value = Util.GetPreviewScreenshotWidth(Convert.ToInt32(screenshotWidth.Value)).ToString(); m_adapter.UpdateConfiguration(screenshotWidth); } } private void Pre_Version_3_7_7() { var dt = DataAccess.ExecuteSelect("pragma table_info(GameFiles);").Tables[0]; if (!dt.Select("name = 'IsDoom64'").Any()) { DataAccess.ExecuteNonQuery("alter table GameFiles add column 'IsDoom64' INTEGER;"); DataAccess.ExecuteNonQuery("update GameFiles set IsDoom64 = 0"); } } private void UpdateSourcePortsTable() { var dt = DataAccess.ExecuteSelect("pragma table_info(SourcePorts);").Tables[0]; if (!dt.Select("name = 'SettingsFiles'").Any()) DataAccess.ExecuteNonQuery("alter table SourcePorts add column 'SettingsFiles' TEXT;"); if (!dt.Select("name = 'LaunchType'").Any()) DataAccess.ExecuteNonQuery("alter table SourcePorts add column 'LaunchType' TEXT;"); if (!dt.Select("name = 'FileOption'").Any()) DataAccess.ExecuteNonQuery("alter table SourcePorts add column 'FileOption' TEXT;"); if (!dt.Select("name = 'ExtraParameters'").Any()) DataAccess.ExecuteNonQuery("alter table SourcePorts add column 'ExtraParameters' TEXT;"); if (!dt.Select("name = 'AltSaveDirectory'").Any()) DataAccess.ExecuteNonQuery(@"alter table SourcePorts add column 'AltSaveDirectory' TEXT;"); if (!dt.Select("name = 'Archived'").Any()) { DataAccess.ExecuteNonQuery(@"alter table SourcePorts add column 'Archived' INTEGER;"); DataAccess.ExecuteNonQuery("update SourcePorts set Archived = 0"); } } private void UpdateMirrorUrls(string urls, string defaultUrl) { var configuration = m_adapter.GetConfiguration().FirstOrDefault(x => x.Name.Equals("MirrorUrl")); if (configuration == null) return; var newUrls = GetConfigItems(urls); var oldUrls = GetConfigItems(configuration.AvailableValues); if (!GetConfigKey(configuration.Value, oldUrls, out string urlKey) || !newUrls.Any(x => x.Item1.Equals(urlKey))) urlKey = defaultUrl; if (GetConfigValue(urlKey, newUrls, out string value)) configuration.Value = value; configuration.AvailableValues = urls; m_adapter.UpdateConfiguration(configuration); } private static List> GetConfigItems(string text) { List> configItems = new List>(); var items = text.Split(new char[] { ';' }); for (int i = 0; i < items.Length - 1; i+=2) configItems.Add(new Tuple(items[i], items[i + 1])); return configItems; } private static bool GetConfigKey(string data, List> items, out string key) { key = string.Empty; var item = items.FirstOrDefault(x => x.Item2.Equals(data)); if (item == null) return false; key = item.Item1; return true; } private static bool GetConfigValue(string data, List> items, out string value) { value = string.Empty; var item = items.FirstOrDefault(x => x.Item1.Equals(data)); if (item == null) return false; value = item.Item2; return true; } private static T GetDictionaryData(int? id, Dictionary values) { if (id.HasValue && values.ContainsKey(id.Value)) return values[id.Value]; return default(T); } public DataAccess DataAccess { get; set; } } } ================================================ FILE: DoomLauncher/Handlers/VersionUpdateResults.cs ================================================ namespace DoomLauncher.Handlers { public class VersionUpdateResults { public readonly bool RestartRequired; public VersionUpdateResults(bool restartRequired) { RestartRequired = restartRequired; } } } ================================================ FILE: DoomLauncher/IWad/IWadInfo.cs ================================================ using System.IO; namespace DoomLauncher { class IWadInfo { public string Title { get; } public string TileImage { get; } public bool HasMetadata { get; } public IWadInfo(string title, string tileImage, bool hasMeta = false) { Title = title; TileImage = Path.Combine(LauncherPath.GetDataDirectory(), "TileImages", tileImage); HasMetadata = hasMeta; } public static bool TryGetIWadInfo(string fileName, out IWadInfo iwadInfo) { iwadInfo = GetIWadInfo(fileName); return iwadInfo != null; } public static IWadInfo GetIWadInfo(string fileName) { string name = Path.GetFileNameWithoutExtension(fileName).ToUpper(); switch (name) { case "DOOM1": return new IWadInfo("Doom Shareware", "doom.png"); case "DOOM": return new IWadInfo("The Ultimate Doom", "doom.png"); case "DOOM2": return new IWadInfo("Doom II: Hell on Earth", "doom2.png"); case "PLUTONIA": return new IWadInfo("Final Doom: The Plutonia Experiment", "plutonia.png"); case "TNT": return new IWadInfo("Final Doom: TNT: Evilution", "tnt.png"); case "FREEDOOM1": return new IWadInfo("Freedoom: Phase 1", string.Empty); case "FREEDOOM2": return new IWadInfo("Freedoom: Phase 2", string.Empty); case "FREEDM": return new IWadInfo("FreeDM", string.Empty); case "CHEX": return new IWadInfo("Chex Quest", "chexquest.png"); case "CHEX3": return new IWadInfo("Chex Quest 3", "chexquest3.png"); case "HACX": return new IWadInfo("Hacx: Twitch 'n Kill", "hacx.png"); case "HERETIC1": return new IWadInfo("Heretic Shareware", "heretic.png"); case "HERETIC": return new IWadInfo("Heretic: Shadow of the Serpent Riders", "heretic.png"); case "HEXEN": return new IWadInfo("Hexen: Beyond Heretic", "hexen.png"); case "STRIFE0": return new IWadInfo("Strife Demo", "strife.png"); case "STRIFE1": return new IWadInfo("Strife: Quest for the Sigil", "strife.png"); case "DOOM64": return new IWadInfo("Doom 64", "doom64.png"); default: break; } return null; } } } ================================================ FILE: DoomLauncher/Interfaces/IConfigurationData.cs ================================================ namespace DoomLauncher.Interfaces { public interface IConfigurationData { int ConfigID { get; set; } string Name { get; set; } string Value { get; set; } string AvailableValues { get; set; } bool UserCanModify { get; set; } } } ================================================ FILE: DoomLauncher/Interfaces/IConfigurationDataSourceAdapter.cs ================================================ using System.Collections.Generic; namespace DoomLauncher.Interfaces { public interface IConfigurationDataSourceAdapter { IEnumerable GetConfiguration(); void InsertConfiguration(IConfigurationData config); void UpdateConfiguration(IConfigurationData config); IEnumerable GetTags(); void InsertTag(ITagData tag); void UpdateTag(ITagData tag); void DeleteTag(ITagData tag); IEnumerable GetTagMappings(); IEnumerable GetTagMappings(int gameFileID); void InsertTagMapping(ITagMapping tag); void DeleteTagMapping(ITagMapping tag); void DeleteTagMapping(int tagID); IEnumerable GetGameProfiles(int gameFileID); IEnumerable GetGlobalGameProfiles(); void InsertGameProfile(IGameProfile gameProfile); void UpdateGameProfile(IGameProfile gameProfile); void DeleteGameProfile(int gameProfileID); } } ================================================ FILE: DoomLauncher/Interfaces/ICustomParam.cs ================================================ namespace DoomLauncher { public interface ICustomParam { int CustomParamID { get; set; } int GameFileID { get; set; } string FileName { get; set; } string Parameter { get; set; } } } ================================================ FILE: DoomLauncher/Interfaces/IDataSourceAdapter.cs ================================================ using DoomLauncher.DataSources; using System.Collections.Generic; namespace DoomLauncher.Interfaces { public interface IDataSourceAdapter : IGameFileDataSourceAdapter, IIWadDataSourceAdapter, IConfigurationDataSourceAdapter, IStatsDataSourceAdapter { void UpdateGameFiles(GameFileFieldType ftWhere, GameFileFieldType ftSet, object fWhere, object fSet); IEnumerable GetSourcePorts(bool loadArchived = false); IEnumerable GetUtilities(bool loadArchived = false); IEnumerable GetDoom64(bool loadArchived = false); ISourcePortData GetSourcePort(int sourcePortID); void InsertSourcePort(ISourcePortData sourcePort); void UpdateSourcePort(ISourcePortData sourcePort); void DeleteSourcePort(ISourcePortData sourcePort); IEnumerable GetFiles(); IEnumerable GetFiles(IGameFile gameFile); IEnumerable GetFiles(IGameFile gameFile, FileType fileTypeID); IEnumerable GetFiles(FileType fileTypeID); void UpdateFile(IFileData file); void UpdateFiles(int sourcePortID_Where, int? sourcePortID_Set); void InsertFile(IFileData file); void DeleteFile(IFileData file); void DeleteFile(IGameFile file); void DeleteFiles(ISourcePortData sourcePort, FileType fileTypeID); IEnumerable GetGameFiles(ITagData tag); IEnumerable GetGameFiles(IGameFileGetOptions options, ITagData tag); IEnumerable GetCleanupFiles(); void InsertCleanupFile(CleanupFile file); void DeleteCleanupFile(CleanupFile file); //IEnumerable GetCustomParameters(int gameFileID); //void InsertCustomParameter(ICustomParamDataSource data); //void DeleteCustomParameter(ICustomParamDataSource data); } } ================================================ FILE: DoomLauncher/Interfaces/IDatabaseAdapter.cs ================================================ using System.Data.Common; namespace DoomLauncher.Interfaces { public interface IDatabaseAdapter { DbConnection CreateConnection(string connectionString); DbDataAdapter CreateAdapter(); DbParameter CreateParameter(string name, object value); } } ================================================ FILE: DoomLauncher/Interfaces/IFileAssociationView.cs ================================================ using System; using System.Collections.Generic; using System.Windows.Forms; namespace DoomLauncher.Interfaces { public class MenuOptions { public string Title { get; set; } public Func Action { get; set; } } interface IFileAssociationView { void SetContextMenu(ContextMenuStrip menu); void SetData(IGameFile gameFile); void ClearData(); bool DeleteAllowed { get; } bool CopyOrExportAllowed { get; } bool NewAllowed { get; } bool EditAllowed { get; } bool ViewAllowed { get; } bool ChangeOrderAllowed { get; } bool New(); bool Edit(); bool Delete(); void CopyToClipboard(); void CopyAllToClipboard(); void View(); bool MoveFileOrderUp(); bool MoveFileOrderDown(); bool SetFileOrderFirst(); bool Export(); bool ExportAll(); IGameFile GameFile { get; set; } IList MenuOptions { get; } } } ================================================ FILE: DoomLauncher/Interfaces/IFileData.cs ================================================ using System; namespace DoomLauncher.Interfaces { public interface IFileData { int? FileID { get; set; } int GameFileID { get; set; } string FileName { get; set; } DateTime DateCreated { get; set; } FileType FileTypeID { get; set; } int SourcePortID { get; set; } string Description { get; set; } string OriginalFileName { get; set; } string OriginalFilePath { get; set; } string UserTitle { get; set; } string UserDescription { get; set; } string Map { get; set; } int FileOrder { get; set; } bool IsUrl { get; } } } ================================================ FILE: DoomLauncher/Interfaces/IGameFile.cs ================================================ using System; namespace DoomLauncher.Interfaces { public interface IGameFile { int? GameFileID { get; set; } string FileName { get; set; } string FileNameBase { get; } string FileNameNoPath { get; } string LastDirectory { get; } string Title { get; set; } string Author { get; set; } DateTime? ReleaseDate { get; set; } string Description { get; set; } int? IWadID { get; set; } int? SourcePortID { get; set; } string Thumbnail { get; set; } string Comments { get; set; } string Map { get; set; } int? MapCount { get; set; } double? Rating { get; set; } DateTime? LastPlayed { get; set; } DateTime? Downloaded { get; set; } string SettingsMap { get; set; } string SettingsSkill { get; set; } string SettingsExtraParams { get; set; } string SettingsFiles { get; set; } string SettingsFilesSourcePort { get; set; } string SettingsFilesIWAD { get; set; } string SettingsSpecificFiles { get; set; } bool SettingsStat { get; set; } bool SettingsLoadLatestSave { get; set; } bool SettingsSaved { get; set; } bool SettingsExtraParamsOnly { get; set; } int? SettingsGameProfileID { get; set; } int MinutesPlayed { get; set; } int FileSizeBytes { get; set; } bool IsDoom64 { get; set; } bool IsUnmanaged(); bool IsDirectory(); IArchiveReader OpenGameFile(LauncherPath gameFileDirectory); bool ArchiveExists(LauncherPath gameFileDirectory); } } ================================================ FILE: DoomLauncher/Interfaces/IGameFileColumnView.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; namespace DoomLauncher { public interface IGameFileColumnView : IGameFileView, IGameFileSortableView { event CancelEventHandler CustomRowPaint; bool CustomRowColorPaint { get; set; } IGameFile CustomRowPaintDataBoundItem { get; } Color CustomRowPaintForeColor { get; set; } ColumnField[] ColumnFields { get; } void SetColumnFields(IEnumerable columnFields); void SetColumnFormat(string colName, string format); void SetColumnWidth(string key, int width); int GetColumnWidth(string key); string[] GetColumnKeyOrder(); } } ================================================ FILE: DoomLauncher/Interfaces/IGameFileDataSourceAdapter.cs ================================================ using System.Collections.Generic; namespace DoomLauncher.Interfaces { public interface IGameFileDataSourceAdapter { int GetGameFilesCount(); IEnumerable GetGameFiles(); IEnumerable GetGameFiles(IGameFileGetOptions options); IEnumerable GetGameFileIWads(); IEnumerable GetUntaggedGameFiles(); IEnumerable GetGameFileNames(); IGameFile GetGameFile(string fileName); void InsertGameFile(IGameFile gameFile); void UpdateGameFile(IGameFile gameFile); void UpdateGameFile(IGameFile gameFile, GameFileFieldType[] updateFields); void DeleteGameFile(IGameFile gameFile); } } ================================================ FILE: DoomLauncher/Interfaces/IGameFileDownloadable.cs ================================================ using System.ComponentModel; using System.Net; namespace DoomLauncher.Interfaces { interface IGameFileDownloadable { event DownloadProgressChangedEventHandler DownloadProgressChanged; event AsyncCompletedEventHandler DownloadCompleted; void Download(IGameFileDataSourceAdapter adapter, string dlFilename); void Cancel(); string FileName { get; set; } int FileSizeBytes { get; set; } } } ================================================ FILE: DoomLauncher/Interfaces/IGameFileSortableView.cs ================================================ using System.Windows.Forms; namespace DoomLauncher { public interface IGameFileSortableView { string GetSortedColumnKey(); SortOrder GetColumnSort(string key); void SetSortedColumn(string key, SortOrder sort); } } ================================================ FILE: DoomLauncher/Interfaces/IGameFileTile.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public interface IGameFileTile { event MouseEventHandler TileClick; event EventHandler TileDoubleClick; IGameFile GameFile { get; } bool Selected { get; } void SetSelected(bool set); void SetData(IGameFile gameFile, IEnumerable tags); void SetImageLocation(string file); void SetImage(Image image); void ClearData(); } } ================================================ FILE: DoomLauncher/Interfaces/IGameFileView.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Windows.Forms; namespace DoomLauncher { public class GameFileEventArgs { public IGameFile GameFile { get; private set; } public GameFileEventArgs(IGameFile gameFile) { GameFile = gameFile; } } public delegate void GameFileEventHandler(object sender, GameFileEventArgs e); public interface IGameFileView { event EventHandler ItemClick; event EventHandler ItemDoubleClick; event EventHandler SelectionChange; event KeyPressEventHandler ViewKeyPress; event KeyEventHandler ViewKeyDown; event DragEventHandler DragEnter; event DragEventHandler DragDrop; event GameFileEventHandler GameFileEnter; event GameFileEventHandler GameFileLeave; void SetDisplayText(string text); void SetContextMenuStrip(ContextMenuStrip menu); IEnumerable DataSource { get; set; } IGameFile SelectedItem { get; set; } IGameFile[] SelectedItems { get; } IGameFile GameFileForIndex(int index); void RefreshData(); bool Focus(); void UpdateGameFile(IGameFile gameFile); bool MultiSelect { get; set; } bool AllowDrop { get; set; } void SuspendLayout(); void ResumeLayout(); object DoomLauncherParent { get; set; } void SetVisible(bool set); } } ================================================ FILE: DoomLauncher/Interfaces/IGameProfile.cs ================================================ namespace DoomLauncher.Interfaces { public interface IGameProfile { int GameProfileID { get; set; } int? GameFileID { get; set; } int? IWadID { get; set; } int? SourcePortID { get; set; } string Name { get; set; } string SettingsMap { get; set; } string SettingsSkill { get; set; } string SettingsExtraParams { get; set; } string SettingsFiles { get; set; } string SettingsFilesSourcePort { get; set; } string SettingsFilesIWAD { get; set; } string SettingsSpecificFiles { get; set; } bool SettingsStat { get; set; } bool SettingsLoadLatestSave { get; set; } bool SettingsSaved { get; set; } bool SettingsExtraParamsOnly { get; set; } bool IsGlobal { get; } } } ================================================ FILE: DoomLauncher/Interfaces/IIWadData.cs ================================================ namespace DoomLauncher.Interfaces { public interface IIWadData { int IWadID { get; set; } string Name { get; set; } string FileName { get; set; } string FileNameBase { get; } int? GameFileID { get; set; } } } ================================================ FILE: DoomLauncher/Interfaces/IIWadDataSourceAdapter.cs ================================================ using System.Collections.Generic; namespace DoomLauncher.Interfaces { public interface IIWadDataSourceAdapter { IEnumerable GetIWads(); IIWadData GetIWad(int gameFileID); void InsertIWad(IIWadData iwad); void DeleteIWad(IIWadData iwad); void UpdateIWad(IIWadData iwad); } } ================================================ FILE: DoomLauncher/Interfaces/INewFileDetector.cs ================================================ namespace DoomLauncher.Interfaces { public interface INewFileDetector { void StartDetection(); string[] GetNewFiles(); string[] GetModifiedFiles(); string[] GetDeletedFiles(); } } ================================================ FILE: DoomLauncher/Interfaces/ISettingsDataSource.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DoomLauncher.Interfaces { public interface ISettingsDataSource { int GameFileID { get; set; } int SettingsID { get; set; } string SettingsName { get; set; } string SettingsMap { get; set; } string SettingsSkill { get; set; } string SettingsExtraParams { get; set; } string SettingsFiles { get; set; } string SettingsSpecificFiles { get; set; } } } ================================================ FILE: DoomLauncher/Interfaces/ISourcePortData.cs ================================================ using DoomLauncher.SourcePort; namespace DoomLauncher.Interfaces { public interface ISourcePortData { int SourcePortID { get; set; } string Name { get; set; } string Executable { get; set; } string SupportedExtensions { get; set; } LauncherPath Directory { get; set; } string SettingsFiles { get; set; } SourcePortLaunchType LaunchType { get; set; } string FileOption { get; set; } string ExtraParameters { get; set; } LauncherPath AltSaveDirectory { get; set; } bool Archived { get; set; } ISourcePortFlavor GetFlavor(); string GetFullExecutablePath(); LauncherPath GetReadSavePath(); LauncherPath GetLoadSavePath(); } } ================================================ FILE: DoomLauncher/Interfaces/IStatsData.cs ================================================ using System; namespace DoomLauncher { public interface IStatsData { int KillCount { get; set; } int TotalKills { get; set; } int SecretCount { get; set; } int TotalSecrets { get; set; } float LevelTime { get; set; } int ItemCount { get; set; } int TotalItems { get; set; } string MapName { get; set; } DateTime RecordTime { get; set; } int? Skill { get; set; } string FormattedKills { get; } string FormattedSecrets { get; } string FormattedItems { get; } string FormattedTime { get; } int GameFileID { get; set; } int SourcePortID { get; set; } int StatID { get; set; } } } ================================================ FILE: DoomLauncher/Interfaces/IStatsDataSourceAdapter.cs ================================================ using System.Collections.Generic; namespace DoomLauncher.Interfaces { public interface IStatsDataSourceAdapter { IEnumerable GetStats(); IEnumerable GetStats(int gameFileID); IEnumerable GetStats(IEnumerable gameFiles); void InsertStats(IStatsData stats); void UpdateStats(IStatsData stats); void DeleteStatsByFile(int gameFileID); void DeleteStats(int statID); void DeleteStats(ISourcePortData sourcePort); } } ================================================ FILE: DoomLauncher/Interfaces/ITabView.cs ================================================ using System; using System.Collections.Generic; namespace DoomLauncher.Interfaces { public interface ITabView : ICloneable { event EventHandler DataSourceChanging; event EventHandler DataSourceChanged; void SetGameFiles(); void SetGameFiles(IEnumerable searchFields); void SetGameFilesData(IEnumerable gameFiles); void UpdateDataSourceFile(IGameFile gameFile); List GetColumnConfig(); void SetColumnConfig(ColumnField[] columnTextFields, ColumnConfig[] colConfig); bool IsLocal { get; } bool IsEditAllowed { get; } bool IsDeleteAllowed { get; } bool IsSearchAllowed { get; } bool IsPlayAllowed { get; } bool IsAutoSearchAllowed { get; } string Title { get; set; } object Key { get; } IGameFileDataSourceAdapter Adapter { get; set; } IGameFileView GameFileViewControl { get; } } } ================================================ FILE: DoomLauncher/Interfaces/ITagData.cs ================================================ namespace DoomLauncher.Interfaces { public interface ITagData { int TagID { get; set; } string Name { get; set; } bool HasTab { get; set; } bool HasColor { get; set; } int? Color { get; set; } bool ExcludeFromOtherTabs { get; set; } bool Favorite { get; set; } string FavoriteName { get; } } } ================================================ FILE: DoomLauncher/Interfaces/ITagMapLookup.cs ================================================ using System; using System.Collections.Generic; namespace DoomLauncher.Interfaces { public interface ITagMapLookup { event EventHandler TagMappingChanged; void Refresh(ITagData[] tags); void RemoveGameFile(IGameFile gameFile); ITagData[] GetTags(IGameFile gameFile); IEnumerable GetEnumerableTags(IGameFile gameFile); } } ================================================ FILE: DoomLauncher/Interfaces/ITagMapping.cs ================================================ namespace DoomLauncher.Interfaces { public interface ITagMapping { int TagID { get; set; } int FileID { get; set; } } } ================================================ FILE: DoomLauncher/LaunchArgs.cs ================================================ namespace DoomLauncher { public class LaunchArgs { public string LaunchFileName { get; set; } public int? LaunchGameFileID { get; set; } public bool AutoClose { get; set; } } } ================================================ FILE: DoomLauncher/Program.Init.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; using DoomLauncher.Config; using DoomLauncher.WindowsVersion; using Microsoft.Win32; using DoomLauncher.DataSources; namespace DoomLauncher { public static class ProgramInit { public static bool Init() { bool success = false; CleanOldLibraries(); CheckInteropUpdate(); if (VerifyDatabase()) { string dataSource = Path.Combine(LauncherPath.GetDataDirectory(), DbDataSourceAdapter.DatabaseFileName); IDataSourceAdapter dataSourceAdapter = DbDataSourceAdapter.CreateAdapter(); DataCache.Instance.Init(dataSourceAdapter); BackupDatabase(dataSource); CreateSendToLink(); KillRunningApps(); success = VerifyGameFilesDirectory(); } return success; } public static void CheckSystemTheme() { try { var adapter = DataCache.Instance.DataSourceAdapter; if (DataCache.Instance.AppConfiguration.ConfigurationColorTheme != ColorThemeType.System && adapter.GetSourcePorts().Any()) { DataCache.Instance.AppConfiguration.ColorTheme = DataCache.Instance.AppConfiguration.ConfigurationColorTheme; return; } ColorThemeType theme; switch (WindowsVersionInfo.GetTheme()) { case WindowsTheme.Light: theme = ColorThemeType.Default; ColorTheme.Current = new DefaultTheme(); break; case WindowsTheme.Dark: case WindowsTheme.Unknown: default: theme = ColorThemeType.Dark; ColorTheme.Current = new DarkTheme(); break; } DataCache.Instance.AppConfiguration.ColorTheme = theme; } catch { } } private static bool VerifyGameFilesDirectory() { if (Directory.Exists(DataCache.Instance.AppConfiguration.GameFileDirectory.GetFullPath())) return true; bool check = false; try { InitGameFilesDebug(); check = InitFileCheck("GameFiles", "GameFiles_", true); if (!check) { MessageBox.Show("Initialization failure. Could not find DoomLauncher GameFiles directory. Please update your settings to continue.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); DialogResult result; bool success = false; do { success = ShowSettings(true, out result); } while (result != DialogResult.Cancel && !success); check = success; } } catch (Exception ex) { Util.DisplayUnexpectedException(null, ex); } return check; } private static bool ShowSettings(bool allowCancel, out DialogResult result) { SettingsForm form = new SettingsForm(DataCache.Instance.DataSourceAdapter, DataCache.Instance.AppConfiguration); form.SetCancelAllowed(allowCancel); form.StartPosition = FormStartPosition.CenterParent; result = form.ShowDialog(); if (result == DialogResult.OK) { try { DataCache.Instance.AppConfiguration.Refresh(); } catch (DirectoryNotFoundException ex) { MessageBox.Show(string.Format("The directory {0} was not found. DoomLauncher will not operate correctly with invalid paths. " + "Make sure the directory you are setting contains all folders required (Demos, SaveGames, Screenshots, Temp)", ex.Message), "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } catch (Exception ex) { Util.DisplayUnexpectedException(null, ex); return false; } } return true; } [Conditional("DEBUG")] private static void InitGameFilesDebug() { try { string basePath = "GameFiles"; if (!Directory.Exists(basePath)) { Directory.CreateDirectory(Path.Combine(basePath, "Demos")); Directory.CreateDirectory(Path.Combine(basePath, "SaveGames")); Directory.CreateDirectory(Path.Combine(basePath, "Screenshots")); Directory.CreateDirectory(Path.Combine(basePath, "Temp")); } } catch { // For local debug code only, just catch if we testing in debug outside of dev } } private static void BackupDatabase(string dataSource) { FileInfo fi = new FileInfo(dataSource); if (fi.Exists) { Directory.CreateDirectory(Path.Combine(LauncherPath.GetDataDirectory(), "Backup")); string backupName = GetBackupFileName(fi); FileInfo fiBackup = new FileInfo(backupName); if (!fiBackup.Exists) fi.CopyTo(backupName); CleanupBackupDirectory(); } } private static void CleanupBackupDirectory() { string[] files = Directory.GetFiles(Path.Combine(LauncherPath.GetDataDirectory(), "Backup"), "*.sqlite"); List filesInfo = new List(); Array.ForEach(files, x => filesInfo.Add(new FileInfo(x))); List filesInfoOrdered = filesInfo.OrderBy(x => x.CreationTime).ToList(); while (filesInfoOrdered.Count > 10) { filesInfoOrdered.First().Delete(); filesInfoOrdered.RemoveAt(0); } } private static string GetBackupFileName(FileInfo fi) { string end = DateTime.Now.ToString("yyyy_MM_dd") + fi.Extension; return Path.Combine(fi.DirectoryName, "Backup", fi.Name.Replace(fi.Extension, end)); } private static bool VerifyDatabase() { bool check = false; try { if (File.Exists(Path.Combine(LauncherPath.GetDataDirectory(), DbDataSourceAdapter.DatabaseFileName))) { check = true; // Still attempt to delete the init database here for people that manually update (not installed) if (File.Exists(DbDataSourceAdapter.InitDatabaseFileName)) File.Delete(DbDataSourceAdapter.InitDatabaseFileName); return check; } check = InitFileCheck(DbDataSourceAdapter.DatabaseFileName, DbDataSourceAdapter.InitDatabaseFileName, false); if (!check) { MessageBox.Show("Initialization failure. Could not find DoomLauncher database", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (Exception ex) { Util.DisplayUnexpectedException(null, ex); } return check; } private static bool InitFileCheck(string initFile, string file, bool directory) { string dataSource = Path.Combine(Directory.GetCurrentDirectory(), initFile); string initDataSource = Path.Combine(Directory.GetCurrentDirectory(), file); if (directory) { DirectoryInfo diInit = new DirectoryInfo(initDataSource); DirectoryInfo diSource = new DirectoryInfo(dataSource); if (diSource.Exists) { if (diInit.Exists) diInit.Delete(true); } else { if (diInit.Exists) diInit.MoveTo(dataSource); else return false; } } else { FileInfo fiInit = new FileInfo(initDataSource); if (File.Exists(dataSource)) { if (fiInit.Exists) fiInit.Delete(); } else { if (fiInit.Exists) fiInit.MoveTo(dataSource); else return false; } } return true; } private static void CleanOldLibraries() { // Need to remove the old sqlite interop, only if it wasn't done through the installer // The installer doesn't really support x86/x64 folders so the x64 dll is dumped in the main for now if (LauncherPath.IsInstalled()) return; try { string[] libraries = new string[] { "SQLite.Interop.dll", "SQLite.Interop.dll.bak", "7z.dll" }; foreach (string library in libraries) { if (!File.Exists(library)) continue; File.Delete(library); } } catch { // Do not crash if it fails to delete for any reason } } private static void CheckInteropUpdate() { if (LauncherPath.IsInstalled()) return; string[] dirs = new string[] { "x86", "x64" }; string updateFile = Path.Combine("GameFiles\\Temp", UpdateControl.AppUpdateFileName); if (!File.Exists(updateFile) || AllInteropsExist()) return; using (ZipArchive za = ZipFile.OpenRead(updateFile)) { foreach (string dir in dirs) { if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); var entries = za.Entries.Where(x => x.FullName.Contains(dir)); foreach (var entry in entries) entry.ExtractToFile(Path.Combine(Directory.GetCurrentDirectory(), dir, entry.Name), true); } } } private static bool AllInteropsExist() { DirectoryInfo[] dirs = new DirectoryInfo[] { new DirectoryInfo("x86"), new DirectoryInfo("x64") }; foreach (DirectoryInfo dir in dirs) { if (!dir.Exists) return false; FileInfo[] files = dir.GetFiles(); if (!files.Any(x => x.Name.Equals("SQLite.Interop.dll", StringComparison.OrdinalIgnoreCase))) return false; if (!files.Any(x => x.Name.Equals("7z.dll", StringComparison.OrdinalIgnoreCase))) return false; } return true; } private static void CreateSendToLink() { //http://stackoverflow.com/questions/234231/creating-application-shortcut-in-a-directory Type t = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8")); //Windows Script Host Shell Object dynamic shell = Activator.CreateInstance(t); try { string sendToPath = Environment.ExpandEnvironmentVariables(@"%AppData%\Microsoft\Windows\SendTo"); var lnk = shell.CreateShortcut(Path.Combine(sendToPath, "DoomLauncher.lnk")); // This should always exist, but a user did report not having this folder on Windows 10... if (!Directory.Exists(sendToPath)) Directory.CreateDirectory(sendToPath); try { lnk.TargetPath = Path.Combine(Directory.GetCurrentDirectory(), Util.GetExecutableNoPath()); lnk.IconLocation = string.Format(Path.Combine(Directory.GetCurrentDirectory(), "DoomLauncher.ico")); lnk.Save(); } catch { // Do not crash just for failing to create SendTo link } finally { Marshal.FinalReleaseComObject(lnk); } } catch { // Do not crash just for failing to create SendTo link } finally { Marshal.FinalReleaseComObject(shell); } } private static void KillRunningApps() { try { Process currentProc = Process.GetCurrentProcess(); foreach (Process proc in Process.GetProcessesByName("DoomLauncher").Where(x => x.Id != currentProc.Id)) { proc.CloseMainWindow(); Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 10 && !proc.HasExited) { System.Threading.Thread.Sleep(100); proc.Refresh(); } } } catch { MessageBox.Show("Doom Launcher is already running and could not be stopped. It is not recommended to have more than instance running!", "Already Running", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } } } ================================================ FILE: DoomLauncher/Program.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Windows.Forms; namespace DoomLauncher { static class Program { [STAThread] static void Main(string[] args) { try { Directory.SetCurrentDirectory(AssemblyDirectory); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); var splash = new SplashScreen(); splash.StartPosition = FormStartPosition.CenterScreen; splash.Show(); splash.Invalidate(); if (!ProgramInit.Init()) return; ProgramInit.CheckSystemTheme(); MainForm form = new MainForm(GetLaunchArgs(args), splash); form.Load += Form_Load; Application.Run(form); } catch (Exception ex) { Util.DisplayUnexpectedException(null, ex); } } private static async void Form_Load(object sender, EventArgs e) { MainForm form = sender as MainForm; await form.Init(); } static string AssemblyDirectory { get { string codeBase = Assembly.GetExecutingAssembly().CodeBase; UriBuilder uri = new UriBuilder(codeBase); string path = Uri.UnescapeDataString(uri.Path); return Path.GetDirectoryName(path); } } private static readonly string[] ArgKeys = new string[] { nameof(LaunchArgs.LaunchGameFileID), }; private static readonly string[] FlagKeys = new string[] { nameof(LaunchArgs.AutoClose) }; static LaunchArgs GetLaunchArgs(string[] args) { LaunchArgs launchArgs = new LaunchArgs(); for (int i = 0; i < args.Length; i++) { var arg = args[i]; if (!arg.StartsWith("-")) { launchArgs.LaunchFileName = args[i]; break; } arg = arg.Substring(1); var flag = FlagKeys.FirstOrDefault(x => x.Equals(arg, StringComparison.OrdinalIgnoreCase)); if (flag != null) { SetLaunchFlag(launchArgs, flag); continue; } var argKey = ArgKeys.FirstOrDefault(x => x.Equals(arg, StringComparison.OrdinalIgnoreCase)); if (!ArgKeys.Any(x => x.Equals(arg, StringComparison.OrdinalIgnoreCase))) continue; var pi = typeof(LaunchArgs).GetProperty(argKey); if (pi == null) continue; i++; if (i >= args.Length) break; if (!int.TryParse(args[i], out int value)) continue; pi.SetValue(launchArgs, value); } return launchArgs; } private static void SetLaunchFlag(LaunchArgs launchArgs, string flag) { var pi = typeof(LaunchArgs).GetProperty(flag); if (pi == null) return; pi.SetValue(launchArgs, true); } } } ================================================ FILE: DoomLauncher/ProgressBarType.cs ================================================ namespace DoomLauncher { enum ProgressBarType { Copy, Sync, Update, Delete, Search, CreateZip } } ================================================ FILE: DoomLauncher/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.InteropServices; // 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("DoomLauncher")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("DoomLauncher")] [assembly: AssemblyCopyright("")] [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("0c03f10e-9945-4048-8708-d9623e8845ec")] // 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("3.8.1.0")] [assembly: AssemblyFileVersion("3.8.1.0")] ================================================ FILE: DoomLauncher/Properties/Resources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace DoomLauncher.Properties { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DoomLauncher.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Bars { get { object obj = ResourceManager.GetObject("Bars", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap bon2a { get { object obj = ResourceManager.GetObject("bon2a", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap bon2b { get { object obj = ResourceManager.GetObject("bon2b", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Delete { get { object obj = ResourceManager.GetObject("Delete", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap DoomLauncherTile { get { object obj = ResourceManager.GetObject("DoomLauncherTile", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Byte[]. /// internal static byte[] DoomPalette { get { object obj = ResourceManager.GetObject("DoomPalette", resourceCulture); return ((byte[])(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap DownArrow { get { object obj = ResourceManager.GetObject("DownArrow", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Edit { get { object obj = ResourceManager.GetObject("Edit", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Export { get { object obj = ResourceManager.GetObject("Export", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap ExportAll { get { object obj = ResourceManager.GetObject("ExportAll", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap File { get { object obj = ResourceManager.GetObject("File", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap FolderOpen { get { object obj = ResourceManager.GetObject("FolderOpen", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Byte[]. /// internal static byte[] HereticPalette { get { object obj = ResourceManager.GetObject("HereticPalette", resourceCulture); return ((byte[])(obj)); } } /// /// Looks up a localized resource of type System.Byte[]. /// internal static byte[] HexenPalette { get { object obj = ResourceManager.GetObject("HexenPalette", resourceCulture); return ((byte[])(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap kill { get { object obj = ResourceManager.GetObject("kill", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap map { get { object obj = ResourceManager.GetObject("map", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Pin { get { object obj = ResourceManager.GetObject("Pin", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Play { get { object obj = ResourceManager.GetObject("Play", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Save { get { object obj = ResourceManager.GetObject("Save", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Search { get { object obj = ResourceManager.GetObject("Search", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap secret { get { object obj = ResourceManager.GetObject("secret", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap StepBack { get { object obj = ResourceManager.GetObject("StepBack", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Tags { get { object obj = ResourceManager.GetObject("Tags", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap th { get { object obj = ResourceManager.GetObject("th", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap UpArrow { get { object obj = ResourceManager.GetObject("UpArrow", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap Video { get { object obj = ResourceManager.GetObject("Video", resourceCulture); return ((System.Drawing.Bitmap)(obj)); } } } } ================================================ FILE: DoomLauncher/Properties/Resources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\bon2a_extra.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\bon2d.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\POSSL0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\th.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\SOULB0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\DoomLauncherTile.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\PMAPA0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\DownArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Export.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\ExportAll.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\File.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\FolderOpen.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Play.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Save.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Search.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\StepBack.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\UpArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Video.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Bars.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Tags.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\Pin.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\Resources\PLAYPAL.LMP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\HexenPalette.pal;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\HereticPalette.pal;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/Properties/Settings.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace DoomLauncher.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { get { return defaultInstance; } } } } ================================================ FILE: DoomLauncher/Properties/Settings.settings ================================================  ================================================ FILE: DoomLauncher/RecentFileType.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DoomLauncher { public enum RecentFileType { Played, Downloaded } } ================================================ FILE: DoomLauncher/SaveGame/DsgSaveGameReader.cs ================================================ using System.IO; using System.Linq; using System.Text; namespace DoomLauncher.SaveGame { public class DsgSaveGameReader : ISaveGameReader { private readonly string m_file; public DsgSaveGameReader(string file) { m_file = file; } public string GetName() { try { using (var stream = File.OpenRead(m_file)) { byte[] data = new byte[24]; int read = stream.Read(data, 0, data.Length); if (read != -1) { int index = data.ToList().IndexOf(0); if (index == -1) index = data.Length; if (index > 0) return Encoding.UTF8.GetString(data.Take(index).ToArray()); } } } catch { //continue to return the filename if we fail for any reason } return Path.GetFileName(m_file); } } } ================================================ FILE: DoomLauncher/SaveGame/HelionSaveGameReader.cs ================================================ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.IO; using System.Linq; using System.Text; namespace DoomLauncher.SaveGame { public class HelionSaveGameReader : ISaveGameReader { private readonly string m_file; public HelionSaveGameReader(string file) { m_file = file; } public string GetName() { try { string name = TryGetSaveName(m_file); if (!string.IsNullOrEmpty(name)) return name; return Path.GetFileName(m_file); } catch(Exception) { return Path.GetFileName(m_file); } } private static string TryGetSaveName(string fileName) { ZipArchiveReader zipArchive = new ZipArchiveReader(fileName); var entry = zipArchive.Entries.FirstOrDefault(x => x.Name.Equals("save.json", StringComparison.OrdinalIgnoreCase)); if (entry == null) return null; JObject data = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(entry.ReadEntry())) as JObject; JToken textData = data.GetValue("Text"); if (textData == null) return null; return textData.ToString(); } } } ================================================ FILE: DoomLauncher/SaveGame/ISaveGameReader.cs ================================================ namespace DoomLauncher.SaveGame { public interface ISaveGameReader { string GetName(); } } ================================================ FILE: DoomLauncher/SaveGame/ZDoomSaveGameReader.cs ================================================ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; namespace DoomLauncher.SaveGame { public class ZDoomSaveGameReader : ISaveGameReader { private readonly string m_file; public ZDoomSaveGameReader(string file) { m_file = file; } public string GetName() { string name = GetNameFromJson(m_file); if (name == null) name = GetNameFromBinary(m_file); if (name == null) return Path.GetFileName(m_file); return name; } private static string GetNameFromBinary(string file) { MemoryStream ms = new MemoryStream(File.ReadAllBytes(file)); long startPos = Util.ReadAfter(ms, Encoding.UTF8.GetBytes("tEXtTitle")); if (startPos != -1) { ms.Position = ++startPos; long endPos = Util.ReadAfter(ms, new byte[] { 0, 0, 0 }); if (endPos != -1) { int totalLength = (int)(endPos - startPos - 7); if (totalLength > 0) { if (totalLength > 24) totalLength = 24; ms.Position = startPos; byte[] strData = new byte[totalLength]; ms.Read(strData, 0, totalLength); return Encoding.UTF8.GetString(strData); } } } return null; } private static string GetNameFromJson(string file) { try { using (ZipArchive za = ZipFile.OpenRead(file)) { var entry = za.Entries.FirstOrDefault(x => x.Name.Equals("info.json")); if (entry != null) { using (var stream = new StreamReader(entry.Open())) { JObject obj = JsonConvert.DeserializeObject(stream.ReadToEnd()) as JObject; var saveName = obj.Values().FirstOrDefault(x => x.Path == "Title"); if (saveName != null) return saveName.ToString(); } } } } catch { //continue to return the filename if we fail for any reason } return null; } } } ================================================ FILE: DoomLauncher/Search/GameFileFieldType.cs ================================================ namespace DoomLauncher { public enum GameFileFieldType { Filename, Title, Author, Description, ReleaseDate, GameFileID, LastPlayed, Downloaded, Map, SourcePortID, IWadID, Comments, Rating, SettingsMap, SettingsSkill, SettingsExtraParams, SettingsFiles, SettingsSpecificFiles, MapCount, MinutesPlayed, MD5, SettingsStat, SettingsFilesIWAD, SettingsFilesSourcePort, SettingsGameProfileID, SettingsSaved, SettingsLoadLatestSave, SettingsExtraParamsOnly } } ================================================ FILE: DoomLauncher/Search/GameFileGetOptions.cs ================================================ namespace DoomLauncher { public class GameFileGetOptions : IGameFileGetOptions { public GameFileGetOptions() { } public GameFileGetOptions(GameFileSearchField sf) { SearchField = sf; } public GameFileGetOptions(GameFileFieldType[] selectFields) { SelectFields = selectFields; } public GameFileGetOptions(GameFileFieldType[] selectFields, GameFileSearchField searchField) { SelectFields = selectFields; SearchField = searchField; } public GameFileGetOptions(int limit) { Limit = limit; } public GameFileFieldType[] SelectFields { get; set; } public GameFileSearchField SearchField { get; set; } public GameFileFieldType? OrderField { get; set; } public OrderType? OrderBy { get; set; } public int? Limit { get; set; } } } ================================================ FILE: DoomLauncher/Search/GameFileSearchField.cs ================================================ using System.Linq; namespace DoomLauncher { public class GameFileSearchField { private static GameFileFieldType[] s_dateTimeFields = new GameFileFieldType[] { GameFileFieldType.ReleaseDate, GameFileFieldType.Downloaded, GameFileFieldType.LastPlayed }; public GameFileSearchField(GameFileFieldType type, string search) { SearchFieldType = type; SearchText = search; SearchOp = GameFileSearchOp.Equal; } public GameFileSearchField(GameFileFieldType type, GameFileSearchOp op, string search) { SearchFieldType = type; SearchText = search; SearchOp = op; } public static bool IsDateTimeField(GameFileFieldType field) { return s_dateTimeFields.ToList().Contains(field); } public GameFileSearchOp SearchOp { get; set; } public GameFileFieldType SearchFieldType { get; set; } public string SearchText { get; set; } } } ================================================ FILE: DoomLauncher/Search/GameFileSearchOp.cs ================================================ namespace DoomLauncher { public enum GameFileSearchOp { Equal, NotEqual, LessThan, GreaterThan, Like } } ================================================ FILE: DoomLauncher/Search/IGameFileGetOptions.cs ================================================ namespace DoomLauncher { public interface IGameFileGetOptions { GameFileFieldType[] SelectFields { get; set; } GameFileSearchField SearchField { get; set; } GameFileFieldType? OrderField { get; set; } OrderType? OrderBy { get; set; } int? Limit { get; set; } } } ================================================ FILE: DoomLauncher/Search/OrderType.cs ================================================ namespace DoomLauncher { public enum OrderType { Asc, Desc } } ================================================ FILE: DoomLauncher/SourcePort/DoomsdaySourcePortFlavor.cs ================================================ using DoomLauncher.Interfaces; using System; using System.IO; using System.Linq; namespace DoomLauncher.SourcePort { public class DoomsdaySourcePortFlavor : GenericSourcePortFlavor { public DoomsdaySourcePortFlavor(ISourcePortData sourcePortData) : base(sourcePortData) { } public override string IwadParameter(SpData data) => string.Format(" -iwad \"{0}\" -game {1}", Path.GetDirectoryName(data.Value), GetGame(data)); /** doom1-share Shareware Doom v1.9 doom1 Registered Doom v1.9 doom1-ultimate Ultimate Doom* doom2 Doom 2 doom2-plut Final Doom: Plutonia Experiment doom2-tnt Final Doom: TNT Evilution chex Chex Quest hacx HacX heretic-share Shareware Heretic heretic Registered Heretic heretic-ext Heretic: Shadow of the Serpent Riders** hexen Hexen v1.1 hexen-v10 Hexen v1.0 hexen-dk Hexen: Death Kings of Dark Citadel hexen-demo The 4-level Hexen Demo **/ private string GetGame(SpData data) { string filename = Path.GetFileNameWithoutExtension(data.Value); if (filename.Equals("DOOM1", StringComparison.OrdinalIgnoreCase)) return "doom1-share"; if (filename.Equals("DOOM", StringComparison.OrdinalIgnoreCase)) { if (data.GameFile.Map.Contains("E4M1")) return "doom1-ultimate"; else return "doom1"; } if (filename.Equals("DOOM2", StringComparison.OrdinalIgnoreCase)) return "doom2"; if (filename.Equals("PLUTONIA", StringComparison.OrdinalIgnoreCase)) return "doom2-plut"; if (filename.Equals("TNT", StringComparison.OrdinalIgnoreCase)) return "doom2-tnt"; if (filename.Equals("CHEX", StringComparison.OrdinalIgnoreCase)) return "chex"; if (filename.Equals("HACX", StringComparison.OrdinalIgnoreCase)) return "hacx"; if (filename.Equals("HERETIC", StringComparison.OrdinalIgnoreCase)) { if (!data.GameFile.Map.Contains("E2M1")) return "heretic-share"; else if (data.GameFile.Map.Contains("E4M1")) return "heretic-ext"; else return "heretic"; } if (filename.Equals("HEXEN", StringComparison.OrdinalIgnoreCase) || filename.Equals("HEXDD", StringComparison.OrdinalIgnoreCase)) { if (data.AdditionalFiles.Any(x => Path.GetFileNameWithoutExtension(x.FileName).Equals("HEXDD", StringComparison.OrdinalIgnoreCase))) return "hexen-dk"; if(data.GameFile.Map.Contains("MAP41")) //not sure why doomsday cares about this but v10 has unfinished maze map MAP41, v11 does not return "hexen-v10"; if (!data.GameFile.Map.Contains("MAP05")) return "hexen-demo"; return "hexen"; } return "doom2"; } public override bool Supported() => CheckFileNameWithoutExtension("doomsday"); public override bool LoadSaveGameSupported() => false; } } ================================================ FILE: DoomLauncher/SourcePort/GenericSourcePortFlavor.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.SaveGame; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace DoomLauncher.SourcePort { public class GenericSourcePortFlavor : ISourcePortFlavor { protected readonly ISourcePortData m_sourcePortData; public GenericSourcePortFlavor(ISourcePortData sourcePortData) { m_sourcePortData = sourcePortData; } public bool CheckFileNameWithoutExtension(string name) => Path.GetFileNameWithoutExtension(m_sourcePortData.Executable).Equals(name, StringComparison.InvariantCultureIgnoreCase); public bool CheckFileNameContains(string name) => CultureInfo.CurrentCulture.CompareInfo.IndexOf(m_sourcePortData.Executable, name) >= 0; public virtual string IwadParameter(SpData data) => $" -iwad \"{data.Value}\""; public virtual string FileParameter(SpData data) { if (!string.IsNullOrEmpty(m_sourcePortData.FileOption)) return string.Concat(" ", m_sourcePortData.FileOption, " "); return string.Empty; } public virtual string WarpParameter(SpData data) => BuildWarpParameter(data.Value); public virtual string SkillParameter(SpData data) => $" -skill {data.Value}"; public virtual string RecordParameter(SpData data) => $" -record \"{data.Value}\""; public virtual string PlayDemoParameter(SpData data) => $" -playdemo \"{data.Value}\""; public virtual string LoadSaveParameter(SpData data) { if (string.IsNullOrEmpty(data.Value)) return string.Empty; string file = Path.GetFileNameWithoutExtension(data.Value); if (!char.IsDigit(file[file.Length - 1])) return string.Empty; return $"-loadgame {file[file.Length - 1]}"; } public static string BuildWarpParameter(string map) { if (Regex.IsMatch(map, @"^E\dM\d$") || Regex.IsMatch(map, @"^MAP\d\d$")) return BuildWarpLegacy(map); return GetMapParameter(map); } public static string GetMapParameter(string map) => $" +map {map}"; public virtual bool Supported() => true; public virtual bool StatisticsSupported() => false; public virtual bool LoadSaveGameSupported() => true; public virtual string[] GetScreenshotDirectories() => Array.Empty(); public virtual string[] GetSaveGameDirectories() => Array.Empty(); public virtual IStatisticsReader CreateStatisticsReader(IGameFile gameFile, IEnumerable existingStats) => null; public virtual ISaveGameReader CreateSaveGameReader(FileInfo file) { if (file.Extension.Equals(".zds", StringComparison.InvariantCultureIgnoreCase)) return new ZDoomSaveGameReader(file.FullName); else if (file.Extension.Equals(".dsg", StringComparison.InvariantCultureIgnoreCase)) return new DsgSaveGameReader(file.FullName); else if (file.Extension.Equals(".hsg", StringComparison.InvariantCultureIgnoreCase)) return new HelionSaveGameReader(file.FullName); return null; } private static string BuildWarpLegacy(string map) { List numbers = new List(); StringBuilder num = new StringBuilder(); for (int i = 0; i < map.Length; i++) { if (char.IsDigit(map[i])) { num.Append(map[i]); } else { if (num.Length > 0) numbers.Add(num.ToString()); num.Clear(); } } if (num.Length > 0) numbers.Add(num.ToString()); StringBuilder sb = new StringBuilder(); foreach (string number in numbers) { sb.Append(Convert.ToInt32(number)); sb.Append(' '); } if (numbers.Any()) sb.Remove(sb.Length - 1, 1); return $" -warp {sb.ToString()}"; } } } ================================================ FILE: DoomLauncher/SourcePort/HelionSourcePortFlavor.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.IO; namespace DoomLauncher.SourcePort { public class HelionSourcePortFlavor : GenericSourcePortFlavor { public HelionSourcePortFlavor(ISourcePortData sourcePortData) : base(sourcePortData) { } public override string WarpParameter(SpData data) => GetMapParameter(data.Value); public override string LoadSaveParameter(SpData data) => $"-loadgame \"{data.Value}\""; public override bool Supported() => CheckFileNameWithoutExtension("Helion"); public override bool StatisticsSupported() => true; public override IStatisticsReader CreateStatisticsReader(IGameFile gameFile, IEnumerable existingStats) => new LevelstatReader(gameFile, Path.Combine(m_sourcePortData.Directory.GetFullPath(), "levelstat.txt")); } } ================================================ FILE: DoomLauncher/SourcePort/ISourcePortFlavor.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.SaveGame; using System.Collections.Generic; using System.IO; namespace DoomLauncher.SourcePort { public interface ISourcePortFlavor { string IwadParameter(SpData data); string FileParameter(SpData data); string WarpParameter(SpData data); string SkillParameter(SpData data); string RecordParameter(SpData data); string PlayDemoParameter(SpData data); string LoadSaveParameter(SpData data); bool Supported(); //if the exe is supported by this implementation bool StatisticsSupported(); bool LoadSaveGameSupported(); string[] GetScreenshotDirectories(); string[] GetSaveGameDirectories(); IStatisticsReader CreateStatisticsReader(IGameFile gameFile, IEnumerable existingStats); ISaveGameReader CreateSaveGameReader(FileInfo file); } } ================================================ FILE: DoomLauncher/SourcePort/LevelstatSourcePortFlavor.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.IO; namespace DoomLauncher.SourcePort { public class LevelstatSourcePortFlavor : GenericSourcePortFlavor { public LevelstatSourcePortFlavor(ISourcePortData sourcePortData) : base(sourcePortData) { } public override bool Supported() => CheckFileNameContains("crispy-doom") // + crispy-doom-truecolor || CheckFileNameContains("crispy-heretic") // + crispy-heretic-truecolor || CheckFileNameWithoutExtension("so-doom") || CheckFileNameWithoutExtension("inter-heretic") || CheckFileNameContains("boom-plus.exe") // prboom-plus + glboom-plus || CheckFileNameWithoutExtension("dsda-doom") || CheckFileNameWithoutExtension("nyan-doom") || CheckFileNameWithoutExtension("fdwl") || CheckFileNameWithoutExtension("woof") || CheckFileNameWithoutExtension("nugget-doom") || CheckFileNameWithoutExtension("cherry-doom"); public override bool StatisticsSupported() => true; public override IStatisticsReader CreateStatisticsReader(IGameFile gameFile, IEnumerable existingStats) => new LevelstatReader(gameFile, Path.Combine(m_sourcePortData.Directory.GetFullPath(), "levelstat.txt")); } } ================================================ FILE: DoomLauncher/SourcePort/SpData.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Linq; namespace DoomLauncher.SourcePort { public class SpData { private static readonly GameFile EmptyGameFile = new GameFile(); public SpData() { Value = string.Empty; GameFile = EmptyGameFile; AdditionalFiles = new List(); } public SpData(string value, IGameFile gameFile, IEnumerable addFiles) { Value = value; GameFile = gameFile; AdditionalFiles = addFiles.ToList(); } public string Value { get; private set; } public IGameFile GameFile { get; private set; } public List AdditionalFiles { get; private set; } } } ================================================ FILE: DoomLauncher/SourcePort/StatdumpSourcePortFlavor.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.Statistics; using System.Collections.Generic; using System.IO; namespace DoomLauncher.SourcePort { public class StatdumpSourcePortFlavor : GenericSourcePortFlavor { public StatdumpSourcePortFlavor(ISourcePortData sourcePortData) : base(sourcePortData) { } public override bool Supported() => CheckFileNameWithoutExtension("chocolate-doom") || CheckFileNameWithoutExtension("cndoom") || CheckFileNameWithoutExtension("crl-doom") || CheckFileNameWithoutExtension("inter-doom"); public override bool StatisticsSupported() => true; public override IStatisticsReader CreateStatisticsReader(IGameFile gameFile, IEnumerable existingStats) => new StatdumpReader(gameFile, Path.Combine(m_sourcePortData.Directory.GetFullPath(), "stats.txt")); } } ================================================ FILE: DoomLauncher/SourcePort/ZDoomSourcePortFlavor.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace DoomLauncher.SourcePort { public class ZDoomSourcePortFlavor : GenericSourcePortFlavor { private static readonly string[] DirectoryNames = new string[] { "GZDoom", "UZDoom", "VKDoom" }; private static string UserDirectoryBase => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); public static string[] UserSaveGameDirectories => DirectoryNames.Select(x => UserSaveGameDirectory(x)).ToArray(); public static string[] UserScreenshotDirectories => DirectoryNames.Select(x => UserScreenshotDirectory(x)).ToArray(); public static string UserSaveGameDirectory(string name) => Path.Combine(UserDirectoryBase, "Saved Games", name); public static string UserScreenshotDirectory(string name) => Path.Combine(UserDirectoryBase, "Pictures", "Screenshots", name); public ZDoomSourcePortFlavor(ISourcePortData sourcePortData) : base(sourcePortData) { } public override bool Supported() => CheckFileNameContains("zdoom.exe") || CheckFileNameWithoutExtension("zandronum") || CheckFileNameWithoutExtension("vkdoom"); public override string LoadSaveParameter(SpData data) => $"-loadgame \"{data.Value}\""; public override bool StatisticsSupported() => true; public override string[] GetScreenshotDirectories() => UserScreenshotDirectories; public override string[] GetSaveGameDirectories() => UserSaveGameDirectories; public override IStatisticsReader CreateStatisticsReader(IGameFile gameFile, IEnumerable existingStats) => new ZDoomStatsReader(gameFile, m_sourcePortData.Directory.GetFullPath(), existingStats); public override string WarpParameter(SpData data) => GetMapParameter(data.Value); } } ================================================ FILE: DoomLauncher/Statistics/IStatisticsReader.cs ================================================ using DoomLauncher.Interfaces; namespace DoomLauncher { public delegate void NewStatisticsEventHandler(object sender, NewStatisticsEventArgs e); public interface IStatisticsReader { event NewStatisticsEventHandler NewStastics; void Start(); void Stop(); string[] Errors { get; } string LaunchParameter { get; } bool ReadOnClose { get; } void ReadNow(); IGameFile GameFile { get; set; } } } ================================================ FILE: DoomLauncher/Statistics/LevelstatReader.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.Statistics; namespace DoomLauncher { public class LevelstatReader : MultiLineStatReader { private static string s_statRegex = @"\S+-(\d+:)?\d+:\d+.\d+\((\d+:)?\d+:\d+\)K:\d+/\d+I:\d+/\d+S:\d+/\d+"; private static ParseItem[] s_regexItems = new ParseItem[] { new ParseItem(@"\S+-", "-", "MapName"), new ParseItem(@"(\S+)?\S+:\S+\(", "-(", "LevelTime"), new ParseItem(@"\d+/", "/", "KillCount"), new ParseItem(@"K:\d+", "K:", "TotalKills"), new ParseItem(@"\d+/", "/", "ItemCount"), new ParseItem(@"I:\d+", "I:", "TotalItems"), new ParseItem(@"\d+/", "/", "SecretCount"), new ParseItem(@"S:\d+", "S:", "TotalSecrets") }; public LevelstatReader(IGameFile gameFile, string statFile) : base(gameFile, statFile, s_statRegex, s_regexItems) { RemoveNewLines = false; } public override string LaunchParameter { get { return "-levelstat"; } } } } ================================================ FILE: DoomLauncher/Statistics/MultiLineStatReader.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; namespace DoomLauncher.Statistics { public abstract class MultiLineStatReader : StatFileScanner, IStatisticsReader { public event NewStatisticsEventHandler NewStastics; private readonly List m_statistics = new List(); private readonly ParseItem[] m_parseItems; private readonly string m_statRegex; protected MultiLineStatReader(IGameFile gameFile, string statFile, string statRegex, ParseItem[] parseItems) : base(statFile) { GameFile = gameFile; m_parseItems = parseItems; m_statRegex = statRegex; ErrorOnFail = false; ErrorOnParseItem = true; RemoveNewLines = true; } public IGameFile GameFile { get; set; } public bool ReadOnClose { get { return true; } } public bool ErrorOnFail { get; set; } public bool ErrorOnParseItem { get; set; } public bool RemoveNewLines { get; set; } public abstract string LaunchParameter { get; } public void Start() { try { FileInfo fi = new FileInfo(StatFile); if (fi.Exists) fi.Delete(); } catch { //failed, nothing to do } } public void Stop() { //nothing to do on stop } public void ReadNow() { ReadStatistics(); } public string[] Errors { get { return m_errors.ToArray(); } } private void ReadStatistics() { try { string text = GetCleanedFileText(); MatchCollection matches = Regex.Matches(text, m_statRegex, RegexOptions.Singleline); foreach (Match match in matches) { IStatsData stats = null; if (match.Success) stats = ParseLine(match.Value); if (stats != null) { stats.RecordTime = DateTime.Now; stats.GameFileID = GameFile.GameFileID.Value; //Revived monsters adds to kill count if (stats.KillCount > stats.TotalKills) stats.KillCount = stats.TotalKills; if (!m_statistics.Contains(stats)) { m_statistics.Add(stats); NewStastics?.Invoke(this, new NewStatisticsEventArgs(stats)); } } } } catch (Exception ex) { if (ErrorOnFail) m_errors.Add(string.Format("Unexpected exception: {0}{1}{2}", ex.Message, Environment.NewLine, ex.StackTrace)); } } private string GetCleanedFileText() { string text = File.ReadAllText(StatFile); text = text.Replace(" ", string.Empty).Replace("\t", string.Empty); if (RemoveNewLines) text = text.Replace("\r\n", string.Empty).Replace("\n", string.Empty); else text = text.Replace("\r\n", "\n"); return text; } private IStatsData ParseLine(string line) { StatsData ret = new StatsData(); foreach (ParseItem item in m_parseItems) { Match match = Regex.Match(line, item.RegexInput); if (match.Success) { line = ReplaceFirst(line, match.Value); if (item.DataSourceProperty != null) SetStatProperty(ret, item, match.Value); } else if(ErrorOnParseItem) { m_errors.Add(string.Format("Failed to parse {0} from levelstat file.", item.DataSourceProperty)); } } return ret; } private static string ReplaceFirst(string text, string oldValue) { int idx = text.IndexOf(oldValue); if (idx != -1) return string.Concat(text.Substring(0, idx), text.Substring(idx + oldValue.Length)); return text; } } } ================================================ FILE: DoomLauncher/Statistics/NewStatisticsEventArgs.cs ================================================ using System; namespace DoomLauncher { public class NewStatisticsEventArgs : EventArgs { public NewStatisticsEventArgs(IStatsData stats) { Statistics = stats; Update = false; } public NewStatisticsEventArgs(IStatsData stats, bool update) { Statistics = stats; Update = update; } public IStatsData Statistics { get; set; } public bool Update { get; set; } } } ================================================ FILE: DoomLauncher/Statistics/StatFileScanner.cs ================================================ using System; using System.Collections.Generic; using System.Globalization; using System.Reflection; namespace DoomLauncher { public class StatFileScanner { protected List m_errors = new List(); public class ParseItem { public ParseItem(string regexInput, string replace, string dataSourceProperty) { RegexInput = regexInput; Replace = replace; DataSourceProperty = dataSourceProperty; } public string RegexInput { get; set; } public string Replace { get; set; } public string DataSourceProperty { get; set; } } public StatFileScanner(string statFile) { StatFile = statFile; } public string StatFile { get; } protected void SetStatProperty(StatsData stats, ParseItem item, string value) { foreach (char c in item.Replace) value = value.Replace(c.ToString(), string.Empty); PropertyInfo pi = stats.GetType().GetProperty(item.DataSourceProperty); if (item.DataSourceProperty == "LevelTime") { string[] time = value.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (time.Length == 2) pi.SetValue(stats, (Convert.ToSingle(time[0], CultureInfo.InvariantCulture) * 60) + Convert.ToSingle(time[1], CultureInfo.InvariantCulture)); // This is to support hours being written by ports like woof else if (TimeSpan.TryParse(value, AppConfiguration.Culture, out var ts)) pi.SetValue(stats, (float)ts.TotalSeconds); else m_errors.Add($"Failed to parse [{value}] for [LevelTime]"); } else { try { if (pi.PropertyType == typeof(string)) pi.SetValue(stats, value); else if (pi.PropertyType == typeof(int)) pi.SetValue(stats, Convert.ToInt32(value)); else if (pi.PropertyType == typeof(float)) pi.SetValue(stats, Convert.ToSingle(value)); } catch { m_errors.Add(string.Format("Failed for parse value[{0}] for [{1}]", value, item.DataSourceProperty)); } } } } } ================================================ FILE: DoomLauncher/Statistics/StatdumpReader.cs ================================================ using DoomLauncher.Interfaces; namespace DoomLauncher.Statistics { public class StatdumpReader : MultiLineStatReader { private static string s_statRegex = @"=+\w+(/\w+)?=+Time:\d+:\d+\(par:\d+:\d+\)\w+\(\w+\):Kills:\d+(/\d+\(-?\d+%\))?Items:\d+(/\d+\(-?\d+%\))?Secrets:\d+(/\d+)?"; private static ParseItem[] s_regexItems = new ParseItem[] { new ParseItem(@"\w+(/\w+)?", string.Empty, "MapName"), new ParseItem(@"\d+:\d+", string.Empty, "LevelTime"), new ParseItem(@"\d+:\d+", string.Empty, null), //remove par new ParseItem(@"\w+\(\w+\):", string.Empty, null), //remove player name new ParseItem(@":\d+", ":", "KillCount"), new ParseItem(@"Kills/\d+", "Kills/", "TotalKills"), new ParseItem(@":\d+", ":", "ItemCount"), new ParseItem(@"Items/\d+", "Items/", "TotalItems"), new ParseItem(@":\d+", ":", "SecretCount"), new ParseItem(@"Secrets/\d+", "Secrets/", "TotalSecrets") }; public StatdumpReader(IGameFile gameFile, string statFile) : base(gameFile, statFile, s_statRegex, s_regexItems) { ErrorOnParseItem = false; } public override string LaunchParameter { get { return $"-statdump \"{StatFile}\""; } } } } ================================================ FILE: DoomLauncher/Statistics/ZDoomStatsReader.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace DoomLauncher { public class ZDoomStatsReader : IStatisticsReader { public event NewStatisticsEventHandler NewStastics; struct LevelCount { public uint levelcount; } struct LevelStats //item stats excluded for binary (old save format) compatibility { public uint totalkills; public uint killcount; public uint totalsecrets; public uint secretcount; public uint leveltime; } private readonly string m_dir; private readonly List m_detectors = new List(); private readonly List m_statistics; private readonly List m_errors = new List(); public ZDoomStatsReader(IGameFile gameFile, string directory, IEnumerable existingStats) { m_dir = directory; m_statistics = existingStats.ToList(); GameFile = gameFile; } public IGameFile GameFile { get; set; } public string LaunchParameter { get { return string.Empty; } } public bool ReadOnClose { get { return true; } } public void Start() { m_detectors.Clear(); string[] zdsExtensions = new string[] { ".zds" }; m_detectors.Add(new NewFileDetector(zdsExtensions, m_dir, true)); foreach (var userDir in ZDoomSourcePortFlavor.UserSaveGameDirectories) { if (Directory.Exists(userDir)) m_detectors.Add(new NewFileDetector(zdsExtensions, userDir, true)); } m_detectors.ForEach(x => x.StartDetection()); } public void Stop() { // Nothing to do } public void ReadNow() { if (m_detectors.Count == 0) return; List files = new List(); foreach (var detector in m_detectors) { files.AddRange(detector.GetNewFiles()); files.AddRange(detector.GetModifiedFiles()); } if (files.Count > 0) files.ForEach(x => HandleSaveFile(x)); } public string[] Errors { get { return m_errors.ToArray(); } } private void HandleSaveFile(string file) { try { try { ReadSaveFile(file); } catch (IOException) { System.Threading.Thread.Sleep(200); ReadSaveFile(file); //try one more time... } } catch (FileNotFoundException ex) { m_errors.Add(string.Format("The file {0} was not found and could not be parsed.", ex.FileName)); } catch (Exception ex) { m_errors.Add(string.Format("An unexpected error occurred with {0}: {1} {2} {3}", file, ex.Message, Environment.NewLine, ex.StackTrace)); } } private void ReadSaveFile(string file) { lock (m_detectors) { //if we fail to read json (new save format) attempt to read binary (old save format) if (!ParseJson(file)) ParseBinary(file); } } private bool ParseJson(string file) { List stats = null; bool success = false; try { using (ZipArchive za = ZipFile.OpenRead(file)) { var entry = za.Entries.FirstOrDefault(x => x.Name.Equals("globals.json")); if (entry != null) { using (var stream = new StreamReader(entry.Open())) stats = ParseJsonStats(stream); } } } catch { //nothing to do } if (stats != null) { foreach (var stat in stats) HandleStatsData(stat); success = true; } return success; } private static List ParseJsonStats(StreamReader stream) { List stats = new List(); JObject obj = JsonConvert.DeserializeObject(stream.ReadToEnd()) as JObject; var statsource = obj.GetValue("statistics") as JObject; if (statsource == null) return stats; var levels = statsource.GetValue("levels"); if (levels == null) return stats; int? skill = GetSkill(obj); foreach (JObject level in levels.OfType()) { uint totalkills = Convert.ToUInt32(level.GetValue("totalkills")); uint kills = Convert.ToUInt32(level.GetValue("killcount")); //item stats are only included in json (new save format), so they're converted to UInt32 here uint totalitems = Convert.ToUInt32(level.GetValue("totalitems")); uint items = Convert.ToUInt32(level.GetValue("itemcount")); uint totalsecrets = Convert.ToUInt32(level.GetValue("totalsecrets")); uint secrets = Convert.ToUInt32(level.GetValue("secretcount")); uint time = Convert.ToUInt32(level.GetValue("leveltime")); string name = level.GetValue("levelname").ToString(); stats.Add(CreateJsonStatsDataSource(totalkills, kills, totalitems, items, totalsecrets, secrets, time, name, skill)); } return stats; } private static int? GetSkill(JObject obj) { var cvars = obj.GetValue("importantcvars"); if (cvars != null) return GetSKillByImportCvars(cvars); if (obj.GetValue("servercvars") is JObject objectCvars) return GetSkillByServerCvars(objectCvars); return null; } private static int? GetSkillByServerCvars(JObject cvars) { var skill = cvars.GetValue("skill"); if (skill == null) return null; if (int.TryParse(skill.ToString(), out int skillValue)) return skillValue + 1; return null; } private static int? GetSKillByImportCvars(JToken cvars) { string[] stringCvars = cvars.ToString().Split(new char[] { '\\' }); int index = Array.FindIndex(stringCvars, x => x.Equals("skill", StringComparison.OrdinalIgnoreCase)); if (index == -1 || index + 1 >= stringCvars.Length) return null; if (int.TryParse(stringCvars[index + 1], out int parsedSkill)) return parsedSkill + 1; return null; } private void ParseBinary(string file) { MemoryStream ms = new MemoryStream(File.ReadAllBytes(file)); long position = Util.ReadAfter(ms, Encoding.UTF8.GetBytes("sTat")); if (position != -1) ReadStatistics(ms); else m_errors.Add(string.Format("Unable to find statistics in the save file {0}.", file)); } private void ReadStatistics(MemoryStream ms) { LevelCount count = ReadStuctureFromFile(ms); count = CheckLevelCount(count); int stringLength = ReadCount(ms); ms.Position += stringLength - 1; //This part MAY contain the start episode map, just skip it for (int i = 0; i < count.levelcount; i++) { LevelStats stats = ReadStuctureFromFile(ms); stats = CheckStats(stats); ms.Position += 1; //skip NEW_NAME (27) stringLength = ReadCount(ms) - 1; byte[] levelNameBytes = new byte[stringLength]; ms.Read(levelNameBytes, 0, levelNameBytes.Length); string levelName = Encoding.ASCII.GetString(levelNameBytes).ToLower(); StatsData statsData = CreateBinaryStatsDataSource(stats.totalkills, stats.killcount, stats.totalsecrets, stats.secretcount, stats.leveltime, levelName); HandleStatsData(statsData); } } private void HandleStatsData(StatsData statsData) { statsData.GameFileID = GameFile.GameFileID.Value; if (!m_statistics.Contains(statsData)) { m_statistics.Add(statsData); NewStastics?.Invoke(this, new NewStatisticsEventArgs(statsData)); } } private static int s_endianCheck = 0x0000FFFF; //if bytes are set on the other side, then it must be stored backwards private LevelCount CheckLevelCount(LevelCount count) { if (count.levelcount > s_endianCheck) count.levelcount = ReverseBytes(count.levelcount); return count; } private LevelStats CheckStats(LevelStats stats) { if (stats.killcount > s_endianCheck) stats.killcount = ReverseBytes(stats.killcount); if (stats.leveltime > s_endianCheck) stats.leveltime = ReverseBytes(stats.leveltime); if (stats.secretcount > s_endianCheck) stats.secretcount = ReverseBytes(stats.secretcount); if (stats.totalkills > s_endianCheck) stats.totalkills = ReverseBytes(stats.totalkills); if (stats.totalsecrets > s_endianCheck) stats.totalsecrets = ReverseBytes(stats.totalsecrets); return stats; } //separate json and binary to avoid stat overflow with how binary (old save format) is read private static StatsData CreateJsonStatsDataSource(uint totalkills, uint killcount, uint totalitems, uint itemcount, uint totalsecrets, uint secretcount, uint leveltime, string name, int? skill) { float calctime = Convert.ToSingle(leveltime) / 35.0f; StatsData stats = new StatsData { RecordTime = DateTime.Now, TotalKills = (int)totalkills, KillCount = (int)killcount, TotalItems = (int)totalitems, ItemCount = (int)itemcount, TotalSecrets = (int)totalsecrets, SecretCount = (int)secretcount, LevelTime = calctime, MapName = name, Skill = skill }; return stats; } private static StatsData CreateBinaryStatsDataSource(uint totalkills, uint killcount, uint totalsecrets, uint secretcount, uint leveltime, string name) { float calctime = Convert.ToSingle(leveltime) / 35.0f; StatsData stats = new StatsData { RecordTime = DateTime.Now, TotalKills = (int)totalkills, KillCount = (int)killcount, TotalSecrets = (int)totalsecrets, SecretCount = (int)secretcount, LevelTime = calctime, MapName = name }; return stats; } private static int ReadCount(MemoryStream ms) { byte[] len = new byte[1]; int count = 0; int ofs = 0; do { ms.Read(len, 0, 1); count |= (len[0] & 0x7f) << ofs; ofs += 7; } while ((len[0] & 0x80) != 0); return count; } private static uint ReverseBytes(uint value) { return (value & 0x000000FFU) << 24 | (value & 0x0000FF00U) << 8 | (value & 0x00FF0000U) >> 8 | (value & 0xFF000000U) >> 24; } private static T ReadStuctureFromFile(MemoryStream ms) { byte[] bytes = new byte[Marshal.SizeOf(typeof(T))]; ms.Read(bytes, 0, bytes.Length); GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); T obj = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); handle.Free(); return obj; } } } ================================================ FILE: DoomLauncher/Stylize/CToolStripRenderer.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public class CToolStripRenderer : ToolStripProfessionalRenderer { protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) { const string ArrowText = "⏵"; SizeF size = e.Graphics.MeasureString(ArrowText, e.Item.Font); Color color = e.Item.Selected ? ColorTheme.Current.HighlightText : ColorTheme.Current.Text; e.Graphics.DrawString(ArrowText, e.Item.Font, new SolidBrush(color), new PointF(e.Item.Width - size.Width, 1)); } protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) { Color color = e.Item.Selected ? ColorTheme.Current.HighlightText : ColorTheme.Current.Text; e.Graphics.DrawString(e.Item.Text, e.Item.Font, new SolidBrush(color), new PointF(34, 2)); } protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) { if (e.Item.Selected) { Rectangle rectangle = new Rectangle(0, 0, e.Item.Size.Width - 1, e.Item.Size.Height - 1); e.Graphics.FillRectangle(new SolidBrush(ColorTheme.Current.Highlight), rectangle); return; } base.OnRenderMenuItemBackground(e); } } } ================================================ FILE: DoomLauncher/Stylize/DarkTheme.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public class DarkTheme : IThemeColors { public Color TitlebarBackground => WindowDark; public Color WindowDark => Color.FromArgb(255, 32, 32, 32); public Color Window => Color.FromArgb(255, 48, 48, 54); public Color WindowLight => Color.FromArgb(255, 58, 58, 64); public Color Text => Color.White; public Color DisabledText => Color.FromArgb(255, 102, 102, 102); public Color HighlightText => Color.White; public Color Highlight => Color.FromArgb(255, 90, 101, 234); public FlatStyle ComboFlatStyle => FlatStyle.Flat; public FlatStyle ButtonFlatStyle => FlatStyle.Flat; public Color ButtonTextColor => Text; public Color ButtonBackground => Color.FromArgb(255, 90, 101, 234); public Color CheckBoxBackground => WindowLight; public Color TextBoxBackground => WindowDark; public Color DropDownBackground => WindowDark; public Color Border => Color.FromArgb(255, 32, 32, 32); public Color LinkText => Highlight; public Color GridBorder => WindowDark; public Color GridRow => WindowDark; public Color GridRowAlt => WindowDark; public Color Separator => Color.Gray; public Color StatBackgroundGradientFrom => WindowDark; public Color StatBackgroundGradientTo => Window; public Color StatText => Color.White; public Color CloseBackgroundHighlight => Color.FromArgb(255, 213, 50, 38); public Color CloseForeColorHighlight => Color.White; public Color ImageBackground => Color.Black; public Color GridBackground => WindowDark; public bool GridRowBorder => false; public bool IsDark => true; } } ================================================ FILE: DoomLauncher/Stylize/DefaultTheme.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public class DefaultTheme : IThemeColors { public Color TitlebarBackground => WindowLight; public Color WindowDark => SystemColors.Control; public Color Window => SystemColors.Control; public Color WindowLight => SystemColors.Window; public Color Text => SystemColors.WindowText; public Color DisabledText => Color.FromArgb(255, 142, 142, 142); public Color HighlightText => SystemColors.HighlightText; public Color Highlight => SystemColors.Highlight; public Color ButtonTextColor => Text; public FlatStyle ComboFlatStyle => FlatStyle.Standard; public FlatStyle ButtonFlatStyle => FlatStyle.Standard; public Color ButtonBackground => SystemColors.Window; public Color CheckBoxBackground => SystemColors.Window; public Color TextBoxBackground => Color.White; public Color DropDownBackground => SystemColors.Window; public Color Border => SystemColors.ControlDark; public Color LinkText => Highlight; public Color GridBorder => SystemColors.ControlDark; public Color GridRow => SystemColors.Window; public Color GridRowAlt => SystemColors.ControlLight; public Color Separator => Color.Gray; public Color StatBackgroundGradientFrom => Color.Gray; public Color StatBackgroundGradientTo => Color.FromArgb(255, 191, 191, 191); public Color StatText => Color.White; public Color CloseBackgroundHighlight => Color.FromArgb(255, 213, 50, 38); public Color CloseForeColorHighlight => Color.White; public Color ImageBackground => Color.Black; public Color GridBackground => Color.White; public bool GridRowBorder => true; public bool IsDark => false; } } ================================================ FILE: DoomLauncher/Stylize/IThemeColors.cs ================================================ using System.Drawing; using System.Windows.Forms; namespace DoomLauncher { public interface IThemeColors { Color TitlebarBackground { get; } Color Window { get; } Color WindowLight { get; } Color WindowDark { get; } Color Text { get; } Color DisabledText { get; } Color HighlightText { get; } Color Highlight { get; } Color ButtonTextColor { get; } FlatStyle ComboFlatStyle { get; } FlatStyle ButtonFlatStyle { get; } Color ButtonBackground { get; } Color CheckBoxBackground { get; } Color TextBoxBackground { get; } Color DropDownBackground { get; } Color Border { get; } Color LinkText { get; } Color GridBorder { get; } Color GridRow { get; } Color GridRowAlt { get; } Color Separator { get; } Color StatBackgroundGradientFrom { get; } Color StatBackgroundGradientTo { get; } Color StatText { get; } Color CloseBackgroundHighlight { get; } Color CloseForeColorHighlight { get; } Color ImageBackground { get; } Color GridBackground { get; } bool GridRowBorder { get; } bool IsDark { get; } } } ================================================ FILE: DoomLauncher/Stylize/ImmersiveDarkMode.cs ================================================ using System; using System.Runtime.InteropServices; using System.Windows.Forms; using DoomLauncher.WindowsVersion; namespace DoomLauncher { public static class ImmersiveDarkMode { [DllImport("dwmapi.dll")] private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; public static bool UseImmersiveDarkMode(Form form, bool enabled) { try { if (!WindowsVersionInfo.GetOsVersionInfo(out var versionInfo)) return false; if (WindowsVersionInfo.IsWindows10OrGreater(versionInfo, 17763)) { var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1; if (WindowsVersionInfo.IsWindows10OrGreater(versionInfo, 18985)) attribute = DWMWA_USE_IMMERSIVE_DARK_MODE; int useImmersiveDarkMode = enabled ? 1 : 0; return DwmSetWindowAttribute(form.Handle, attribute, ref useImmersiveDarkMode, sizeof(int)) == 0; } } catch { } return false; } } } ================================================ FILE: DoomLauncher/Stylize/StyledMessageBox.cs ================================================ using DoomLauncher.Forms; using System.Drawing; using System.Windows.Forms; namespace DoomLauncher.Stylize { public static class StyledMessageBox { public static DialogResult Show(IWin32Window window, string text, string caption, MessageBoxButtons buttons, MessageBoxIcon icon = MessageBoxIcon.Error, MessageBoxDefaultButton defaultButton = MessageBoxDefaultButton.Button1) { var systemIcon = SystemIcons.Information; switch (icon) { case MessageBoxIcon.Error: systemIcon = SystemIcons.Error; break; case MessageBoxIcon.Warning: systemIcon = SystemIcons.Warning; break; case MessageBoxIcon.Question: systemIcon = SystemIcons.Question; break; case MessageBoxIcon.Asterisk: systemIcon = SystemIcons.Asterisk; break; } var form = new MessageCheckBox(caption, text, string.Empty, systemIcon, buttons, defaultButton); form.SetShowCheckBox(false); form.StartPosition = FormStartPosition.CenterParent; return form.ShowDialog(window); } } } ================================================ FILE: DoomLauncher/Stylize/Stylizer.cs ================================================ using System; using System.Drawing; using System.Windows.Forms; using DoomLauncher.Controls; namespace DoomLauncher { public enum StylizerOptions { None, SetupTitleBar, StylizeTitleBar } internal class Stylizer { private readonly struct StylizeControlResults { public StylizeControlResults(bool loadRequired) { LoadRequired = loadRequired; } public readonly bool LoadRequired; } private static readonly IThemeColors CurrentTheme = ColorTheme.Current; public static void Stylize(Form form, bool designMode, StylizerOptions options = StylizerOptions.None) { StylizeInternal(form, designMode, options, null); } private static void StylizeInternal(Form form, bool designMode, StylizerOptions options, Func shouldStylize) { bool registerLoad = false; if (designMode) return; if (options.HasFlag(StylizerOptions.SetupTitleBar)) SetupTitleBar(form); foreach (Control control in form.Controls) { if (control is CRichTextBox) registerLoad = true; var results = StylizeControlInternal(control, designMode, options, shouldStylize); registerLoad = registerLoad || results.LoadRequired; } if (!designMode && registerLoad) form.Load += Form_Load; } private static void Form_Load(object sender, EventArgs e) { Form form = (Form)sender; form.Load -= Form_Load; StylizeInternal(form, false, StylizerOptions.None, ShouldStylize); } private static bool ShouldStylize(Control control) { return control is CRichTextBox; } public static void SetupTitleBar(Form form) { form.ShowIcon = false; form.Load += Form_LoadDarkMode; var titleBars = form.Controls.Find("titleBar", true); if (titleBars.Length > 0 && titleBars[0].Parent is TableLayoutPanel tbl) { tbl.Controls.Remove(titleBars[0]); if (tbl.RowStyles.Count > 0) tbl.RowStyles[0].Height = 0; form.Height -= titleBars[0].Height; } } private static void Form_LoadDarkMode(object sender, EventArgs e) { var form = (Form)sender; if (ColorTheme.Current.IsDark) ImmersiveDarkMode.UseImmersiveDarkMode(form, true); } public static void StylizeControl(Control control, bool designMode, StylizerOptions options = StylizerOptions.None, Func shouldStylize = null) { StylizeControlInternal(control, designMode, options, shouldStylize); } private static StylizeControlResults StylizeControlInternal(Control control, bool designMode, StylizerOptions options, Func shouldStylize = null) { if (designMode || (!options.HasFlag(StylizerOptions.StylizeTitleBar) && control is TitleBarControl)) return new StylizeControlResults(false); bool loadRequired = control is CRichTextBox; if (shouldStylize == null || shouldStylize(control)) { if (control is DataGridView grid) StyleGrid(grid); else if (control is FormButton formButton) StyleFormButton(formButton); else if (control is Button button) StyleButton(button); else if (control is TextBox textBox) StyleTextBox(textBox); else if (control is CRichTextBox richTextBox) StyleRichTextBox(richTextBox); else if (control is LinkLabel linkLabel) StyleLinkLabel(linkLabel); else if (control is ComboBox comboBox) StyleCombo(comboBox); else if (control is CheckBox checkBox) StyleCheckBox(checkBox); else if (control is CheckedListBox checkedListBox) StyleCheckdListBox(checkedListBox); else if (control is ContextMenuStrip cms) StyleContextMenuStrip(cms); else if (control is GroupBox groupBox) StyleGroupBox(groupBox); else if (control is SlideShowPictureBox pb) StylePictureBox(pb); else StyleDefault(control); } foreach (Control subControl in control.Controls) { var results = StylizeControlInternal(subControl, designMode, options); loadRequired = loadRequired || results.LoadRequired; } return new StylizeControlResults(loadRequired); } private static void StylePictureBox(SlideShowPictureBox pb) { pb.BackColor = CurrentTheme.ImageBackground; } private static void StyleTextBox(TextBox textBox) { textBox.BackColor = CurrentTheme.TextBoxBackground; textBox.ForeColor = CurrentTheme.Text; textBox.BorderStyle = BorderStyle.FixedSingle; } private static void StyleRichTextBox(CRichTextBox textBox) { textBox.BackColor = CurrentTheme.TextBoxBackground; textBox.ForeColor = CurrentTheme.Text; } private static void StyleGroupBox(GroupBox groupBox) { groupBox.Paint += GroupBox_Paint; } private static void GroupBox_Paint(object sender, PaintEventArgs e) { GroupBox box = (GroupBox)sender; Graphics g = e.Graphics; e.Graphics.Clear(SystemColors.Control); e.Graphics.DrawString(box.Text, box.Font, Brushes.Black, 0, 0); Brush textBrush = new SolidBrush(CurrentTheme.Text); Brush borderBrush = new SolidBrush(CurrentTheme.Border); Pen borderPen = new Pen(borderBrush); SizeF strSize = g.MeasureString(box.Text, box.Font); Rectangle rect = new Rectangle(box.ClientRectangle.X, box.ClientRectangle.Y + (int)(strSize.Height / 2), box.ClientRectangle.Width - 1, box.ClientRectangle.Height - (int)(strSize.Height / 2) - 1); g.Clear(box.BackColor); g.DrawString(box.Text, box.Font, textBrush, box.Padding.Left, 0); g.DrawLine(borderPen, rect.Location, new Point(rect.X, rect.Y + rect.Height)); g.DrawLine(borderPen, new Point(rect.X + rect.Width, rect.Y), new Point(rect.X + rect.Width, rect.Y + rect.Height)); g.DrawLine(borderPen, new Point(rect.X, rect.Y + rect.Height), new Point(rect.X + rect.Width, rect.Y + rect.Height)); g.DrawLine(borderPen, new Point(rect.X, rect.Y), new Point(rect.X + box.Padding.Left, rect.Y)); g.DrawLine(borderPen, new Point(rect.X + box.Padding.Left + (int)(strSize.Width), rect.Y), new Point(rect.X + rect.Width, rect.Y)); } private static void StyleDefault(Control control) { control.BackColor = CurrentTheme.Window; control.ForeColor = CurrentTheme.Text; } public static void StylizeControl(ToolStripDropDownButton control, bool designMode) { if (designMode) return; control.BackColor = CurrentTheme.Window; control.ForeColor = CurrentTheme.Text; foreach (var item in control.DropDownItems) { if (item is ToolStripMenuItem menuItem) { menuItem.BackColor = CurrentTheme.Window; menuItem.ForeColor = CurrentTheme.Text; menuItem.Paint += MenuItem_Paint; StyleMenuDropDownItems(menuItem); continue; } if (item is ToolStripSeparator toolStripSeparator) toolStripSeparator.Paint += ToolStripSeparator_Paint; } } private static void MenuItem_Paint(object sender, PaintEventArgs e) { if (sender is ToolStripMenuItem item) { PointF point = new PointF(24, 2); Rectangle rectangle = new Rectangle(2, 0, item.Size.Width - 4, item.Size.Height); if (item.Selected) { e.Graphics.FillRectangle(new SolidBrush(ColorTheme.Current.Highlight), rectangle); e.Graphics.DrawString(item.Text, item.Font, new SolidBrush(ColorTheme.Current.HighlightText), point); return; } e.Graphics.FillRectangle(new SolidBrush(ColorTheme.Current.Window), rectangle); e.Graphics.DrawString(item.Text, item.Font, new SolidBrush(ColorTheme.Current.Text), point); } } private static void StyleContextMenuStrip(ContextMenuStrip cms) { cms.Renderer = new CToolStripRenderer(); cms.BackColor = CurrentTheme.Window; cms.ForeColor = CurrentTheme.Text; cms.ShowCheckMargin = false; cms.ShowImageMargin = false; foreach (var item in cms.Items) { if (!(item is ToolStripMenuItem menuItem)) continue; menuItem.BackColor = CurrentTheme.Window; menuItem.ForeColor = CurrentTheme.Text; StyleMenuDropDownItems(menuItem); } } private static void StyleMenuDropDownItems(ToolStripMenuItem menuItem) { foreach (var subItem in menuItem.DropDownItems) { if (subItem is ToolStripSeparator separator) { separator.Paint += ToolStripSeparator_Paint; continue; } if (!(subItem is ToolStripMenuItem subMenuItem)) continue; subMenuItem.BackColor = CurrentTheme.Window; subMenuItem.ForeColor = CurrentTheme.Text; } } private static void StyleCheckdListBox(CheckedListBox checkedListBox) { checkedListBox.BorderStyle = BorderStyle.None; } private static void StyleLinkLabel(LinkLabel label) { label.LinkColor = CurrentTheme.LinkText; } private static void StyleButton(Button button) { button.FlatStyle = CurrentTheme.ButtonFlatStyle; if (button.FlatStyle == FlatStyle.Flat) button.FlatAppearance.BorderSize = 0; button.ForeColor = CurrentTheme.ButtonTextColor; button.BackColor = CurrentTheme.ButtonBackground; } private static void StyleFormButton(FormButton button) { button.FlatStyle = FlatStyle.Flat; button.FlatAppearance.BorderSize = 0; button.BackColor = CurrentTheme.Window; } private static void StyleCheckBox(CheckBox checkBox) { checkBox.ForeColor = CurrentTheme.Text; checkBox.BackColor = CurrentTheme.CheckBoxBackground; checkBox.FlatStyle = FlatStyle.Flat; checkBox.TextAlign = ContentAlignment.MiddleRight; checkBox.FlatAppearance.BorderSize = 0; checkBox.FlatAppearance.MouseOverBackColor = CurrentTheme.Window; checkBox.FlatAppearance.CheckedBackColor = CurrentTheme.Window; checkBox.FlatAppearance.MouseDownBackColor = CurrentTheme.Window; checkBox.AutoSize = true; } private static void StyleCombo(ComboBox comboBox) { comboBox.BackColor = CurrentTheme.TextBoxBackground; comboBox.ForeColor = CurrentTheme.Text; comboBox.DrawMode = DrawMode.OwnerDrawFixed; comboBox.FlatStyle = CurrentTheme.ComboFlatStyle; // CComboBox implements it's own DrawItem if (comboBox is CComboBox) return; comboBox.DrawItem += CComboBox.ComboBoxDrawItem; } private static void StyleGrid(DataGridView view) { view.DefaultCellStyle.NullValue = "N/A"; view.RowHeadersVisible = false; view.AutoGenerateColumns = false; view.ShowCellToolTips = false; view.EnableHeadersVisualStyles = false; view.ColumnHeadersDefaultCellStyle.BackColor = CurrentTheme.Window; view.ColumnHeadersDefaultCellStyle.ForeColor = CurrentTheme.Text; view.ColumnHeadersDefaultCellStyle.SelectionBackColor = view.ColumnHeadersDefaultCellStyle.BackColor; view.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.None; view.ColumnHeadersDefaultCellStyle.Padding = new Padding(0, 4, 0, 4); view.ForeColor = CurrentTheme.Text; view.BackgroundColor = CurrentTheme.GridBackground; view.RowsDefaultCellStyle.ForeColor = CurrentTheme.Text; view.RowsDefaultCellStyle.BackColor = CurrentTheme.GridRow; view.AlternatingRowsDefaultCellStyle.ForeColor = CurrentTheme.Text; view.AlternatingRowsDefaultCellStyle.BackColor = CurrentTheme.GridRowAlt; view.DefaultCellStyle.SelectionBackColor = CurrentTheme.Highlight; view.BorderStyle = BorderStyle.None; view.Paint += View_Paint; if (!CurrentTheme.GridRowBorder) view.CellPainting += View_CellPainting; } private static void View_Paint(object sender, PaintEventArgs e) { DataGridView view = sender as DataGridView; Rectangle rect = new Rectangle(view.ClientRectangle.Location, new Size(view.ClientRectangle.Width -1, view.ClientRectangle.Height - 1)); e.Graphics.DrawRectangle(new Pen(CurrentTheme.GridBorder), rect); } private static void View_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) { e.AdvancedBorderStyle.Right = DataGridViewAdvancedCellBorderStyle.None; e.AdvancedBorderStyle.Bottom = DataGridViewAdvancedCellBorderStyle.None; e.AdvancedBorderStyle.Left = DataGridViewAdvancedCellBorderStyle.None; e.AdvancedBorderStyle.Top = DataGridViewAdvancedCellBorderStyle.None; } private static void ToolStripSeparator_Paint(object sender, PaintEventArgs e) { ToolStripSeparator toolStripSeparator = (ToolStripSeparator)sender; int width = toolStripSeparator.Width; int height = toolStripSeparator.Height; Color foreColor = CurrentTheme.Separator; Color backColor = CurrentTheme.Window; DpiScale dpiScale = new DpiScale(e.Graphics); int pad = dpiScale.ScaleIntX(8); e.Graphics.FillRectangle(new SolidBrush(backColor), 0, 0, width, height); e.Graphics.DrawLine(new Pen(foreColor), pad, height / 2, width, height / 2); } } } ================================================ FILE: DoomLauncher/TabViews/BasicTabViewCtrl.Designer.cs ================================================ namespace DoomLauncher { partial class BasicTabViewCtrl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.SuspendLayout(); // // BasicTabViewCtrl // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Name = "BasicTabViewCtrl"; this.ResumeLayout(false); } #endregion } } ================================================ FILE: DoomLauncher/TabViews/BasicTabViewCtrl.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class BasicTabViewCtrl : UserControl, ITabView, ICloneable { public event EventHandler DataSourceChanging; public event EventHandler DataSourceChanged; protected readonly object m_key; protected readonly GameFileFieldType[] m_selectFields; protected GameFileViewFactory m_factory; public BasicTabViewCtrl(object key, string title, IGameFileDataSourceAdapter adapter, GameFileFieldType[] selectFields, GameFileViewFactory factory) : this (key, title, adapter, selectFields, factory.CreateGameFileView()) { m_factory = factory; } protected BasicTabViewCtrl(object key, string title, IGameFileDataSourceAdapter adapter, GameFileFieldType[] selectFields, IGameFileView view) { InitializeComponent(); m_key = key; Title = title; Adapter = adapter; m_selectFields = selectFields.ToArray(); UserControl ctrl = (UserControl)view; ctrl.Anchor = AnchorStyles.Top | AnchorStyles.Left; ctrl.Dock = DockStyle.Fill; Controls.Add(ctrl); GameFileView = (IGameFileView)ctrl; } public virtual object Clone() { // TODO this is also dumb BasicTabViewCtrl view = new BasicTabViewCtrl(m_key, Title, Adapter, m_selectFields, GameFileViewFactory.CreateGameFileViewGrid()); SetBaseCloneProperties(view); return view; } protected void SetBaseCloneProperties(ITabView view) { if (GameFileViewControl is IGameFileColumnView columnView) view.SetColumnConfig(columnView.ColumnFields, GetColumnConfig().ToArray()); else view.SetColumnConfig(GameFileViewFactory.DefaultColumnTextFields, DataCache.Instance.GetColumnConfig()); } public List GetColumnConfig() { string tabKey = Key.ToString(); if (GameFileViewControl is IGameFileColumnView columnView) { List config = new List(); foreach (string key in columnView.GetColumnKeyOrder()) config.Add(new ColumnConfig(tabKey, key, columnView.GetColumnWidth(key), columnView.GetColumnSort(key))); return config; } else if (GameFileViewControl is IGameFileSortableView sortableView) { List config = new List(); string key = sortableView.GetSortedColumnKey(); if (!string.IsNullOrEmpty(key) && sortableView.GetColumnSort(key) != SortOrder.None) config.Add(new ColumnConfig(tabKey, key, 0, sortableView.GetColumnSort(key))); return config; } else { throw new InvalidOperationException("GameFileViewControl is not IGameFileSortableView"); } } public void SetColumnConfig(ColumnField[] columnTextFields, ColumnConfig[] colConfig) { var currentColumnConfig = GetColumnConfig(); if (CompareColumnConfig(currentColumnConfig, colConfig)) return; string key = Key.ToString(); if (GameFileViewControl is IGameFileColumnView columnView) { columnView.SuspendLayout(); var sortedColumns = SortColumns(key, columnTextFields, colConfig); columnView.SetColumnFields(sortedColumns); SetSortedColumn(key, columnView, colConfig); SetColumnWidths(key, columnView, colConfig); columnView.SetColumnFormat("ReleaseDate", CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); columnView.SetColumnFormat("Downloaded", CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); columnView.SetColumnFormat("LastPlayed", CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); GameFileViewControl.ResumeLayout(); } else if (GameFileViewControl is IGameFileSortableView sortableView) { var sortedColumns = SortColumns(key, columnTextFields, colConfig); foreach (var sortColumn in sortedColumns) { ColumnConfig config = colConfig.FirstOrDefault(x => x.Sort != SortOrder.None && x.Parent == key && sortColumn.DataKey.Equals(x.Column, StringComparison.InvariantCultureIgnoreCase)); if (config != null) { sortableView.SetSortedColumn(config.Column, config.Sort); break; } } } } private bool CompareColumnConfig(IList x, IList y) { if (x.Count != y.Count) return false; for (int i = 0; i < x.Count; i++) { var configX = x[i]; var configY = y[i]; if (configX.Column != configY.Column) return false; // Ignore the last column. Set to take up entire width and the values will never match. if (i != x.Count - 1 && configX.Width != configY.Width) return false; } return true; } private static ColumnField[] SortColumns(string tab, ColumnField[] items, ColumnConfig[] config) { List ret = new List(); //Pre 1.1.0 update check. Without this maps will be thrown on the end of the column list bool setMapLocation = config.Length > 0 && !config.Any(x => x.Column.Equals("mapcount", StringComparison.OrdinalIgnoreCase)); foreach (ColumnConfig configItem in config) { var check = items.FirstOrDefault(x => configItem.Parent == tab && x.DataKey.Equals(configItem.Column, StringComparison.InvariantCultureIgnoreCase)); if (check != null) ret.Add(check); } //continue with pre 1.1.0 check (this can probably be removed at this point, though) if (setMapLocation && ret.Count > 4) { var maps = items.FirstOrDefault(x => x.Title.Equals("maps", StringComparison.InvariantCultureIgnoreCase)); if (maps != null) { ret.Remove(maps); ret.Insert(4, maps); } } return ret.Union(items).ToArray(); } private static void SetColumnWidths(string tab, IGameFileColumnView ctrl, ColumnConfig[] config) { IEnumerable configSet = config.Where(x => x.Parent == tab); foreach (ColumnConfig col in configSet) { ctrl.SetColumnWidth(col.Column, col.Width); } } private static void SetSortedColumn(string tab, IGameFileColumnView gameFileViewControl, ColumnConfig[] colConfig) { ColumnConfig config = colConfig.FirstOrDefault(x => x.Parent == tab && x.Sort != SortOrder.None); if (config != null) gameFileViewControl.SetSortedColumn(config.Column, config.Sort); } public IGameFileView GameFileViewControl { get { return GameFileView; } } public object Key { get { return m_key; } } protected virtual bool FilterIWads { get; set; } = true; public virtual void SetGameFiles() { GameFileGetOptions options = new GameFileGetOptions(); options.SelectFields = m_selectFields; SetDataSource(Adapter.GetGameFiles(options)); } public virtual void SetGameFiles(IEnumerable searchFields) { IEnumerable items = Enumerable.Empty(); foreach (GameFileSearchField sf in searchFields) items = items.Union(Adapter.GetGameFiles(new GameFileGetOptions(m_selectFields, sf))); SetDataSource(items); } public void SetGameFilesData(IEnumerable gameFiles) { SetDataSource(gameFiles); } public virtual void UpdateDataSourceFile(IGameFile gameFile) { if (GameFileView.DataSource != null) { foreach (IGameFile item in GameFileView.DataSource) { if (item.Equals(gameFile)) { Array.ForEach(item.GetType().GetProperties().Where(x => x.SetMethod != null).ToArray(), x => x.SetValue(item, x.GetValue(gameFile))); GameFileView.UpdateGameFile(gameFile); ((UserControl)GameFileView).Invalidate(true); break; } } } } protected void SetDisplayText(string text) { GameFileView.SetDisplayText(text); } protected void SetDataSource(IEnumerable gameFiles) { if (FilterIWads && !(Adapter is IdGamesDataAdapater)) gameFiles = gameFiles.Except(Adapter.GetGameFileIWads()); var args = new GameFileListEventArgs(gameFiles); DataSourceChanging?.Invoke(this, args); gameFiles = args.GameFiles; if (!gameFiles.Any()) { GameFileView.DataSource = null; GameFileView.SetDisplayText("No Results Found"); } else { GameFileView.DataSource = gameFiles.ToList(); } DataSourceChanged?.Invoke(this, args); } public virtual bool IsLocal { get { return true; } } public virtual bool IsEditAllowed { get { return true; } } public virtual bool IsDeleteAllowed { get { return true; } } public virtual bool IsSearchAllowed { get { return true; } } public virtual bool IsPlayAllowed { get { return true; } } public virtual bool IsAutoSearchAllowed { get { return true; } } public string Title { get; set; } public virtual IGameFileDataSourceAdapter Adapter { get; set; } public IGameFileView GameFileView { get; private set; } } } ================================================ FILE: DoomLauncher/TabViews/BasicTabViewCtrl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: DoomLauncher/TabViews/IWadTabViewCtrl.Designer.cs ================================================ namespace DoomLauncher { partial class IWadTabViewCtrl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { components = new System.ComponentModel.Container(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; } #endregion } } ================================================ FILE: DoomLauncher/TabViews/IWadTabViewCtrl.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.Linq; namespace DoomLauncher { public partial class IWadTabViewCtrl : LocalTabViewCtrl { private static readonly GameFileFieldType[] SearchFields = new[] { GameFileFieldType.GameFileID, GameFileFieldType.Filename }; private readonly IDataSourceAdapter m_dsAdapter; public IWadTabViewCtrl(object key, string title, IDataSourceAdapter adapter, GameFileFieldType[] selectFields, ITagMapLookup lookup, GameFileViewFactory factory) : base(key, title, adapter, selectFields, lookup, factory) { InitializeComponent(); m_dsAdapter = adapter; } public override void SetGameFiles() { SetDataSource(m_dsAdapter.GetGameFileIWads()); } public override void SetGameFiles(IEnumerable searchFields) { var gameFileIWads = m_dsAdapter.GetGameFileIWads(); IEnumerable items = Enumerable.Empty(); foreach (GameFileSearchField sf in searchFields) items = items.Union(Adapter.GetGameFiles(new GameFileGetOptions(SearchFields, sf))); gameFileIWads = gameFileIWads.Where(x => items.Any(y => x.GameFileID == y.GameFileID)); SetDataSource(gameFileIWads); } public override bool IsSearchAllowed => true; protected override bool FilterIWads => false; } } ================================================ FILE: DoomLauncher/TabViews/IdGamesTabViewCtrl.Designer.cs ================================================ namespace DoomLauncher { partial class IdGamesTabViewCtrl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { components = new System.ComponentModel.Container(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; } #endregion } } ================================================ FILE: DoomLauncher/TabViews/IdGamesTabViewCtrl.cs ================================================ using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { public partial class IdGamesTabViewCtrl : OptionsTabViewCtrl { private bool m_working; private string m_errorMessage; public IdGamesTabViewCtrl(object key, string title, IGameFileDataSourceAdapter adapter, GameFileFieldType[] selectFields, GameFileViewFactory factory) : base(key, title, adapter, selectFields, factory) { InitializeComponent(); } public override void SetGameFiles() { SetGameFiles(null); } public override void SetGameFiles(IEnumerable searchFields) { if (m_working) return; m_working = true; IdGamesDataSource = null; SetDisplayText("Searching..."); BackgroundWorker worker = new BackgroundWorker(); worker.DoWork += UpdateIdGamesView_Worker; worker.RunWorkerCompleted += UpdateIdGamesViewCompleted; worker.RunWorkerAsync(searchFields); } private void UpdateIdGamesView_Worker(object sender, DoWorkEventArgs e) { m_errorMessage = string.Empty; try { IEnumerable searchFields = e.Argument as IEnumerable; if (searchFields == null || !searchFields.Any()) { IdGamesDataSource = Adapter.GetGameFiles(); } else { IEnumerable ret = new IGameFile[0]; foreach (GameFileSearchField sf in searchFields) ret = ret.Union(Adapter.GetGameFiles(new GameFileGetOptions(sf))); IdGamesDataSource = ret; } } catch (IdGamesBadResponseException ex) { m_errorMessage = ex.Message; IdGamesDataSource = null; } catch (Exception ex) { m_errorMessage = $"Failed to query id games: {ex.Message}"; IdGamesDataSource = null; } } private void UpdateIdGamesViewCompleted(object sender, EventArgs e) { m_working = false; if (m_errorMessage.Length > 0) { SetDisplayText(m_errorMessage); return; } if (IdGamesDataSource != null) { SetDataSource(IdGamesDataSource); } else { SetDisplayText("Error retrieving data from id Games"); MessageBox.Show(this, "There was an error retrieving data from id Games.", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } public override bool IsLocal { get { return false; } } public override bool IsEditAllowed { get { return false; } } public override bool IsDeleteAllowed { get { return false; } } public override bool IsSearchAllowed { get { return true; } } public override bool IsPlayAllowed { get { return false; } } public override bool IsAutoSearchAllowed { get { return false; } } public IEnumerable IdGamesDataSource { get; set; } protected override bool FilterIWads { get { return false; } } } } ================================================ FILE: DoomLauncher/TabViews/LocalTabViewCtrl.cs ================================================ using DoomLauncher.Interfaces; using System.Drawing; using System.Linq; namespace DoomLauncher { public class LocalTabViewCtrl : BasicTabViewCtrl { private readonly ITagMapLookup m_tagLookup; public LocalTabViewCtrl(object key, string title, IGameFileDataSourceAdapter adapter, GameFileFieldType[] selectFields, ITagMapLookup lookup, GameFileViewFactory factory) : base(key, title, adapter, selectFields, factory) { GameFileViewControl.DoomLauncherParent = this; m_tagLookup = lookup; if (m_tagLookup != null && GameFileViewControl is IGameFileColumnView columnView) { columnView.CustomRowColorPaint = true; columnView.CustomRowPaint += GameFileViewControl_CustomRowPaint; } } void GameFileViewControl_CustomRowPaint(object sender, System.ComponentModel.CancelEventArgs e) { if (GameFileViewControl is IGameFileColumnView columnView) { e.Cancel = false; IGameFile gameFile = columnView.CustomRowPaintDataBoundItem; if (gameFile != null) { ITagData tag = m_tagLookup.GetTags(gameFile).FirstOrDefault(x => x.HasColor && x.Color.HasValue); if (tag != null) columnView.CustomRowPaintForeColor = Color.FromArgb(tag.Color.Value); else columnView.CustomRowPaintForeColor = ColorTheme.Current.Text; } } } } } ================================================ FILE: DoomLauncher/TabViews/OptionsTabViewCtrl.Designer.cs ================================================ namespace DoomLauncher { partial class OptionsTabViewCtrl { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { components = new System.ComponentModel.Container(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; } #endregion } } ================================================ FILE: DoomLauncher/TabViews/OptionsTabViewCtrl.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Linq; namespace DoomLauncher { public partial class OptionsTabViewCtrl : LocalTabViewCtrl { public OptionsTabViewCtrl(object key, string title, IGameFileDataSourceAdapter adapter, GameFileFieldType[] selectFields, GameFileViewFactory factory) : base(key, title, adapter, selectFields, null, factory) { InitializeComponent(); } public OptionsTabViewCtrl(object key, string title, IGameFileDataSourceAdapter adapter, GameFileFieldType[] selectFields, ITagMapLookup lookup, GameFileViewFactory factory) : base(key, title, adapter, selectFields, lookup, factory) { InitializeComponent(); } public GameFileGetOptions Options { get; set; } public override void SetGameFiles() { if (Options != null) SetGameFiles(null); else base.SetGameFiles(); } public override void SetGameFiles(IEnumerable searchFields) { if (Options != null) { GameFileGetOptions options = new GameFileGetOptions(); options.Limit = 25; options.OrderBy = OrderType.Desc; options.OrderField = GameFileFieldType.Downloaded; options.SelectFields = base.m_selectFields; if (searchFields != null && searchFields.Any()) { IEnumerable items = new IGameFile[0]; foreach (GameFileSearchField sf in searchFields) { options.SearchField = sf; items = items.Union(Adapter.GetGameFiles(options)); } base.SetDataSource(items); } else { base.SetDataSource(Adapter.GetGameFiles(options)); } } else { base.SetGameFiles(searchFields); } } } } ================================================ FILE: DoomLauncher/TabViews/TagTabView.Designer.cs ================================================ namespace DoomLauncher { partial class TagTabView { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// [System.CodeDom.Compiler.GeneratedCode("Winform Designer", "VS2015 SP1")] private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Text = "TagTabView"; } #endregion } } ================================================ FILE: DoomLauncher/TabViews/TagTabView.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Linq; namespace DoomLauncher { public partial class TagTabView : BasicTabViewCtrl { private IDataSourceAdapter m_tagAdapter; public TagTabView(object key, string title, IDataSourceAdapter adapter, GameFileFieldType[] selectFields, ITagData tag, GameFileViewFactory factory) : this(key, title, adapter, selectFields, tag, factory.CreateGameFileView()) { m_factory = factory; } public TagTabView(object key, string title, IDataSourceAdapter adapter, GameFileFieldType[] selectFields, ITagData tag, IGameFileView view) : base(key, title, adapter, selectFields, view) { InitializeComponent(); TagDataSource = tag; m_tagAdapter = adapter; FilterIWads = false; } public override object Clone() { TagTabView view = new TagTabView(m_key, Title, m_tagAdapter, m_selectFields, TagDataSource, GameFileViewFactory.CreateGameFileViewGrid()); SetBaseCloneProperties(view); return view; } public override void SetGameFiles() { GameFileGetOptions options = new GameFileGetOptions(); options.SelectFields = m_selectFields; SetDataSource(m_tagAdapter.GetGameFiles(options, TagDataSource)); } public override void SetGameFiles(IEnumerable searchFields) { IEnumerable items = new IGameFile[0]; foreach (GameFileSearchField sf in searchFields) { items = items.Union(m_tagAdapter.GetGameFiles(new GameFileGetOptions(m_selectFields, sf), TagDataSource)); } SetDataSource(items); } public ITagData TagDataSource { get; set; } } } ================================================ FILE: DoomLauncher/TabViews/UntaggedTabView.cs ================================================ using DoomLauncher.Interfaces; using System.Collections.Generic; using System.Linq; namespace DoomLauncher { class UntaggedTabView : LocalTabViewCtrl { public UntaggedTabView(object key, string title, IGameFileDataSourceAdapter adapter, GameFileFieldType[] selectFields, ITagMapLookup lookup, GameFileViewFactory factory) : base(key, title, adapter, selectFields, lookup, factory) { } public override void SetGameFiles() { SetDataSource(Adapter.GetUntaggedGameFiles()); } public override void SetGameFiles(IEnumerable searchFields) { IEnumerable items = new IGameFile[0]; var untaggedFiles = Adapter.GetUntaggedGameFiles(); foreach (GameFileSearchField sf in searchFields) { var search = Adapter.GetGameFiles(new GameFileGetOptions(m_selectFields, sf)); items = items.Union(untaggedFiles.Intersect(search)); } SetDataSource(items); } } } ================================================ FILE: DoomLauncher/TextFileParsers/ZdlParser.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; namespace DoomLauncher { public class ZdlParser { private static string s_regexFull = @"{0}=.*"; private static string s_regex = @"{0}="; private readonly List m_errors = new List(); private readonly IEnumerable m_sourcePorts; private readonly IEnumerable m_iwads; public ZdlParser(IEnumerable sourcePorts, IEnumerable iwads) { m_sourcePorts = sourcePorts; m_iwads = iwads; } public string[] Errors { get { return m_errors.ToArray(); } } public IGameFile[] Parse(string fileText) { m_errors.Clear(); List gameFiles = new List(); fileText = fileText.Replace("\r\n", "\n"); ; if (!fileText.StartsWith("[zdl.save]")) { m_errors.Add("Not a valid zdl file"); return new IGameFile[] {}; } string zdlFilePath = null; int zdlFileNum = 0; do { zdlFilePath = FindValue(fileText, string.Format("file{0}", zdlFileNum), s_regexFull).Trim(); zdlFileNum++; if (!string.IsNullOrEmpty(zdlFilePath)) { GameFile gameFile = new GameFile(); gameFile.FileName = zdlFilePath; gameFiles.Add(gameFile); } } while (!string.IsNullOrEmpty(zdlFilePath)); if (gameFiles.Count > 0) { string skill = FindValue(fileText, "skill", s_regexFull); string port = FindValue(fileText, "port", s_regexFull); string warp = FindValue(fileText, "warp", s_regexFull); string iwad = FindValue(fileText, "iwad", s_regexFull); string parameters = FindValue(fileText, "extra", s_regexFull); IGameFile gameFile = gameFiles[0]; gameFile.SettingsSkill = skill; gameFile.SettingsMap = warp; gameFile.Map = warp; gameFile.SettingsExtraParams = parameters; gameFile.SourcePortID = GetSourcePort(port); gameFile.IWadID = GetIWad(iwad); } else { m_errors.Add("Did not contain any files (e.g. file=0)"); } return gameFiles.ToArray(); } private int? GetSourcePort(string port) { ISourcePortData sp = m_sourcePorts.FirstOrDefault(x => x.Name.Equals(port, StringComparison.InvariantCultureIgnoreCase)); if (sp == null) m_errors.Add(string.Format("Could not find Source Port - {0}", port)); if (sp != null) return sp.SourcePortID; return null; } private int? GetIWad(string iwad) { iwad = Path.GetFileNameWithoutExtension(iwad); IIWadData data = m_iwads.FirstOrDefault(x => Path.GetFileNameWithoutExtension(x.FileName).Equals(iwad, StringComparison.InvariantCultureIgnoreCase)); if (data == null) m_errors.Add(string.Format("Could not find IWAD - {0}", iwad)); if (data != null) return data.IWadID; return null; } private string FindValue(string text, string category, string regexFull) { Match m = new Regex(string.Format(regexFull, category), RegexOptions.Multiline).Match(text); if (m.Success) return m.Value.Substring((new Regex(string.Format(s_regex, category)).Match(m.Value).Value.Length)); return string.Empty; } } } ================================================ FILE: DoomLauncher/Update/ApplicationUpdate.cs ================================================ using Octokit; using System; using System.Linq; using System.Threading.Tasks; namespace DoomLauncher { public class ApplicationUpdate { private readonly TimeSpan m_timeout; public ApplicationUpdate(TimeSpan timeout) { m_timeout = timeout; } public async Task GetUpdateApplicationInfo(Version currentVersion) { try { var github = new GitHubClient(new ProductHeaderValue($"{Util.GitHubUser}.{Util.GitHubRepositoryName}")); github.SetRequestTimeout(m_timeout); var release = await github.Repository.Release.GetLatest(Util.GitHubUser, Util.GitHubRepositoryName); if (release != null) { Version remoteVersion = new Version(release.Name); if (remoteVersion > currentVersion) { ReleaseAsset asset; if (LauncherPath.IsInstalled()) asset = release.Assets.FirstOrDefault(x => x.Name.EndsWith("_install.zip")); else asset = release.Assets.FirstOrDefault(x => x.Name.EndsWith(".zip") && !x.Name.EndsWith("_install.zip")); if (asset != null) return new ApplicationUpdateInfo(remoteVersion, asset.BrowserDownloadUrl, release.HtmlUrl, asset.CreatedAt.LocalDateTime); } } } catch(RateLimitExceededException) { return null; } return null; } } } ================================================ FILE: DoomLauncher/Update/ApplicationUpdateInfo.cs ================================================ using System; namespace DoomLauncher { public class ApplicationUpdateInfo { public ApplicationUpdateInfo(Version version, string downloadUrl, string releasePage, DateTime releaseDate) { Version = version; DownloadUrl = downloadUrl; ReleasePageUrl = releasePage; ReleaseDate = releaseDate; } public readonly Version Version; public readonly string DownloadUrl; public readonly string ReleasePageUrl; public readonly DateTime ReleaseDate; } } ================================================ FILE: DoomLauncher/Update/ApplicationUpdater.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Windows.Forms; namespace DoomLauncher { class RenameFile { public RenameFile(string originalName, string newName) { OriginalName = originalName; NewName = newName; } public string OriginalName; public string NewName; } public class ApplicationUpdater { private static readonly string s_updateExtension = ".bak"; private readonly string m_zipArchive; private readonly string m_executingDirectory; public string LastError { get; private set; } = string.Empty; public ApplicationUpdater(string zipArchive, string executingDirectory) { m_zipArchive = zipArchive; m_executingDirectory = executingDirectory; } public bool Execute() { if (LauncherPath.IsInstalled()) return ExecuteInstallUpdate(); else return ExecuteNonInstallUpdate(); } private bool ExecuteInstallUpdate() { try { string extractFolder = Path.Combine(Path.GetTempPath(), "DoomLauncherUpdate"); if (Directory.Exists(extractFolder)) Directory.Delete(extractFolder, true); using (ZipArchive za = ZipFile.OpenRead(m_zipArchive)) za.ExtractToDirectory(extractFolder); string setup = Path.Combine(extractFolder, "setup.exe"); if (File.Exists(setup)) { Process.Start(setup); Application.Exit(); } } catch(Exception ex) { SetLastErrorException(ex); return false; } return true; } private bool ExecuteNonInstallUpdate() { List renamedFiles = new List(); try { using (ZipArchive za = ZipFile.OpenRead(m_zipArchive)) { var entries = za.Entries.Where(x => x.Name.Length > 0 && (x.FullName.StartsWith("DoomLauncher\\") || x.FullName.StartsWith("DoomLauncher/"))); if (!entries.Any()) { LastError = "Could not find DoomLauncher folder in archive."; return false; } foreach (var entry in entries) { string file = GetCopyFile(entry); FileInfo fileInfo = new FileInfo(file); if (fileInfo.Exists) renamedFiles.Add(new RenameFile(fileInfo.Name, RenameAppFile(fileInfo))); entry.ExtractToFile(file); } } } catch (Exception ex) { SetLastErrorException(ex); RevertRenamedFiles(renamedFiles); return false; } Process.Start(Path.Combine(m_executingDirectory, Util.GetExecutableNoPath())); return true; } private string GetCopyFile(ZipArchiveEntry entry) { if (entry.FullName.Contains("x86")) return Path.Combine(Directory.GetCurrentDirectory(), "x86", entry.Name); if (entry.FullName.Contains("x64")) return Path.Combine(Directory.GetCurrentDirectory(), "x64", entry.Name); return Path.Combine(Directory.GetCurrentDirectory(), entry.Name); } private void SetLastErrorException(Exception ex) { LastError = string.Concat("**ERROR TRACE**", Environment.NewLine, ex.Message, Environment.NewLine, ex.StackTrace); } private void RevertRenamedFiles(List renamedFiles) { foreach (RenameFile file in renamedFiles) { if (File.Exists(file.OriginalName)) File.Delete(file.OriginalName); File.Move(file.NewName, file.OriginalName); } } private string RenameAppFile(FileInfo fileInfo) { string backupName = CreateBackupFileName(fileInfo); if (File.Exists(backupName)) File.Delete(backupName); fileInfo.MoveTo(backupName); return backupName; } private string CreateBackupFileName(FileInfo fileInfo) { return fileInfo.FullName + s_updateExtension; } public static void CleanupUpdateFiles(string executingDirectory) { var files = Directory.GetFiles(executingDirectory).Where(x => x.EndsWith(s_updateExtension)); try { foreach (string file in files) File.Delete(file); } catch { //not critical } } } } ================================================ FILE: DoomLauncher/WindowsVersion/WindowsVersion.cs ================================================ using Microsoft.Win32; using System; namespace DoomLauncher.WindowsVersion { public enum WindowsTheme { Dark, Light, Unknown } public static class WindowsVersionInfo { public static bool GetOsVersionInfo(out Version versionInfo) { try { versionInfo = Environment.OSVersion.Version; return true; } catch { versionInfo = new Version(); return false; } } public static bool IsWindows10OrGreater(Version versionInfo, int build) => versionInfo.Major >= 10 && versionInfo.Build >= build; public static WindowsTheme GetTheme() { try { int res = (int)Registry.GetValue( "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", "AppsUseLightTheme", -1); switch (res) { case 0: return WindowsTheme.Dark; case 1: return WindowsTheme.Light; default: return WindowsTheme.Unknown; } } catch { return WindowsTheme.Unknown; } } } } ================================================ FILE: DoomLauncher/app.manifest ================================================  true ================================================ FILE: DoomLauncher/packages.config ================================================  ================================================ FILE: DoomLauncher.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoomLauncher", "DoomLauncher\DoomLauncher.csproj", "{938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}" ProjectSection(ProjectDependencies) = postProject {52357DD1-59A8-4412-98CA-6601D84B66B4} = {52357DD1-59A8-4412-98CA-6601D84B66B4} EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CheckBoxComboBox", "CheckBoxComboBox\CheckBoxComboBox.csproj", "{70A25201-8EA4-48F8-A4A6-ED13ADF8823C}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WadReader", "WadReader\WadReader.csproj", "{15A9A8FC-096E-4458-AB7F-02386740CF4E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoomLauncherRelease", "DoomLauncherRelease\DoomLauncherRelease.csproj", "{52357DD1-59A8-4412-98CA-6601D84B66B4}" EndProject Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "Setup", "Setup\Setup.vdproj", "{28E7A7D3-E32C-4D2F-AA60-7EF483DEB3A9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BindingListView", "BindingListView\BindingListView.csproj", "{75AF36A8-7797-4023-B183-5B63D448420A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTest", "UnitTest\UnitTest.csproj", "{2E593D49-B240-49BE-9E9D-5DD1250EEDF3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|Mixed Platforms = Debug|Mixed Platforms Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|Mixed Platforms = Release|Mixed Platforms Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Debug|Any CPU.Build.0 = Debug|Any CPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Debug|x86.ActiveCfg = Debug|Any CPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Release|Any CPU.Build.0 = Release|Any CPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Release|Mixed Platforms.Build.0 = Release|Any CPU {938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}.Release|x86.ActiveCfg = Release|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Debug|Any CPU.Build.0 = Debug|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Debug|x86.ActiveCfg = Debug|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Release|Any CPU.ActiveCfg = Release|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Release|Any CPU.Build.0 = Release|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Release|Mixed Platforms.Build.0 = Release|Any CPU {70A25201-8EA4-48F8-A4A6-ED13ADF8823C}.Release|x86.ActiveCfg = Release|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Debug|Any CPU.Build.0 = Debug|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Debug|x86.ActiveCfg = Debug|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Release|Any CPU.ActiveCfg = Release|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Release|Any CPU.Build.0 = Release|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Release|Mixed Platforms.Build.0 = Release|Any CPU {15A9A8FC-096E-4458-AB7F-02386740CF4E}.Release|x86.ActiveCfg = Release|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Debug|Any CPU.Build.0 = Debug|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Debug|x86.ActiveCfg = Debug|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Debug|x86.Build.0 = Debug|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Release|Any CPU.ActiveCfg = Release|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Release|Any CPU.Build.0 = Release|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Release|Mixed Platforms.Build.0 = Release|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Release|x86.ActiveCfg = Release|Any CPU {52357DD1-59A8-4412-98CA-6601D84B66B4}.Release|x86.Build.0 = Release|Any CPU {28E7A7D3-E32C-4D2F-AA60-7EF483DEB3A9}.Debug|Any CPU.ActiveCfg = Debug {28E7A7D3-E32C-4D2F-AA60-7EF483DEB3A9}.Debug|Mixed Platforms.ActiveCfg = Debug {28E7A7D3-E32C-4D2F-AA60-7EF483DEB3A9}.Debug|x86.ActiveCfg = Debug {28E7A7D3-E32C-4D2F-AA60-7EF483DEB3A9}.Release|Any CPU.ActiveCfg = Release {28E7A7D3-E32C-4D2F-AA60-7EF483DEB3A9}.Release|Mixed Platforms.ActiveCfg = Release {28E7A7D3-E32C-4D2F-AA60-7EF483DEB3A9}.Release|x86.ActiveCfg = Release {75AF36A8-7797-4023-B183-5B63D448420A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Debug|Any CPU.Build.0 = Debug|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Debug|x86.ActiveCfg = Debug|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Debug|x86.Build.0 = Debug|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Release|Any CPU.ActiveCfg = Release|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Release|Any CPU.Build.0 = Release|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Release|Mixed Platforms.Build.0 = Release|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Release|x86.ActiveCfg = Release|Any CPU {75AF36A8-7797-4023-B183-5B63D448420A}.Release|x86.Build.0 = Release|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Debug|x86.ActiveCfg = Debug|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Debug|x86.Build.0 = Debug|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Release|Any CPU.Build.0 = Release|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Release|Mixed Platforms.Build.0 = Release|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Release|x86.ActiveCfg = Release|Any CPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {847DA28F-AD38-47C7-9576-BCB3C6B863F1} EndGlobalSection EndGlobal ================================================ FILE: DoomLauncherRelease/App.config ================================================ ================================================ FILE: DoomLauncherRelease/DoomLauncherRelease.csproj ================================================  Debug AnyCPU {52357DD1-59A8-4412-98CA-6601D84B66B4} Exe Properties DoomLauncherRelease DoomLauncherRelease v4.8 512 true AnyCPU true full false bin\Debug\ DEBUG;TRACE prompt 4 AnyCPU pdbonly true bin\Release\ TRACE prompt 4 ================================================ FILE: DoomLauncherRelease/Program.cs ================================================ using System; using System.IO; using System.IO.Compression; using System.Reflection; namespace DoomLauncherRelease { static class Program { static string s_argPath; static int Main(string[] args) { if (args.Length > 0) s_argPath = args[0]; string outputDir = "ReleaseBuild"; string buildDir = CreateBuildDirectory(outputDir); if (!Directory.Exists(Path.Combine(GetBaseDir(), outputDir))) Directory.CreateDirectory(Path.Combine(GetBaseDir(), outputDir)); Directory.CreateDirectory(buildDir); CopyBuildFiles(buildDir); string zipfile = CreateZipFile(outputDir, buildDir, false); string zipfiledest = Path.Combine(GetBaseDir(), outputDir, Path.GetFileName(zipfile)); File.Copy(zipfile, zipfiledest, true); zipfile = CreateZipFile(Path.Combine(GetBaseDir(), "Setup", GetBuildOutputDir()), buildDir, true); zipfiledest = Path.Combine(GetBaseDir(), outputDir, Path.GetFileName(zipfile)); File.Copy(zipfile, zipfiledest, true); return 0; } private static string CreateBuildDirectory(string outputDir) { if (Directory.Exists(outputDir)) Directory.Delete(outputDir, true); return Path.Combine(outputDir, "DoomLauncher"); } private static string CreateZipFile(string outputDir, string buildDir, bool installer) { string zipfile = $"DoomLauncher_{GetVersion(buildDir)}{GetInstallPost(installer)}.zip"; if (File.Exists(zipfile)) File.Delete(zipfile); ZipFile.CreateFromDirectory(outputDir, zipfile); return zipfile; } private static string GetInstallPost(bool installer) { if (installer) return "_install"; return string.Empty; } private static string GetVersion(string buildDir) { string prefix = string.Empty; #if DEBUG prefix = "debug_"; #endif var v = AssemblyName.GetAssemblyName(Path.Combine(buildDir, "DoomLauncher.exe")).Version; return prefix + v.ToString(); } private static void CopyBuildFiles(string outputdir) { foreach (string dir in GetDirectoriesToCreate()) Directory.CreateDirectory(Path.Combine(outputdir, dir)); var files = GetFilesToCopy(); string buildDir = Path.Combine(GetDoomLauncherDir(), GetBuildBinOutputDir()); foreach (var file in files) File.Copy(Path.Combine(buildDir, file), Path.Combine(outputdir, file)); File.Copy(Path.Combine(GetDoomLauncherDir(), "DoomLauncher.sqlite"), Path.Combine(outputdir, "DoomLauncher_.sqlite")); string tileimages = "TileImages"; string source = Path.Combine(buildDir, tileimages); string dest = Path.Combine(outputdir, tileimages); Directory.CreateDirectory(Path.Combine(outputdir, tileimages)); var imagefiles = Directory.GetFiles(source); Array.ForEach(imagefiles, x => File.Copy(x, Path.Combine(dest, Path.GetFileName(x)))); } static string[] GetDirectoriesToCreate() => new string[] { "x86", "x64" }; static string[] GetFilesToCopy() { return new string[] { "CheckBoxComboBox.dll", "DoomLauncher.exe", "DoomLauncher.exe.config", "DoomLauncher.ico", "DoomLauncher.VisualElementsManifest.xml", "Equin.ApplicationFramework.BindingListView.dll", "Help.pdf", "Newtonsoft.Json.dll", "Newtonsoft.Json.xml", "System.Data.SQLite.dll", "System.Memory.dll", "System.Numerics.Vectors.dll", "System.Runtime.CompilerServices.Unsafe.dll", "System.Buffers.dll", "WadReader.dll", "Octokit.dll", "Octokit.xml", "SevenZipSharp.dll", "SharpCompress.dll", "x64\\SQLite.Interop.dll", "x86\\SQLite.Interop.dll", "x64\\7z.dll", "x86\\7z.dll", }; } static string GetDoomLauncherDir() { return Path.Combine(GetBaseDir(), "DoomLauncher"); } static string GetBaseDir() { if (string.IsNullOrEmpty(s_argPath)) return Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\")); else return s_argPath; } static string GetBuildBinOutputDir() { #if DEBUG return Path.Combine("bin", GetBuildOutputDir()); #else return Path.Combine("bin", GetBuildOutputDir()); #endif } static string GetBuildOutputDir() { #if DEBUG return "Debug"; #else return "Release"; #endif } } } ================================================ FILE: DoomLauncherRelease/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.InteropServices; // 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("DoomLauncherRelease")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("DoomLauncherRelease")] [assembly: AssemblyCopyright("Copyright © 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("52357dd1-59a8-4412-98ca-6601d84b66b4")] // 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("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, 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 them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU 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 Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: Newtonsoft.Json.xml ================================================ Newtonsoft.Json Represents a BSON Oid (object id). Gets or sets the value of the Oid. The value of the Oid. Initializes a new instance of the class. The Oid value. Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data. Gets or sets a value indicating whether binary data reading should compatible with incorrect Json.NET 3.5 written binary. true if binary data reading will be compatible with incorrect Json.NET 3.5 written binary; otherwise, false. Gets or sets a value indicating whether the root object will be read as a JSON array. true if the root object will be read as a JSON array; otherwise, false. Gets or sets the used when reading values from BSON. The used when reading values from BSON. Initializes a new instance of the class. The stream. Initializes a new instance of the class. The reader. Initializes a new instance of the class. The stream. if set to true the root object will be read as a JSON array. The used when reading values from BSON. Initializes a new instance of the class. The reader. if set to true the root object will be read as a JSON array. The used when reading values from BSON. Reads the next JSON token from the stream. true if the next token was read successfully; false if there are no more tokens to read. Changes the to Closed. Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. Gets or sets the used when writing values to BSON. When set to no conversion will occur. The used when writing values to BSON. Initializes a new instance of the class. The stream. Initializes a new instance of the class. The writer. Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. Writes the end. The token. Writes out a comment /*...*/ containing the specified text. Text to place inside the comment. Writes the start of a constructor with the given name. The name of the constructor. Writes raw JSON. The raw JSON to write. Writes raw JSON where a value is expected and updates the writer's state. The raw JSON to write. Writes the beginning of a JSON array. Writes the beginning of a JSON object. Writes the property name of a name/value pair on a JSON object. The name of the property. Closes this stream and the underlying stream. Writes a value. An error will raised if the value cannot be written as a single JSON token. The value to write. Writes a null value. Writes an undefined value. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a [] value. The [] value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a [] value that represents a BSON object id. The Object ID value to write. Writes a BSON regex. The regex pattern. The regex options. Specifies how constructors are used when initializing objects during deserialization by the . First attempt to use the public default constructor, then fall back to single parameterized constructor, then the non-public default constructor. Json.NET will use a non-public default constructor before falling back to a parameterized constructor. Converts a binary value to and from a base 64 string value. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Converts a to and from JSON and BSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Create a custom object The object type to convert. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Creates an object which will then be populated by the serializer. Type of the object. The created object. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Gets a value indicating whether this can write JSON. true if this can write JSON; otherwise, false. Converts a to and from JSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified value type. Type of the value. true if this instance can convert the specified value type; otherwise, false. Converts a to and from JSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified value type. Type of the value. true if this instance can convert the specified value type; otherwise, false. Provides a base class for converting a to and from JSON. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Converts a F# discriminated union type to and from JSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Converts an Entity Framework EntityKey to and from JSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Converts an ExpandoObject to and from JSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Gets a value indicating whether this can write JSON. true if this can write JSON; otherwise, false. Converts a to and from the ISO 8601 date format (e.g. 2008-04-12T12:53Z). Gets or sets the date time styles used when converting a date to and from JSON. The date time styles used when converting a date to and from JSON. Gets or sets the date time format used when converting a date to and from JSON. The date time format used when converting a date to and from JSON. Gets or sets the culture used when converting a date to and from JSON. The culture used when converting a date to and from JSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Converts a to and from a JavaScript date constructor (e.g. new Date(52231943)). Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing property value of the JSON that is being converted. The calling serializer. The object value. Converts a to and from JSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Converts a to and from JSON and BSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Converts an to and from its name string value. Gets or sets a value indicating whether the written enum text should be camel case. true if the written enum text will be camel case; otherwise, false. Gets or sets a value indicating whether integer values are allowed. true if integers are allowed; otherwise, false. Initializes a new instance of the class. Initializes a new instance of the class. true if the written enum text will be camel case; otherwise, false. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Converts a to and from a string (e.g. "1.2.3.4"). Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing property value of the JSON that is being converted. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Converts XML to and from JSON. Gets or sets the name of the root element to insert when deserializing to XML if the JSON structure has produces multiple root elements. The name of the deserialize root element. Gets or sets a flag to indicate whether to write the Json.NET array attribute. This attribute helps preserve arrays when converting the written XML back to JSON. true if the array attibute is written to the XML; otherwise, false. Gets or sets a value indicating whether to write the root JSON object. true if the JSON root object is omitted; otherwise, false. Writes the JSON representation of the object. The to write to. The calling serializer. The value. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Checks if the attributeName is a namespace attribute. Attribute name to test. The attribute name prefix if it has one, otherwise an empty string. true if attribute name is for a namespace attribute, otherwise false. Determines whether this instance can convert the specified value type. Type of the value. true if this instance can convert the specified value type; otherwise, false. Specifies how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. Floating point numbers are parsed to . Floating point numbers are parsed to . Specifies how dates are formatted when writing JSON text. Dates are written in the ISO 8601 format, e.g. "2012-03-21T05:40Z". Dates are written in the Microsoft JSON format, e.g. "\/Date(1198908717056)\/". Specifies how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON text. Date formatted strings are not parsed to a date type and are read as strings. Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to . Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to . Specifies how to treat the time value when converting between string and . Treat as local time. If the object represents a Coordinated Universal Time (UTC), it is converted to the local time. Treat as a UTC. If the object represents a local time, it is converted to a UTC. Treat as a local time if a is being converted to a string. If a string is being converted to , convert to a local time if a time zone is specified. Time zone information should be preserved when converting. Specifies default value handling options for the . Include members where the member value is the same as the member's default value when serializing objects. Included members are written to JSON. Has no effect when deserializing. Ignore members where the member value is the same as the member's default value when serializing objects so that is is not written to JSON. This option will ignore all default values (e.g. null for objects and nullable types; 0 for integers, decimals and floating point numbers; and false for booleans). The default value ignored can be changed by placing the on the property. Members with a default value but no JSON will be set to their default value when deserializing. Ignore members where the member value is the same as the member's default value when serializing objects and sets members to their default value when deserializing. Specifies float format handling options when writing special floating point numbers, e.g. , and with . Write special floating point values as strings in JSON, e.g. "NaN", "Infinity", "-Infinity". Write special floating point values as symbols in JSON, e.g. NaN, Infinity, -Infinity. Note that this will produce non-valid JSON. Write special floating point values as the property's default value in JSON, e.g. 0.0 for a property, null for a property. Specifies formatting options for the . No special formatting is applied. This is the default. Causes child objects to be indented according to the and settings. Provides an interface for using pooled arrays. The array type content. Rent a array from the pool. This array must be returned when it is no longer needed. The minimum required length of the array. The returned array may be longer. The rented array from the pool. This array must be returned when it is no longer needed. Return an array to the pool. The array that is being returned. Provides an interface to enable a class to return line and position information. Gets a value indicating whether the class can return line information. true if LineNumber and LinePosition can be provided; otherwise, false. Gets the current line number. The current line number or 0 if no line information is available (for example, HasLineInfo returns false). Gets the current line position. The current line position or 0 if no line information is available (for example, HasLineInfo returns false). Instructs the how to serialize the collection. Gets or sets a value indicating whether null items are allowed in the collection. true if null items are allowed in the collection; otherwise, false. Initializes a new instance of the class. Initializes a new instance of the class with a flag indicating whether the array can contain null items A flag indicating whether the array can contain null items. Initializes a new instance of the class with the specified container Id. The container Id. Instructs the to use the specified constructor when deserializing that object. Instructs the how to serialize the object. Gets or sets the id. The id. Gets or sets the title. The title. Gets or sets the description. The description. Gets or sets the collection's items converter. The collection's items converter. The parameter list to use when constructing the described by ItemConverterType. If null, the default constructor is used. When non-null, there must be a constructor defined in the that exactly matches the number, order, and type of these parameters. [JsonContainer(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })] Gets or sets the of the . The of the . The parameter list to use when constructing the described by NamingStrategyType. If null, the default constructor is used. When non-null, there must be a constructor defined in the that exactly matches the number, order, and type of these parameters. [JsonContainer(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })] Gets or sets a value that indicates whether to preserve object references. true to keep object reference; otherwise, false. The default is false. Gets or sets a value that indicates whether to preserve collection's items references. true to keep collection's items object references; otherwise, false. The default is false. Gets or sets the reference loop handling used when serializing the collection's items. The reference loop handling. Gets or sets the type name handling used when serializing the collection's items. The type name handling. Initializes a new instance of the class. Initializes a new instance of the class with the specified container Id. The container Id. Provides methods for converting between common language runtime types and JSON types. Gets or sets a function that creates default . Default settings are automatically used by serialization methods on , and and on . To serialize without using any default settings create a with . Represents JavaScript's boolean value true as a string. This field is read-only. Represents JavaScript's boolean value false as a string. This field is read-only. Represents JavaScript's null as a string. This field is read-only. Represents JavaScript's undefined as a string. This field is read-only. Represents JavaScript's positive infinity as a string. This field is read-only. Represents JavaScript's negative infinity as a string. This field is read-only. Represents JavaScript's NaN as a string. This field is read-only. Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation using the specified. The value to convert. The format the date will be converted to. The time zone handling when the date is converted to a string. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation using the specified. The value to convert. The format the date will be converted to. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. The string delimiter character. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. The string delimiter character. The string escape handling. A JSON string representation of the . Converts the to its JSON string representation. The value to convert. A JSON string representation of the . Serializes the specified object to a JSON string. The object to serialize. A JSON string representation of the object. Serializes the specified object to a JSON string using formatting. The object to serialize. Indicates how the output is formatted. A JSON string representation of the object. Serializes the specified object to a JSON string using a collection of . The object to serialize. A collection converters used while serializing. A JSON string representation of the object. Serializes the specified object to a JSON string using formatting and a collection of . The object to serialize. Indicates how the output is formatted. A collection converters used while serializing. A JSON string representation of the object. Serializes the specified object to a JSON string using . The object to serialize. The used to serialize the object. If this is null, default serialization settings will be used. A JSON string representation of the object. Serializes the specified object to a JSON string using a type, formatting and . The object to serialize. The used to serialize the object. If this is null, default serialization settings will be used. The type of the value being serialized. This parameter is used when is Auto to write out the type name if the type of the value does not match. Specifing the type is optional. A JSON string representation of the object. Serializes the specified object to a JSON string using formatting and . The object to serialize. Indicates how the output is formatted. The used to serialize the object. If this is null, default serialization settings will be used. A JSON string representation of the object. Serializes the specified object to a JSON string using a type, formatting and . The object to serialize. Indicates how the output is formatted. The used to serialize the object. If this is null, default serialization settings will be used. The type of the value being serialized. This parameter is used when is Auto to write out the type name if the type of the value does not match. Specifing the type is optional. A JSON string representation of the object. Asynchronously serializes the specified object to a JSON string. Serialization will happen on a new thread. The object to serialize. A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. Asynchronously serializes the specified object to a JSON string using formatting. Serialization will happen on a new thread. The object to serialize. Indicates how the output is formatted. A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. Asynchronously serializes the specified object to a JSON string using formatting and a collection of . Serialization will happen on a new thread. The object to serialize. Indicates how the output is formatted. The used to serialize the object. If this is null, default serialization settings will be used. A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. Deserializes the JSON to a .NET object. The JSON to deserialize. The deserialized object from the JSON string. Deserializes the JSON to a .NET object using . The JSON to deserialize. The used to deserialize the object. If this is null, default serialization settings will be used. The deserialized object from the JSON string. Deserializes the JSON to the specified .NET type. The JSON to deserialize. The of object being deserialized. The deserialized object from the JSON string. Deserializes the JSON to the specified .NET type. The type of the object to deserialize to. The JSON to deserialize. The deserialized object from the JSON string. Deserializes the JSON to the given anonymous type. The anonymous type to deserialize to. This can't be specified traditionally and must be infered from the anonymous type passed as a parameter. The JSON to deserialize. The anonymous type object. The deserialized anonymous type from the JSON string. Deserializes the JSON to the given anonymous type using . The anonymous type to deserialize to. This can't be specified traditionally and must be infered from the anonymous type passed as a parameter. The JSON to deserialize. The anonymous type object. The used to deserialize the object. If this is null, default serialization settings will be used. The deserialized anonymous type from the JSON string. Deserializes the JSON to the specified .NET type using a collection of . The type of the object to deserialize to. The JSON to deserialize. Converters to use while deserializing. The deserialized object from the JSON string. Deserializes the JSON to the specified .NET type using . The type of the object to deserialize to. The object to deserialize. The used to deserialize the object. If this is null, default serialization settings will be used. The deserialized object from the JSON string. Deserializes the JSON to the specified .NET type using a collection of . The JSON to deserialize. The type of the object to deserialize. Converters to use while deserializing. The deserialized object from the JSON string. Deserializes the JSON to the specified .NET type using . The JSON to deserialize. The type of the object to deserialize to. The used to deserialize the object. If this is null, default serialization settings will be used. The deserialized object from the JSON string. Asynchronously deserializes the JSON to the specified .NET type. Deserialization will happen on a new thread. The type of the object to deserialize to. The JSON to deserialize. A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. Asynchronously deserializes the JSON to the specified .NET type using . Deserialization will happen on a new thread. The type of the object to deserialize to. The JSON to deserialize. The used to deserialize the object. If this is null, default serialization settings will be used. A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. Asynchronously deserializes the JSON to the specified .NET type. Deserialization will happen on a new thread. The JSON to deserialize. A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. Asynchronously deserializes the JSON to the specified .NET type using . Deserialization will happen on a new thread. The JSON to deserialize. The type of the object to deserialize to. The used to deserialize the object. If this is null, default serialization settings will be used. A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. Populates the object with values from the JSON string. The JSON to populate values from. The target object to populate values onto. Populates the object with values from the JSON string using . The JSON to populate values from. The target object to populate values onto. The used to deserialize the object. If this is null, default serialization settings will be used. Asynchronously populates the object with values from the JSON string using . The JSON to populate values from. The target object to populate values onto. The used to deserialize the object. If this is null, default serialization settings will be used. A task that represents the asynchronous populate operation. Serializes the XML node to a JSON string. The node to serialize. A JSON string of the XmlNode. Serializes the XML node to a JSON string using formatting. The node to serialize. Indicates how the output is formatted. A JSON string of the XmlNode. Serializes the XML node to a JSON string using formatting and omits the root object if is true. The node to serialize. Indicates how the output is formatted. Omits writing the root object. A JSON string of the XmlNode. Deserializes the XmlNode from a JSON string. The JSON string. The deserialized XmlNode Deserializes the XmlNode from a JSON string nested in a root elment specified by . The JSON string. The name of the root element to append when deserializing. The deserialized XmlNode Deserializes the XmlNode from a JSON string nested in a root elment specified by and writes a .NET array attribute for collections. The JSON string. The name of the root element to append when deserializing. A flag to indicate whether to write the Json.NET array attribute. This attribute helps preserve arrays when converting the written XML back to JSON. The deserialized XmlNode Serializes the to a JSON string. The node to convert to JSON. A JSON string of the XNode. Serializes the to a JSON string using formatting. The node to convert to JSON. Indicates how the output is formatted. A JSON string of the XNode. Serializes the to a JSON string using formatting and omits the root object if is true. The node to serialize. Indicates how the output is formatted. Omits writing the root object. A JSON string of the XNode. Deserializes the from a JSON string. The JSON string. The deserialized XNode Deserializes the from a JSON string nested in a root elment specified by . The JSON string. The name of the root element to append when deserializing. The deserialized XNode Deserializes the from a JSON string nested in a root elment specified by and writes a .NET array attribute for collections. The JSON string. The name of the root element to append when deserializing. A flag to indicate whether to write the Json.NET array attribute. This attribute helps preserve arrays when converting the written XML back to JSON. The deserialized XNode Converts an object to and from JSON. Writes the JSON representation of the object. The to write to. The value. The calling serializer. Reads the JSON representation of the object. The to read from. Type of the object. The existing value of object being read. The calling serializer. The object value. Determines whether this instance can convert the specified object type. Type of the object. true if this instance can convert the specified object type; otherwise, false. Gets the of the JSON produced by the JsonConverter. JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. The of the JSON produced by the JsonConverter. Gets a value indicating whether this can read JSON. true if this can read JSON; otherwise, false. Gets a value indicating whether this can write JSON. true if this can write JSON; otherwise, false. Instructs the to use the specified when serializing the member or class. Gets the of the . The of the . The parameter list to use when constructing the described by ConverterType. If null, the default constructor is used. Initializes a new instance of the class. Type of the . Initializes a new instance of the class. Type of the . Parameter list to use when constructing the . Can be null. Represents a collection of . Instructs the how to serialize the collection. Initializes a new instance of the class. Initializes a new instance of the class with the specified container Id. The container Id. The exception thrown when an error occurs during JSON serialization or deserialization. Initializes a new instance of the class. Initializes a new instance of the class with a specified error message. The error message that explains the reason for the exception. Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. The error message that explains the reason for the exception. The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. Initializes a new instance of the class. The that holds the serialized object data about the exception being thrown. The that contains contextual information about the source or destination. The parameter is null. The class name is null or is zero (0). Instructs the to deserialize properties with no matching class member into the specified collection and write values during serialization. Gets or sets a value that indicates whether to write extension data when serializing the object. true to write extension data when serializing the object; otherwise, false. The default is true. Gets or sets a value that indicates whether to read extension data when deserializing the object. true to read extension data when deserializing the object; otherwise, false. The default is true. Initializes a new instance of the class. Instructs the not to serialize the public field or public read/write property value. Instructs the how to serialize the object. Gets or sets the member serialization. The member serialization. Gets or sets a value that indicates whether the object's properties are required. A value indicating whether the object's properties are required. Initializes a new instance of the class. Initializes a new instance of the class with the specified member serialization. The member serialization. Initializes a new instance of the class with the specified container Id. The container Id. Instructs the to always serialize the member with the specified name. Gets or sets the used when serializing the property's collection items. The collection's items . The parameter list to use when constructing the described by ItemConverterType. If null, the default constructor is used. When non-null, there must be a constructor defined in the that exactly matches the number, order, and type of these parameters. [JsonProperty(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })] Gets or sets the of the . The of the . The parameter list to use when constructing the described by NamingStrategyType. If null, the default constructor is used. When non-null, there must be a constructor defined in the that exactly matches the number, order, and type of these parameters. [JsonProperty(NamingStrategyType = typeof(MyNamingStrategy), NamingStrategyParameters = new object[] { 123, "Four" })] Gets or sets the null value handling used when serializing this property. The null value handling. Gets or sets the default value handling used when serializing this property. The default value handling. Gets or sets the reference loop handling used when serializing this property. The reference loop handling. Gets or sets the object creation handling used when deserializing this property. The object creation handling. Gets or sets the type name handling used when serializing this property. The type name handling. Gets or sets whether this property's value is serialized as a reference. Whether this property's value is serialized as a reference. Gets or sets the order of serialization of a member. The numeric order of serialization. Gets or sets a value indicating whether this property is required. A value indicating whether this property is required. Gets or sets the name of the property. The name of the property. Gets or sets the the reference loop handling used when serializing the property's collection items. The collection's items reference loop handling. Gets or sets the the type name handling used when serializing the property's collection items. The collection's items type name handling. Gets or sets whether this property's collection items are serialized as a reference. Whether this property's collection items are serialized as a reference. Initializes a new instance of the class. Initializes a new instance of the class with the specified name. Name of the property. Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data. Specifies the state of the reader. The Read method has not been called. The end of the file has been reached successfully. Reader is at a property. Reader is at the start of an object. Reader is in an object. Reader is at the start of an array. Reader is in an array. The Close method has been called. Reader has just read a value. Reader is at the start of a constructor. Reader in a constructor. An error occurred that prevents the read operation from continuing. The end of the file has been reached successfully. Gets the current reader state. The current reader state. Gets or sets a value indicating whether the underlying stream or should be closed when the reader is closed. true to close the underlying stream or when the reader is closed; otherwise false. The default is true. Gets or sets a value indicating whether multiple pieces of JSON content can be read from a continuous stream without erroring. true to support reading multiple pieces of JSON content; otherwise false. The default is false. Gets the quotation mark character used to enclose the value of a string. Get or set how time zones are handling when reading JSON. Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON. Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. Get or set how custom date formatted strings are parsed when reading JSON. Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . Gets the type of the current JSON token. Gets the text value of the current JSON token. Gets The Common Language Runtime (CLR) type for the current JSON token. Gets the depth of the current token in the JSON document. The depth of the current token in the JSON document. Gets the path of the current JSON token. Gets or sets the culture used when reading JSON. Defaults to . Initializes a new instance of the class with the specified . Reads the next JSON token from the stream. true if the next token was read successfully; false if there are no more tokens to read. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a []. A [] or a null reference if the next JSON token is null. This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Skips the children of the current token. Sets the current token. The new token. Sets the current token and value. The new token. The value. Sets the state based on current token type. Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. Releases unmanaged and - optionally - managed resources true to release both managed and unmanaged resources; false to release only unmanaged resources. Changes the to Closed. The exception thrown when an error occurs while reading JSON text. Gets the line number indicating where the error occurred. The line number indicating where the error occurred. Gets the line position indicating where the error occurred. The line position indicating where the error occurred. Gets the path to the JSON where the error occurred. The path to the JSON where the error occurred. Initializes a new instance of the class. Initializes a new instance of the class with a specified error message. The error message that explains the reason for the exception. Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. The error message that explains the reason for the exception. The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. Initializes a new instance of the class. The that holds the serialized object data about the exception being thrown. The that contains contextual information about the source or destination. The parameter is null. The class name is null or is zero (0). Instructs the to always serialize the member, and require the member has a value. The exception thrown when an error occurs during JSON serialization or deserialization. Initializes a new instance of the class. Initializes a new instance of the class with a specified error message. The error message that explains the reason for the exception. Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. The error message that explains the reason for the exception. The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. Initializes a new instance of the class. The that holds the serialized object data about the exception being thrown. The that contains contextual information about the source or destination. The parameter is null. The class name is null or is zero (0). Serializes and deserializes objects into and from the JSON format. The enables you to control how objects are encoded into JSON. Occurs when the errors during serialization and deserialization. Gets or sets the used by the serializer when resolving references. Gets or sets the used by the serializer when resolving type names. Gets or sets the used by the serializer when writing trace messages. The trace writer. Gets or sets the equality comparer used by the serializer when comparing references. The equality comparer. Gets or sets how type name writing and reading is handled by the serializer. should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom when deserializing with a value other than TypeNameHandling.None. Gets or sets how a type name assembly is written and resolved by the serializer. The type name assembly format. Gets or sets how object references are preserved by the serializer. Get or set how reference loops (e.g. a class referencing itself) is handled. Get or set how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. Get or set how null values are handled during serialization and deserialization. Get or set how null default are handled during serialization and deserialization. Gets or sets how objects are created during deserialization. The object creation handling. Gets or sets how constructors are used during deserialization. The constructor handling. Gets or sets how metadata properties are used during deserialization. The metadata properties handling. Gets a collection that will be used during serialization. Collection that will be used during serialization. Gets or sets the contract resolver used by the serializer when serializing .NET objects to JSON and vice versa. Gets or sets the used by the serializer when invoking serialization callback methods. The context. Indicates how JSON text output is formatted. Get or set how dates are written to JSON text. Get or set how time zones are handling during serialization and deserialization. Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON. Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. Get or set how special floating point numbers, e.g. , and , are written as JSON text. Get or set how strings are escaped when writing JSON text. Get or set how and values are formatted when writing JSON text, and the expected date format when reading JSON text. Gets or sets the culture used when reading JSON. Defaults to . Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . Gets a value indicating whether there will be a check for additional JSON content after deserializing an object. true if there will be a check for additional JSON content after deserializing an object; otherwise, false. Initializes a new instance of the class. Creates a new instance. The will not use default settings from . A new instance. The will not use default settings from . Creates a new instance using the specified . The will not use default settings from . The settings to be applied to the . A new instance using the specified . The will not use default settings from . Creates a new instance. The will use default settings from . A new instance. The will use default settings from . Creates a new instance using the specified . The will use default settings from as well as the specified . The settings to be applied to the . A new instance using the specified . The will use default settings from as well as the specified . Populates the JSON values onto the target object. The that contains the JSON structure to reader values from. The target object to populate values onto. Populates the JSON values onto the target object. The that contains the JSON structure to reader values from. The target object to populate values onto. Deserializes the JSON structure contained by the specified . The that contains the JSON structure to deserialize. The being deserialized. Deserializes the JSON structure contained by the specified into an instance of the specified type. The containing the object. The of object being deserialized. The instance of being deserialized. Deserializes the JSON structure contained by the specified into an instance of the specified type. The containing the object. The type of the object to deserialize. The instance of being deserialized. Deserializes the JSON structure contained by the specified into an instance of the specified type. The containing the object. The of object being deserialized. The instance of being deserialized. Serializes the specified and writes the JSON structure to a Stream using the specified . The used to write the JSON structure. The to serialize. Serializes the specified and writes the JSON structure to a Stream using the specified . The used to write the JSON structure. The to serialize. The type of the value being serialized. This parameter is used when is Auto to write out the type name if the type of the value does not match. Specifing the type is optional. Serializes the specified and writes the JSON structure to a Stream using the specified . The used to write the JSON structure. The to serialize. The type of the value being serialized. This parameter is used when is Auto to write out the type name if the type of the value does not match. Specifing the type is optional. Serializes the specified and writes the JSON structure to a Stream using the specified . The used to write the JSON structure. The to serialize. Specifies the settings on a object. Gets or sets how reference loops (e.g. a class referencing itself) is handled. Reference loop handling. Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. Missing member handling. Gets or sets how objects are created during deserialization. The object creation handling. Gets or sets how null values are handled during serialization and deserialization. Null value handling. Gets or sets how null default are handled during serialization and deserialization. The default value handling. Gets or sets a collection that will be used during serialization. The converters. Gets or sets how object references are preserved by the serializer. The preserve references handling. Gets or sets how type name writing and reading is handled by the serializer. should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom when deserializing with a value other than TypeNameHandling.None. The type name handling. Gets or sets how metadata properties are used during deserialization. The metadata properties handling. Gets or sets how a type name assembly is written and resolved by the serializer. The type name assembly format. Gets or sets how constructors are used during deserialization. The constructor handling. Gets or sets the contract resolver used by the serializer when serializing .NET objects to JSON and vice versa. The contract resolver. Gets or sets the equality comparer used by the serializer when comparing references. The equality comparer. Gets or sets the used by the serializer when resolving references. The reference resolver. Gets or sets a function that creates the used by the serializer when resolving references. A function that creates the used by the serializer when resolving references. Gets or sets the used by the serializer when writing trace messages. The trace writer. Gets or sets the used by the serializer when resolving type names. The binder. Gets or sets the error handler called during serialization and deserialization. The error handler called during serialization and deserialization. Gets or sets the used by the serializer when invoking serialization callback methods. The context. Get or set how and values are formatted when writing JSON text, and the expected date format when reading JSON text. Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . Indicates how JSON text output is formatted. Get or set how dates are written to JSON text. Get or set how time zones are handling during serialization and deserialization. Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON. Get or set how special floating point numbers, e.g. , and , are written as JSON. Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text. Get or set how strings are escaped when writing JSON text. Gets or sets the culture used when reading JSON. Defaults to . Gets a value indicating whether there will be a check for additional content after deserializing an object. true if there will be a check for additional content after deserializing an object; otherwise, false. Initializes a new instance of the class. Represents a reader that provides fast, non-cached, forward-only access to JSON text data. Initializes a new instance of the class with the specified . The TextReader containing the XML data to read. Gets or sets the reader's character buffer pool. Reads the next JSON token from the stream. true if the next token was read successfully; false if there are no more tokens to read. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a []. A [] or a null reference if the next JSON token is null. This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Changes the state to closed. Gets a value indicating whether the class can return line information. true if LineNumber and LinePosition can be provided; otherwise, false. Gets the current line number. The current line number or 0 if no line information is available (for example, HasLineInfo returns false). Gets the current line position. The current line position or 0 if no line information is available (for example, HasLineInfo returns false). Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. Gets or sets the writer's character array pool. Gets or sets how many IndentChars to write for each level in the hierarchy when is set to Formatting.Indented. Gets or sets which character to use to quote attribute values. Gets or sets which character to use for indenting when is set to Formatting.Indented. Gets or sets a value indicating whether object names will be surrounded with quotes. Creates an instance of the JsonWriter class using the specified . The TextWriter to write to. Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. Closes this stream and the underlying stream. Writes the beginning of a JSON object. Writes the beginning of a JSON array. Writes the start of a constructor with the given name. The name of the constructor. Writes the specified end token. The end token to write. Writes the property name of a name/value pair on a JSON object. The name of the property. Writes the property name of a name/value pair on a JSON object. The name of the property. A flag to indicate whether the text should be escaped when it is written as a JSON property name. Writes indent characters. Writes the JSON value delimiter. Writes an indent space. Writes a value. An error will raised if the value cannot be written as a single JSON token. The value to write. Writes a null value. Writes an undefined value. Writes raw JSON. The raw JSON to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a [] value. The [] value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes out a comment /*...*/ containing the specified text. Text to place inside the comment. Writes out the given white space. The string of white space characters. Specifies the type of JSON token. This is returned by the if a method has not been called. An object start token. An array start token. A constructor start token. An object property name. A comment. Raw JSON. An integer. A float. A string. A boolean. A null token. An undefined token. An object end token. An array end token. A constructor end token. A Date. Byte data. Represents a reader that provides validation. JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. Sets an event handler for receiving schema validation errors. Gets the text value of the current JSON token. Gets the depth of the current token in the JSON document. The depth of the current token in the JSON document. Gets the path of the current JSON token. Gets the quotation mark character used to enclose the value of a string. Gets the type of the current JSON token. Gets the Common Language Runtime (CLR) type for the current JSON token. Initializes a new instance of the class that validates the content returned from the given . The to read from while validating. Gets or sets the schema. The schema. Gets the used to construct this . The specified in the constructor. Reads the next JSON token from the stream as a . A . Reads the next JSON token from the stream as a []. A [] or a null reference if the next JSON token is null. Reads the next JSON token from the stream as a . A . Reads the next JSON token from the stream as a . A . Reads the next JSON token from the stream as a . A . Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . This method will return null at the end of an array. Reads the next JSON token from the stream as a . A . Reads the next JSON token from the stream. true if the next token was read successfully; false if there are no more tokens to read. Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. Gets or sets a value indicating whether the underlying stream or should be closed when the writer is closed. true to close the underlying stream or when the writer is closed; otherwise false. The default is true. Gets the top. The top. Gets the state of the writer. Gets the path of the writer. Indicates how JSON text output is formatted. Get or set how dates are written to JSON text. Get or set how time zones are handling when writing JSON text. Get or set how strings are escaped when writing JSON text. Get or set how special floating point numbers, e.g. , and , are written to JSON text. Get or set how and values are formatting when writing JSON text. Gets or sets the culture used when writing JSON. Defaults to . Creates an instance of the JsonWriter class. Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. Closes this stream and the underlying stream. Writes the beginning of a JSON object. Writes the end of a JSON object. Writes the beginning of a JSON array. Writes the end of an array. Writes the start of a constructor with the given name. The name of the constructor. Writes the end constructor. Writes the property name of a name/value pair on a JSON object. The name of the property. Writes the property name of a name/value pair on a JSON object. The name of the property. A flag to indicate whether the text should be escaped when it is written as a JSON property name. Writes the end of the current JSON object or array. Writes the current token and its children. The to read the token from. Writes the current token. The to read the token from. A flag indicating whether the current token's children should be written. Writes the token and its value. The to write. The value to write. A value is only required for tokens that have an associated value, e.g. the property name for . A null value can be passed to the method for token's that don't have a value, e.g. . Writes the token. The to write. Writes the specified end token. The end token to write. Writes indent characters. Writes the JSON value delimiter. Writes an indent space. Writes a null value. Writes an undefined value. Writes raw JSON without changing the writer's state. The raw JSON to write. Writes raw JSON where a value is expected and updates the writer's state. The raw JSON to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a [] value. The [] value to write. Writes a value. The value to write. Writes a value. An error will raised if the value cannot be written as a single JSON token. The value to write. Writes out a comment /*...*/ containing the specified text. Text to place inside the comment. Writes out the given white space. The string of white space characters. Releases unmanaged and - optionally - managed resources true to release both managed and unmanaged resources; false to release only unmanaged resources. Sets the state of the JsonWriter, The JsonToken being written. The value being written. The exception thrown when an error occurs while reading JSON text. Gets the path to the JSON where the error occurred. The path to the JSON where the error occurred. Initializes a new instance of the class. Initializes a new instance of the class with a specified error message. The error message that explains the reason for the exception. Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. The error message that explains the reason for the exception. The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. Initializes a new instance of the class. The that holds the serialized object data about the exception being thrown. The that contains contextual information about the source or destination. The parameter is null. The class name is null or is zero (0). Specifies how JSON comments are handled when loading JSON. Ignore comments. Load comments as a with type . Specifies how line information is handled when loading JSON. Ignore line information. Load line information. Contains the LINQ to JSON extension methods. Returns a collection of tokens that contains the ancestors of every token in the source collection. The type of the objects in source, constrained to . An of that contains the source collection. An of that contains the ancestors of every token in the source collection. Returns a collection of tokens that contains every token in the source collection, and the ancestors of every token in the source collection. The type of the objects in source, constrained to . An of that contains the source collection. An of that contains every token in the source collection, the ancestors of every token in the source collection. Returns a collection of tokens that contains the descendants of every token in the source collection. The type of the objects in source, constrained to . An of that contains the source collection. An of that contains the descendants of every token in the source collection. Returns a collection of tokens that contains every token in the source collection, and the descendants of every token in the source collection. The type of the objects in source, constrained to . An of that contains the source collection. An of that contains every token in the source collection, and the descendants of every token in the source collection. Returns a collection of child properties of every object in the source collection. An of that contains the source collection. An of that contains the properties of every object in the source collection. Returns a collection of child values of every object in the source collection with the given key. An of that contains the source collection. The token key. An of that contains the values of every token in the source collection with the given key. Returns a collection of child values of every object in the source collection. An of that contains the source collection. An of that contains the values of every token in the source collection. Returns a collection of converted child values of every object in the source collection with the given key. The type to convert the values to. An of that contains the source collection. The token key. An that contains the converted values of every token in the source collection with the given key. Returns a collection of converted child values of every object in the source collection. The type to convert the values to. An of that contains the source collection. An that contains the converted values of every token in the source collection. Converts the value. The type to convert the value to. A cast as a of . A converted value. Converts the value. The source collection type. The type to convert the value to. A cast as a of . A converted value. Returns a collection of child tokens of every array in the source collection. The source collection type. An of that contains the source collection. An of that contains the values of every token in the source collection. Returns a collection of converted child tokens of every array in the source collection. An of that contains the source collection. The type to convert the values to. The source collection type. An that contains the converted values of every token in the source collection. Returns the input typed as . An of that contains the source collection. The input typed as . Returns the input typed as . The source collection type. An of that contains the source collection. The input typed as . Represents a collection of objects. The type of token Gets the with the specified key. Represents a JSON array. Gets the container's children tokens. The container's children tokens. Gets the node type for this . The type. Initializes a new instance of the class. Initializes a new instance of the class from another object. A object to copy from. Initializes a new instance of the class with the specified content. The contents of the array. Initializes a new instance of the class with the specified content. The contents of the array. Loads an from a . A that will be read for the content of the . A that contains the JSON that was read from the specified . Loads an from a . A that will be read for the content of the . The used to load the JSON. If this is null, default load settings will be used. A that contains the JSON that was read from the specified . Load a from a string that contains JSON. A that contains JSON. A populated from the string that contains JSON. Load a from a string that contains JSON. A that contains JSON. The used to load the JSON. If this is null, default load settings will be used. A populated from the string that contains JSON. Creates a from an object. The object that will be used to create . A with the values of the specified object Creates a from an object. The object that will be used to create . The that will be used to read the object. A with the values of the specified object Writes this token to a . A into which this method will write. A collection of which will be used when writing the token. Gets the with the specified key. The with the specified key. Gets or sets the at the specified index. Determines the index of a specific item in the . The object to locate in the . The index of if found in the list; otherwise, -1. Inserts an item to the at the specified index. The zero-based index at which should be inserted. The object to insert into the . is not a valid index in the . The is read-only. Removes the item at the specified index. The zero-based index of the item to remove. is not a valid index in the . The is read-only. Returns an enumerator that iterates through the collection. A that can be used to iterate through the collection. Adds an item to the . The object to add to the . The is read-only. Removes all items from the . The is read-only. Determines whether the contains a specific value. The object to locate in the . true if is found in the ; otherwise, false. Copies to. The array. Index of the array. Gets a value indicating whether the is read-only. true if the is read-only; otherwise, false. Removes the first occurrence of a specific object from the . The object to remove from the . true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . The is read-only. Represents a JSON constructor. Gets the container's children tokens. The container's children tokens. Gets or sets the name of this constructor. The constructor name. Gets the node type for this . The type. Initializes a new instance of the class. Initializes a new instance of the class from another object. A object to copy from. Initializes a new instance of the class with the specified name and content. The constructor name. The contents of the constructor. Initializes a new instance of the class with the specified name and content. The constructor name. The contents of the constructor. Initializes a new instance of the class with the specified name. The constructor name. Writes this token to a . A into which this method will write. A collection of which will be used when writing the token. Gets the with the specified key. The with the specified key. Loads an from a . A that will be read for the content of the . A that contains the JSON that was read from the specified . Loads an from a . A that will be read for the content of the . The used to load the JSON. If this is null, default load settings will be used. A that contains the JSON that was read from the specified . Represents a token that can contain other tokens. Occurs when the list changes or an item in the list changes. Occurs before an item is added to the collection. Occurs when the items list of the collection has changed, or the collection is reset. Gets the container's children tokens. The container's children tokens. Raises the event. The instance containing the event data. Raises the event. The instance containing the event data. Raises the event. The instance containing the event data. Gets a value indicating whether this token has child tokens. true if this token has child values; otherwise, false. Get the first child token of this token. A containing the first child token of the . Get the last child token of this token. A containing the last child token of the . Returns a collection of the child tokens of this token, in document order. An of containing the child tokens of this , in document order. Returns a collection of the child values of this token, in document order. The type to convert the values to. A containing the child values of this , in document order. Returns a collection of the descendant tokens for this token in document order. An containing the descendant tokens of the . Returns a collection of the tokens that contain this token, and all descendant tokens of this token, in document order. An containing this token, and all the descendant tokens of the . Adds the specified content as children of this . The content to be added. Adds the specified content as the first children of this . The content to be added. Creates an that can be used to add tokens to the . An that is ready to have content written to it. Replaces the children nodes of this token with the specified content. The content. Removes the child nodes from this token. Merge the specified content into this . The content to be merged. Merge the specified content into this using . The content to be merged. The used to merge the content. Gets the count of child JSON tokens. The count of child JSON tokens Represents a collection of objects. The type of token An empty collection of objects. Initializes a new instance of the struct. The enumerable. Returns an enumerator that iterates through the collection. A that can be used to iterate through the collection. Returns an enumerator that iterates through a collection. An object that can be used to iterate through the collection. Gets the with the specified key. Determines whether the specified is equal to this instance. The to compare with this instance. true if the specified is equal to this instance; otherwise, false. Determines whether the specified is equal to this instance. The to compare with this instance. true if the specified is equal to this instance; otherwise, false. Returns a hash code for this instance. A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. Represents a JSON object. Gets the container's children tokens. The container's children tokens. Occurs when a property value changes. Occurs when a property value is changing. Initializes a new instance of the class. Initializes a new instance of the class from another object. A object to copy from. Initializes a new instance of the class with the specified content. The contents of the object. Initializes a new instance of the class with the specified content. The contents of the object. Gets the node type for this . The type. Gets an of this object's properties. An of this object's properties. Gets a the specified name. The property name. A with the specified name or null. Gets an of this object's property values. An of this object's property values. Gets the with the specified key. The with the specified key. Gets or sets the with the specified property name. Loads an from a . A that will be read for the content of the . A that contains the JSON that was read from the specified . Loads an from a . A that will be read for the content of the . The used to load the JSON. If this is null, default load settings will be used. A that contains the JSON that was read from the specified . Load a from a string that contains JSON. A that contains JSON. A populated from the string that contains JSON. Load a from a string that contains JSON. A that contains JSON. The used to load the JSON. If this is null, default load settings will be used. A populated from the string that contains JSON. Creates a from an object. The object that will be used to create . A with the values of the specified object Creates a from an object. The object that will be used to create . The that will be used to read the object. A with the values of the specified object Writes this token to a . A into which this method will write. A collection of which will be used when writing the token. Gets the with the specified property name. Name of the property. The with the specified property name. Gets the with the specified property name. The exact property name will be searched for first and if no matching property is found then the will be used to match a property. Name of the property. One of the enumeration values that specifies how the strings will be compared. The with the specified property name. Tries to get the with the specified property name. The exact property name will be searched for first and if no matching property is found then the will be used to match a property. Name of the property. The value. One of the enumeration values that specifies how the strings will be compared. true if a value was successfully retrieved; otherwise, false. Adds the specified property name. Name of the property. The value. Removes the property with the specified name. Name of the property. true if item was successfully removed; otherwise, false. Tries the get value. Name of the property. The value. true if a value was successfully retrieved; otherwise, false. Returns an enumerator that iterates through the collection. A that can be used to iterate through the collection. Raises the event with the provided arguments. Name of the property. Raises the event with the provided arguments. Name of the property. Returns the properties for this instance of a component. A that represents the properties for this component instance. Returns the properties for this instance of a component using the attribute array as a filter. An array of type that is used as a filter. A that represents the filtered properties for this component instance. Returns a collection of custom attributes for this instance of a component. An containing the attributes for this object. Returns the class name of this instance of a component. The class name of the object, or null if the class does not have a name. Returns the name of this instance of a component. The name of the object, or null if the object does not have a name. Returns a type converter for this instance of a component. A that is the converter for this object, or null if there is no for this object. Returns the default event for this instance of a component. An that represents the default event for this object, or null if this object does not have events. Returns the default property for this instance of a component. A that represents the default property for this object, or null if this object does not have properties. Returns an editor of the specified type for this instance of a component. A that represents the editor for this object. An of the specified type that is the editor for this object, or null if the editor cannot be found. Returns the events for this instance of a component using the specified attribute array as a filter. An array of type that is used as a filter. An that represents the filtered events for this component instance. Returns the events for this instance of a component. An that represents the events for this component instance. Returns an object that contains the property described by the specified property descriptor. A that represents the property whose owner is to be found. An that represents the owner of the specified property. Returns the responsible for binding operations performed on this object. The expression tree representation of the runtime value. The to bind this object. Specifies the settings used when merging JSON. Gets or sets the method used when merging JSON arrays. The method used when merging JSON arrays. Gets or sets how how null value properties are merged. How null value properties are merged. Represents a JSON property. Gets the container's children tokens. The container's children tokens. Gets the property name. The property name. Gets or sets the property value. The property value. Initializes a new instance of the class from another object. A object to copy from. Gets the node type for this . The type. Initializes a new instance of the class. The property name. The property content. Initializes a new instance of the class. The property name. The property content. Writes this token to a . A into which this method will write. A collection of which will be used when writing the token. Loads an from a . A that will be read for the content of the . A that contains the JSON that was read from the specified . Loads an from a . A that will be read for the content of the . The used to load the JSON. If this is null, default load settings will be used. A that contains the JSON that was read from the specified . Represents a view of a . Initializes a new instance of the class. The name. When overridden in a derived class, returns whether resetting an object changes its value. true if resetting the component changes its value; otherwise, false. The component to test for reset capability. When overridden in a derived class, gets the current value of the property on a component. The value of a property for a given component. The component with the property for which to retrieve the value. When overridden in a derived class, resets the value for this property of the component to the default value. The component with the property value that is to be reset to the default value. When overridden in a derived class, sets the value of the component to a different value. The component with the property value that is to be set. The new value. When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted. true if the property should be persisted; otherwise, false. The component with the property to be examined for persistence. When overridden in a derived class, gets the type of the component this property is bound to. A that represents the type of component this property is bound to. When the or methods are invoked, the object specified might be an instance of this type. When overridden in a derived class, gets a value indicating whether this property is read-only. true if the property is read-only; otherwise, false. When overridden in a derived class, gets the type of the property. A that represents the type of the property. Gets the hash code for the name of the member. The hash code for the name of the member. Represents a raw JSON string. Initializes a new instance of the class from another object. A object to copy from. Initializes a new instance of the class. The raw json. Creates an instance of with the content of the reader's current token. The reader. An instance of with the content of the reader's current token. Represents an abstract JSON token. Gets a comparer that can compare two tokens for value equality. A that can compare two nodes for value equality. Gets or sets the parent. The parent. Gets the root of this . The root of this . Gets the node type for this . The type. Gets a value indicating whether this token has child tokens. true if this token has child values; otherwise, false. Compares the values of two tokens, including the values of all descendant tokens. The first to compare. The second to compare. true if the tokens are equal; otherwise false. Gets the next sibling token of this node. The that contains the next sibling token. Gets the previous sibling token of this node. The that contains the previous sibling token. Gets the path of the JSON token. Adds the specified content immediately after this token. A content object that contains simple content or a collection of content objects to be added after this token. Adds the specified content immediately before this token. A content object that contains simple content or a collection of content objects to be added before this token. Returns a collection of the ancestor tokens of this token. A collection of the ancestor tokens of this token. Returns a collection of tokens that contain this token, and the ancestors of this token. A collection of tokens that contain this token, and the ancestors of this token. Returns a collection of the sibling tokens after this token, in document order. A collection of the sibling tokens after this tokens, in document order. Returns a collection of the sibling tokens before this token, in document order. A collection of the sibling tokens before this token, in document order. Gets the with the specified key. The with the specified key. Gets the with the specified key converted to the specified type. The type to convert the token to. The token key. The converted token value. Get the first child token of this token. A containing the first child token of the . Get the last child token of this token. A containing the last child token of the . Returns a collection of the child tokens of this token, in document order. An of containing the child tokens of this , in document order. Returns a collection of the child tokens of this token, in document order, filtered by the specified type. The type to filter the child tokens on. A containing the child tokens of this , in document order. Returns a collection of the child values of this token, in document order. The type to convert the values to. A containing the child values of this , in document order. Removes this token from its parent. Replaces this token with the specified token. The value. Writes this token to a . A into which this method will write. A collection of which will be used when writing the token. Returns the indented JSON for this token. The indented JSON for this token. Returns the JSON for this token using the given formatting and converters. Indicates how the output is formatted. A collection of which will be used when writing the token. The JSON for this token using the given formatting and converters. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to []. The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an explicit conversion from to . The value. The result of the conversion. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from [] to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Performs an implicit conversion from to . The value to create a from. The initialized with the specified value. Creates an for this token. An that can be used to read this token and its descendants. Creates a from an object. The object that will be used to create . A with the value of the specified object Creates a from an object using the specified . The object that will be used to create . The that will be used when reading the object. A with the value of the specified object Creates the specified .NET type from the . The object type that the token will be deserialized to. The new object created from the JSON value. Creates the specified .NET type from the . The object type that the token will be deserialized to. The new object created from the JSON value. Creates the specified .NET type from the using the specified . The object type that the token will be deserialized to. The that will be used when creating the object. The new object created from the JSON value. Creates the specified .NET type from the using the specified . The object type that the token will be deserialized to. The that will be used when creating the object. The new object created from the JSON value. Creates a from a . An positioned at the token to read into this . An that contains the token and its descendant tokens that were read from the reader. The runtime type of the token is determined by the token type of the first token encountered in the reader. Creates a from a . An positioned at the token to read into this . The used to load the JSON. If this is null, default load settings will be used. An that contains the token and its descendant tokens that were read from the reader. The runtime type of the token is determined by the token type of the first token encountered in the reader. Load a from a string that contains JSON. A that contains JSON. A populated from the string that contains JSON. Load a from a string that contains JSON. A that contains JSON. The used to load the JSON. If this is null, default load settings will be used. A populated from the string that contains JSON. Creates a from a . An positioned at the token to read into this . The used to load the JSON. If this is null, default load settings will be used. An that contains the token and its descendant tokens that were read from the reader. The runtime type of the token is determined by the token type of the first token encountered in the reader. Creates a from a . An positioned at the token to read into this . An that contains the token and its descendant tokens that were read from the reader. The runtime type of the token is determined by the token type of the first token encountered in the reader. Selects a using a JPath expression. Selects the token that matches the object path. A that contains a JPath expression. A , or null. Selects a using a JPath expression. Selects the token that matches the object path. A that contains a JPath expression. A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression. A . Selects a collection of elements using a JPath expression. A that contains a JPath expression. An that contains the selected elements. Selects a collection of elements using a JPath expression. A that contains a JPath expression. A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression. An that contains the selected elements. Returns the responsible for binding operations performed on this object. The expression tree representation of the runtime value. The to bind this object. Returns the responsible for binding operations performed on this object. The expression tree representation of the runtime value. The to bind this object. Creates a new instance of the . All child tokens are recursively cloned. A new instance of the . Adds an object to the annotation list of this . The annotation to add. Get the first annotation object of the specified type from this . The type of the annotation to retrieve. The first annotation object that matches the specified type, or null if no annotation is of the specified type. Gets the first annotation object of the specified type from this . The of the annotation to retrieve. The first annotation object that matches the specified type, or null if no annotation is of the specified type. Gets a collection of annotations of the specified type for this . The type of the annotations to retrieve. An that contains the annotations for this . Gets a collection of annotations of the specified type for this . The of the annotations to retrieve. An of that contains the annotations that match the specified type for this . Removes the annotations of the specified type from this . The type of annotations to remove. Removes the annotations of the specified type from this . The of annotations to remove. Compares tokens to determine whether they are equal. Determines whether the specified objects are equal. The first object of type to compare. The second object of type to compare. true if the specified objects are equal; otherwise, false. Returns a hash code for the specified object. The for which a hash code is to be returned. A hash code for the specified object. The type of is a reference type and is null. Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data. Gets the at the reader's current position. Initializes a new instance of the class. The token to read from. Reads the next JSON token from the stream. true if the next token was read successfully; false if there are no more tokens to read. Gets the path of the current JSON token. Specifies the type of token. No token type has been set. A JSON object. A JSON array. A JSON constructor. A JSON object property. A comment. An integer value. A float value. A string value. A boolean value. A null value. An undefined value. A date value. A raw JSON value. A collection of bytes value. A Guid value. A Uri value. A TimeSpan value. Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data. Gets the at the writer's current position. Gets the token being writen. The token being writen. Initializes a new instance of the class writing to the given . The container being written to. Initializes a new instance of the class. Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. Closes this stream and the underlying stream. Writes the beginning of a JSON object. Writes the beginning of a JSON array. Writes the start of a constructor with the given name. The name of the constructor. Writes the end. The token. Writes the property name of a name/value pair on a JSON object. The name of the property. Writes a value. An error will raised if the value cannot be written as a single JSON token. The value to write. Writes a null value. Writes an undefined value. Writes raw JSON. The raw JSON to write. Writes out a comment /*...*/ containing the specified text. Text to place inside the comment. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a [] value. The [] value to write. Writes a value. The value to write. Writes a value. The value to write. Writes a value. The value to write. Represents a value in JSON (string, integer, date, etc). Initializes a new instance of the class from another object. A object to copy from. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Initializes a new instance of the class with the given value. The value. Gets a value indicating whether this token has child tokens. true if this token has child values; otherwise, false. Creates a comment with the given value. The value. A comment with the given value. Creates a string with the given value. The value. A string with the given value. Creates a null value. A null value. Creates a undefined value. A undefined value. Gets the node type for this . The type. Gets or sets the underlying token value. The underlying token value. Writes this token to a . A into which this method will write. A collection of which will be used when writing the token. Indicates whether the current object is equal to another object of the same type. true if the current object is equal to the parameter; otherwise, false. An object to compare with this object. Determines whether the specified is equal to the current . The to compare with the current . true if the specified is equal to the current ; otherwise, false. The parameter is null. Serves as a hash function for a particular type. A hash code for the current . Returns a that represents this instance. A that represents this instance. Returns a that represents this instance. The format. A that represents this instance. Returns a that represents this instance. The format provider. A that represents this instance. Returns a that represents this instance. The format. The format provider. A that represents this instance. Returns the responsible for binding operations performed on this object. The expression tree representation of the runtime value. The to bind this object. Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. An object to compare with this instance. A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance is less than . Zero This instance is equal to . Greater than zero This instance is greater than . is not the same type as this instance. Specifies the settings used when loading JSON. Gets or sets how JSON comments are handled when loading JSON. The JSON comment handling. Gets or sets how JSON line info is handled when loading JSON. The JSON line info handling. Specifies how JSON arrays are merged together. Concatenate arrays. Union arrays, skipping items that already exist. Replace all array items. Merge array items together, matched by index. Specifies how null value properties are merged. The content's null value properties will be ignored during merging. The content's null value properties will be merged. Specifies the member serialization options for the . All public members are serialized by default. Members can be excluded using or . This is the default member serialization mode. Only members marked with or are serialized. This member serialization mode can also be set by marking the class with . All public and private fields are serialized. Members can be excluded using or . This member serialization mode can also be set by marking the class with and setting IgnoreSerializableAttribute on to false. Specifies metadata property handling options for the . Read metadata properties located at the start of a JSON object. Read metadata properties located anywhere in a JSON object. Note that this setting will impact performance. Do not try to read metadata properties. Specifies missing member handling options for the . Ignore a missing member and do not attempt to deserialize it. Throw a when a missing member is encountered during deserialization. Specifies null value handling options for the . Include null values when serializing and deserializing objects. Ignore null values when serializing and deserializing objects. Specifies how object creation is handled by the . Reuse existing objects, create new objects when needed. Only reuse existing objects. Always create new objects. Specifies reference handling options for the . Note that references cannot be preserved when a value is set via a non-default constructor such as types that implement ISerializable. Do not preserve references when serializing types. Preserve references when serializing into a JSON object structure. Preserve references when serializing into a JSON array structure. Preserve references when serializing. Specifies reference loop handling options for the . Throw a when a loop is encountered. Ignore loop references and do not serialize. Serialize loop references. Indicating whether a property is required. The property is not required. The default state. The property must be defined in JSON but can be a null value. The property must be defined in JSON and cannot be a null value. The property is not required but it cannot be a null value. Contains the JSON schema extension methods. JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. Determines whether the is valid. JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. The source to test. The schema to test with. true if the specified is valid; otherwise, false. Determines whether the is valid. JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. The source to test. The schema to test with. When this method returns, contains any error messages generated while validating. true if the specified is valid; otherwise, false. Validates the specified . JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. The source to test. The schema to test with. Validates the specified . JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. The source to test. The schema to test with. The validation event handler. An in-memory representation of a JSON Schema. JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. Gets or sets the id. Gets or sets the title. Gets or sets whether the object is required. Gets or sets whether the object is read only. Gets or sets whether the object is visible to users. Gets or sets whether the object is transient. Gets or sets the description of the object. Gets or sets the types of values allowed by the object. The type. Gets or sets the pattern. The pattern. Gets or sets the minimum length. The minimum length. Gets or sets the maximum length. The maximum length. Gets or sets a number that the value should be divisble by. A number that the value should be divisble by. Gets or sets the minimum. The minimum. Gets or sets the maximum. The maximum. Gets or sets a flag indicating whether the value can not equal the number defined by the "minimum" attribute. A flag indicating whether the value can not equal the number defined by the "minimum" attribute. Gets or sets a flag indicating whether the value can not equal the number defined by the "maximum" attribute. A flag indicating whether the value can not equal the number defined by the "maximum" attribute. Gets or sets the minimum number of items. The minimum number of items. Gets or sets the maximum number of items. The maximum number of items. Gets or sets the of items. The of items. Gets or sets a value indicating whether items in an array are validated using the instance at their array position from . true if items are validated using their array position; otherwise, false. Gets or sets the of additional items. The of additional items. Gets or sets a value indicating whether additional items are allowed. true if additional items are allowed; otherwise, false. Gets or sets whether the array items must be unique. Gets or sets the of properties. The of properties. Gets or sets the of additional properties. The of additional properties. Gets or sets the pattern properties. The pattern properties. Gets or sets a value indicating whether additional properties are allowed. true if additional properties are allowed; otherwise, false. Gets or sets the required property if this property is present. The required property if this property is present. Gets or sets the a collection of valid enum values allowed. A collection of valid enum values allowed. Gets or sets disallowed types. The disallow types. Gets or sets the default value. The default value. Gets or sets the collection of that this schema extends. The collection of that this schema extends. Gets or sets the format. The format. Initializes a new instance of the class. Reads a from the specified . The containing the JSON Schema to read. The object representing the JSON Schema. Reads a from the specified . The containing the JSON Schema to read. The to use when resolving schema references. The object representing the JSON Schema. Load a from a string that contains schema JSON. A that contains JSON. A populated from the string that contains JSON. Parses the specified json. The json. The resolver. A populated from the string that contains JSON. Writes this schema to a . A into which this method will write. Writes this schema to a using the specified . A into which this method will write. The resolver used. Returns a that represents the current . A that represents the current . Returns detailed information about the schema exception. JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. Gets the line number indicating where the error occurred. The line number indicating where the error occurred. Gets the line position indicating where the error occurred. The line position indicating where the error occurred. Gets the path to the JSON where the error occurred. The path to the JSON where the error occurred. Initializes a new instance of the class. Initializes a new instance of the class with a specified error message. The error message that explains the reason for the exception. Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. The error message that explains the reason for the exception. The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. Initializes a new instance of the class. The that holds the serialized object data about the exception being thrown. The that contains contextual information about the source or destination. The parameter is null. The class name is null or is zero (0). Generates a from a specified . JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. Gets or sets how undefined schemas are handled by the serializer. Gets or sets the contract resolver. The contract resolver. Generate a from the specified type. The type to generate a from. A generated from the specified type. Generate a from the specified type. The type to generate a from. The used to resolve schema references. A generated from the specified type. Generate a from the specified type. The type to generate a from. Specify whether the generated root will be nullable. A generated from the specified type. Generate a from the specified type. The type to generate a from. The used to resolve schema references. Specify whether the generated root will be nullable. A generated from the specified type. Resolves from an id. JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. Gets or sets the loaded schemas. The loaded schemas. Initializes a new instance of the class. Gets a for the specified reference. The id. A for the specified reference. The value types allowed by the . JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. No type specified. String type. Float type. Integer type. Boolean type. Object type. Array type. Null type. Any type. Specifies undefined schema Id handling options for the . JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. Do not infer a schema Id. Use the .NET type name as the schema Id. Use the assembly qualified .NET type name as the schema Id. Returns detailed information related to the . JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. Gets the associated with the validation error. The JsonSchemaException associated with the validation error. Gets the path of the JSON location where the validation error occurred. The path of the JSON location where the validation error occurred. Gets the text description corresponding to the validation error. The text description. Represents the callback method that will handle JSON schema validation events and the . JSON Schema validation has been moved to its own package. See http://www.newtonsoft.com/jsonschema for more details. A snake case naming strategy. Initializes a new instance of the class. A flag indicating whether dictionary keys should be processed. A flag indicating whether explicitly specified property names should be processed, e.g. a property name customized with a . Initializes a new instance of the class. Resolves the specified property name. The property name to resolve. The resolved property name. A camel case naming strategy. Initializes a new instance of the class. A flag indicating whether dictionary keys should be processed. A flag indicating whether explicitly specified property names should be processed, e.g. a property name customized with a . Initializes a new instance of the class. Resolves the specified property name. The property name to resolve. The resolved property name. Resolves member mappings for a type, camel casing property names. Initializes a new instance of the class. Used by to resolves a for a given . Gets a value indicating whether members are being get and set using dynamic code generation. This value is determined by the runtime permissions available. true if using dynamic code generation; otherwise, false. Gets or sets the default members search flags. The default members search flags. Gets or sets a value indicating whether compiler generated members should be serialized. true if serialized compiler generated members; otherwise, false. Gets or sets a value indicating whether to ignore the interface when serializing and deserializing types. true if the interface will be ignored when serializing and deserializing types; otherwise, false. Gets or sets a value indicating whether to ignore the attribute when serializing and deserializing types. true if the attribute will be ignored when serializing and deserializing types; otherwise, false. Gets or sets the naming strategy used to resolve how property names and dictionary keys are serialized. The naming strategy used to resolve how property names and dictionary keys are serialized. Initializes a new instance of the class. Initializes a new instance of the class. If set to true the will use a cached shared with other resolvers of the same type. Sharing the cache will significantly improve performance with multiple resolver instances because expensive reflection will only happen once. This setting can cause unexpected behavior if different instances of the resolver are suppose to produce different results. When set to false it is highly recommended to reuse instances with the . Resolves the contract for a given type. The type to resolve a contract for. The contract for a given type. Gets the serializable members for the type. The type to get serializable members for. The serializable members for the type. Creates a for the given type. Type of the object. A for the given type. Creates the constructor parameters. The constructor to create properties for. The type's member properties. Properties for the given . Creates a for the given . The matching member property. The constructor parameter. A created for the given . Resolves the default for the contract. Type of the object. The contract's default . Creates a for the given type. Type of the object. A for the given type. Creates a for the given type. Type of the object. A for the given type. Creates a for the given type. Type of the object. A for the given type. Creates a for the given type. Type of the object. A for the given type. Creates a for the given type. Type of the object. A for the given type. Creates a for the given type. Type of the object. A for the given type. Creates a for the given type. Type of the object. A for the given type. Determines which contract type is created for the given type. Type of the object. A for the given type. Creates properties for the given . The type to create properties for. /// The member serialization mode for the type. Properties for the given . Creates the used by the serializer to get and set values from a member. The member. The used by the serializer to get and set values from a member. Creates a for the given . The member's parent . The member to create a for. A created for the given . Resolves the name of the property. Name of the property. Resolved name of the property. Resolves the key of the dictionary. By default is used to resolve dictionary keys. Key of the dictionary. Resolved key of the dictionary. Gets the resolved name of the property. Name of the property. Name of the property. The default naming strategy. Property names and dictionary keys are unchanged. Resolves the specified property name. The property name to resolve. The resolved property name. The default serialization binder used when resolving and loading classes from type names. When overridden in a derived class, controls the binding of a serialized object to a type. Specifies the name of the serialized object. Specifies the name of the serialized object. The type of the object the formatter creates a new instance of. When overridden in a derived class, controls the binding of a serialized object to a type. The type of the object the formatter creates a new instance of. Specifies the name of the serialized object. Specifies the name of the serialized object. Represents a trace writer that writes to the application's instances. Gets the that will be used to filter the trace messages passed to the writer. For example a filter level of Info will exclude Verbose messages and include Info, Warning and Error messages. The that will be used to filter the trace messages passed to the writer. Writes the specified trace level, message and optional exception. The at which to write this trace. The trace message. The trace exception. This parameter is optional. Get and set values for a using dynamic methods. Initializes a new instance of the class. The member info. Sets the value. The target to set the value on. The value to set on the target. Gets the value. The target to get the value from. The value. Provides information surrounding an error. Gets the error. The error. Gets the original object that caused the error. The original object that caused the error. Gets the member that caused the error. The member that caused the error. Gets the path of the JSON location where the error occurred. The path of the JSON location where the error occurred. Gets or sets a value indicating whether this is handled. true if handled; otherwise, false. Provides data for the Error event. Gets the current object the error event is being raised against. The current object the error event is being raised against. Gets the error context. The error context. Initializes a new instance of the class. The current object. The error context. Get and set values for a using dynamic methods. Initializes a new instance of the class. The member info. Sets the value. The target to set the value on. The value to set on the target. Gets the value. The target to get the value from. The value. Provides methods to get attributes. Returns a collection of all of the attributes, or an empty collection if there are no attributes. When true, look up the hierarchy chain for the inherited custom attribute. A collection of s, or an empty collection. Returns a collection of attributes, identified by type, or an empty collection if there are no attributes. The type of the attributes. When true, look up the hierarchy chain for the inherited custom attribute. A collection of s, or an empty collection. Used by to resolves a for a given . Resolves the contract for a given type. The type to resolve a contract for. The contract for a given type. A base class for resolving how property names and dictionary keys are serialized. A flag indicating whether dictionary keys should be processed. Defaults to false. A flag indicating whether explicitly specified property names, e.g. a property name customized with a , should be processed. Defaults to false. Gets the serialized name for a given property name. The initial property name. A flag indicating whether the property has had a name explicitly specfied. The serialized property name. Gets the serialized key for a given dictionary key. The initial dictionary key. The serialized dictionary key. Resolves the specified property name. The property name to resolve. The resolved property name. Used to resolve references when serializing and deserializing JSON by the . Resolves a reference to its object. The serialization context. The reference to resolve. The object that Gets the reference for the sepecified object. The serialization context. The object to get a reference for. The reference to the object. Determines whether the specified object is referenced. The serialization context. The object to test for a reference. true if the specified object is referenced; otherwise, false. Adds a reference to the specified object. The serialization context. The reference. The object to reference. Represents a trace writer. Gets the that will be used to filter the trace messages passed to the writer. For example a filter level of Info will exclude Verbose messages and include Info, Warning and Error messages. The that will be used to filter the trace messages passed to the writer. Writes the specified trace level, message and optional exception. The at which to write this trace. The trace message. The trace exception. This parameter is optional. Provides methods to get and set values. Sets the value. The target to set the value on. The value to set on the target. Gets the value. The target to get the value from. The value. Contract details for a used by the . Gets the of the collection items. The of the collection items. Gets a value indicating whether the collection type is a multidimensional array. true if the collection type is a multidimensional array; otherwise, false. Gets or sets the function used to create the object. When set this function will override . The function used to create the object. Gets a value indicating whether the creator has a parameter with the collection values. true if the creator has a parameter with the collection values; otherwise, false. Initializes a new instance of the class. The underlying type for the contract. Contract details for a used by the . Gets or sets the default collection items . The converter. Gets or sets a value indicating whether the collection items preserve object references. true if collection items preserve object references; otherwise, false. Gets or sets the collection item reference loop handling. The reference loop handling. Gets or sets the collection item type name handling. The type name handling. Initializes a new instance of the class. The underlying type for the contract. Handles serialization callback events. The object that raised the callback event. The streaming context. Handles serialization error callback events. The object that raised the callback event. The streaming context. The error context. Sets extension data for an object during deserialization. The object to set extension data on. The extension data key. The extension data value. Gets extension data for an object during serialization. The object to set extension data on. Contract details for a used by the . Gets the underlying type for the contract. The underlying type for the contract. Gets or sets the type created during deserialization. The type created during deserialization. Gets or sets whether this type contract is serialized as a reference. Whether this type contract is serialized as a reference. Gets or sets the default for this contract. The converter. Gets or sets all methods called immediately after deserialization of the object. The methods called immediately after deserialization of the object. Gets or sets all methods called during deserialization of the object. The methods called during deserialization of the object. Gets or sets all methods called after serialization of the object graph. The methods called after serialization of the object graph. Gets or sets all methods called before serialization of the object. The methods called before serialization of the object. Gets or sets all method called when an error is thrown during the serialization of the object. The methods called when an error is thrown during the serialization of the object. Gets or sets the method called immediately after deserialization of the object. The method called immediately after deserialization of the object. Gets or sets the method called during deserialization of the object. The method called during deserialization of the object. Gets or sets the method called after serialization of the object graph. The method called after serialization of the object graph. Gets or sets the method called before serialization of the object. The method called before serialization of the object. Gets or sets the method called when an error is thrown during the serialization of the object. The method called when an error is thrown during the serialization of the object. Gets or sets the default creator method used to create the object. The default creator method used to create the object. Gets or sets a value indicating whether the default creator is non public. true if the default object creator is non-public; otherwise, false. Contract details for a used by the . Gets or sets the property name resolver. The property name resolver. Gets or sets the dictionary key resolver. The dictionary key resolver. Gets the of the dictionary keys. The of the dictionary keys. Gets the of the dictionary values. The of the dictionary values. Gets or sets the function used to create the object. When set this function will override . The function used to create the object. Gets a value indicating whether the creator has a parameter with the dictionary values. true if the creator has a parameter with the dictionary values; otherwise, false. Initializes a new instance of the class. The underlying type for the contract. Contract details for a used by the . Gets the object's properties. The object's properties. Gets or sets the property name resolver. The property name resolver. Initializes a new instance of the class. The underlying type for the contract. Contract details for a used by the . Gets or sets the ISerializable object constructor. The ISerializable object constructor. Initializes a new instance of the class. The underlying type for the contract. Contract details for a used by the . Initializes a new instance of the class. The underlying type for the contract. Contract details for a used by the . Gets or sets the object member serialization. The member object serialization. Gets or sets a value that indicates whether the object's properties are required. A value indicating whether the object's properties are required. Gets the object's properties. The object's properties. Gets the constructor parameters required for any non-default constructor Gets a collection of instances that define the parameters used with . Gets or sets the override constructor used to create the object. This is set when a constructor is marked up using the JsonConstructor attribute. The override constructor. Gets or sets the parametrized constructor used to create the object. The parametrized constructor. Gets or sets the function used to create the object. When set this function will override . This function is called with a collection of arguments which are defined by the collection. The function used to create the object. Gets or sets the extension data setter. Gets or sets the extension data getter. Gets or sets the extension data value type. Initializes a new instance of the class. The underlying type for the contract. Contract details for a used by the . Initializes a new instance of the class. The underlying type for the contract. Maps a JSON property to a .NET member or constructor parameter. Gets or sets the name of the property. The name of the property. Gets or sets the type that declared this property. The type that declared this property. Gets or sets the order of serialization of a member. The numeric order of serialization. Gets or sets the name of the underlying member or parameter. The name of the underlying member or parameter. Gets the that will get and set the during serialization. The that will get and set the during serialization. Gets or sets the for this property. The for this property. Gets or sets the type of the property. The type of the property. Gets or sets the for the property. If set this converter takes presidence over the contract converter for the property type. The converter. Gets or sets the member converter. The member converter. Gets or sets a value indicating whether this is ignored. true if ignored; otherwise, false. Gets or sets a value indicating whether this is readable. true if readable; otherwise, false. Gets or sets a value indicating whether this is writable. true if writable; otherwise, false. Gets or sets a value indicating whether this has a member attribute. true if has a member attribute; otherwise, false. Gets the default value. The default value. Gets or sets a value indicating whether this is required. A value indicating whether this is required. Gets or sets a value indicating whether this property preserves object references. true if this instance is reference; otherwise, false. Gets or sets the property null value handling. The null value handling. Gets or sets the property default value handling. The default value handling. Gets or sets the property reference loop handling. The reference loop handling. Gets or sets the property object creation handling. The object creation handling. Gets or sets or sets the type name handling. The type name handling. Gets or sets a predicate used to determine whether the property should be serialize. A predicate used to determine whether the property should be serialize. Gets or sets a predicate used to determine whether the property should be deserialized. A predicate used to determine whether the property should be deserialized. Gets or sets a predicate used to determine whether the property should be serialized. A predicate used to determine whether the property should be serialized. Gets or sets an action used to set whether the property has been deserialized. An action used to set whether the property has been deserialized. Returns a that represents this instance. A that represents this instance. Gets or sets the converter used when serializing the property's collection items. The collection's items converter. Gets or sets whether this property's collection items are serialized as a reference. Whether this property's collection items are serialized as a reference. Gets or sets the the type name handling used when serializing the property's collection items. The collection's items type name handling. Gets or sets the the reference loop handling used when serializing the property's collection items. The collection's items reference loop handling. A collection of objects. Initializes a new instance of the class. The type. When implemented in a derived class, extracts the key from the specified element. The element from which to extract the key. The key for the specified element. Adds a object. The property to add to the collection. Gets the closest matching object. First attempts to get an exact case match of propertyName and then a case insensitive match. Name of the property. A matching property if found. Gets a property by property name. The name of the property to get. Type property name string comparison. A matching property if found. Contract details for a used by the . Initializes a new instance of the class. The underlying type for the contract. Lookup and create an instance of the JsonConverter type described by the argument. The JsonConverter type to create. Optional arguments to pass to an initializing constructor of the JsonConverter. If null, the default constructor is used. Represents a trace writer that writes to memory. When the trace message limit is reached then old trace messages will be removed as new messages are added. Gets the that will be used to filter the trace messages passed to the writer. For example a filter level of Info will exclude Verbose messages and include Info, Warning and Error messages. The that will be used to filter the trace messages passed to the writer. Initializes a new instance of the class. Writes the specified trace level, message and optional exception. The at which to write this trace. The trace message. The trace exception. This parameter is optional. Returns an enumeration of the most recent trace messages. An enumeration of the most recent trace messages. Returns a of the most recent trace messages. A of the most recent trace messages. Represents a method that constructs an object. The object type to create. When applied to a method, specifies that the method is called when an error occurs serializing an object. Provides methods to get attributes from a , , or . Initializes a new instance of the class. The instance to get attributes for. This parameter should be a , , or . Returns a collection of all of the attributes, or an empty collection if there are no attributes. When true, look up the hierarchy chain for the inherited custom attribute. A collection of s, or an empty collection. Returns a collection of attributes, identified by type, or an empty collection if there are no attributes. The type of the attributes. When true, look up the hierarchy chain for the inherited custom attribute. A collection of s, or an empty collection. Get and set values for a using reflection. Initializes a new instance of the class. The member info. Sets the value. The target to set the value on. The value to set on the target. Gets the value. The target to get the value from. The value. Specifies how strings are escaped when writing JSON text. Only control characters (e.g. newline) are escaped. All non-ASCII and control characters (e.g. newline) are escaped. HTML (<, >, &, ', ") and control characters (e.g. newline) are escaped. Specifies type name handling options for the . should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom when deserializing with a value other than TypeNameHandling.None. Do not include the .NET type name when serializing types. Include the .NET type name when serializing into a JSON object structure. Include the .NET type name when serializing into a JSON array structure. Always include the .NET type name when serializing. Include the .NET type name when the type of the object being serialized is not the same as its declared type. Determines whether the collection is null or empty. The collection. true if the collection is null or empty; otherwise, false. Adds the elements of the specified collection to the specified generic IList. The list to add to. The collection of elements to add. Converts the value to the specified type. If the value is unable to be converted, the value is checked whether it assignable to the specified type. The value to convert. The culture to use when converting. The type to convert or cast the value to. The converted type. If conversion was unsuccessful, the initial value is returned if assignable to the target type. Helper method for generating a MetaObject which calls a specific method on Dynamic that returns a result Helper method for generating a MetaObject which calls a specific method on Dynamic, but uses one of the arguments for the result. Helper method for generating a MetaObject which calls a specific method on Dynamic, but uses one of the arguments for the result. Returns a Restrictions object which includes our current restrictions merged with a restriction limiting our type Gets a dictionary of the names and values of an Enum type. Gets a dictionary of the names and values of an Enum type. The enum type to get names and values for. Gets the type of the typed collection's items. The type. The type of the typed collection's items. Gets the member's underlying type. The member. The underlying type of the member. Determines whether the member is an indexed property. The member. true if the member is an indexed property; otherwise, false. Determines whether the property is an indexed property. The property. true if the property is an indexed property; otherwise, false. Gets the member's value on the object. The member. The target object. The member's value on the object. Sets the member's value on the target object. The member. The target. The value. Determines whether the specified MemberInfo can be read. The MemberInfo to determine whether can be read. /// if set to true then allow the member to be gotten non-publicly. true if the specified MemberInfo can be read; otherwise, false. Determines whether the specified MemberInfo can be set. The MemberInfo to determine whether can be set. if set to true then allow the member to be set non-publicly. if set to true then allow the member to be set if read-only. true if the specified MemberInfo can be set; otherwise, false. Builds a string. Unlike StringBuilder this class lets you reuse it's internal buffer. Determines whether the string is all white space. Empty string will return false. The string to test whether it is all white space. true if the string is all white space; otherwise, false. Specifies the state of the . An exception has been thrown, which has left the in an invalid state. You may call the method to put the in the Closed state. Any other method calls results in an being thrown. The method has been called. An object is being written. A array is being written. A constructor is being written. A property is being written. A write method has not been called. ================================================ FILE: README.adoc ================================================ = Doom Launcher https://ci.appveyor.com/project/hobomaster22/doomlauncher[image:https://ci.appveyor.com/api/projects/status/github/nstlaurent/doomlauncher?svg=true[Build status]] image:https://api.codacy.com/project/badge/Grade/f77deda96cfb4b90a201d09cb0014009[link="https://app.codacy.com/manual/nstlaurent/DoomLauncher?utm_source=github.com&utm_medium=referral&utm_content=nstlaurent/DoomLauncher&utm_campaign=Badge_Grade_Settings"] Doom Launcher is a _Doom_ frontend for Windows PC. Instead of being a simple utility to launch your files, it serves as database for all your _Doom_ engine games and mods. It can be compared to https://www.quaddicted.com/tools/quake_injector[Quake Injector], a popular tool for _Quake_. image::https://i.imgur.com/TIg4kNK.png[IWads Tile View Showcase] Large tile view for IWads tab. IWADs such as _Freedoom_ and _Heretic_ can be played with Doom Launcher. image::https://i.imgur.com/mYqC1QO.png[Custom Files Tile View Showcase] Large tile view for a custom tag named "Playing". Doom Launcher can play custom mapsets such as Eviternity, Back to Saturn X, Plutonia 2, and more! == Features * Direct download and metadata update from /idgames (through API). * Add ZIP archives containing WAD/PK3/DEH/TXT files. * Automatic scraping of title, author, release date and description from included text files. * Automatically load files when selecting an IWAD or source port. * Add comments and ratings for files. * Import screenshots directly from a source port into the database. * Maintain demos and saved games (DSG/ZDS supported). * Set launch parameters and warp into any map in a file. * Select specific files within a ZIP archive. * Tag files with custom colored tags. * Screen filters to simulate CRT monitors. * Record play-statistics for each map completed in a play session. Supported source ports includes ZDoom, PrBoom+, CNDoom, Chocolate Doom, and Crispy Doom. * Automatic daily database backups (SQLite database files). * Create shortcuts to quickly launch files. Many more features are documented in the Help file. Please read it if you have any concerns regarding certain features. Doom Launcher is written by Hobomaster22 in C# and requires .NET 4.5. If you want to report bugs or suggest features, submit issues to the GitHub repository. To contribute to Doom Launcher, fork a copy of the repository and submit your changes via a pull request. == Building Doom Launcher Download a copy of the repository onto your Windows system and open up the solution file on Visual Studio. Visual Studio 2017 or later is recommended. You may create new unit tests to help verify that your changes work before submitting them. Doom Launcher currently supports Windows 7 and later. Mac and Linux are unsupported. In order to build the Setup module for Visual Studio 2022, you may need to separately install support for Installer projects: https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects == Development Tools SQLite Browser. Great tool for viewing and editing the DoomLauncher.sqlite database: https://sqlitebrowser.org/ SonarLint. A great code analysis extension for Visual Studio. Highly recommended and used for most of Doom Launcher's development: https://www.sonarlint.org/visualstudio/ == Doom Launcher Links GitHub Repository: https://github.com/nstlaurent/DoomLauncher Doomworld Thread: https://www.doomworld.com/vb/doom-general/69346-doom-launcher-doom-frontend-database/ Realm667: https://realm667.com/index.php/en/kunena/doom-launcher ================================================ FILE: RELEASENOTES.md ================================================ # 3.9.0 ## Features: ## Bug Fixes: - Address winrar vulnerability with relative file paths - Fix utility form hiding Save/Cancel buttons ================================================ FILE: Setup/EnableLaunchApplication.js ================================================ // EnableLaunchApplication.js // Performs a post-build fixup of an msi to launch a specific file when the install has completed // Configurable values var checkboxChecked = true; // Is the checkbox on the finished dialog checked by default? var checkboxText = "Launch [ProductName]"; // Text for the checkbox on the finished dialog var filename = "DoomLauncher.exe"; // The name of the executable to launch - change this to match the file you want to launch at the end of your setup // Constant values from Windows Installer var msiOpenDatabaseModeTransact = 1; var msiViewModifyInsert = 1; var msiViewModifyUpdate = 2; var msiViewModifyAssign = 3; var msiViewModifyReplace = 4; var msiViewModifyDelete = 6; if (WScript.Arguments.Length != 1) { WScript.StdErr.WriteLine(WScript.ScriptName + " file"); WScript.Quit(1); } var filespec = WScript.Arguments(0); var installer = WScript.CreateObject("WindowsInstaller.Installer"); var database = installer.OpenDatabase(filespec, msiOpenDatabaseModeTransact); var sql; var view; var record; try { var fileId = FindFileIdentifier(database, filename); if (!fileId) throw "Unable to find '" + filename + "' in File table"; WScript.Echo("Updating the Control table..."); // Modify the Control_Next of BannerBmp control to point to the new CheckBox sql = "SELECT `Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help` FROM `Control` WHERE `Dialog_`='FinishedForm' AND `Control`='BannerBmp'"; view = database.OpenView(sql); view.Execute(); record = view.Fetch(); record.StringData(11) = "CheckboxLaunch"; view.Modify(msiViewModifyReplace, record); view.Close(); // Insert the new CheckBox control // sql = "INSERT INTO `Control` (`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help`) VALUES ('FinishedForm', 'CheckboxLaunch', 'CheckBox', '9', '201', '343', '12', '3', 'LAUNCHAPP', '{\\VSI_MS_Sans_Serif13.0_0_0}" + checkboxText + "', 'CloseButton', '|')"; sql = "INSERT INTO `Control` (`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, `Attributes`, `Property`, `Text`, `Control_Next`, `Help`) VALUES ('FinishedForm', 'CheckboxLaunch', 'CheckBox', '9', '201', '343', '12', '3', 'LAUNCHAPP', '{\\VSI_MS_Sans_Serif13.0_0_0}" + checkboxText + "', 'Line1', '|')"; view = database.OpenView(sql); view.Execute(); view.Close(); WScript.Echo("Updating the ControlEvent table..."); // Modify the Order of the EndDialog event of the FinishedForm to 1 sql = "SELECT `Dialog_`, `Control_`, `Event`, `Argument`, `Condition`, `Ordering` FROM `ControlEvent` WHERE `Dialog_`='FinishedForm' AND `Event`='EndDialog'"; view = database.OpenView(sql); view.Execute(); record = view.Fetch(); record.IntegerData(6) = 1; view.Modify(msiViewModifyReplace, record); view.Close(); // Insert the Event to launch the application sql = "INSERT INTO `ControlEvent` (`Dialog_`, `Control_`, `Event`, `Argument`, `Condition`, `Ordering`) VALUES ('FinishedForm', 'CloseButton', 'DoAction', 'VSDCA_Launch', 'LAUNCHAPP=1', '0')"; view = database.OpenView(sql); view.Execute(); view.Close(); WScript.Echo("Updating the CustomAction table..."); // Insert the custom action to launch the application when finished sql = "INSERT INTO `CustomAction` (`Action`, `Type`, `Source`, `Target`) VALUES ('VSDCA_Launch', '210', '" + fileId + "', '')"; view = database.OpenView(sql); view.Execute(); view.Close(); if (checkboxChecked) { WScript.Echo("Updating the Property table..."); // Set the default value of the CheckBox sql = "INSERT INTO `Property` (`Property`, `Value`) VALUES ('LAUNCHAPP', '1')"; view = database.OpenView(sql); view.Execute(); view.Close(); } database.Commit(); } catch(e) { WScript.StdErr.WriteLine(e); WScript.Quit(1); } function FindFileIdentifier(database, fileName) { // First, try to find the exact file name var sql = "SELECT `File` FROM `File` WHERE `FileName`='" + fileName + "'"; var view = database.OpenView(sql); view.Execute(); var record = view.Fetch(); if (record) { var value = record.StringData(1); view.Close(); return value; } view.Close(); // The file may be in SFN|LFN format. Look for a filename in this case next sql = "SELECT `File`, `FileName` FROM `File`"; view = database.OpenView(sql); view.Execute(); record = view.Fetch(); while (record) { if (StringEndsWith(record.StringData(2), "|" + fileName)) { var value = record.StringData(1); view.Close(); return value; } record = view.Fetch(); } view.Close(); } function StringEndsWith(str, value) { if (str.length < value.length) return false; return (str.indexOf(value, str.length - value.length) != -1); } ================================================ FILE: Setup/Setup.vdproj ================================================ "DeployProject" { "VSVersion" = "3:800" "ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}" "IsWebType" = "8:FALSE" "ProjectName" = "8:Setup" "LanguageId" = "3:1033" "CodePage" = "3:1252" "UILanguageId" = "3:1033" "SccProjectName" = "8:" "SccLocalPath" = "8:" "SccAuxPath" = "8:" "SccProvider" = "8:" "Hierarchy" { "Entry" { "MsmKey" = "8:_1C93A3EC442FFDADD64CDB667BDAD615" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_21428D848FA27A1FD153A4AF7A69EBDF" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_3EFFB1D4728D71DE53D6382742470624" "OwnerKey" = "8:_1C93A3EC442FFDADD64CDB667BDAD615" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_414E5D336CA68B1036F857F024A3C6EE" "OwnerKey" = "8:_B1CA06F4B4E252B91130AB980E18DA3A" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_414E5D336CA68B1036F857F024A3C6EE" "OwnerKey" = "8:_21428D848FA27A1FD153A4AF7A69EBDF" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_414E5D336CA68B1036F857F024A3C6EE" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_47ED56C7EB324440D431476CFC4B464F" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_47ED56C7EB324440D431476CFC4B464F" "OwnerKey" = "8:_A10269FC379C147CCB8769DE2D6F9F7D" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_47ED56C7EB324440D431476CFC4B464F" "OwnerKey" = "8:_ABE333C419918B2BF59661AE04BCC362" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4C299C0D4D6FACA0FC44E97E3C17127E" "OwnerKey" = "8:_414E5D336CA68B1036F857F024A3C6EE" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_4C299C0D4D6FACA0FC44E97E3C17127E" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_5DB2DA7BE02309DAFE869508B8075705" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_63E4F764E8B041E59FDBB33BE88C745C" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_63F541A093684BECBF8F162892F5DDA4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_74FC6FAABD4108DF17921DE769C87250" "OwnerKey" = "8:_D3A03CE7A6A10218D50538ABC3F286D4" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_793F4759666A25557CFCC0B97E0B4887" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_96F6698AADF846BC8304AE597FD426CE" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_9A83643D15D94C130995E27924600F76" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A10269FC379C147CCB8769DE2D6F9F7D" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A13D3674ED2FB9924FD359296BC077FE" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A213F9066FBE0CFB6B1BBC5E912D196E" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A665674D765D7FDCEA2A0EB6F5D6A9DA" "OwnerKey" = "8:_B1CA06F4B4E252B91130AB980E18DA3A" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A665674D765D7FDCEA2A0EB6F5D6A9DA" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A665674D765D7FDCEA2A0EB6F5D6A9DA" "OwnerKey" = "8:_414E5D336CA68B1036F857F024A3C6EE" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_A8200F3634D437B54C477CEC4774A818" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_ABE333C419918B2BF59661AE04BCC362" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B1CA06F4B4E252B91130AB980E18DA3A" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_B518A3AC4C66E28527F62B0FC4B34AD2" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BF7618CDF7DD398B31A670E17C6A462A" "OwnerKey" = "8:_414E5D336CA68B1036F857F024A3C6EE" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BF7618CDF7DD398B31A670E17C6A462A" "OwnerKey" = "8:_5DB2DA7BE02309DAFE869508B8075705" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_BF7618CDF7DD398B31A670E17C6A462A" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CA115A09384060BEEC500614FAFF40C6" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_CD74C87BD809123E1A5229D324317104" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D3A03CE7A6A10218D50538ABC3F286D4" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_D3D41A0E7A164D13BC1D3D21BF885195" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E4970A73CC6B52AE551FFEEDB2D14E93" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E4970A73CC6B52AE551FFEEDB2D14E93" "OwnerKey" = "8:_CA115A09384060BEEC500614FAFF40C6" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E4970A73CC6B52AE551FFEEDB2D14E93" "OwnerKey" = "8:_A10269FC379C147CCB8769DE2D6F9F7D" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_E53668B26E65467EA92586BA68363BBA" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F331CB5D2789C9E642B9E8FB101942BD" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_F8E515DA6830450C9C12B875F513E6B2" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_21428D848FA27A1FD153A4AF7A69EBDF" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_5DB2DA7BE02309DAFE869508B8075705" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_CA115A09384060BEEC500614FAFF40C6" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_A10269FC379C147CCB8769DE2D6F9F7D" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_E4970A73CC6B52AE551FFEEDB2D14E93" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_B1CA06F4B4E252B91130AB980E18DA3A" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_414E5D336CA68B1036F857F024A3C6EE" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_BF7618CDF7DD398B31A670E17C6A462A" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_4C299C0D4D6FACA0FC44E97E3C17127E" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_A665674D765D7FDCEA2A0EB6F5D6A9DA" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_A8200F3634D437B54C477CEC4774A818" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_1C93A3EC442FFDADD64CDB667BDAD615" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_3EFFB1D4728D71DE53D6382742470624" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_F331CB5D2789C9E642B9E8FB101942BD" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_CD74C87BD809123E1A5229D324317104" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_ABE333C419918B2BF59661AE04BCC362" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_47ED56C7EB324440D431476CFC4B464F" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_A13D3674ED2FB9924FD359296BC077FE" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_793F4759666A25557CFCC0B97E0B4887" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_A213F9066FBE0CFB6B1BBC5E912D196E" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_D3A03CE7A6A10218D50538ABC3F286D4" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_74FC6FAABD4108DF17921DE769C87250" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_9A83643D15D94C130995E27924600F76" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_B518A3AC4C66E28527F62B0FC4B34AD2" "MsmSig" = "8:_UNDEFINED" } } "Configurations" { "Debug" { "DisplayName" = "8:Debug" "IsDebugOnly" = "11:TRUE" "IsReleaseOnly" = "11:FALSE" "OutputFilename" = "8:Debug\\DoomLauncher.msi" "PackageFilesAs" = "3:2" "PackageFileSize" = "3:-2147483648" "CabType" = "3:1" "Compression" = "3:2" "SignOutput" = "11:FALSE" "CertificateFile" = "8:" "PrivateKeyFile" = "8:" "TimeStampServer" = "8:" "InstallerBootstrapper" = "3:2" "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" { "Enabled" = "11:TRUE" "PromptEnabled" = "11:TRUE" "PrerequisitesLocation" = "2:1" "Url" = "8:" "ComponentsUrl" = "8:" "Items" { "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2" { "Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)" "ProductCode" = "8:.NETFramework,Version=v4.7.2" } } } } "Release" { "DisplayName" = "8:Release" "IsDebugOnly" = "11:FALSE" "IsReleaseOnly" = "11:TRUE" "OutputFilename" = "8:Release\\DoomLauncher.msi" "PackageFilesAs" = "3:2" "PackageFileSize" = "3:-2147483648" "CabType" = "3:1" "Compression" = "3:2" "SignOutput" = "11:FALSE" "CertificateFile" = "8:" "PrivateKeyFile" = "8:" "TimeStampServer" = "8:" "InstallerBootstrapper" = "3:2" "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}" { "Enabled" = "11:TRUE" "PromptEnabled" = "11:TRUE" "PrerequisitesLocation" = "2:1" "Url" = "8:" "ComponentsUrl" = "8:" "Items" { "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2" { "Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)" "ProductCode" = "8:.NETFramework,Version=v4.7.2" } } } } } "Deployable" { "CustomAction" { } "DefaultFeature" { "Name" = "8:DefaultFeature" "Title" = "8:" "Description" = "8:" } "ExternalPersistence" { "LaunchCondition" { "{A06ECF26-33A3-4562-8140-9B0E340D4F24}:_1101EDA8E6114AF5AA607FADF3EEE1CE" { "Name" = "8:.NET Framework" "Message" = "8:[VSDNETMSG]" "FrameworkVersion" = "8:.NETFramework,Version=v4.7.2" "AllowLaterVersions" = "11:FALSE" "InstallUrl" = "8:http://go.microsoft.com/fwlink/?LinkId=863262" } } } "File" { "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_1C93A3EC442FFDADD64CDB667BDAD615" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:Octokit, Version=0.50.0.0, Culture=neutral, processorArchitecture=MSIL" "ScatterAssemblies" { "_1C93A3EC442FFDADD64CDB667BDAD615" { "Name" = "8:Octokit.dll" "Attributes" = "3:512" } } "SourcePath" = "8:Octokit.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_21428D848FA27A1FD153A4AF7A69EBDF" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:WadReader, Version=3.8.0.0, Culture=neutral, processorArchitecture=MSIL" "ScatterAssemblies" { "_21428D848FA27A1FD153A4AF7A69EBDF" { "Name" = "8:WadReader.dll" "Attributes" = "3:512" } } "SourcePath" = "8:WadReader.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_3EFFB1D4728D71DE53D6382742470624" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:TRUE" "AssemblyAsmDisplayName" = "8:System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" "ScatterAssemblies" { "_3EFFB1D4728D71DE53D6382742470624" { "Name" = "8:System.Net.Http.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Net.Http.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_414E5D336CA68B1036F857F024A3C6EE" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL" "ScatterAssemblies" { "_414E5D336CA68B1036F857F024A3C6EE" { "Name" = "8:System.Memory.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Memory.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_47ED56C7EB324440D431476CFC4B464F" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL" "ScatterAssemblies" { "_47ED56C7EB324440D431476CFC4B464F" { "Name" = "8:EntityFramework.dll" "Attributes" = "3:512" } } "SourcePath" = "8:EntityFramework.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_4C299C0D4D6FACA0FC44E97E3C17127E" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" "ScatterAssemblies" { "_4C299C0D4D6FACA0FC44E97E3C17127E" { "Name" = "8:System.Numerics.Vectors.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Numerics.Vectors.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_5DB2DA7BE02309DAFE869508B8075705" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Text.Encoding.CodePages, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" "ScatterAssemblies" { "_5DB2DA7BE02309DAFE869508B8075705" { "Name" = "8:System.Text.Encoding.CodePages.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Text.Encoding.CodePages.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_63E4F764E8B041E59FDBB33BE88C745C" { "SourcePath" = "8:..\\DoomLauncher\\Help.pdf" "TargetName" = "8:Help.pdf" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_63F541A093684BECBF8F162892F5DDA4" { "SourcePath" = "8:..\\DoomLauncher\\DoomLauncher.sqlite" "TargetName" = "8:DoomLauncher.sqlite" "Tag" = "8:" "Folder" = "8:_9B4D77A4535A4C37862948F60E306337" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_74FC6FAABD4108DF17921DE769C87250" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:TRUE" "AssemblyAsmDisplayName" = "8:System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL" "ScatterAssemblies" { "_74FC6FAABD4108DF17921DE769C87250" { "Name" = "8:System.IO.Compression.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.IO.Compression.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_793F4759666A25557CFCC0B97E0B4887" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:Equin.ApplicationFramework.BindingListView, Version=1.4.9419.14939, Culture=neutral, processorArchitecture=MSIL" "ScatterAssemblies" { "_793F4759666A25557CFCC0B97E0B4887" { "Name" = "8:Equin.ApplicationFramework.BindingListView.dll" "Attributes" = "3:512" } } "SourcePath" = "8:Equin.ApplicationFramework.BindingListView.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_96F6698AADF846BC8304AE597FD426CE" { "SourcePath" = "8:..\\DoomLauncher\\TileImages\\DoomLauncherTileSmall.png" "TargetName" = "8:DoomLauncherTileSmall.png" "Tag" = "8:" "Folder" = "8:_C51A08AA1501484EB70A3FA6F7D31F44" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_9A83643D15D94C130995E27924600F76" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.IO.Compression, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" "ScatterAssemblies" { "_9A83643D15D94C130995E27924600F76" { "Name" = "8:System.IO.Compression.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.IO.Compression.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_A10269FC379C147CCB8769DE2D6F9F7D" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Data.SQLite.EF6, Version=1.0.115.5, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL" "ScatterAssemblies" { "_A10269FC379C147CCB8769DE2D6F9F7D" { "Name" = "8:System.Data.SQLite.EF6.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Data.SQLite.EF6.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_A13D3674ED2FB9924FD359296BC077FE" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:CheckBoxComboBox, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL" "ScatterAssemblies" { "_A13D3674ED2FB9924FD359296BC077FE" { "Name" = "8:CheckBoxComboBox.dll" "Attributes" = "3:512" } } "SourcePath" = "8:CheckBoxComboBox.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_A213F9066FBE0CFB6B1BBC5E912D196E" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" "ScatterAssemblies" { "_A213F9066FBE0CFB6B1BBC5E912D196E" { "Name" = "8:System.Net.Http.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Net.Http.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_A665674D765D7FDCEA2A0EB6F5D6A9DA" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL" "ScatterAssemblies" { "_A665674D765D7FDCEA2A0EB6F5D6A9DA" { "Name" = "8:System.Buffers.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Buffers.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_A8200F3634D437B54C477CEC4774A818" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:SevenZipSharp, Version=0.64.3890.29348, Culture=neutral, PublicKeyToken=20de82c62b055c88, processorArchitecture=MSIL" "ScatterAssemblies" { "_A8200F3634D437B54C477CEC4774A818" { "Name" = "8:SevenZipSharp.dll" "Attributes" = "3:512" } } "SourcePath" = "8:SevenZipSharp.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_ABE333C419918B2BF59661AE04BCC362" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL" "ScatterAssemblies" { "_ABE333C419918B2BF59661AE04BCC362" { "Name" = "8:EntityFramework.SqlServer.dll" "Attributes" = "3:512" } } "SourcePath" = "8:EntityFramework.SqlServer.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_B1CA06F4B4E252B91130AB980E18DA3A" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:SharpCompress, Version=0.31.0.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL" "ScatterAssemblies" { "_B1CA06F4B4E252B91130AB980E18DA3A" { "Name" = "8:SharpCompress.dll" "Attributes" = "3:512" } } "SourcePath" = "8:SharpCompress.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_B518A3AC4C66E28527F62B0FC4B34AD2" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Collections.Specialized, Version=4.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" "ScatterAssemblies" { "_B518A3AC4C66E28527F62B0FC4B34AD2" { "Name" = "8:System.Collections.Specialized.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Collections.Specialized.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_BF7618CDF7DD398B31A670E17C6A462A" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" "ScatterAssemblies" { "_BF7618CDF7DD398B31A670E17C6A462A" { "Name" = "8:System.Runtime.CompilerServices.Unsafe.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Runtime.CompilerServices.Unsafe.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_CA115A09384060BEEC500614FAFF40C6" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Data.SQLite.Linq, Version=1.0.115.5, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL" "ScatterAssemblies" { "_CA115A09384060BEEC500614FAFF40C6" { "Name" = "8:System.Data.SQLite.Linq.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Data.SQLite.Linq.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_CD74C87BD809123E1A5229D324317104" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:Gameloop.Vdf, Version=0.6.2.0, Culture=neutral, processorArchitecture=MSIL" "ScatterAssemblies" { "_CD74C87BD809123E1A5229D324317104" { "Name" = "8:Gameloop.Vdf.dll" "Attributes" = "3:512" } } "SourcePath" = "8:Gameloop.Vdf.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_D3A03CE7A6A10218D50538ABC3F286D4" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" "ScatterAssemblies" { "_D3A03CE7A6A10218D50538ABC3F286D4" { "Name" = "8:System.IO.Compression.FileSystem.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.IO.Compression.FileSystem.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_D3D41A0E7A164D13BC1D3D21BF885195" { "SourcePath" = "8:..\\DoomLauncher\\DoomLauncher.ico" "TargetName" = "8:DoomLauncher.ico" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_E4970A73CC6B52AE551FFEEDB2D14E93" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:System.Data.SQLite, Version=1.0.115.5, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL" "ScatterAssemblies" { "_E4970A73CC6B52AE551FFEEDB2D14E93" { "Name" = "8:System.Data.SQLite.dll" "Attributes" = "3:512" } } "SourcePath" = "8:System.Data.SQLite.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E53668B26E65467EA92586BA68363BBA" { "SourcePath" = "8:..\\DoomLauncher\\TileImages\\DoomLauncherTile.png" "TargetName" = "8:DoomLauncherTile.png" "Tag" = "8:" "Folder" = "8:_C51A08AA1501484EB70A3FA6F7D31F44" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_F331CB5D2789C9E642B9E8FB101942BD" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" "AssemblyAsmDisplayName" = "8:Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL" "ScatterAssemblies" { "_F331CB5D2789C9E642B9E8FB101942BD" { "Name" = "8:Newtonsoft.Json.dll" "Attributes" = "3:512" } } "SourcePath" = "8:Newtonsoft.Json.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_F8E515DA6830450C9C12B875F513E6B2" { "SourcePath" = "8:..\\DoomLauncher\\bin\\Release\\x64\\SQLite.Interop.dll" "TargetName" = "8:SQLite.Interop.dll" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } } "FileType" { } "Folder" { "{1525181F-901A-416C-8A58-119130FE478E}:_A30BDE48063D40559A4AA683AD667FA8" { "Name" = "8:#1915" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:AppDataFolder" "Folders" { "{9EF0B969-E518-4E46-987F-47570745A589}:_9B4D77A4535A4C37862948F60E306337" { "Name" = "8:DoomLauncher" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_7DC055EFD29D4888A87A26EAFEE910F2" "Folders" { "{9EF0B969-E518-4E46-987F-47570745A589}:_C51A08AA1501484EB70A3FA6F7D31F44" { "Name" = "8:TileImages" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:_57DC2A5EDC2441C2A67D5AE7844953B3" "Folders" { } } } } } } "{1525181F-901A-416C-8A58-119130FE478E}:_B56F790FDFAF46A29FFB44FBE969E6D8" { "Name" = "8:#1919" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:ProgramMenuFolder" "Folders" { } } "{1525181F-901A-416C-8A58-119130FE478E}:_BAF67D21DFBD46829DB043058C22639C" { "Name" = "8:#1916" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:DesktopFolder" "Folders" { } } "{3C67513D-01DD-4637-8A68-80971EB9504F}:_FBFCC08B40C94547A700104B2C2BD264" { "DefaultLocation" = "8:[ProgramFilesFolder]\\[ProductName]" "Name" = "8:#1925" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" "Property" = "8:TARGETDIR" "Folders" { } } } "LaunchCondition" { } "Locator" { } "MsiBootstrapper" { "LangId" = "3:1033" "RequiresElevation" = "11:FALSE" } "Product" { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:Doom Launcher" "ProductCode" = "8:{BC9120A6-2D1C-4667-9DC6-2372631A353E}" "PackageCode" = "8:{5039230A-92D2-4A7B-8BC7-63BEDF91E30B}" "UpgradeCode" = "8:{CAFDF197-4B50-4AFE-BCFE-0432710657F1}" "AspNetVersion" = "8:2.0.50727.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:TRUE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:FALSE" "ProductVersion" = "8:3.8.1" "Manufacturer" = "8:Doom Launcher" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:https://github.com/nstlaurent/DoomLauncher" "Title" = "8:Doom Launcher" "Subject" = "8:" "ARPCONTACT" = "8:Doom Launcher" "Keywords" = "8:" "ARPCOMMENTS" = "8:" "ARPURLINFOABOUT" = "8:https://github.com/nstlaurent/DoomLauncher" "ARPPRODUCTICON" = "8:_D3D41A0E7A164D13BC1D3D21BF885195" "ARPIconIndex" = "3:0" "SearchPath" = "8:" "UseSystemSearchPath" = "11:TRUE" "TargetPlatform" = "3:1" "PreBuildEvent" = "8:" "PostBuildEvent" = "8:cscript.exe \"$(ProjectDir)EnableLaunchApplication.js\" \"$(BuiltOuputPath)\"" "RunPostBuildEvent" = "3:0" } "Registry" { "HKLM" { "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_6B123DB311874B56BC86F2A8DF5F3CBD" { "Name" = "8:Software" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_F27AE53B80AC491290867BAC6434DF65" { "Name" = "8:[Manufacturer]" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { } "Values" { } } } "Values" { } } } } "HKCU" { "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_C2E9A6715DB74966B4CAF78055881C9A" { "Name" = "8:Software" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_E0C062D5235D447BB3D7253F6A9212E4" { "Name" = "8:[Manufacturer]" "Condition" = "8:" "AlwaysCreate" = "11:FALSE" "DeleteAtUninstall" = "11:FALSE" "Transitive" = "11:FALSE" "Keys" { } "Values" { } } } "Values" { } } } } "HKCR" { "Keys" { } } "HKU" { "Keys" { } } "HKPU" { "Keys" { } } } "Sequences" { } "Shortcut" { "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_02E60DB2622949319196B5232F111A59" { "Name" = "8:Doom Launcher" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:1" "IconIndex" = "3:0" "Transitive" = "11:FALSE" "Target" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "Folder" = "8:_BAF67D21DFBD46829DB043058C22639C" "WorkingFolder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Icon" = "8:_D3D41A0E7A164D13BC1D3D21BF885195" "Feature" = "8:" } "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_168056505F944161A3AF990F9BBF606E" { "Name" = "8:Doom Launcher" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:1" "IconIndex" = "3:0" "Transitive" = "11:FALSE" "Target" = "8:_FECC9E8503254BEAAAEB23AD08285AF7" "Folder" = "8:_B56F790FDFAF46A29FFB44FBE969E6D8" "WorkingFolder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Icon" = "8:_D3D41A0E7A164D13BC1D3D21BF885195" "Feature" = "8:" } } "UserInterface" { "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_0F0846073CA34A55A00E70F82404341E" { "Name" = "8:#1901" "Sequence" = "3:2" "Attributes" = "3:2" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_A5E698F392D24B01A8A5FCF3299D180C" { "Sequence" = "3:100" "DisplayName" = "8:Progress" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminProgressDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "ShowProgress" { "Name" = "8:ShowProgress" "DisplayName" = "8:#1009" "Description" = "8:#1109" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_39CDF33039E843DB90F748C59AFE477E" { "UseDynamicProperties" = "11:FALSE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdUserInterface.wim" } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_464C61E00BC24857BFD2079873DA3AD2" { "Name" = "8:#1902" "Sequence" = "3:1" "Attributes" = "3:3" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_25813063E9634D6FA002919DC10995ED" { "Sequence" = "3:100" "DisplayName" = "8:Finished" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdFinishedDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "UpdateText" { "Name" = "8:UpdateText" "DisplayName" = "8:#1058" "Description" = "8:#1158" "Type" = "3:15" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1258" "DefaultValue" = "8:#1258" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_72D9FECC5E434397A41E249261136160" { "Name" = "8:#1901" "Sequence" = "3:1" "Attributes" = "3:2" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_BFC54B4D0DE74551A5E313C2F929D039" { "Sequence" = "3:100" "DisplayName" = "8:Progress" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdProgressDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "ShowProgress" { "Name" = "8:ShowProgress" "DisplayName" = "8:#1009" "Description" = "8:#1109" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_7F07C70E02A04427938A3B53C3FD4003" { "Name" = "8:#1900" "Sequence" = "3:2" "Attributes" = "3:1" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_140589FE107B46FF9D841D6761BFB76F" { "Sequence" = "3:100" "DisplayName" = "8:Welcome" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminWelcomeDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "CopyrightWarning" { "Name" = "8:CopyrightWarning" "DisplayName" = "8:#1002" "Description" = "8:#1102" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:2" "Value" = "8:" "DefaultValue" = "8:#1202" "UsePlugInResources" = "11:TRUE" } "Welcome" { "Name" = "8:Welcome" "DisplayName" = "8:#1003" "Description" = "8:#1103" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1203" "DefaultValue" = "8:#1203" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_AB8429EF68D945268941A73B6A6EF126" { "Sequence" = "3:300" "DisplayName" = "8:Confirm Installation" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminConfirmDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_C4FD201642BC47AF867B77AADFEB6B7E" { "Sequence" = "3:200" "DisplayName" = "8:Installation Folder" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminFolderDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_BE766F3F95F64D538F731118812C9C19" { "Name" = "8:#1902" "Sequence" = "3:2" "Attributes" = "3:3" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_198CB92FBBAC45EEB49710B202ECE826" { "Sequence" = "3:100" "DisplayName" = "8:Finished" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdAdminFinishedDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } "{2479F3F5-0309-486D-8047-8187E2CE5BA0}:_F22B87893EA54E47829025A8B33918F0" { "UseDynamicProperties" = "11:FALSE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdBasicDialogs.wim" } "{DF760B10-853B-4699-99F2-AFF7185B4A62}:_FA9BAFDFAB7A4BE184271A7E7816D0A0" { "Name" = "8:#1900" "Sequence" = "3:1" "Attributes" = "3:1" "Dialogs" { "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_A4BA3AF7E28F4A799BA9C63C9520FEDC" { "Sequence" = "3:300" "DisplayName" = "8:Confirm Installation" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdConfirmDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_B5C46140174D49FBB846CFED0048D83B" { "Sequence" = "3:100" "DisplayName" = "8:Welcome" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdWelcomeDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "CopyrightWarning" { "Name" = "8:CopyrightWarning" "DisplayName" = "8:#1002" "Description" = "8:#1102" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:2" "Value" = "8:" "DefaultValue" = "8:#1202" "UsePlugInResources" = "11:TRUE" } "Welcome" { "Name" = "8:Welcome" "DisplayName" = "8:#1003" "Description" = "8:#1103" "Type" = "3:3" "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:1" "Value" = "8:#1203" "DefaultValue" = "8:#1203" "UsePlugInResources" = "11:TRUE" } } } "{688940B3-5CA9-4162-8DEE-2993FA9D8CBC}:_FF2A45A0F11D4970B085D61A75CF9835" { "Sequence" = "3:200" "DisplayName" = "8:Installation Folder" "UseDynamicProperties" = "11:TRUE" "IsDependency" = "11:FALSE" "SourcePath" = "8:\\VsdFolderDlg.wid" "Properties" { "BannerBitmap" { "Name" = "8:BannerBitmap" "DisplayName" = "8:#1001" "Description" = "8:#1101" "Type" = "3:8" "ContextData" = "8:Bitmap" "Attributes" = "3:4" "Setting" = "3:1" "UsePlugInResources" = "11:TRUE" } "InstallAllUsersVisible" { "Name" = "8:InstallAllUsersVisible" "DisplayName" = "8:#1059" "Description" = "8:#1159" "Type" = "3:5" "ContextData" = "8:1;True=1;False=0" "Attributes" = "3:0" "Setting" = "3:0" "Value" = "3:1" "DefaultValue" = "3:1" "UsePlugInResources" = "11:TRUE" } } } } } } "MergeModule" { } "ProjectOutput" { "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_FECC9E8503254BEAAAEB23AD08285AF7" { "SourcePath" = "8:..\\DoomLauncher\\obj\\Debug\\DoomLauncher.exe" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FBFCC08B40C94547A700104B2C2BD264" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" "ReadOnly" = "11:FALSE" "Hidden" = "11:FALSE" "System" = "11:FALSE" "Permanent" = "11:FALSE" "SharedLegacy" = "11:FALSE" "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" "IsDependency" = "11:FALSE" "IsolateTo" = "8:" "ProjectOutputGroupRegister" = "3:1" "OutputConfiguration" = "8:" "OutputGroupCanonicalName" = "8:Built" "OutputProjectGuid" = "8:{938B529A-9AFD-4AE0-9F0F-5CB9527AD2F1}" "ShowKeyOutput" = "11:TRUE" "ExcludeFilters" { } } } } } ================================================ FILE: UnitTest/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.InteropServices; // 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("UnitTest")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("UnitTest")] [assembly: AssemblyCopyright("Copyright © 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("2e593d49-b240-49be-9e9d-5dd1250eedf3")] // 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("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: UnitTest/Resources/TestSteamInstall/config/libraryfolders.vdf ================================================ "libraryfolders" { "0" { "irrelevantStuff" "blah" "path" "Resources\\TestSteamLibrary1" "apps" { "48700" "4947778366" "228980" "383082318" } "whatevs" "010101010" } "1" { "path" "Resources\\TestSteamLibrary2" "apps" { "70" "589449723" "220" "3831084326" } } } ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/appmanifest_2280.acf ================================================ "AppState" { "blahblah" "123" "appid" "2280" "name" "DOOM + DOOM II" "installdir" "TestDoom" "irrelevantInfo" "99999998" } ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/appmanifest_2390.acf ================================================ "AppState" { "something" "xxx" "appid" "2390" "name" "Heretic" "installdir" "TestHeretic" "nonsense" "33333434" } ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/common/TestDoom/base/doom.wad ================================================ doom.wad ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/common/TestDoom/rerelease/doom2.wad ================================================ doom2.wad ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/common/TestDoom/rerelease/id1.wad ================================================ id1.wad ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/common/TestDoom/rerelease/masterlevels.wad ================================================ masterlevels.wad ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/common/TestDoom/rerelease/nerve.wad ================================================ nerve.wad ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/common/TestDoom/rerelease/plutonia.wad ================================================ plutonia.wad ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/common/TestDoom/rerelease/sigil.wad ================================================ sigil.wad ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/common/TestDoom/rerelease/tnt.wad ================================================ tnt.wad ================================================ FILE: UnitTest/Resources/TestSteamLibrary1/steamapps/common/TestHeretic/base/heretic.wad ================================================ heretic.wad ================================================ FILE: UnitTest/Resources/TestSteamLibrary2/steamapps/appmanifest_2360.acf ================================================ "AppState" { "ignoreme" "xxxxx" "appid" "2360" "name" "Hexen" "installdir" "TestHexen" "ignoremetoo" "yyy" } ================================================ FILE: UnitTest/Resources/TestSteamLibrary2/steamapps/appmanifest_2390.acf ================================================ "AppState" { "something" "xxx" "appid" "2390" "name" "Heretic" "installdir" "TestHeretic" "nonsense" "33333434" } ================================================ FILE: UnitTest/Resources/TestSteamLibrary2/steamapps/common/TestHeretic/base/heretic.wad ================================================ heretic.wad (library2) ================================================ FILE: UnitTest/Resources/TestSteamLibrary2/steamapps/common/TestHexen/base/hexen.wad ================================================ hexen.wad ================================================ FILE: UnitTest/TestInit.cs ================================================ using Microsoft.VisualStudio.TestTools.UnitTesting; using DoomLauncher; using UnitTest.Tests; using System.IO; namespace UnitTest { [TestClass] static class TestInit { [AssemblyInitialize()] public static void TestInitialize(TestContext testContext) { DbDataSourceAdapter adapter = (DbDataSourceAdapter)TestUtil.CreateAdapter(); DataCache.Instance.Init(adapter); VersionHandler versionHandler = new VersionHandler(adapter.DataAccess, adapter, new AppConfiguration(adapter)); versionHandler.HandleVersionUpdate(); } } } ================================================ FILE: UnitTest/Tests/Helpers/DirectoriesConfiguration.cs ================================================ using DoomLauncher; using DoomLauncher.Config; namespace UnitTest.Tests { class DirectoriesConfiguration : IDirectoriesConfiguration { public LauncherPath GameFileDirectory { get; set; } public LauncherPath ScreenshotDirectory { get; set; } public LauncherPath SaveGameDirectory { get; set; } public LauncherPath TempDirectory { get; set; } public LauncherPath DemoDirectory { get; set; } public LauncherPath ThumbnailDirectory { get; set; } } } ================================================ FILE: UnitTest/Tests/Helpers/Tree.cs ================================================ using System.Collections.Generic; using System.Linq; using System.Text; namespace UnitTest.Tests { public class Tree { public string Name { get; } public byte[] Content { get; } public string ContentString => Encoding.UTF7.GetString(Content); public List Children { get; } public bool HasChildren => Children.Count > 0; public Tree(string name, params Tree[] children) : this(name, name, children) { } public Tree(string name, byte[] content, params Tree[] children) { Name = name; Content = content; Children = children.ToList(); } public Tree(string name, string content, params Tree[] children) { Name = name; Content = Encoding.UTF7.GetBytes(content); Children = children.ToList(); } } } ================================================ FILE: UnitTest/Tests/Helpers/TreeEntry.cs ================================================ using DoomLauncher; using System; using System.Linq; namespace UnitTest.Tests { public class TreeEntry : AbstractArchiveEntry { public Tree Tree { get; } public TreeEntry(Tree tree) { Tree = tree; } public TreeReader GetTreeReader() { if (Tree.HasChildren) return new TreeReader(Tree); else return null; } public override long Length => Tree.Content.Length; public override string Name => Tree.Name; public override string FullName => Name; public override bool ExtractRequired => false; public override bool IsDirectory => false; public override void ExtractToFile(string file, bool overwrite = false) { throw new NotImplementedException(); } public override string GetNameWithoutExtension() => Name; public override void Read(byte[] buffer, int offset, int length) { Tree.Content.Take(buffer.Length).ToArray().CopyTo(buffer, offset); } } } ================================================ FILE: UnitTest/Tests/Helpers/TreeReader.cs ================================================ using DoomLauncher; using System.Collections.Generic; using System.Linq; namespace UnitTest.Tests { public class TreeReader : IArchiveReader { public static int TreeReadersCreated { get; set; } public static int TreeReadersDisposed { get; set; } public Tree Tree { get; } public TreeReader(Tree tree) { Tree = tree; TreeReadersCreated++; } public IEnumerable Entries => Tree.Children.Select(tree => new TreeEntry(tree)); public bool EntriesHaveExtensions => false; public void Dispose() { TreeReadersDisposed++; } } } ================================================ FILE: UnitTest/Tests/TestArchives.cs ================================================ using DoomLauncher; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; namespace UnitTest.Tests { [TestClass] public class TestArchives { [DllImport("kernel32.dll", BestFitMapping = false, ThrowOnUnmappableChar = true)] public static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string fileName); [TestMethod] public void TestCompressedArchives() { string[] files = new string[] { "archive.zip", "archive.rar", "archive.7z" }; // Unit test architecture may not match so we have to check load failure ArchiveReader.SetSevenZipInteropLibrary(Path.Combine("x64", "7z.dll")); if (LoadLibrary(ArchiveReader.SevenZipInteropLibrary) == IntPtr.Zero) ArchiveReader.SetSevenZipInteropLibrary(Path.Combine("x86", "7z.dll")); foreach (string file in files) { using (IArchiveReader archive = ArchiveReader.Create(Path.Combine("Resources", file))) { List entries = archive.Entries.OrderBy(x => x.FullName).ToList(); int index = 0; // .NET zip implementation doesn't include the first folder as an entry... if (archive is ZipArchiveReader) { Assert.AreEqual(5, entries.Count); } else { Assert.AreEqual(6, entries.Count); Assert.AreEqual("Folder/", GetFullPath(entries[0])); Assert.IsTrue(entries[0].IsDirectory); index = 1; } Assert.AreEqual("Folder/SubFolder/", GetFullPath(entries[index])); Assert.IsTrue(entries[index].IsDirectory); Assert.AreEqual("Folder/SubFolder/othertextfile.txt", GetFullPath(entries[index + 1])); Assert.IsFalse(entries[index + 1].IsDirectory); Assert.AreEqual("Folder/switch.WAD", GetFullPath(entries[index + 2])); Assert.IsFalse(entries[index + 2].IsDirectory); Assert.AreEqual("Folder/textfile.txt", GetFullPath(entries[index + 3])); Assert.IsFalse(entries[index + 3].IsDirectory); Assert.AreEqual("mapinfo.txt", GetFullPath(entries[index + 4])); Assert.IsFalse(entries[index + 4].IsDirectory); } } } private static string GetFullPath(IArchiveEntry entry) { string path = entry.FullName.Replace("\\", "/"); if (entry.IsDirectory && !path.EndsWith("/")) path += "/"; return path; } } } ================================================ FILE: UnitTest/Tests/TestAutomaticSteamCheck.cs ================================================ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using DoomLauncher.GameStores; using DoomLauncher; using DoomLauncher.Interfaces; using DoomLauncher.DataSources; using System.Threading.Tasks; namespace UnitTest.Tests { [TestClass] public class TestAutomaticSteamCheck { private IDataSourceAdapter database; [TestInitialize] public void Initialize() { database = TestUtil.CreateAdapter(); } [TestCleanup] public void CleanDatabase() { var dataAccess = ((DbDataSourceAdapter)database).DataAccess; dataAccess.ExecuteNonQuery("delete from GameFiles"); } [TestMethod] public async Task AddGamesFromSteam_IgnoresWadsThatAreAlreadyThere() { // Already existing files database.InsertGameFile(new GameFile() { FileName = @"C:\gamefiles\doom.zip" }); database.InsertGameFile(new GameFile() { FileName = "sigil.wad" }); var gameStoreFiles = new GameStoreFiles( new List() { @"C:\SteamLib1\Ultimate Doom\doom.wad", @"C:\SteamLib1\Ultimate Doom\doom2.wad" }, new List() { @"C:\SteamLib1\Ultimate Doom\sigil.wad", @"C:\SteamLib1\Ultimate Doom\id1.wad" }, null); var automaticSteamCheck = new AutomaticGameStoreCheck(() => gameStoreFiles, database); List loadedIwads = new List(); List loadedPwads = new List(); string loadedDoom64Exe = null; await automaticSteamCheck.LoadGamesFromGameStores( async list => loadedIwads = list, async list => loadedPwads = list, async exe => loadedDoom64Exe = exe); Assert.IsTrue(loadedIwads.Exists(file => file.Equals(@"C:\SteamLib1\Ultimate Doom\doom2.wad"))); Assert.IsFalse(loadedIwads.Exists(file => file.Equals(@"C:\SteamLib1\Ultimate Doom\doom.wad"))); // Already exists Assert.IsTrue(loadedPwads.Exists(file => file.Equals(@"C:\SteamLib1\Ultimate Doom\id1.wad"))); Assert.IsFalse(loadedPwads.Exists(file => file.Equals(@"C:\SteamLib1\Ultimate Doom\sigil.wad"))); // Already exists } [TestMethod] public async Task AddGamesFromSteam_DoesNothingIfNoIwads() { var gameStoreFiles = new GameStoreFiles( new List(), // No IWads new List() { @"C:\SteamLib1\Ultimate Doom\sigil.wad" }, null); var automaticSteamCheck = new AutomaticGameStoreCheck(() => gameStoreFiles, database); bool didTheIwadThing = false; bool didThePwadThing = false; bool didTheDoom64Thing = false; await automaticSteamCheck.LoadGamesFromGameStores( async list => didTheIwadThing = true, async list => didThePwadThing = true, async exe => didTheDoom64Thing = true); Assert.IsTrue(didThePwadThing); Assert.IsFalse(didTheIwadThing); Assert.IsFalse(didTheDoom64Thing); } [TestMethod] public async Task AddGamesFromSteam_DoesNothingIfNoPwads() { // Steam library containing "Ultimate Doom" var gameStoreFiles = new GameStoreFiles( new List() { @"C:\SteamLib1\Ultimate Doom\doom2.wad" }, new List(), null); // No PWads var automaticSteamCheck = new AutomaticGameStoreCheck(() => gameStoreFiles, database); bool didTheIwadThing = false; bool didThePwadThing = false; bool didTheDoom64Thing = false; await automaticSteamCheck.LoadGamesFromGameStores( async list => didTheIwadThing = true, async list => didThePwadThing = true, async exe => didTheDoom64Thing = true); Assert.IsFalse(didThePwadThing); Assert.IsTrue(didTheIwadThing); Assert.IsFalse(didTheDoom64Thing); } } } ================================================ FILE: UnitTest/Tests/TestCldDemoParser.cs ================================================ using DoomLauncher.Demo; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; namespace UnitTest.Tests { [TestClass] public class TestCldDemoParser { [TestMethod] public void TestCldDemoParserFile() { string file = "zandemo.cld"; File.Copy(Path.Combine("Resources", file), file, true); IDemoParser parser = DemoUtil.GetDemoParser(file); Assert.IsNotNull(parser); string[] files = parser.GetRequiredFiles(); Assert.AreEqual("doom2.wad", files[0]); Assert.AreEqual("pyrrhic_.wad", files[1]); } } } ================================================ FILE: UnitTest/Tests/TestCnDoomStats.cs ================================================ using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using DoomLauncher; using DoomLauncher.DataSources; using System.IO; using System.Collections.Generic; namespace UnitTest.Tests { [TestClass] public class TestCnDoomStats { private readonly List m_args = new List(); [Ignore] [TestMethod] public void TestCnDoomStatFile() { string file = @" Competition Doom 2.0.3.2 Z_Init: Init zone memory allocation daemon. zone memory: 029C0020, 2000000 allocated for zone V_Init: allocate screens. M_LoadDefaults: Load system defaults. saving config in default.cfg W_Init: Init WADfiles. adding C:\DoomLauncher\GameFiles\Temp\DOOM.WAD =========================================================================== The Ultimate DOOM =========================================================================== Competition Doom is free software, covered by the GNU General Public License. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. You are welcome to change and distribute copies under certain conditions. See the source for more information. =========================================================================== I_Init: Setting up machine state. NET_Init: Init network subsystem. M_Init: Init miscellaneous info. R_Init: Init DOOM refresh daemon - .......................... P_Init: Init Playloop state. S_Init: Setting up sound. D_CheckNetGame: Checking network game status. startskill 2 deathmatch: 0 startmap: 1 startepisode: 1 player 1 of 1 (1 nodes) Emulating the behavior of the 'Ultimate Doom' executable. HU_Init: Setting up heads up display. ST_Init: Init status bar. I_InitGraphics: Windowboxed (1280x960 within 1920x1080) I_InitStretchTables: Generating lookup tables.... I_InitGraphics: Windowboxed (1280x960 within 1920x1080) ### E1M1 ###################################### # # # Time: 00:22.57 Kills: 3/6 # # Items: 8/37 Secrets: 1/3 # # # ################### Total time: 00:00:22.57 ### ### E1M2 ###################################### # # # Time: 00:45.20 Kills: 4/41 # # Items: 6/42 Secrets: 3/6 # # # ################### Total time: 00:01:07.77 ### ### MAP11 ##################################### # # # Time: 05:03.23 Kills: 82/71 # # Items: 19/21 Secrets: 2/3 # # # ################### Total time: 00:05:03.23 ### "; CNDoomStatsReader statsReader = new CNDoomStatsReader(new GameFile() { GameFileID = 1 }, "stdout.txt"); statsReader.NewStastics += StatsReader_NewStastics; File.WriteAllText("stdout.txt", file); statsReader.ReadNow(); Assert.AreEqual(3, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("E1M1", m_args[0].Statistics.MapName); Assert.AreEqual(3, m_args[0].Statistics.KillCount); Assert.AreEqual(6, m_args[0].Statistics.TotalKills); Assert.AreEqual(8, m_args[0].Statistics.ItemCount); Assert.AreEqual(37, m_args[0].Statistics.TotalItems); Assert.AreEqual(1, m_args[0].Statistics.SecretCount); Assert.AreEqual(3, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(22.57f, m_args[0].Statistics.LevelTime); Assert.AreEqual("E1M2", m_args[1].Statistics.MapName); Assert.AreEqual(4, m_args[1].Statistics.KillCount); Assert.AreEqual(41, m_args[1].Statistics.TotalKills); Assert.AreEqual(6, m_args[1].Statistics.ItemCount); Assert.AreEqual(42, m_args[1].Statistics.TotalItems); Assert.AreEqual(3, m_args[1].Statistics.SecretCount); Assert.AreEqual(6, m_args[1].Statistics.TotalSecrets); Assert.AreEqual(45.20f, m_args[1].Statistics.LevelTime); //Kill count should not exceed total Assert.AreEqual("MAP11", m_args[2].Statistics.MapName); Assert.AreEqual(71, m_args[2].Statistics.KillCount); Assert.AreEqual(71, m_args[2].Statistics.TotalKills); Assert.AreEqual(19, m_args[2].Statistics.ItemCount); Assert.AreEqual(21, m_args[2].Statistics.TotalItems); Assert.AreEqual(2, m_args[2].Statistics.SecretCount); Assert.AreEqual(3, m_args[2].Statistics.TotalSecrets); Assert.AreEqual(303.23f, m_args[2].Statistics.LevelTime); } private void StatsReader_NewStastics(object sender, NewStatisticsEventArgs e) { m_args.Add(e); } } } ================================================ FILE: UnitTest/Tests/TestDirectoryAdapter.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.IO; using System.IO.Compression; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestDirectoryAdapter { private static string s_testdir = "TestAdapterDirectory"; private static string[] s_testfilenames = new string[] { "test1.zip", "file.zip", "another_file.zip", "doom_file.zip", "doom_mod.zip" }; [TestInitialize] public void Initialize() { if (Directory.Exists(s_testdir)) Directory.Delete(s_testdir, true); Directory.CreateDirectory(s_testdir); Array.ForEach(s_testfilenames, x => CreateZip(x)); } private static void CreateZip(string x) { using (var zip = ZipFile.Open(Path.Combine(s_testdir, x), ZipArchiveMode.Create)) { zip.CreateEntry("file1.txt"); } } [TestMethod] public void TestFileCount() { DirectoryDataSourceAdapter adapter = CreateAdapter(); Assert.AreEqual(s_testfilenames.Length, adapter.GetGameFilesCount()); } [TestMethod] public void TestFileNames() { DirectoryDataSourceAdapter adapter = CreateAdapter(); var filenames = adapter.GetGameFileNames(); Assert.AreEqual(s_testfilenames.Length, filenames.Count()); foreach (var file in s_testfilenames) Assert.IsTrue(filenames.Contains(file)); } [TestMethod] public void TestGetFiles() { DirectoryDataSourceAdapter adapter = CreateAdapter(); var gameFiles = adapter.GetGameFiles(); Assert.AreEqual(s_testfilenames.Length, gameFiles.Count()); foreach (var file in s_testfilenames) Assert.IsNotNull(gameFiles.FirstOrDefault(x => x.FileName == file)); } [TestMethod] public void TestGetFile() { DirectoryDataSourceAdapter adapter = CreateAdapter(); var gameFile = adapter.GetGameFile(s_testfilenames[0]); Assert.IsNotNull(gameFile); Assert.AreEqual(s_testfilenames[0], gameFile.FileName); gameFile = adapter.GetGameFile("garbagefile.zip"); Assert.IsNull(gameFile); } [TestMethod] public void TestGetFilesOptions() { DirectoryDataSourceAdapter adapter = CreateAdapter(); GameFileGetOptions options = new GameFileGetOptions(2); //limit is the only support option var gameFiles = adapter.GetGameFiles(options); Assert.AreEqual(2, gameFiles.Count()); } [TestMethod] public void TestDeleteFile() { DirectoryDataSourceAdapter adapter = CreateAdapter(); Assert.AreEqual(s_testfilenames.Length, adapter.GetGameFilesCount()); adapter.DeleteGameFile(new GameFile() { FileName = s_testfilenames[0] }); Assert.AreEqual(s_testfilenames.Length - 1, adapter.GetGameFilesCount()); } private static DirectoryDataSourceAdapter CreateAdapter() { return new DirectoryDataSourceAdapter(new LauncherPath(s_testdir)); ; } } } ================================================ FILE: UnitTest/Tests/TestDirectoryArchive.cs ================================================ using DoomLauncher; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Threading.Tasks; namespace UnitTest.Tests { [TestClass] public class TestDirectoryArchive { [TestMethod] public void TestData() { string file = "Directory.zip"; TestUtil.CopyResourceFile(file); ZipArchive za = ZipFile.OpenRead(file); string dir = "TestDirectory"; if (Directory.Exists(dir)) Directory.Delete(dir, true); za.ExtractToDirectory(dir); DirectoryArchiveReader directoryArchive = new DirectoryArchiveReader(dir); var entries = directoryArchive.Entries; Assert.AreEqual(13, entries.Count()); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("map01.wad"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("map02.wad"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("map03.wad"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("map01.mid"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("map02.mid"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("map03.mid"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("tex1.png"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("tex2.png"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("tex3.png"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("sound1.wav"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("mapinfo.txt"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("sndinfo.txt"))); Assert.IsNotNull(entries.FirstOrDefault(x => x.Name.Equals("textures.txt"))); } } } ================================================ FILE: UnitTest/Tests/TestDoom64SyncAction.cs ================================================ using DoomLauncher.Interfaces; using DoomLauncher; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DoomLauncher.Handlers.Sync; using DoomLauncher.DataSources; namespace UnitTest.Tests { [TestClass] public class TestDoom64SyncAction { private IDataSourceAdapter database; [TestInitialize] public void Initialize() { database = TestUtil.CreateAdapter(); } [TestMethod] public void ApplyToGameFile_IdentifiesDoom64FileFromMapInfo() { var syncAction = new Doom64SyncAction(database); GameFile file = new GameFile { GameFileID = 1 }; var mapInfoData = new string[] { "classtype = something" }; var result = syncAction.ApplyToGameFile(file, null, mapInfoData); Assert.IsFalse(result.Failed); Assert.IsTrue(file.IsDoom64); } [TestMethod] public void ApplyToGameFile_IdentifiesNonDoom64FileFromMapInfo() { var syncAction = new Doom64SyncAction(database); GameFile file = new GameFile { GameFileID = 1 }; var mapInfoData = new string[] { "somethingElse = something" }; var result = syncAction.ApplyToGameFile(file, null, mapInfoData); Assert.IsFalse(result.Failed); Assert.IsFalse(file.IsDoom64); } [TestMethod] public void ApplyToGameFile_LinksDoom64GameFileWithDoom64IWad() { var syncAction = new Doom64SyncAction(database); GameFile file = new GameFile { GameFileID = 1 }; var mapInfoData = new string[] { "classtype = something" }; IGameFile doom64IWad = database.GetGameFile("doom64.zip"); if (doom64IWad != null) { database.DeleteGameFile(doom64IWad); } IGameFile newDoom64IWad = new GameFile { GameFileID = 777, FileName = "doom64.zip", IWadID = 666 }; database.InsertGameFile(newDoom64IWad); var result = syncAction.ApplyToGameFile(file, null, mapInfoData); Assert.IsFalse(result.Failed); Assert.AreEqual(666, file.IWadID); } } } ================================================ FILE: UnitTest/Tests/TestDoom64TitlePicSyncAction.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Handlers.Sync; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; namespace UnitTest.Tests { [TestClass] public class TestDoom64TitlePicSyncAction { [TestMethod] public void ApplyGameFile_FindsDoom64TitlePic() { var gameFile = new GameFile() { GameFileID = 11, FileName = "horizon.zip" }; var syncAction = new Doom64TitlePicSyncAction(); var reader = ArchiveReader.Create(Path.Combine("Resources", "doom64_with_titlepic.zip")); var result = syncAction.ApplyToGameFile(gameFile, reader, new string[0]); Assert.IsTrue(result.GetTitlePic(gameFile, out _)); } [TestMethod] public void ApplyGameFile_DoesntFindOtherImages() { var gameFile = new GameFile() { GameFileID = 44, FileName = "blah.zip" }; var syncAction = new Doom64TitlePicSyncAction(); var reader = ArchiveReader.Create(Path.Combine("Resources", "doom64_without_titlepic.zip")); var result = syncAction.ApplyToGameFile(gameFile, reader, new string[0]); Assert.IsFalse(result.GetTitlePic(gameFile, out _)); } } } ================================================ FILE: UnitTest/Tests/TestDsgSave.cs ================================================ using DoomLauncher.SaveGame; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace UnitTest.Tests { [TestClass] public class TestDsgSave { [TestMethod] public void TestPrBoomSave() { string save1 = "prboomsave1.dsg"; TestUtil.CopyResourceFile(save1); DsgSaveGameReader reader = new DsgSaveGameReader(save1); Assert.AreEqual("NEW2", reader.GetName()); } [TestMethod] public void TestChocolateDoomSave() { string save1 = "chocosave1.dsg"; TestUtil.CopyResourceFile(save1); DsgSaveGameReader reader = new DsgSaveGameReader(save1); Assert.AreEqual("CHOCOTESTTESTTESTTESTTE", reader.GetName()); } } } ================================================ FILE: UnitTest/Tests/TestFile.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestFile { private IDataSourceAdapter database; [TestInitialize] public void Initialize() { database = TestUtil.CreateAdapter(); } [TestCleanup] public void CleanDatabase() { var dataAccess = ((DbDataSourceAdapter)database).DataAccess; dataAccess.ExecuteNonQuery("delete from Files"); } [TestMethod] public void GetFiles_GameFile_ReturnsMatchingFiles() { var gameFileId = 37; var file1 = new FileData { FileName = "Rabbits.txt", FileTypeID = FileType.SaveGame, FileOrder = 33, Description = "Furry creatures", DateCreated = DateTime.Parse("2/1/2018"), GameFileID = gameFileId, SourcePortID = 2, OriginalFileName = "Bunnies.txt", OriginalFilePath = "lagomorphs\\Bunnies.txt", UserTitle = "All about rabbits", UserDescription = "I didn't understand it", Map = "zzz" }; var file2 = new FileData { FileName = "unreal.txt", FileTypeID = FileType.TileImage, FileOrder = 2, Description = "Tim Sweeney's revenge", DateCreated = DateTime.Parse("5/6/1996"), GameFileID = gameFileId, SourcePortID = 8, OriginalFileName = "unrealio_dealio.txt", OriginalFilePath = "whynot\\unrealio_dealio.txt", UserTitle = "The truth about Unreal Tournament", UserDescription = "A gripping tale", Map = "aaa" }; var wrongFile = new FileData { FileName = "wrong.txt", FileTypeID = FileType.Unknown, FileOrder = 2, Description = "Wrong file", DateCreated = DateTime.Parse("1/7/1991"), GameFileID = -1, SourcePortID = 9, OriginalFileName = "wrongity_wrong.txt", OriginalFilePath = "bad\\wrongity_wrong.txt", UserTitle = "It's wrong", UserDescription = "Don't use this one", Map = "666" }; database.InsertFile(file1); database.InsertFile(file2); database.InsertFile(wrongFile); var files = database.GetFiles(new GameFile { GameFileID = gameFileId }); Assert.AreEqual(2, files.Count()); var retrieved1 = files.Where(x => x.FileName.Equals(file1.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(file1, retrieved1, "FileID")); var retrieved2 = files.Where(x => x.FileName.Equals(file2.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(file2, retrieved2, "FileID")); Assert.AreEqual(0, files.Where(x => x.FileName.Equals(wrongFile.FileName)).Count()); } [TestMethod] public void GetFiles_GameFile_FileType_ReturnsMatchingFiles() { var gameFileId = 999; FileType fileType = FileType.Thumbnail; var file1 = new FileData { FileName = "red.txt", FileTypeID = fileType, FileOrder = 24, Description = "A redulent color", DateCreated = DateTime.Parse("9/3/2033"), GameFileID = gameFileId, SourcePortID = 2, OriginalFileName = "hongse.txt", OriginalFilePath = "colors\\hongse.txt", UserTitle = "It's another color", UserDescription = "Primary color, a bit angry", Map = "yyy" }; var wrongGameIdFile = new FileData { FileName = "wrong.txt", FileTypeID = fileType, FileOrder = 2, Description = "Wrong file", DateCreated = DateTime.Parse("1/7/1991"), GameFileID = 444, SourcePortID = 9, OriginalFileName = "wrongity_wrong.txt", OriginalFilePath = "bad\\wrongity_wrong.txt", UserTitle = "Wrong game ID", UserDescription = "Don't use this one", Map = "666" }; var wrongFileTypeFile = new FileData { FileName = "false.txt", FileTypeID = FileType.SaveGame, FileOrder = 3, Description = "False, tricksy file", DateCreated = DateTime.Parse("3/8/2000"), GameFileID = gameFileId, SourcePortID = 8, OriginalFileName = "this_aint_it.txt", OriginalFilePath = "incorrect\\this_aint_it.txt", UserTitle = "Wrong file type", UserDescription = "Absolutely not", Map = "667" }; database.InsertFile(file1); database.InsertFile(wrongGameIdFile); database.InsertFile(wrongFileTypeFile); var files = database.GetFiles(new GameFile { GameFileID = gameFileId }, fileType); Assert.AreEqual(1, files.Count()); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(file1, files.First(), "FileID")); } [TestMethod] public void GetFiles_NoArgs_ReturnsAllFiles() { var file1 = new FileData { FileName = "hippo.txt", FileTypeID = FileType.Demo, FileOrder = 3, Description = "river horse", DateCreated = DateTime.Parse("6/9/2033"), GameFileID = 555, SourcePortID = 2, OriginalFileName = "hippopotamus.txt", OriginalFilePath = "animals\\hippopotamus.txt", UserTitle = "I like hippos", UserDescription = "They are very hungry", Map = "hjkl" }; var file2 = new FileData { FileName = "giraffe.txt", FileTypeID = FileType.Screenshot, FileOrder = 9, Description = "long neck deer", DateCreated = DateTime.Parse("1/1/1992"), GameFileID = 553, SourcePortID = 6, OriginalFileName = "giraffe_pattern.txt", OriginalFilePath = "animals\\giraffe_pattern.txt", UserTitle = "Long neck giraffe", UserDescription = "Why are their necks so long", Map = "aaa" }; database.InsertFile(file1); database.InsertFile(file2); var files = database.GetFiles(); Assert.AreEqual(2, files.Count()); var retrieved1 = files.Where(x => x.FileName.Equals(file1.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(file1, retrieved1, "FileID")); var retrieved2 = files.Where(x => x.FileName.Equals(file2.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(file2, retrieved2, "FileID")); } [TestMethod] public void GetFiles_FileType_ReturnsMatchingFiles() { var file1 = new FileData { FileName = "cacodemon.txt", FileTypeID = FileType.Demo, FileOrder = 1, Description = "red floaty boi", DateCreated = DateTime.Parse("1/2/2011"), GameFileID = 236, SourcePortID = 2, OriginalFileName = "caco.txt", OriginalFilePath = "monsters\\caco.txt", UserTitle = "The fanciest demon", UserDescription = "Three double shotty hits on a good day", Map = "yyy" }; var file2 = new FileData { FileName = "imp.txt", FileTypeID = FileType.Demo, FileOrder = 1, Description = "brown fella", DateCreated = DateTime.Parse("3/6/1998"), GameFileID = 353, SourcePortID = 8, OriginalFileName = "impy.txt", OriginalFilePath = "monsters\\impy.txt", UserTitle = "One shotty blast", UserDescription = "Two if you miss", Map = "yyy" }; var wrongFile = new FileData { FileName = "oops.txt", FileTypeID = FileType.Thumbnail, FileOrder = 8, Description = "wrong file type", DateCreated = DateTime.Parse("5/3/1997"), GameFileID = 222, SourcePortID = 9, OriginalFileName = "whoops.txt", OriginalFilePath = "ohno\\whoops.txt", UserTitle = "Very much the wrong one", UserDescription = "no way", Map = "www" }; database.InsertFile(file1); database.InsertFile(file2); database.InsertFile(wrongFile); var files = database.GetFiles(FileType.Demo); Assert.AreEqual(2, files.Count()); var retrieved1 = files.Where(x => x.FileName.Equals(file1.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(file1, retrieved1, "FileID")); var retrieved2 = files.Where(x => x.FileName.Equals(file2.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(file2, retrieved2, "FileID")); } [TestMethod] public void UpdateFile_UpdatesTheRightFieldsInTheRightFile() { var file1 = new FileData { FileName = "pain_elemental.txt", FileTypeID = FileType.SaveGame, FileOrder = 11, Description = "brown caco", DateCreated = DateTime.Parse("11/7/2004"), GameFileID = 222, SourcePortID = 2, OriginalFileName = "paine.txt", OriginalFilePath = "monsters\\paine.txt", UserTitle = "What if a demon shot more demons out of its mouth", UserDescription = "What if a pain elemental shot more pain elementals out of its mouth", Map = "s34" }; var wrongFile = new FileData { FileName = "hellknight.txt", FileTypeID = FileType.Screenshot, FileOrder = 2, Description = "the cheapest monster", DateCreated = DateTime.Parse("1/3/2007"), GameFileID = 222, SourcePortID = 5, OriginalFileName = "hk.txt", OriginalFilePath = "monsters\\hk.txt", UserTitle = "Change the color, ship it", UserDescription = "Deadlines are deadlines", Map = "ooo" }; database.InsertFile(file1); database.InsertFile(wrongFile); // Need to fetch so that the primary key is populated. var savedFiles = database.GetFiles(new GameFile { GameFileID = 222 }); var savedFile1 = savedFiles.Where(x => x.FileName.Equals(file1.FileName)).First(); // Update every updatable field with new value savedFile1.SourcePortID = 33; savedFile1.Description = "New Description"; savedFile1.FileOrder = 44; savedFile1.DateCreated = new DateTime(2011, 3, 2); savedFile1.UserTitle = "New UserTitle"; savedFile1.UserDescription = "New UserDescription"; savedFile1.Map = "New map"; database.UpdateFile(savedFile1); var updatedFiles = database.GetFiles(new GameFile { GameFileID = 222 }); // Values were updated in that record var retrieved1 = updatedFiles.Where(x => x.FileID.Equals(savedFile1.FileID)).First(); Assert.AreEqual(file1.FileName, retrieved1.FileName); Assert.AreEqual(33, retrieved1.SourcePortID); Assert.AreEqual("New Description", retrieved1.Description); Assert.AreEqual(44, retrieved1.FileOrder); Assert.AreEqual(new DateTime(2011, 3, 2), retrieved1.DateCreated); Assert.AreEqual("New UserTitle", retrieved1.UserTitle); Assert.AreEqual("New UserDescription", retrieved1.UserDescription); Assert.AreEqual("New map", retrieved1.Map); // The other record was unaffected var retrieved2 = updatedFiles.Where(x => x.FileName.Equals(wrongFile.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(wrongFile, retrieved2, "FileID")); } [TestMethod] public void DeleteFile_FileID_DeletesJustThatFile() { var file1 = new FileData { FileName = "chaingunner.txt", FileTypeID = FileType.Demo, FileOrder = 5, Description = "Bastard hitscanner", DateCreated = DateTime.Parse("7/11/2006"), GameFileID = 123, SourcePortID = 9, OriginalFileName = "chaingun_dude.txt", OriginalFilePath = "monsters\\chaingun_dude.txt", UserTitle = "I hate this guy", UserDescription = "Kill them kill them", Map = "E1M2" }; var wrongFile = new FileData { FileName = "wrongo.txt", FileTypeID = FileType.TileImage, FileOrder = 99, Description = "Something else", DateCreated = DateTime.Parse("11/7/1921"), GameFileID = 123, SourcePortID = 88, OriginalFileName = "nope.txt", OriginalFilePath = "bad\\nope.txt", UserTitle = "Yeah nah", UserDescription = "Womp womp womp", Map = "555" }; database.InsertFile(file1); database.InsertFile(wrongFile); // Need to fetch so that the primary key is populated. var savedFiles = database.GetFiles(); var savedFile1 = savedFiles.Where(x => x.FileName.Equals(file1.FileName)).First(); database.DeleteFile(savedFile1); var files = database.GetFiles(); Assert.AreEqual(1, files.Count()); Assert.AreEqual(wrongFile.FileName, files.First().FileName); } [TestMethod] public void DeleteFile_GameFileID_DeletesMatchingFiles() { var file1 = new FileData { FileName = "pinky_demon.txt", FileTypeID = FileType.Thumbnail, FileOrder = 8, Description = "oink", DateCreated = DateTime.Parse("04/06/2008"), GameFileID = 777, SourcePortID = 9, OriginalFileName = "pinky.txt", OriginalFilePath = "monsters\\pinky.txt", UserTitle = "Should be called piggy demon am I right", UserDescription = "Use a rocket for the dumbest suicide imaginable", Map = "E1M3" }; var file2 = new FileData { FileName = "revenant.txt", FileTypeID = FileType.SaveGame, FileOrder = 10, Description = "evil bastard", DateCreated = DateTime.Parse("1/1/2014"), GameFileID = 777, SourcePortID = 8, OriginalFileName = "rev.txt", OriginalFilePath = "monsters\\rev.txt", UserTitle = "Awful horrible demon", UserDescription = "Kill it before it kills you", Map = "E2M4" }; var wrongFile = new FileData { FileName = "wrongy.txt", FileTypeID = FileType.Demo, FileOrder = 99, Description = "Naaaah", DateCreated = DateTime.Parse("7/2/1933"), GameFileID = 11, SourcePortID = 60, OriginalFileName = "bah.txt", OriginalFilePath = "bad\\bah.txt", UserTitle = "bah humbug", UserDescription = "Don't pick me", Map = "MAP04" }; database.InsertFile(file1); database.InsertFile(file2); database.InsertFile(wrongFile); database.DeleteFile(new GameFile { GameFileID = 777 }); var files = database.GetFiles(); Assert.AreEqual(1, files.Count()); Assert.AreEqual(wrongFile.FileName, files.First().FileName); } } } ================================================ FILE: UnitTest/Tests/TestFileDetector.cs ================================================ using DoomLauncher; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestFileDetector { private NewFileDetector m_detector; private static string s_testFileDir = "testfiles"; [TestInitialize] public void Init() { m_detector = new NewFileDetector(new string[] { ".zds", ".dsg" }, s_testFileDir); if (Directory.Exists(s_testFileDir)) Directory.Delete(s_testFileDir, true); Directory.CreateDirectory(s_testFileDir); } [TestCleanup] public void Cleanup() { Directory.Delete(s_testFileDir, true); } private static void CreateTestFile(string filename) { File.WriteAllText(Path.Combine(s_testFileDir, filename), string.Empty); } private static void UpdateTestFile(string filename) { File.WriteAllText(Path.Combine(s_testFileDir, filename), "test"); } private static void DeleteTestFile(string filename) { File.Delete(Path.Combine(s_testFileDir, filename)); } private static bool ContainsFile(string[] filenames, string filename) { return filenames.Any(x => Path.GetFileName(x) == filename); } [TestMethod] public void TestEmpty() { m_detector.StartDetection(); Assert.AreEqual(0, m_detector.GetNewFiles().Length); Assert.AreEqual(0, m_detector.GetModifiedFiles().Length); } [TestMethod] public void TestNew() { m_detector.StartDetection(); CreateTestFile("test1.zds"); Assert.AreEqual(1, m_detector.GetNewFiles().Length); Assert.IsTrue(ContainsFile(m_detector.GetNewFiles(), "test1.zds")); CreateTestFile("test1.dsg"); Assert.AreEqual(2, m_detector.GetNewFiles().Length); Assert.IsTrue(ContainsFile(m_detector.GetNewFiles(), "test1.dsg")); } [TestMethod] public void TestNewWithExisting() { CreateTestFile("test1.zds"); CreateTestFile("test1.dsg"); m_detector.StartDetection(); Assert.AreEqual(0, m_detector.GetNewFiles().Length); Assert.AreEqual(0, m_detector.GetModifiedFiles().Length); CreateTestFile("test2.zds"); Assert.AreEqual(1, m_detector.GetNewFiles().Length); Assert.IsTrue(ContainsFile(m_detector.GetNewFiles(), "test2.zds")); } [Ignore] [TestMethod] public void TestModified() { CreateTestFile("test1.zds"); CreateTestFile("test1.dsg"); m_detector.StartDetection(); UpdateTestFile("test1.zds"); Assert.AreEqual(0, m_detector.GetNewFiles().Length); Assert.AreEqual(1, m_detector.GetModifiedFiles().Length); Assert.IsTrue(ContainsFile(m_detector.GetModifiedFiles(), "test1.zds")); CreateTestFile("test2.zds"); UpdateTestFile("test1.dsg"); Assert.AreEqual(1, m_detector.GetNewFiles().Length); Assert.AreEqual(2, m_detector.GetModifiedFiles().Length); Assert.IsTrue(ContainsFile(m_detector.GetModifiedFiles(), "test1.dsg")); Assert.IsTrue(ContainsFile(m_detector.GetNewFiles(), "test2.zds")); } [TestMethod] public void TestDelete() { CreateTestFile("test1.zds"); CreateTestFile("test1.dsg"); m_detector.StartDetection(); Assert.AreEqual(0, m_detector.GetNewFiles().Length); DeleteTestFile("test1.zds"); Assert.AreEqual(1, m_detector.GetDeletedFiles().Length); Assert.IsTrue(ContainsFile(m_detector.GetDeletedFiles(), "test1.zds")); DeleteTestFile("test1.dsg"); Assert.AreEqual(2, m_detector.GetDeletedFiles().Length); Assert.IsTrue(ContainsFile(m_detector.GetDeletedFiles(), "test1.dsg")); } } } ================================================ FILE: UnitTest/Tests/TestGameConfSyncAction.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Handlers.Sync; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestGameConfSyncAction { private DbDataSourceAdapter database; [TestInitialize] public void Initialize() { database = (DbDataSourceAdapter)TestUtil.CreateAdapter(); } [TestCleanup] public void Cleanup() { database.DataAccess.ExecuteNonQuery("delete from IWads"); } [TestMethod] public void ApplyToGameFile_SetsTitle() { GameFile gameFile = new GameFile { GameFileID = 1, Title = "Wrong Title", }; Tree files = new Tree("root", new Tree("GAMECONF", "{\"data\": { \"title\": \"Bingo Bongo\" }} ")); var reader = new TreeReader(files); var action = new GameConfSyncAction(database); action.ApplyToGameFile(gameFile, reader, new string[0]); Assert.AreEqual("Bingo Bongo", gameFile.Title); } [TestMethod] public void ApplyToGameFile_SetsAuthor() { GameFile gameFile = new GameFile { GameFileID = 1, Author = "Wrong Author", }; Tree files = new Tree("root", new Tree("GAMECONF", "{\"data\": { \"author\": \"Bob the Dog\" }} ")); var reader = new TreeReader(files); var action = new GameConfSyncAction(database); action.ApplyToGameFile(gameFile, reader, new string[0]); Assert.AreEqual("Bob the Dog", gameFile.Author); } [TestMethod] public void ApplyToGameFile_SetsDescription() { GameFile gameFile = new GameFile { GameFileID = 1, Description = "Wrong Description", }; Tree files = new Tree("root", new Tree("GAMECONF", "{\"data\": { \"description\": \"A fancy dog.\" }} ")); var reader = new TreeReader(files); var action = new GameConfSyncAction(database); action.ApplyToGameFile(gameFile, reader, new string[0]); Assert.AreEqual("A fancy dog.", gameFile.Description); } [TestMethod] public void ApplyToGameFile_SetsIwad() { GameFile gameFile = new GameFile { GameFileID = 1, }; database.InsertIWad(new IWadData { Name = "doom2.zip" }); var iwadId = database.GetIWads().FirstOrDefault(x => x.Name == "doom2.zip").IWadID; Tree files = new Tree("root", new Tree("GAMECONF", "{\"data\": { \"iwad\": \"doom2.wad\" }} ")); var reader = new TreeReader(files); var action = new GameConfSyncAction(database); action.ApplyToGameFile(gameFile, reader, new string[0]); Assert.AreEqual(iwadId, gameFile.IWadID); } [TestMethod] public void ApplyToGameFile_IgnoresAbsentGAMECONF() { GameFile gameFile = new GameFile { GameFileID = 1, Title = "Old Title", }; Tree files = new Tree("root", new Tree("WADINFO", "{\"data\": { \"title\": \"Eviternity\" }} ")); var reader = new TreeReader(files); var action = new GameConfSyncAction(database); action.ApplyToGameFile(gameFile, reader, new string[0]); Assert.AreEqual("Old Title", gameFile.Title); } } } ================================================ FILE: UnitTest/Tests/TestGameFile.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; namespace UnitTest.Tests { [TestClass] public class TestGameFile { private IDataSourceAdapter database; private GameFile CreateGameFile(string filename, int salt) { DateTime dt = DateTime.Parse("1/1/17"); GameFile gameFile = new GameFile { FileName = filename, Author = "the author" + salt, Description = "the description" + salt, Title = "the title" + salt, Comments = "the comments" + salt, ReleaseDate = dt, Downloaded = dt, LastPlayed = dt, IWadID = 22 + salt, Rating = 33 + salt, MinutesPlayed = 44 + salt, MapCount = 55 + salt, SourcePortID = 66 + salt, SettingsExtraParams = "the extra params" + salt, SettingsFiles = "the settings files" + salt, SettingsFilesIWAD = "the settings files iwad" + salt, SettingsFilesSourcePort = "the settings files source port" + salt, SettingsMap = "the settings map" + salt, SettingsSkill = "the settings skill" + salt, SettingsSpecificFiles = "the settings specific files" + salt, SettingsGameProfileID = 567 + salt }; return gameFile; } [TestInitialize] public void Initialize() { database = TestUtil.CreateAdapter(); } [TestCleanup] public void CleanDatabase() { var dataAccess = ((DbDataSourceAdapter)database).DataAccess; dataAccess.ExecuteNonQuery("delete from GameFiles"); } [TestMethod] public void GetGameFiles_ReturnsAllGameFiles () { var gameFile1 = new GameFile { FileName = "cacodemon.png", Author = "John Romero", Description = "Red ball of fury", Title = "Cacodemon image", Comments = "Hi res", ReleaseDate = DateTime.Parse("2/1/2018"), Downloaded = DateTime.Parse("3/5/2024"), LastPlayed = DateTime.Parse("4/5/2024"), IWadID = 9, Rating = 4, MinutesPlayed = 55, MapCount = 4, SourcePortID = 3, SettingsExtraParams = "extra", SettingsFiles = "settings file", SettingsFilesIWAD = "settings file iwad", SettingsFilesSourcePort = "settings file sp", SettingsMap = "settings map", SettingsSkill = "settings skill", SettingsSpecificFiles = "ssf", SettingsGameProfileID = 44 }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "baron.png", Author = "Bobby Prince", Description = "tough but fair", Title = "Baron of Hell", Comments = "Made from clay models", ReleaseDate = null, Downloaded = null, LastPlayed = null, IWadID = null, Rating = null, MinutesPlayed = 4, MapCount = null, SourcePortID = null, SettingsExtraParams = "aaa", SettingsFiles = "bbb", SettingsFilesIWAD = "ccc", SettingsFilesSourcePort = "ddd", SettingsMap = "eee", SettingsSkill = "fff", SettingsSpecificFiles = "ggg", SettingsGameProfileID = null }; database.InsertGameFile(gameFile2); var gameFiles = database.GetGameFiles(); Assert.AreEqual(gameFiles.Count(), 2); Assert.AreEqual(database.GetGameFiles().Count(), 2); Assert.AreEqual(database.GetGameFilesCount(), 2); Assert.AreEqual(database.GetGameFileNames().Count(), 2); var retrieved1 = gameFiles.Where(x => x.FileName.Equals(gameFile1.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(gameFile1, retrieved1, "GameFileID")); var retrieved2 = gameFiles.Where(x => x.FileName.Equals(gameFile2.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(gameFile2, retrieved2, "GameFileID")); } [TestMethod] public void GetGameFiles_Options_ReturnsTheSpecifiedFields() { var gameFile1 = CreateGameFile("keen.zip", 0); gameFile1.Map = "Dangerous Dave"; database.InsertGameFile(gameFile1); var fields = new GameFileFieldType[] { GameFileFieldType.Filename, GameFileFieldType.Map }; var options = new GameFileGetOptions(fields); var gameFiles = database.GetGameFiles(options); Assert.AreEqual(1, gameFiles.Count()); var retrievedGameFile1 = gameFiles.First(); Assert.AreEqual("", retrievedGameFile1.Description); Assert.IsNull(retrievedGameFile1.Rating); Assert.AreEqual(gameFile1.FileName, retrievedGameFile1.FileName); Assert.AreEqual(gameFile1.Map, retrievedGameFile1.Map); } [TestMethod] public void GetGameFiles_Options_ReturnsMatching() { var gameFile1 = CreateGameFile("dukenukem.zip", 1); gameFile1.Rating = 5; gameFile1.Comments = "righto!!!"; database.InsertGameFile(gameFile1); var wrongGameFile = CreateGameFile("jazzjackrabbit.zip", 2); database.InsertGameFile(wrongGameFile); var options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.Filename, "dukenukem.zip")); var retrievedGameFiles = database.GetGameFiles(options); Assert.AreEqual(1, retrievedGameFiles.Count()); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(gameFile1, retrievedGameFiles.First(), "GameFileID")); } [TestMethod] public void GetGameFiles_Options_ReturnsLike() { var gameFile1 = CreateGameFile("banana.zip", 3); database.InsertGameFile(gameFile1); var gameFile2 = CreateGameFile("banano.zip", 4); database.InsertGameFile(gameFile2); var gameFile3 = CreateGameFile("zanany.zip", 5); database.InsertGameFile(gameFile3); var wrongGameFile = CreateGameFile("cucumber.zip", 6); database.InsertGameFile(wrongGameFile); var options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.Filename, GameFileSearchOp.Like, "%anan%")); var retrievedGameFiles = database.GetGameFiles(options); Assert.AreEqual(3, retrievedGameFiles.Count()); var retrievedGameFile1 = retrievedGameFiles.Where(x => x.FileName.Equals(gameFile1.FileName)).FirstOrDefault(); Assert.IsNotNull(retrievedGameFile1); var retrievedGameFile2 = retrievedGameFiles.Where(x => x.FileName.Equals(gameFile2.FileName)).FirstOrDefault(); Assert.IsNotNull(retrievedGameFile2); var retrievedGameFile3 = retrievedGameFiles.Where(x => x.FileName.Equals(gameFile3.FileName)).FirstOrDefault(); Assert.IsNotNull(retrievedGameFile3); } [TestMethod] public void GetGameFiles_Options_ReturnsNotMatching() { var gameFile1 = CreateGameFile("batman.zip", 7); gameFile1.Comments = "b"; database.InsertGameFile(gameFile1); var gameFile2 = CreateGameFile("robin.zip", 8); gameFile2.Comments = "r"; database.InsertGameFile(gameFile2); var gameFile3 = CreateGameFile("joker.zip", 9); gameFile3.Comments = "j"; database.InsertGameFile(gameFile3); var options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.Comments, GameFileSearchOp.NotEqual, "r")); var retrievedGameFiles = database.GetGameFiles(options); Assert.AreEqual(2, retrievedGameFiles.Count()); var retrievedGameFile1 = retrievedGameFiles.Where(x => x.FileName.Equals(gameFile1.FileName)).FirstOrDefault(); Assert.IsNotNull(retrievedGameFile1); var retrievedGameFile3 = retrievedGameFiles.Where(x => x.FileName.Equals(gameFile3.FileName)).FirstOrDefault(); Assert.IsNotNull(retrievedGameFile3); } [TestMethod] public void GetGameFiles_Options_ReturnsLessThan() { var gameFile1 = CreateGameFile("harpo.zip", 10); gameFile1.Rating = 1; database.InsertGameFile(gameFile1); var gameFile2 = CreateGameFile("groucho.zip", 11); gameFile2.Rating = 2; database.InsertGameFile(gameFile2); var gameFile3 = CreateGameFile("chico.zip", 12); gameFile3.Rating = 3; database.InsertGameFile(gameFile3); var options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.Rating, GameFileSearchOp.LessThan, "3")); var retrievedGameFiles = database.GetGameFiles(options); Assert.AreEqual(2, retrievedGameFiles.Count()); var retrievedGameFile1 = retrievedGameFiles.Where(x => x.FileName.Equals(gameFile1.FileName)).FirstOrDefault(); Assert.IsNotNull(retrievedGameFile1); var retrievedGameFile2 = retrievedGameFiles.Where(x => x.FileName.Equals(gameFile2.FileName)).FirstOrDefault(); Assert.IsNotNull(retrievedGameFile2); } [TestMethod] public void GetGameFiles_Options_ReturnsGreaterThan() { var gameFile1 = CreateGameFile("green.zip", 13); gameFile1.MapCount = 5; database.InsertGameFile(gameFile1); var gameFile2 = CreateGameFile("blue.zip", 14); gameFile2.MapCount = 6; database.InsertGameFile(gameFile2); var gameFile3 = CreateGameFile("red.zip", 15); gameFile3.MapCount = null; database.InsertGameFile(gameFile3); var options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.MapCount, GameFileSearchOp.GreaterThan, "5")); var retrievedGameFiles = database.GetGameFiles(options); Assert.AreEqual(1, retrievedGameFiles.Count()); var retrievedGameFile2 = retrievedGameFiles.Where(x => x.FileName.Equals(gameFile2.FileName)).FirstOrDefault(); Assert.AreEqual(gameFile2.FileName, "blue.zip"); } [TestMethod] public void GetGameFiles_Options_ReturnsMatchingWithSelectedFields() { var gameFile1 = CreateGameFile("doomslayer.zip", 16); gameFile1.Map = "E1M1"; gameFile1.Author = "Adrian Carmack"; database.InsertGameFile(gameFile1); var wrongGameFile = CreateGameFile("animalcrossing.zip", 16); database.InsertGameFile(wrongGameFile); var fields = new GameFileFieldType[] { GameFileFieldType.Map, GameFileFieldType.Author }; var options = new GameFileGetOptions(fields, new GameFileSearchField(GameFileFieldType.Filename, "doomslayer.zip")); var retrievedGameFiles = database.GetGameFiles(options); Assert.AreEqual(1, retrievedGameFiles.Count()); var retrievedGameFile1 = retrievedGameFiles.First(); Assert.AreEqual(gameFile1.Map, retrievedGameFile1.Map); Assert.AreEqual(gameFile1.Author, retrievedGameFile1.Author); } [TestMethod] public void GetGameFile_ReturnsGameFileWithMatchingFilename() { var gameFile1 = CreateGameFile("blah.zip", 17); database.InsertGameFile(gameFile1); var wrongGameFile = CreateGameFile("wrongwrongwrong.zip", 18); database.InsertGameFile(wrongGameFile); var retrievedGameFile = database.GetGameFile("blah.zip"); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(gameFile1, retrievedGameFile, "GameFileID")); } [TestMethod] public void UpdateGameFile_UpdatesFields() { var gameFile1 = CreateGameFile("explosion.zip", 20); database.InsertGameFile(gameFile1); var wrongGameFile = CreateGameFile("notme.zip", 21); database.InsertGameFile(wrongGameFile); var savedGameFile1 = database.GetGameFile("explosion.zip"); savedGameFile1.Comments = "Hi"; savedGameFile1.Description = "Good stuff"; savedGameFile1.MinutesPlayed = 444; database.UpdateGameFile(savedGameFile1); var retrievedGameFile1 = database.GetGameFile("explosion.zip"); Assert.IsTrue(TestUtil.AllFieldsEqual(savedGameFile1, retrievedGameFile1)); var retrievedWrongGameFile = database.GetGameFile("notme.zip"); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(wrongGameFile, retrievedWrongGameFile, "GameFileID")); } [TestMethod] public void UpdateGameFile_UpdatesOnlySpecifiedFields() { var gameFile1 = CreateGameFile("rabbit.zip", 22); gameFile1.Comments = "Don't update this"; gameFile1.Description = "Update me"; database.InsertGameFile(gameFile1); var wrongGameFile = CreateGameFile("incorrect.zip", 23); database.InsertGameFile(wrongGameFile); var savedGameFile1 = database.GetGameFile("rabbit.zip"); savedGameFile1.Description = "The new value"; savedGameFile1.Comments = "Should be ignored"; database.UpdateGameFile(savedGameFile1, new GameFileFieldType[] { GameFileFieldType.Description }); var retrievedGameFile1 = database.GetGameFile("rabbit.zip"); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(gameFile1, retrievedGameFile1, "Description", "GameFileID")); Assert.AreEqual("The new value", retrievedGameFile1.Description); var retrievedWrongGameFile = database.GetGameFile("incorrect.zip"); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(wrongGameFile, retrievedWrongGameFile, "GameFileID")); } [TestMethod] public void DeleteGameFile_DeletesExistingGameFile() { var gameFile1 = CreateGameFile("deleteme.zip", 24); database.InsertGameFile(gameFile1); var wrongGameFile = CreateGameFile("keepme.zip", 25); database.InsertGameFile(wrongGameFile); var savedGameFile1 = database.GetGameFile("deleteme.zip"); database.DeleteGameFile(savedGameFile1); var retrievedGameFiles = database.GetGameFiles(); Assert.AreEqual(1, retrievedGameFiles.Count()); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(wrongGameFile, retrievedGameFiles.First(), "GameFileID")); } [TestMethod] public void UpdateGameFile_UpdatesConditionally() { var gameFile1 = CreateGameFile("right1.zip", 26); gameFile1.Author = "Michael Abrash"; gameFile1.Comments = "replace me"; database.InsertGameFile(gameFile1); var gameFile2 = CreateGameFile("right2.zip", 27); gameFile2.Author = "Michael Abrash"; gameFile2.Comments = "replace me too"; database.InsertGameFile(gameFile2); var wrongGameFile = CreateGameFile("wrongo.zip", 28); wrongGameFile.Author = "Scott Miller"; wrongGameFile.Comments = "don't replace me"; database.InsertGameFile(wrongGameFile); //Note: This function is only used in SourcePortViewForm.cs database.UpdateGameFiles(GameFileFieldType.Author, GameFileFieldType.Comments, "Michael Abrash", "The new comments"); var retrievedGameFile1 = database.GetGameFile("right1.zip"); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(gameFile1, retrievedGameFile1, "GameFileID", "Comments")); Assert.AreEqual("The new comments", retrievedGameFile1.Comments); var retrievedGameFile2 = database.GetGameFile("right2.zip"); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(gameFile2, retrievedGameFile2, "GameFileID", "Comments")); Assert.AreEqual("The new comments", retrievedGameFile2.Comments); var retrievedWrongGameFile = database.GetGameFile("wrongo.zip"); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(wrongGameFile, retrievedWrongGameFile, "GameFileID")); } [TestMethod] public void ArchiveExists_RecognisesManagedGameFiles() { var gameFilesPath = new LauncherPath("GameFiles"); var madeUpGameFile = CreateGameFile("madeup.zip", 14); Assert.IsFalse(madeUpGameFile.IsUnmanaged()); Assert.IsFalse(madeUpGameFile.ArchiveExists(gameFilesPath)); var gameFileInArchive = CreateGameFile("realthing.zip", 77); File.Create(Path.Combine(gameFilesPath.GetFullPath(), "realthing.zip")).Close(); Assert.IsFalse(gameFileInArchive.IsUnmanaged()); Assert.IsTrue(gameFileInArchive.ArchiveExists(gameFilesPath)); } [TestMethod] public void ArchiveExists_RecognisesUnmanagedGameFiles() { var gameFilesPath = new LauncherPath("GameFiles"); var resourcesPath = new LauncherPath("Resources"); var madeUpGameFile = CreateGameFile(Path.Combine(resourcesPath.GetFullPath(), "does-not-exist.zip"), 999); Assert.IsTrue(madeUpGameFile.IsUnmanaged()); Assert.IsFalse(madeUpGameFile.ArchiveExists(gameFilesPath)); // Resources\pyrrhic.zip is a file that exists in the testing folder var gameFileUnmanaged = CreateGameFile(Path.Combine(resourcesPath.GetFullPath(), "pyrrhic.zip"), 999); Assert.IsTrue(gameFileUnmanaged.IsUnmanaged()); Assert.IsTrue(gameFileUnmanaged.ArchiveExists(gameFilesPath)); } [TestMethod] public void ArchiveExists_RecognisesUnmanagedGameDirectories() { var gameFilesPath = new LauncherPath("GameFiles"); var resourcesPath = new LauncherPath("Resources"); // Resources\TestSteamInstall is a directory that exists in the testing folder var gameFileDirectory = CreateGameFile(Path.Combine(resourcesPath.GetFullPath(), "TestSteamInstall"), 999); Assert.IsTrue(gameFileDirectory.IsDirectory()); Assert.IsTrue(gameFileDirectory.ArchiveExists(gameFilesPath)); } } } ================================================ FILE: UnitTest/Tests/TestGameFileTags.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; namespace UnitTest.Tests { [TestClass] public class TestGameFileTags { private IDataSourceAdapter database; private GameFile CreateGameFile(string filename) { DateTime dt = DateTime.Parse("1/1/17"); GameFile gameFile = new GameFile { FileName = filename, Author = "the author", Description = "the description", Title = "the title", Comments = "the comments", ReleaseDate = dt, Downloaded = dt, LastPlayed = dt, IWadID = 22, Rating = 33, MinutesPlayed = 44, MapCount = 55, SourcePortID = 66, SettingsExtraParams = "the extra params", SettingsFiles = "the settings files", SettingsFilesIWAD = "the settings files iwad", SettingsFilesSourcePort = "the settings files source port", SettingsMap = "the settings map", SettingsSkill = "the settings skill", SettingsSpecificFiles = "the settings specific files", SettingsGameProfileID = 567 }; return gameFile; } [TestInitialize] public void Initialize() { database = TestUtil.CreateAdapter(); } [TestCleanup] public void CleanDatabase() { var dataAccess = ((DbDataSourceAdapter)database).DataAccess; dataAccess.ExecuteNonQuery("delete from GameFiles"); dataAccess.ExecuteNonQuery("delete from Tags"); dataAccess.ExecuteNonQuery("delete from TagMapping"); } [TestMethod] public void GetTags_ReturnsAllTags() { var gameFile1 = CreateGameFile("foo.zip"); var tag1 = new TagData { Name = "Cool maps", HasTab = true, HasColor = true, Color = 0xff00ff, ExcludeFromOtherTabs = true, }; var tag2 = new TagData { Name = "Cool bananas", HasTab = false, HasColor = false, Color = null, ExcludeFromOtherTabs = false, }; database.InsertTag(tag1); database.InsertTag(tag2); var tags = database.GetTags(); var retrievedTag1 = tags.Where(x => x.Name.Equals(tag1.Name)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(tag1, retrievedTag1, "TagID")); var retrievedTag2 = tags.Where(x => x.Name.Equals(tag2.Name)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(tag2, retrievedTag2, "TagID")); } [TestMethod] public void GetTagMappings_ReturnsAllTagMappings() { var tagMapping1 = new TagMapping { TagID = 1, FileID = 2 }; var tagMapping2 = new TagMapping { TagID = 77, FileID = 44 }; database.InsertTagMapping(tagMapping1); database.InsertTagMapping(tagMapping2); var tagMappings = database.GetTagMappings(); var retrievedTagMapping1 = tagMappings.Where(x => x.TagID.Equals(tagMapping1.TagID)).First(); Assert.IsTrue(TestUtil.AllFieldsEqual(tagMapping1, retrievedTagMapping1)); var retrievedTagMapping2 = tagMappings.Where(x => x.TagID.Equals(tagMapping2.TagID)).First(); Assert.IsTrue(TestUtil.AllFieldsEqual(tagMapping2, retrievedTagMapping2)); } [TestMethod] public void GetGameFiles_Tag_ReturnsMatchingGameFiles() { var taggedFile = CreateGameFile("taggy.zip"); var wrongFile = CreateGameFile("wrong.zip"); var tag = new TagData { Name = "Fave" }; database.InsertGameFile(taggedFile); database.InsertGameFile(wrongFile); database.InsertTag(tag); var retrievedGameFile1 = database.GetGameFile("taggy.zip"); var retrievedTag = database.GetTags().First(); database.InsertTagMapping(new TagMapping() { TagID = retrievedTag.TagID, FileID = (int)retrievedGameFile1.GameFileID }); var retrievedGameFiles = database.GetGameFiles(retrievedTag); Assert.AreEqual(1, retrievedGameFiles.Count()); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(taggedFile, retrievedGameFiles.First(), "GameFileID")); } [TestMethod] public void GetGameFiles_Options_Tag_ReturnsMatchingTaggedGameFiles() { var rightTaggedFile = CreateGameFile("right_tagged.zip"); rightTaggedFile.Description = "Robots"; var rightUntaggedFile = CreateGameFile("right_untagged.zip"); rightUntaggedFile.Description = "Robots"; var wrongTaggedFile = CreateGameFile("wrong_tagged.zip"); var wrongUntaggedFile = CreateGameFile("wrong_untagged.zip"); var tag = new TagData { Name = "Wow" }; database.InsertGameFile(rightTaggedFile); database.InsertGameFile(rightUntaggedFile); database.InsertGameFile(wrongTaggedFile); database.InsertGameFile(wrongUntaggedFile); database.InsertTag(tag); var retrievedRightTaggedFile = database.GetGameFile("right_tagged.zip"); var retrievedWrongTaggedFile = database.GetGameFile("wrong_tagged.zip"); var retrievedTag = database.GetTags().First(); database.InsertTagMapping(new TagMapping() { TagID = retrievedTag.TagID, FileID = (int)retrievedRightTaggedFile.GameFileID }); database.InsertTagMapping(new TagMapping() { TagID = retrievedTag.TagID, FileID = (int)retrievedWrongTaggedFile.GameFileID }); var options = new GameFileGetOptions(new GameFileSearchField(GameFileFieldType.Description, GameFileSearchOp.Equal, "Robots")); var retrievedGameFiles = database.GetGameFiles(options, retrievedTag); Assert.AreEqual(1, retrievedGameFiles.Count()); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(rightTaggedFile, retrievedGameFiles.First(), "GameFileID")); } } } ================================================ FILE: UnitTest/Tests/TestGameInfoSyncAction.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Handlers.Sync; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace UnitTest.Tests { [TestClass] public class TestGameInfoSyncAction { [TestMethod] public void ApplyToGameFile_FindsTitleInGAMEINFO() { var gameFile = new GameFile() { FileName = "blah.wad" }; var syncAction = new GameInfoSyncAction(); Tree files = new Tree("root", new Tree("GAMEINFO", "IWAD = \"blah.wad\"\nSTARTUPTITLE = \"The Big Tree\"\nSOMETHING_ELSE = blah")); var reader = new TreeReader(files); var result = syncAction.ApplyToGameFile(gameFile, reader, new string[0]); Assert.AreEqual("The Big Tree", gameFile.Title); } [TestMethod] public void ApplyToGameFile_FindsTitleInGAMEINFOTxt() { var gameFile = new GameFile() { FileName = "blah.wad" }; var syncAction = new GameInfoSyncAction(); Tree files = new Tree("root", new Tree("GAMEINFO.txt", "IWAD = \"blah.wad\"\nSTARTUPTITLE = \"The Big Banana\"\nSOMETHING_ELSE = blah")); var reader = new TreeReader(files); var result = syncAction.ApplyToGameFile(gameFile, reader, new string[0]); Assert.AreEqual("The Big Banana", gameFile.Title); } [TestMethod] public void ApplyToGameFile_DoesntFindsTitleInAFileCalledSomethingElse() { var gameFile = new GameFile() { FileName = "blah.wad" }; var syncAction = new GameInfoSyncAction(); Tree files = new Tree("root", new Tree("GAMEINF", "IWAD = \"blah.wad\"\nSTARTUPTITLE = \"The Big Banana\"\nSOMETHING_ELSE = blah")); var reader = new TreeReader(files); var result = syncAction.ApplyToGameFile(gameFile, reader, new string[0]); Assert.IsTrue(string.IsNullOrEmpty(gameFile.Title)); } } } ================================================ FILE: UnitTest/Tests/TestGameLauncher.cs ================================================ using DoomLauncher; using DoomLauncher.Adapters.Launch; using DoomLauncher.Config; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestGameLauncher { private static readonly string LocalFile1 = @"Local\file1.wad"; private static readonly string LocalIwad1 = @"Local\iwad1.wad"; [TestInitialize] public void Init() { CreateDirectoriesAndFiles(); } [TestCleanup] public void Cleanup() { if (Directory.Exists("Temp")) Directory.Delete("Temp", true); } [TestMethod] public void TestWarp() { for(int i = 0; i < 4; i++) { for(int j = 0; j < 9; j++) { string map = string.Format("E{0}M{1}", i + 1, j + 1); Assert.AreEqual(GenericSourcePortFlavor.BuildWarpParameter(map), string.Format(" -warp {0} {1}", i + 1, j + 1)); } } for(int i = 0; i < 32; i++) { string map = null; if (i + 1 > 9) map = string.Format("MAP{0}", i + 1); else map = string.Format("MAP0{0}", i + 1); Assert.AreEqual(GenericSourcePortFlavor.BuildWarpParameter(map), string.Format(" -warp {0}", i + 1)); } Assert.AreEqual(" -warp 0", GenericSourcePortFlavor.BuildWarpParameter("MAP00")); } [TestMethod] public void TestMap() { Assert.AreEqual(" +map START", GenericSourcePortFlavor.BuildWarpParameter("START")); Assert.AreEqual(" +map MAPSTART", GenericSourcePortFlavor.BuildWarpParameter("MAPSTART")); Assert.AreEqual(" +map MAP01START", GenericSourcePortFlavor.BuildWarpParameter("MAP01START")); Assert.AreEqual(" +map MAP1", GenericSourcePortFlavor.BuildWarpParameter("MAP1")); Assert.AreEqual(" +map MAP001", GenericSourcePortFlavor.BuildWarpParameter("MAP001")); } [TestMethod] public void TestParameters() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; GameLauncher launcher = new GameLauncher(directories); //test .wad and deh string launch = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), GetTestPort(".wad,.deh"), false).LaunchString; string check = string.Format("-file \"{0}\" -deh \"{1}\"", Path.Combine(Directory.GetCurrentDirectory(), "Temp", "test1.wad"), Path.Combine(Directory.GetCurrentDirectory(), "Temp", "test1.deh")); Assert.AreEqual(check.Trim(), launch.Trim()); Assert.IsTrue(File.Exists(Path.Combine(directories.TempDirectory.GetFullPath(), "test1.wad"))); Assert.IsTrue(File.Exists(Path.Combine(directories.TempDirectory.GetFullPath(), "test1.deh"))); //.wad only launch = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), GetTestPort(".wad"), false).LaunchString; check = string.Format("-file \"{0}\"", Path.Combine(Directory.GetCurrentDirectory(), "Temp", "test1.wad")); Assert.AreEqual(check.Trim(), launch.Trim()); } [TestMethod] public void TestExtractFalse() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var features = new List() { new GameFilesLaunchFeature(new List() { GetTestFile() }, null, false) }; GameLauncher launcher = new GameLauncher(directories, features); launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), GetTestPort(".wad,.deh"), false); Assert.IsFalse(File.Exists(Path.Combine(directories.TempDirectory.GetFullPath(), "test1.wad"))); Assert.IsFalse(File.Exists(Path.Combine(directories.TempDirectory.GetFullPath(), "test1.deh"))); } [TestMethod] public void TestParametersSourcePortExtraParams() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var features = new List() { new ExtraParametersLaunchFeature("-extra", false) }; GameLauncher launcher = new GameLauncher(directories, features); var port = GetTestPort(".wad,.deh"); port.ExtraParameters = "-extra"; string launch = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), port, false).LaunchString; string check = string.Format("-file \"{0}\" -deh \"{1}\" -extra", Path.Combine(Directory.GetCurrentDirectory(), "Temp", "test1.wad"), Path.Combine(Directory.GetCurrentDirectory(), "Temp", "test1.deh")); Assert.AreEqual(check.Trim(), launch.Trim()); Assert.IsTrue(File.Exists(Path.Combine(directories.TempDirectory.GetFullPath(), "test1.wad"))); Assert.IsTrue(File.Exists(Path.Combine(directories.TempDirectory.GetFullPath(), "test1.deh"))); } [TestMethod] public void TestSkillAndMap() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var features = new List() { new MapSkillLaunchFeature("MAP01", "3") }; GameLauncher launcher = new GameLauncher(directories, features); var port = GetPrBoomTestPort(".wad,.deh"); var launch = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), port, false).LaunchString; Assert.IsTrue(launch.Contains("-skill 3")); Assert.IsTrue(launch.Contains("-warp 1")); } [TestMethod] public void TestSkillAndMapZdoom() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var features = new List() { new MapSkillLaunchFeature("MAP01", "3") }; GameLauncher launcher = new GameLauncher(directories, features); var port = GetTestPort(".wad,.deh"); var launch = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), port, false).LaunchString; Assert.IsTrue(launch.Contains("-skill 3")); Assert.IsTrue(launch.Contains("+map MAP01")); } [TestMethod] public void TestRecord() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var features = new List() { new RecordLaunchFeature() }; GameLauncher launcher = new GameLauncher(directories, features); var parameters = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), GetTestPort(".wad,.deh"), false); Assert.IsNotNull(parameters.RecordedFileName); Assert.IsTrue(parameters.LaunchString.Contains(string.Concat("-record \"", parameters.RecordedFileName, "\""))); } [TestMethod] public void TestPlayDemo() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; string demofile = Path.Combine(directories.TempDirectory.GetFullPath(), "testplay.lmp"); var features = new List() { new PlayDemoLaunchFeature(demofile) }; GameLauncher launcher = new GameLauncher(directories, features); var parameters = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), GetTestPort(".wad,.deh"), false); //the file doesn't exist Assert.IsTrue(parameters.Failed); Assert.IsNotNull(parameters.ErrorMessage); File.WriteAllText(demofile, "test"); var launch = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), GetTestPort(".wad,.deh"), false).LaunchString; Assert.IsTrue(launch.Contains(string.Concat("-playdemo \"", demofile, "\""))); } [TestMethod] public void TestAdditionalFiles() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var additionalFiles = GetTestFiles().Skip(1).ToList(); var features = new List() { new GameFilesLaunchFeature(additionalFiles, null) }; GameLauncher launcher = new GameLauncher(directories, features); string launch = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), GetTestPort(".wad,.pk3,.deh"), false).LaunchString; //-file parameters should be together, then -deh files should be together string check = string.Format(" -file \"{0}\\Temp\\test2.wad\" \"{0}\\Temp\\test2.pk3\" \"{0}\\Temp\\test3.wad\" \"{0}\\Temp\\test3.pk3\" \"{0}\\Temp\\test4.wad\" \"{0}\\Temp\\test4.pk3\" \"{0}\\Temp\\test1.wad\" \"{0}\\Temp\\test1.pk3\" -deh \"{0}\\Temp\\test2.deh\" \"{0}\\Temp\\test3.deh\" \"{0}\\Temp\\test4.deh\" \"{0}\\Temp\\test1.deh\" ", Directory.GetCurrentDirectory()); Assert.AreEqual(check.Trim(), launch.Trim()); var tempPath = directories.TempDirectory.GetFullPath(); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test2.wad"))); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test3.wad"))); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test4.wad"))); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test1.wad"))); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test2.deh"))); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test3.deh"))); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test4.deh"))); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test1.deh"))); } [TestMethod] public void TestBadFile() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; GameLauncher launcher = new GameLauncher(directories); var parameters = launcher.GetLaunchParameters(new GameFile() { FileName = "bad.zip" }, GetAddFiles(), GetTestPort(".wad,.deh"), false); Assert.IsTrue(parameters.Failed); Assert.IsNotNull(parameters.ErrorMessage); Assert.IsTrue(parameters.ErrorMessage.Contains("bad.zip")); } [TestMethod] public void TestBadAdditionalFile() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var additionalFiles = new List { new GameFile() { FileName = "badadd.zip" } }; var features = new List() { new GameFilesLaunchFeature(additionalFiles, null) }; GameLauncher adapter = new GameLauncher(directories, features); var parameters = adapter.GetLaunchParameters(GetTestFile(), GetAddFiles(), GetTestPort(".wad,.deh"), false); Assert.IsTrue(parameters.Failed); Assert.IsNotNull(parameters.ErrorMessage); Assert.IsTrue(parameters.ErrorMessage.Contains("badadd.zip")); } [TestMethod] public void TestIndividualFiles() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; string[] wads; using (ZipArchive za = ZipFile.OpenRead(Path.Combine("GameFiles", GetTestFiles().First().FileName))) wads = za.Entries.Where(x => Path.GetExtension(x.Name) == ".wad").Select(x => x.FullName).ToArray(); var features = new List() { new GameFilesLaunchFeature(null, wads.ToList()) }; GameLauncher launcher = new GameLauncher(directories, features); var parameters = launcher.GetLaunchParameters(GetTestFiles().First(), GetAddFiles(), GetTestPort(".wad,.deh"), false); Assert.IsNull(parameters.ErrorMessage); Assert.IsTrue(parameters.LaunchString.Contains(".wad")); Assert.IsFalse(parameters.LaunchString.Contains(".deh")); } [TestMethod] public void TestIndividualFilesMultiple() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; List wads = new List(); using (ZipArchive za = ZipFile.OpenRead(Path.Combine("GameFiles", GetTestFiles().First().FileName))) wads.AddRange(za.Entries.Where(x => Path.GetExtension(x.Name) == ".wad").Select(x => x.FullName)); using (ZipArchive za = ZipFile.OpenRead(Path.Combine("GameFiles", GetTestFiles().Skip(1).First().FileName))) wads.AddRange(za.Entries.Where(x => Path.GetExtension(x.Name) == ".wad").Select(x => x.FullName)); using (ZipArchive za = ZipFile.OpenRead(Path.Combine("GameFiles", GetTestFiles().Skip(2).First().FileName))) wads.AddRange(za.Entries.Where(x => Path.GetExtension(x.Name) == ".wad").Select(x => x.FullName)); //this is test4.zip and it will NOT be added to the additional files list using (ZipArchive za = ZipFile.OpenRead(Path.Combine("GameFiles", GetTestFiles().Skip(3).First().FileName))) wads.AddRange(za.Entries.Where(x => Path.GetExtension(x.Name) == ".wad").Select(x => x.FullName)); var additionalFiles = GetTestFiles().Skip(1).Take(2).ToList(); //test2.wad, test3.wad only var features = new List() { new GameFilesLaunchFeature(additionalFiles, wads) }; GameLauncher launcher = new GameLauncher(directories, features); var parameters = launcher.GetLaunchParameters(GetTestFiles().First(), GetAddFiles(), GetTestPort(".wad,.deh"), false); Assert.IsNull(parameters.ErrorMessage); Assert.IsTrue(parameters.LaunchString.Contains("test1.wad")); Assert.IsTrue(parameters.LaunchString.Contains("test2.wad")); Assert.IsTrue(parameters.LaunchString.Contains("test3.wad")); Assert.IsFalse(parameters.LaunchString.Contains("test4.wad")); Assert.IsFalse(parameters.LaunchString.Contains(".deh")); var tempPath = directories.TempDirectory.GetFullPath(); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test1.wad"))); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test2.wad"))); Assert.IsTrue(File.Exists(Path.Combine(tempPath, "test3.wad"))); Assert.IsFalse(File.Exists(Path.Combine(tempPath, "test4.wad"))); Assert.IsFalse(File.Exists(Path.Combine(tempPath, "test1.deh"))); Assert.IsFalse(File.Exists(Path.Combine(tempPath, "test2.deh"))); Assert.IsFalse(File.Exists(Path.Combine(tempPath, "test3.deh"))); Assert.IsFalse(File.Exists(Path.Combine(tempPath, "test4.deh"))); } [TestMethod] public void TestIndividualPathedFiles() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var specialFiles = new List { Path.Combine("data", "test.wad") }; var features = new List() { new GameFilesLaunchFeature(null, specialFiles) }; GameLauncher adapter = new GameLauncher(directories, features); IGameFile gameFile = new GameFile() { FileName = "testpathed.zip" }; var parameters = adapter.GetLaunchParameters(gameFile, GetAddFiles(), GetTestPort(".wad,.deh"), false); Assert.IsNull(parameters.ErrorMessage); Assert.IsTrue(parameters.LaunchString.Contains("test.wad")); Assert.IsTrue(File.Exists(Path.Combine(directories.TempDirectory.GetFullPath(), "test.wad"))); } [TestMethod] public void TestExtraWithStats() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var features = new List() { new ExtraParametersLaunchFeature("-extratest", false), new SourcePortExtraParametersLaunchFeature(), new StatisticsReaderLaunchFeature() }; GameLauncher launcher = new GameLauncher(directories, features); var boomPort = GetPrBoomTestPort(".wad,.deh"); boomPort.ExtraParameters = "-boomextra"; string launch = launcher.GetLaunchParameters(GetTestFile(), GetAddFiles(), boomPort, false).LaunchString; Assert.IsTrue(launch.Contains(" -extratest ")); Assert.IsTrue(launch.Contains(" -boomextra ")); Assert.IsTrue(launch.Contains(" -levelstat")); } [TestMethod] public void RelativeUnmanagedFiles() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var iwad = new GameFile() { FileName = LocalIwad1 }; var features = new List() { new IWadLaunchFeature(iwad) }; GameLauncher launcher = new GameLauncher(directories, features); var port = GetPrBoomTestPort(".wad,.deh"); var gameFile = new GameFile() { FileName = LocalFile1 }; string launch = launcher.GetLaunchParameters(gameFile, GetAddFiles(), port, false).LaunchString; string check = string.Format("-iwad \"{0}\" -file \"{1}\"", Path.Combine(Directory.GetCurrentDirectory(), LocalIwad1), Path.Combine(Directory.GetCurrentDirectory(), LocalFile1)); Assert.AreEqual(check, launch); } [TestMethod] public void VariableReplacements() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var iwad = new GameFile() { FileName = LocalIwad1 }; var features = new List() { new ExtraParametersLaunchFeature("-savedir $iwad/$filename", false), new IWadLaunchFeature(iwad) }; var launcher = new GameLauncher(directories,features); var port = GetPrBoomTestPort(".wad,.deh"); var gameFile = new GameFile() { FileName = LocalFile1 }; var launch = launcher.GetLaunchParameters(gameFile, GetAddFiles(), port, false).LaunchString; Assert.IsTrue(launch.Contains("-savedir iwad1/file1")); } [TestMethod] public void ExtraParametersOnly() { IDirectoriesConfiguration directories = new DirectoriesConfiguration() { GameFileDirectory = new LauncherPath("GameFiles"), TempDirectory = new LauncherPath("Temp") }; var iwad = new GameFile() { FileName = LocalIwad1 }; var features = new List() { new ExtraParametersLaunchFeature("-blah -blob", true), new IWadLaunchFeature(iwad), new MapSkillLaunchFeature("E1M2", null) }; var launcher = new GameLauncher(directories, features); var port = GetPrBoomTestPort(".wad,.deh"); var gameFile = new GameFile() { FileName = LocalFile1 }; var launch = launcher.GetLaunchParameters(gameFile, GetAddFiles(), port, false).LaunchString; Assert.IsTrue(launch.Contains("-blah -blob")); Assert.IsFalse(launch.Contains("E1M2")); Assert.IsFalse(launch.Contains("iwad")); } private void CreateDirectoriesAndFiles() { if (Directory.Exists("GameFiles")) Directory.Delete("GameFiles", true); if (Directory.Exists("Temp")) Directory.Delete("Temp", true); if (Directory.Exists("SourcePorts")) Directory.Delete("SourcePorts", true); if (Directory.Exists("Local")) Directory.Delete("Local", true); Directory.CreateDirectory("GameFiles"); CreateTestFiles(); CreateTestPathedFile(); Directory.CreateDirectory("Local"); CreateRelativeFiles(); Directory.CreateDirectory("Temp"); Directory.CreateDirectory("SourcePorts"); File.WriteAllBytes(@"SourcePorts\zdoom.exe", new byte[] { }); } private static void CreateTestPathedFile() { string filename = @"GameFiles\testpathed.zip"; if (File.Exists(filename)) File.Delete(filename); using (ZipArchive za = ZipFile.Open(filename, ZipArchiveMode.Create)) { string writeFile = Path.Combine(@"GameFiles\", "test.wad"); File.WriteAllBytes(writeFile, new byte[] { }); za.CreateEntryFromFile(writeFile, Path.Combine("data", "test.wad")); } } private static void CreateTestFiles() { for (int i = 1; i < 5; i++) { string[] files = new string[] { string.Format("test{0}.wad", i), string.Format("test{0}.deh", i), string.Format("test{0}.pk3", i), string.Format("test{0}.txt", i) }; string filename = string.Format(@"GameFiles\test{0}.zip", i); if (File.Exists(filename)) File.Delete(filename); using (ZipArchive za = ZipFile.Open(filename, ZipArchiveMode.Create)) { foreach (string file in files) { string writeFile = Path.Combine(@"GameFiles\", file); File.WriteAllBytes(writeFile, new byte[] { }); za.CreateEntryFromFile(writeFile, file); } } } } private void CreateRelativeFiles() { File.WriteAllText(LocalFile1, ""); File.WriteAllText(LocalIwad1, ""); } private static List GetTestFiles() { List files = new List(); for (int i = 1; i < 5; i++) files.Add(new GameFile { FileName = string.Format("test{0}.zip", i) }); return files; } private static IGameFile GetTestFile() { return new GameFile { FileName = "test1.zip" }; } private static IGameFile[] GetAddFiles() { return Array.Empty(); } private static ISourcePortData GetTestPort(string extensions) { return new SourcePortData { Executable = "zdoom.exe", Directory = new LauncherPath("SourcePorts"), SupportedExtensions = extensions, FileOption = "-file" }; } private static ISourcePortData GetPrBoomTestPort(string extensions) { return new SourcePortData { Executable = "prboom-plus.exe", Directory = new LauncherPath("SourcePorts"), SupportedExtensions = extensions, FileOption = "-file" }; } } } ================================================ FILE: UnitTest/Tests/TestGameProfile.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestGameProfile { private static string[] IgnoreFields = new[] { nameof(GameProfile.GameProfileID) }; [TestMethod] public void TestGameProfileData() { var adapter = TestUtil.CreateAdapter(); TestUtil.CleanDatabase(adapter); TestInsert(); TestGameProfileSelect(); TestGameProfileUpdate(); TestGameProfileDelete(); } [TestMethod] public void TestGlobalGameProfileData() { var adapter = TestUtil.CreateAdapter(); TestUtil.CleanDatabase(adapter); var globalProfile1 = GameProfile.CreateGlobalProfile("Global Profile 1"); var globalProfile2 = GameProfile.CreateGlobalProfile("Global Profile 2"); SetFields(globalProfile1); SetFields(globalProfile2); adapter.InsertGameProfile(globalProfile1); adapter.InsertGameProfile(globalProfile2); TestInsert(); var globalProfiles = adapter.GetGlobalGameProfiles().ToList(); Assert.AreEqual(2, globalProfiles.Count); Assert.AreEqual("Global Profile 1", globalProfiles[0].Name); Assert.AreEqual("Global Profile 2", globalProfiles[1].Name); globalProfile1 = (GameProfile)globalProfiles[0]; globalProfile2 = (GameProfile)globalProfiles[1]; globalProfile1.Name = "Updated Global Profile 1"; globalProfile2.Name = "Updated Global Profile 2"; adapter.UpdateGameProfile(globalProfile1); adapter.UpdateGameProfile(globalProfile2); globalProfiles = adapter.GetGlobalGameProfiles().ToList(); Assert.AreEqual(2, globalProfiles.Count); Assert.AreEqual("Updated Global Profile 1", globalProfiles[0].Name); Assert.AreEqual("Updated Global Profile 2", globalProfiles[1].Name); adapter.DeleteGameProfile(globalProfile1.GameProfileID); globalProfiles = adapter.GetGlobalGameProfiles().ToList(); Assert.AreEqual(1, globalProfiles.Count); Assert.AreEqual("Updated Global Profile 2", globalProfiles[0].Name); } public void TestInsert() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); foreach (var profile in CreateTestProfiles()) adapter.InsertGameProfile(profile); } public void TestGameProfileSelect() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var testProfiles = CreateTestProfiles(); foreach (var profile in testProfiles) { var profiles = adapter.GetGameProfiles(profile.GameFileID.Value).ToList(); Assert.AreEqual(1, profiles.Count); Assert.IsTrue(TestUtil.AllFieldsEqual(profile, profiles.First(), IgnoreFields)); } } public void TestGameProfileUpdate() { List profiles = new List(); IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var allTestProfiles = CreateTestProfiles(); foreach (var profile in allTestProfiles) profiles.AddRange(adapter.GetGameProfiles(profile.GameFileID.Value)); foreach (var profile in profiles) { profile.Name += profile.Name; profile.SourcePortID += profile.SourcePortID; profile.IWadID += profile.IWadID; profile.SettingsExtraParams += profile.SettingsExtraParams; profile.SettingsFiles += profile.SettingsFiles; profile.SettingsFilesIWAD += profile.SettingsFilesIWAD; profile.SettingsFilesSourcePort += profile.SettingsFilesSourcePort; profile.SettingsMap += profile.SettingsMap; profile.SettingsSkill += profile.SettingsSkill; profile.SettingsSpecificFiles += profile.SettingsSpecificFiles; profile.SettingsStat = !profile.SettingsStat; profile.SettingsSaved = !profile.SettingsSaved; adapter.UpdateGameProfile(profile); var testProfiles = adapter.GetGameProfiles(profile.GameFileID.Value).ToList(); Assert.AreEqual(1, testProfiles.Count); Assert.IsTrue(TestUtil.AllFieldsEqual(profile, testProfiles.First(), IgnoreFields)); } } public void TestGameProfileDelete() { List testProfiles = new List(); IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var allTestProfiles = CreateTestProfiles(); foreach (var profile in allTestProfiles) testProfiles.AddRange(adapter.GetGameProfiles(profile.GameFileID.Value)); foreach (var profile in testProfiles) { var profiles = adapter.GetGameProfiles(profile.GameFileID.Value).ToList(); Assert.AreEqual(1, profiles.Count); adapter.DeleteGameProfile(profile.GameProfileID); profiles = adapter.GetGameProfiles(profile.GameFileID.Value).ToList(); Assert.AreEqual(0, profiles.Count); } } private List CreateTestProfiles() { List profiles = new List(); for (int i = 0; i < 10; i++) { profiles.Add(new GameProfile() { GameProfileID = i + 1, GameFileID = i, Name = i.ToString(), SourcePortID = i, IWadID = i, SettingsExtraParams = i.ToString(), SettingsFiles = i.ToString(), SettingsFilesIWAD = i.ToString(), SettingsFilesSourcePort = i.ToString(), SettingsMap = i.ToString(), SettingsSkill = i.ToString(), SettingsSpecificFiles = i.ToString(), SettingsStat = false, SettingsSaved = false }); } return profiles; } private void SetFields(IGameProfile gameProfile) { gameProfile.SourcePortID = 1; gameProfile.IWadID = 1; } } } ================================================ FILE: UnitTest/Tests/TestGameProfileUtil.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.Diagnostics.Contracts; using DoomLauncher.Handlers; namespace UnitTest.Tests { [TestClass] public class TestGameProfileUtil { [TestMethod] public void GetAllProfiles() { var adapter = TestUtil.CreateAdapter(); TestUtil.CleanDatabase(adapter); foreach (var profile in CreateTestProfiles(1, new [] { "Test 1", "A Profile", "Mods" })) adapter.InsertGameProfile(profile); foreach (var profile in CreateTestProfiles(2, new[] { "New Profile", "Doom", "Mega Profile", "Another one" })) adapter.InsertGameProfile(profile); var globalProfile1 = GameProfile.CreateGlobalProfile("Z Global Profile 1"); var globalProfile2 = GameProfile.CreateGlobalProfile("A Global Profile 2"); SetFields(globalProfile1); SetFields(globalProfile2); adapter.InsertGameProfile(globalProfile1); adapter.InsertGameProfile(globalProfile2); var profiles = GameProfileUtil.GetAllProfiles(adapter, new GameFile { GameFileID = 1 }, out var globalProfiles); Assert.AreEqual(6, profiles.Count); Assert.AreEqual(2, globalProfiles.Count); // Global profiles first sorted by name, then default with game file profiles Assert.AreEqual(profiles[0].Name, "A Global Profile 2"); Assert.AreEqual(profiles[1].Name, "Z Global Profile 1"); Assert.AreEqual(profiles[2].Name, "Default Profile"); Assert.AreEqual(profiles[3].Name, "A Profile"); Assert.AreEqual(profiles[4].Name, "Mods"); Assert.AreEqual(profiles[5].Name, "Test 1"); profiles = GameProfileUtil.GetAllProfiles(adapter, new GameFile { GameFileID = 2 }, out globalProfiles); Assert.AreEqual(7, profiles.Count); Assert.AreEqual(2, globalProfiles.Count); Assert.AreEqual(profiles[0].Name, "A Global Profile 2"); Assert.AreEqual(profiles[1].Name, "Z Global Profile 1"); Assert.AreEqual(profiles[2].Name, "Default Profile"); Assert.AreEqual(profiles[3].Name, "Another one"); Assert.AreEqual(profiles[4].Name, "Doom"); Assert.AreEqual(profiles[5].Name, "Mega Profile"); Assert.AreEqual(profiles[6].Name, "New Profile"); profiles = GameProfileUtil.GetAllProfiles(adapter, new GameFile { GameFileID = 3 }, out globalProfiles); Assert.AreEqual(3, profiles.Count); Assert.AreEqual(2, globalProfiles.Count); Assert.AreEqual(profiles[0].Name, "A Global Profile 2"); Assert.AreEqual(profiles[1].Name, "Z Global Profile 1"); Assert.AreEqual(profiles[2].Name, "Default Profile"); var gameFile= new GameFile { GameFileID = 1 }; Assert.IsTrue(GameProfileUtil.NameExists(adapter, gameFile, globalProfiles, profiles[0].GameProfileID, "z Global Profile 1")); Assert.IsFalse(GameProfileUtil.NameExists(adapter, gameFile, globalProfiles, profiles[0].GameProfileID, "z Global Profile 3**")); Assert.IsTrue(GameProfileUtil.NameExists(adapter, gameFile, globalProfiles, profiles[0].GameProfileID, GameFile.DefaultProfileName)); Assert.IsTrue(GameProfileUtil.NameExists(adapter, gameFile, globalProfiles, profiles[0].GameProfileID, "mods")); Assert.IsFalse(GameProfileUtil.NameExists(adapter, gameFile, globalProfiles, profiles[0].GameProfileID, "new profile test")); TestUtil.CleanDatabase(adapter); } private List CreateTestProfiles(int gameFileId, string[] names) { List profiles = new List(); int i = 1000; foreach (var name in names) { profiles.Add(new GameProfile() { GameProfileID = i + 1, GameFileID = gameFileId, Name = name, SourcePortID = i, IWadID = i, SettingsExtraParams = i.ToString(), SettingsFiles = i.ToString(), SettingsFilesIWAD = i.ToString(), SettingsFilesSourcePort = i.ToString(), SettingsMap = i.ToString(), SettingsSkill = i.ToString(), SettingsSpecificFiles = i.ToString(), SettingsStat = false, SettingsSaved = false }); i++; } return profiles; } private void SetFields(IGameProfile gameProfile) { gameProfile.SourcePortID = 1; gameProfile.IWadID = 1; } } } ================================================ FILE: UnitTest/Tests/TestGameStoreFiles.cs ================================================ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using DoomLauncher.GameStores; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestGameStoreFiles { [TestMethod] public void Combine_AddsLists() { var gameStoreFiles1 = new GameStoreFiles(new List { "a.wad", "b.wad" }, new List { "x.wad" }, null); var gameStoreFiles2 = new GameStoreFiles(new List { "c.wad" }, new List { "y.wad", "z.wad" }, null); var result = gameStoreFiles1.Combine(gameStoreFiles2); Assert.IsTrue(new List { "a.wad", "b.wad", "c.wad" }.SequenceEqual(result.InstalledIWads)); Assert.IsTrue(new List { "x.wad", "y.wad", "z.wad" }.SequenceEqual(result.InstalledPWads)); } [TestMethod] public void Combine_AddsListsIgnoringDuplicates() { var gameStoreFiles1 = new GameStoreFiles(new List { @"base\a.wad", "b.wad" }, new List { @"base\x.wad" }, null); var gameStoreFiles2 = new GameStoreFiles(new List { @"release\a.wad" }, new List { @"release\x.wad", "z.wad" }, null); var result = gameStoreFiles1.Combine(gameStoreFiles2); Assert.IsTrue(new List { @"base\a.wad", "b.wad" }.SequenceEqual(result.InstalledIWads)); Assert.IsTrue(new List { @"base\x.wad", "z.wad" }.SequenceEqual(result.InstalledPWads)); } } } ================================================ FILE: UnitTest/Tests/TestIWad.cs ================================================ using DoomLauncher; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestIWad { private IIWadDataSourceAdapter database; [TestInitialize] public void Initialize() { database = TestUtil.CreateAdapter(); } [TestCleanup] public void CleanDatabase() { var dataAccess = ((DbDataSourceAdapter)database).DataAccess; dataAccess.ExecuteNonQuery("delete from IWads"); } [TestMethod] public void GetIWads_ReturnsAllIWads() { var iWad1 = new IWadData() { FileName = "Doom.wad", Name = "Doom", GameFileID = 1 }; var iWad2 = new IWadData() { FileName = "Doom2.wad", Name = "Doom II", GameFileID = 2 }; database.InsertIWad(iWad1); database.InsertIWad(iWad2); var iWads = database.GetIWads(); Assert.AreEqual(2, iWads.Count()); var retrieved1 = iWads.Where(x => x.FileName.Equals(iWad1.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(iWad1, retrieved1, "IWadID")); var retrieved2 = iWads.Where(x => x.FileName.Equals(iWad2.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(iWad2, retrieved2, "IWadID")); } [TestMethod] public void GetIWad_GetsIWadForGameFileID() { var iWad1 = new IWadData() { FileName = "Heretic.wad", Name = "Heretic", GameFileID = 5 }; database.InsertIWad(iWad1); var retrievedIWad = database.GetIWad(5); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(iWad1, retrievedIWad, "IWadID")); } [TestMethod] public void DeleteIWad_DeletesTheIWad() { var iWad1 = new IWadData() { FileName = "plutonia.wad", Name = "The Plutonia Experiment", GameFileID = 3 }; var wrongIWad = new IWadData() { FileName = "tnt.wad", Name = "TNT Evilution", GameFileID = 4 }; database.InsertIWad(iWad1); database.InsertIWad(wrongIWad); // Need to fetch so that the primary key is populated. var savedIWad1 = database.GetIWad(3); database.DeleteIWad(savedIWad1); var iWads = database.GetIWads(); Assert.AreEqual(1, iWads.Count()); var retrieved2 = iWads.First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(wrongIWad, retrieved2, "IWadID")); } [TestMethod] public void UpdateIWad_UpdatesTheRightFieldsInTheRightIWad() { var iWad1 = new IWadData() { FileName = "heretic.wad", Name = "Heretic", GameFileID = 5 }; var wrongIWad = new IWadData() { FileName = "chex.wad", Name = "Chex Quest", GameFileID = 6 }; database.InsertIWad(iWad1); database.InsertIWad(wrongIWad); // Need to fetch so that the primary key is populated. var savedIWad1 = database.GetIWad(5); savedIWad1.FileName = "daikatana.wad"; savedIWad1.Name = "Daikatana"; savedIWad1.GameFileID = 7; database.UpdateIWad(savedIWad1); var updatedIWads = database.GetIWads(); Assert.AreEqual(2, updatedIWads.Count()); var retrieved1 = updatedIWads.Where(x => x.IWadID.Equals(savedIWad1.IWadID)).First(); Assert.AreEqual("daikatana.wad", retrieved1.FileName); Assert.AreEqual("Daikatana", retrieved1.Name); Assert.AreEqual(7, retrieved1.GameFileID); var retrieved2 = updatedIWads.Where(x => x.FileName.Equals(wrongIWad.FileName)).First(); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(wrongIWad, retrieved2, "IWadID")); } } } ================================================ FILE: UnitTest/Tests/TestIWadTitlesSyncAction.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Handlers.Sync; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace UnitTest.Tests { [TestClass] public class TestIWadTitlesSyncAction { [TestMethod] public void ApplyToGameFile_ReplacesTitleOfIWads() { IGameFile gameFile = new GameFile { FileName = "heretic.zip", Title = "Wrong title" }; var action = new IWadTitlesSyncAction(); action.ApplyToGameFile(gameFile, null, null); Assert.AreEqual("Heretic: Shadow of the Serpent Riders", gameFile.Title); } [TestMethod] public void ApplyToGameFile_DoesntReplaceTitleOfNonIWads() { IGameFile gameFile = new GameFile { FileName = "floom.zip", Title = "blah" }; var action = new IWadTitlesSyncAction(); action.ApplyToGameFile(gameFile, null, null); Assert.AreEqual("blah", gameFile.Title); } } } ================================================ FILE: UnitTest/Tests/TestIdGamesFileParser.cs ================================================ using DoomLauncher.Handlers.Sync; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Globalization; namespace UnitTest.Tests { [TestClass] public class TestIdGamesFileParser { private static string[] s_formats = new string[] { "dd/M/yy", "dd/MM/yyyy", "dd MMMM yyyy" }; [TestMethod] public void TestEmpty() { IdGamesTextFileParser parser = new IdGamesTextFileParser(s_formats); var info = parser.Parse(string.Empty); Assert.AreEqual(string.Empty, info.Title); Assert.AreEqual(string.Empty, info.Author); Assert.AreEqual(null, info.ReleaseDate); Assert.AreEqual(string.Empty, info.Description); } [TestMethod] public void TestBadDates() { IdGamesTextFileParser parser = new IdGamesTextFileParser(s_formats); var info = parser.Parse("Release date: this is garbage"); Assert.AreEqual(null, info.ReleaseDate); parser.Parse("Release date: "); Assert.AreEqual(null, info.ReleaseDate); parser.Parse("Release date:"); Assert.AreEqual(null, info.ReleaseDate); parser.Parse("Release date: 1234"); Assert.AreEqual(null, info.ReleaseDate); } [TestMethod] public void TestDates() { IdGamesTextFileParser parser = new IdGamesTextFileParser(s_formats); string[] dates = new string[] { "Release date : 5/April/16", "Release date:4/05/2016", " Release date: 4.05.2016", " Release date: 4 05 2016", "Date finished: 5 April 2016", "release Date: April 5th 2016", "Release date: April 5th, 2016", "Release date: April 5th 16", "Release date: junk 5 April 2016 junk", }; DateTime assert = DateTime.Parse("4/5/2016", CultureInfo.InvariantCulture); foreach (string date in dates) { var info = parser.Parse(date); Assert.AreEqual(info.ReleaseDate, assert); } } [TestMethod] public void TestStrings() { string test = @"=========================================================================== Advanced engine needed : PrBoom+ or GZdoom. Primary purpose : Deathmatch =========================================================================== TitLe : Onslaught DM 3 (v.1.1) Filename : onsl3.wad Release DATE : 01/17/07 AUTHORS : Hobomaster22, Ak-01 dEsCrIpTioN : 21 head to head deathmatch maps with faced paced action. This is a 1on1 specific mapset. For FFA more than 4 players is not recommended."; IdGamesTextFileParser parser = new IdGamesTextFileParser(s_formats); var info = parser.Parse(test); Assert.AreEqual("Onslaught DM 3 (v.1.1)", info.Title); Assert.AreEqual(DateTime.Parse("01/17/07", CultureInfo.InvariantCulture), info.ReleaseDate); Assert.AreEqual("Hobomaster22, Ak-01", info.Author); //Could be Author: or Authors: Assert.AreEqual("21 head to head deathmatch maps with faced paced action. This is a 1on1 specific mapset. For FFA more than 4 players is not recommended.", info.Description); test = test.Replace("AUTHORS", "AUTHOR"); parser.Parse(test); Assert.AreEqual("Onslaught DM 3 (v.1.1)", info.Title); Assert.AreEqual(DateTime.Parse("01/17/07", CultureInfo.InvariantCulture), info.ReleaseDate); Assert.AreEqual("Hobomaster22, Ak-01", info.Author); //Could be Author: or Authors: Assert.AreEqual("21 head to head deathmatch maps with faced paced action. This is a 1on1 specific mapset. For FFA more than 4 players is not recommended.", info.Description); test = @"=========================================================================== Primary purpose : Deathmatch =========================================================================== TitLe : Filename : Release DATE : AUTHORS : dEsCrIpTioN : "; info = parser.Parse(test); Assert.AreEqual(string.Empty, info.Title); Assert.AreEqual(null, info.ReleaseDate); Assert.AreEqual(string.Empty, info.Author); Assert.AreEqual(string.Empty, info.Description); } } } ================================================ FILE: UnitTest/Tests/TestIdGamesTextInfo.cs ================================================ using DoomLauncher.Handlers.Sync; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; namespace UnitTest.Tests { [TestClass] public class TestIdGamesTextInfo { [TestMethod] public void TestQualityInfoCountsNonNullNonWhitespace() { var info1 = new IdGamesTextInfo(null, " ", null, "", null); Assert.AreEqual(0, info1.QualityScore); var info2 = new IdGamesTextInfo("Eviternity", "", null, null, null); Assert.AreEqual(1, info2.QualityScore); var info3 = new IdGamesTextInfo("Jenesis", "James \"Jimmy\" Paddock", null, " ", " "); Assert.AreEqual(2, info3.QualityScore); var info4 = new IdGamesTextInfo("Pirate Doom!", "Arch", new DateTime(2014, 6, 18), " ", ""); Assert.AreEqual(3, info4.QualityScore); var info5 = new IdGamesTextInfo("My House", "Steve Nelson", new DateTime(2023, 3, 2), "Just a normal regular wad just hanging out", null); Assert.AreEqual(4, info5.QualityScore); } [TestMethod] public void NullStringsBecomeEmptyStrings() { var info = new IdGamesTextInfo(null, null, null, null, null); Assert.AreEqual("", info.Title); Assert.AreEqual("", info.Author); Assert.AreEqual("", info.Description); } [TestMethod] public void Combine_PrefersOurFields() { var info1 = new IdGamesTextInfo("Fancy Lands", "Rufus Wallykins", null, "Does not exist", "Doom 2"); var info2 = new IdGamesTextInfo("ignoreme", "ignore me", new DateTime(2024, 5, 3), "IGNORE ME", "Doom"); var result1 = info1.Combine(info2); Assert.AreEqual(new IdGamesTextInfo("Fancy Lands", "Rufus Wallykins", new DateTime(2024, 5, 3), "Does not exist", "Doom 2"), result1); var result2 = info2.Combine(info1); Assert.AreEqual(info2, result2); } } } ================================================ FILE: UnitTest/Tests/TestIgnoreRarExploit.cs ================================================ using DoomLauncher.Archive.Rar; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestIgnoreRarExploit { [TestMethod] public void RarFileWithRelativePathIsIgnored() { // Contains calc.exe file pointed to ../../../../Microsoft/Windows/Start Menu/Programs/Startup/poc.bat using (RarArchiveReader reader = new RarArchiveReader("Resources/poc.rar")) { Assert.AreEqual(0, reader.Entries.Count()); } } } } ================================================ FILE: UnitTest/Tests/TestKnownWadsSyncAction.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Handlers.Sync; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestKnownWadsSyncAction { IDataSourceAdapter database; [TestInitialize] public void Initialize() { database = TestUtil.CreateAdapter(); } [TestCleanup] public void Cleanup() { var dataAccess = ((DbDataSourceAdapter)database).DataAccess; dataAccess.ExecuteNonQuery("delete from IWads"); } [TestMethod] public void ApplyToGameFile_DoesNothingToUnknownGameFile() { var action = new KnownWadsSyncAction(database); var gameFile = new GameFile() { FileName = "Unknown.zip", Title = "MyTitle" }; action.ApplyToGameFile(gameFile, null, null); Assert.AreEqual("MyTitle", gameFile.Title); Assert.IsNull(gameFile.IWadID); } [TestMethod] public void ApplyToGameFile_SetsTheTitleForHexDD() { var action = new KnownWadsSyncAction(database); var gameFile = new GameFile() { FileName = "hexdd.zip", Title = "Replace me" }; action.ApplyToGameFile(gameFile, null, null); Assert.AreEqual("Hexen: Deathkings of the Dark Citadel", gameFile.Title); } [TestMethod] public void ApplyToGameFile_SetsTheHexenIWadForHexDD() { var action = new KnownWadsSyncAction(database); IIWadData hexenIwad = new IWadData() { FileName = "hexen.wad" }; database.InsertIWad(hexenIwad); hexenIwad = database.GetIWads().FirstOrDefault(iwad => iwad.FileNameBase.Equals("hexen")); Assert.IsNotNull(hexenIwad); Assert.IsNotNull(hexenIwad.IWadID); var gameFile = new GameFile() { FileName = "hexdd.zip", Title = "Replace me" }; action.ApplyToGameFile(gameFile, null, null); Assert.AreEqual(hexenIwad.IWadID, gameFile.IWadID); } } } ================================================ FILE: UnitTest/Tests/TestLaunchParameters.cs ================================================ using DoomLauncher.Adapters.Launch; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace UnitTest.Tests { [TestClass] public class TestLaunchParameters { [TestMethod] public void Combine_AppendsTwoParams() { var a = LaunchParameters.Param("a"); var b = LaunchParameters.Param("b"); var result = a.Combine(b); Assert.AreEqual("a b", result.LaunchString); } [TestMethod] public void Combine_EmptyHasNoEffect() { var a = LaunchParameters.Param("blah"); Assert.AreEqual("blah", a.Combine(LaunchParameters.EMPTY).LaunchString); Assert.AreEqual("blah", LaunchParameters.EMPTY.Combine(a).LaunchString); } [TestMethod] public void Combine_TakesTheFirstRecordedFile() { var a = LaunchParameters.EMPTY.WithRecordedFileName("recording.file"); var b = LaunchParameters.EMPTY.WithRecordedFileName("other.file"); var result = a.Combine(b); Assert.AreEqual("recording.file", result.RecordedFileName); } [TestMethod] public void Combine_TakesRecordedFileIfNotAlreadyPresent() { var a = LaunchParameters.Param("p1"); var b = LaunchParameters.EMPTY.WithRecordedFileName("therecording.file"); var result = a.Combine(b); Assert.AreEqual("therecording.file", result.RecordedFileName); } [TestMethod] public void Combine_ExclusiveParameterIgnoresOtherParameters() { var a = LaunchParameters.Param("a"); var b = LaunchParameters.Param("b"); var c = LaunchParameters.ExclusiveParam("c"); var d = LaunchParameters.Param("d"); var result = a.Combine(b).Combine(c).Combine(d); Assert.AreEqual("c", result.LaunchString); } [TestMethod] public void Combine_KeepsTheFailure() { var failed1 = LaunchParameters.Failure("WRONG!"); var failed2 = LaunchParameters.Failure("NO!"); var ok = LaunchParameters.Param("ok"); var result1 = failed1.Combine(ok); Assert.IsTrue(result1.Failed); Assert.AreEqual("WRONG!", result1.ErrorMessage); var result2 = ok.Combine(failed2); Assert.IsTrue(result2.Failed); Assert.AreEqual("NO!", result2.ErrorMessage); } [TestMethod] public void Combine_StacksVariableReplacements() { var param1 = LaunchParameters.EMPTY.WithVariableReplacement("filename", "bongo.wad"); var param2 = LaunchParameters.EMPTY.WithVariableReplacement("iwad", "freedoom.wad"); var appliedParam = LaunchParameters.Param("$somethingElse $filename needs $iwad"); var result1 = appliedParam.Combine(param1).Combine(param2); var result2 = param2.Combine(param1).Combine(appliedParam); var result3 = param1.Combine(appliedParam).Combine(param2); Assert.AreEqual("$somethingElse bongo.wad needs freedoom.wad", result1.LaunchString); Assert.AreEqual("$somethingElse bongo.wad needs freedoom.wad", result2.LaunchString); Assert.AreEqual("$somethingElse bongo.wad needs freedoom.wad", result3.LaunchString); } } } ================================================ FILE: UnitTest/Tests/TestLauncherPath.cs ================================================ using DoomLauncher; using DoomLauncher.Handlers; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; namespace UnitTest.Tests { [TestClass] public class TestLauncherPath { [TestMethod] public void TestPaths() { string folder = "relativepath\\"; LauncherPath path = new LauncherPath(folder); Assert.AreEqual(folder, path.GetPossiblyRelativePath()); Assert.AreEqual(Path.Combine(Directory.GetCurrentDirectory(), folder), path.GetFullPath()); folder = Path.Combine("\\subdir", "relativepath\\"); string newfolder = Path.Combine(Directory.GetCurrentDirectory(), folder); path = new LauncherPath(newfolder); Assert.AreEqual(folder, path.GetPossiblyRelativePath()); Assert.AreEqual(Path.Combine(Directory.GetCurrentDirectory(), folder), path.GetFullPath()); } [TestMethod] public void LauncherPathFile() { string file = "C:\\directory\\files\\file.zip"; var launcherPath = new LauncherPath(file); Assert.AreEqual(file, launcherPath.GetFullPath()); Assert.AreEqual(file, launcherPath.GetPossiblyRelativePath()); } [TestMethod] public void LauncherPathRelativeFile() { string relative = "files\\file.zip"; string file = Path.Combine(Directory.GetCurrentDirectory(), relative); var launcherPath = new LauncherPath(file); Assert.AreEqual(file, launcherPath.GetFullPath()); Assert.AreEqual(relative, launcherPath.GetPossiblyRelativePath()); } [TestMethod] public void GetRelativeDirectory() { string relative = "something\\local\\"; string path = Path.Combine(Directory.GetCurrentDirectory(), relative); var launcherPath = new LauncherPath(path); Assert.AreEqual(path, launcherPath.GetFullPath()); Assert.AreEqual("something\\local", launcherPath.GetPossiblyRelativePath()); } [TestMethod] public void Empty() { var launcherPath = new LauncherPath(string.Empty); Assert.AreEqual(string.Empty, launcherPath.GetFullPath()); Assert.AreEqual(string.Empty, launcherPath.GetPossiblyRelativePath()); } [TestMethod] public void PartiallyQualified() { Assert.IsTrue(PathExtensions.IsPartiallyQualified("somefile")); Assert.IsTrue(PathExtensions.IsPartiallyQualified("somefile\\")); Assert.IsTrue(PathExtensions.IsPartiallyQualified("\\somefile")); Assert.IsTrue(PathExtensions.IsPartiallyQualified("\\somefile\\")); Assert.IsFalse(PathExtensions.IsPartiallyQualified("c:\\somefile")); Assert.IsFalse(PathExtensions.IsPartiallyQualified("c:\\somefile\\")); } } } ================================================ FILE: UnitTest/Tests/TestLevelstat.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.IO; namespace UnitTest.Tests { [TestClass] public class TestLevelstat { private readonly List m_args = new List(); [TestMethod] public void TestLevelstatFile() { string stats = @"MAP01 - 0:04.23 (0:04) K: 12/123 I: 5/33 S: 0/1 MAP02 - 0:03.83 (0:07) K: 23/234 I: 7/28 S: 1/2 "; File.WriteAllText("statfile.txt", stats); LevelstatReader statsReader = new LevelstatReader(new GameFile() { GameFileID = 1 }, "statfile.txt"); statsReader.NewStastics += StatsReader_NewStastics; statsReader.ReadNow(); Assert.AreEqual(2, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("MAP01", m_args[0].Statistics.MapName); Assert.AreEqual(12, m_args[0].Statistics.KillCount); Assert.AreEqual(123, m_args[0].Statistics.TotalKills); Assert.AreEqual(5, m_args[0].Statistics.ItemCount); Assert.AreEqual(33, m_args[0].Statistics.TotalItems); Assert.AreEqual(0, m_args[0].Statistics.SecretCount); Assert.AreEqual(1, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(4.23f, m_args[0].Statistics.LevelTime); Assert.AreEqual("MAP02", m_args[1].Statistics.MapName); Assert.AreEqual(23, m_args[1].Statistics.KillCount); Assert.AreEqual(234, m_args[1].Statistics.TotalKills); Assert.AreEqual(7, m_args[1].Statistics.ItemCount); Assert.AreEqual(28, m_args[1].Statistics.TotalItems); Assert.AreEqual(1, m_args[1].Statistics.SecretCount); Assert.AreEqual(2, m_args[1].Statistics.TotalSecrets); Assert.AreEqual(3.83f, m_args[1].Statistics.LevelTime); //Add another level, should one be one new level read stats = @"MAP01 - 0:04.23 (0:04) K: 12/123 I: 5/33 S: 0/1 MAP02 - 0:03.83 (0:07) K: 23/234 I: 7/28 S: 1/2 MAP03 - 0:04.83 (0:07) K: 123/1234 I: 22/50 S: 2/2"; File.WriteAllText("statfile.txt", stats); statsReader.ReadNow(); Assert.AreEqual(3, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("MAP03", m_args[2].Statistics.MapName); Assert.AreEqual(123, m_args[2].Statistics.KillCount); Assert.AreEqual(1234, m_args[2].Statistics.TotalKills); Assert.AreEqual(22, m_args[2].Statistics.ItemCount); Assert.AreEqual(50, m_args[2].Statistics.TotalItems); Assert.AreEqual(2, m_args[2].Statistics.SecretCount); Assert.AreEqual(2, m_args[2].Statistics.TotalSecrets); Assert.AreEqual(4.83f, m_args[2].Statistics.LevelTime); } [TestMethod] public void TestLevelstatFileMultiRead() { string stats = @"MAP01 - 0:13.66 (0:13) K: 8/19 I: 3/9 S:3/5 MAP02 - 0:24.20 (0:37) K: 12/70 I: 10/20 S: 0/1 MAP03 - 0:35.51 (1:12) K: 14/56 I: 8/9 S: 0/1 "; File.WriteAllText("statfile.txt", stats); LevelstatReader statsReader = new LevelstatReader(new GameFile() { GameFileID = 1 }, "statfile.txt"); statsReader.NewStastics += StatsReader_NewStastics; statsReader.ReadNow(); Assert.AreEqual(3, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("MAP01", m_args[0].Statistics.MapName); Assert.AreEqual(8, m_args[0].Statistics.KillCount); Assert.AreEqual(19, m_args[0].Statistics.TotalKills); Assert.AreEqual(3, m_args[0].Statistics.ItemCount); Assert.AreEqual(9, m_args[0].Statistics.TotalItems); Assert.AreEqual(3, m_args[0].Statistics.SecretCount); Assert.AreEqual(5, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(13.66f, m_args[0].Statistics.LevelTime); Assert.AreEqual("MAP02", m_args[1].Statistics.MapName); Assert.AreEqual(12, m_args[1].Statistics.KillCount); Assert.AreEqual(70, m_args[1].Statistics.TotalKills); Assert.AreEqual(10, m_args[1].Statistics.ItemCount); Assert.AreEqual(20, m_args[1].Statistics.TotalItems); Assert.AreEqual(0, m_args[1].Statistics.SecretCount); Assert.AreEqual(1, m_args[1].Statistics.TotalSecrets); Assert.AreEqual(24.2f, m_args[1].Statistics.LevelTime); Assert.AreEqual("MAP03", m_args[2].Statistics.MapName); Assert.AreEqual(14, m_args[2].Statistics.KillCount); Assert.AreEqual(56, m_args[2].Statistics.TotalKills); Assert.AreEqual(8, m_args[2].Statistics.ItemCount); Assert.AreEqual(9, m_args[2].Statistics.TotalItems); Assert.AreEqual(0, m_args[2].Statistics.SecretCount); Assert.AreEqual(1, m_args[2].Statistics.TotalSecrets); Assert.AreEqual(35.51f, m_args[2].Statistics.LevelTime); } [TestMethod] public void TestLevelstatHours() { string stats = @"MAP04 - 1:05:50.80 (1:28:46) K: 207/206 I: 66/66 S: 1/1"; File.WriteAllText("statfile.txt", stats); LevelstatReader statsReader = new LevelstatReader(new GameFile() { GameFileID = 1 }, "statfile.txt"); statsReader.NewStastics += StatsReader_NewStastics; statsReader.ReadNow(); Assert.AreEqual(1, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("MAP04", m_args[0].Statistics.MapName); Assert.AreEqual(206, m_args[0].Statistics.KillCount); Assert.AreEqual(206, m_args[0].Statistics.TotalKills); Assert.AreEqual(66, m_args[0].Statistics.ItemCount); Assert.AreEqual(66, m_args[0].Statistics.TotalItems); Assert.AreEqual(1, m_args[0].Statistics.SecretCount); Assert.AreEqual(1, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(3950.80004882813f, m_args[0].Statistics.LevelTime); } private void StatsReader_NewStastics(object sender, NewStatisticsEventArgs e) { m_args.Add(e); } } } ================================================ FILE: UnitTest/Tests/TestLoadFiles.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Handlers; using DoomLauncher.Interfaces; using DoomLauncher.SourcePort; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json.Converters; using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace UnitTest.Tests { [TestClass] public class TestLoadFiles { IDataSourceAdapter database; [TestInitialize] public void Initialize() { database = TestUtil.CreateAdapter(); } [TestCleanup] public void CleanDatabase() { var dataAccess = ((DbDataSourceAdapter)database).DataAccess; dataAccess.ExecuteNonQuery("delete from GameFiles"); dataAccess.ExecuteNonQuery("delete from IWads"); dataAccess.ExecuteNonQuery("delete from SourcePorts"); } [TestMethod] public void GetIWadFiles_ReturnsFilesDirectlyAssociatedWithTheIWad() { var gameProfile = new GameFile { FileName = "foo.wad", SettingsFilesIWAD = "green.wad;blue.wad" }; database.InsertGameFile(gameProfile); var gameFile1 = new GameFile // Won't get used { FileName = "whocares.wad", SettingsFilesIWAD = "nope.wad" }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "green.wad" }; database.InsertGameFile(gameFile2); var gameFile3 = new GameFile { FileName = "blue.wad" }; database.InsertGameFile(gameFile3); var wrongGameFile = new GameFile { FileName = "nope.wad" }; database.InsertGameFile(wrongGameFile); var fileLoadHandler = new FileLoadHandler(database, gameFile1, gameProfile); var iwadFiles = fileLoadHandler.GetIWadFiles(); Assert.AreEqual(2, iwadFiles.Count); Assert.IsTrue(iwadFiles.Exists(x => x.FileName.Equals("green.wad"))); Assert.IsTrue(iwadFiles.Exists(x => x.FileName.Equals("blue.wad"))); Assert.IsFalse(fileLoadHandler.IsIWadFile(gameProfile)); Assert.IsFalse(fileLoadHandler.IsIWadFile(gameFile1)); Assert.IsFalse(fileLoadHandler.IsIWadFile(wrongGameFile)); Assert.IsTrue(fileLoadHandler.IsIWadFile(gameFile2)); Assert.IsTrue(fileLoadHandler.IsIWadFile(gameFile3)); } [TestMethod] public void GetIWadFiles_ReturnsFilesIndirectlyAssociatedWithTheIWad() { // if the iwad has a file attached to it because of a source port, that file should not be included when the iwad is selected // e.g. GZDoom has noendgame.wad User launched DOOM2.WAD with GZDoom. This adds noendgame to the additional files of DOOM2.WAD. // if the user selects DOOM2.WAD with another file (and not GZDoom as the port), noendgame.wad should not be added var iwadGameFile = InsertIWadAndGameFile("DOOM2.zip", settingsFiles: "rightfile.zip;ignoreme1.zip", settingsFilesSourcePort: "ignoreme1.zip"); // Should cancel out the same file in SettingsFiles var gameProfile = new GameFile { FileName = "profile.wad", SettingsFilesIWAD = "ignoreme2.wad", // Should get ignored because there is an IWadID IWadID = iwadGameFile.IWadID }; database.InsertGameFile(gameProfile); var gameFile1 = new GameFile // Not used to find the IWAD additional files { FileName = "whoCares.wad", SettingsFilesIWAD = "ignoreme3.wad", IWadID = iwadGameFile.IWadID }; database.InsertGameFile(gameFile1); var rightFile = new GameFile { FileName = "rightfile.zip" }; database.InsertGameFile(rightFile); var ignoreFile1 = new GameFile { FileName = "ignoreme1.zip" }; database.InsertGameFile(ignoreFile1); var ignoreFile2 = new GameFile { FileName = "ignoreme2.zip" }; database.InsertGameFile(ignoreFile2); var ignoreFile3 = new GameFile { FileName = "ignoreme3.zip" }; database.InsertGameFile(ignoreFile3); var fileLoadHandler = new FileLoadHandler(database, gameFile1, gameProfile); var iwadFiles = fileLoadHandler.GetIWadFiles(); Assert.AreEqual(1, iwadFiles.Count); Assert.AreEqual("rightfile.zip", iwadFiles.First().FileName); Assert.IsTrue(fileLoadHandler.IsIWadFile(rightFile)); Assert.IsFalse(fileLoadHandler.IsIWadFile(gameFile1)); Assert.IsFalse(fileLoadHandler.IsIWadFile(ignoreFile1)); Assert.IsFalse(fileLoadHandler.IsIWadFile(ignoreFile2)); Assert.IsFalse(fileLoadHandler.IsIWadFile(ignoreFile3)); Assert.IsFalse(fileLoadHandler.IsIWadFile(iwadGameFile)); } [TestMethod] public void GetSourcePortFiles_ReturnsFilesAssociatedWithTheSourcePort() { var gameProfile = new GameFile { FileName = "blah.wad", SettingsFilesSourcePort = "a.wad;b.wad" }; database.InsertGameFile(gameProfile); var gameFile1 = new GameFile { FileName = "ignore me.wad" }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "a.wad" }; database.InsertGameFile(gameFile2); var gameFile3 = new GameFile { FileName = "b.wad" }; database.InsertGameFile(gameFile3); var fileLoadHandler = new FileLoadHandler(database, gameFile1, gameProfile); var sourcePortFiles = fileLoadHandler.GetSourcePortFiles(); Assert.AreEqual(2, sourcePortFiles.Count); Assert.IsTrue(sourcePortFiles.Exists(x => x.FileName.Equals("a.wad"))); Assert.IsTrue(sourcePortFiles.Exists(x => x.FileName.Equals("b.wad"))); Assert.IsFalse(fileLoadHandler.IsSourcePortFile(gameFile1)); Assert.IsFalse(fileLoadHandler.IsSourcePortFile(gameProfile)); Assert.IsTrue(fileLoadHandler.IsSourcePortFile(gameFile2)); Assert.IsTrue(fileLoadHandler.IsSourcePortFile(gameFile3)); } [TestMethod] public void GetCurrentAdditionalFiles_ReturnsAssociatedFiles() { var gameProfile = new GameFile { FileName = "PROFILE.wad", SettingsFiles = "first.wad;second.wad" }; database.InsertGameFile(gameProfile); var gameFile1 = new GameFile { FileName = "myhouse.wad", SettingsFiles = "nope.wad;nah.wad" // These should be ignored }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "first.wad" }; database.InsertGameFile(gameFile2); var gameFile3 = new GameFile { FileName = "second.wad" }; database.InsertGameFile(gameFile3); var wrongGameFile = new GameFile { FileName = "random.wad" }; database.InsertGameFile(wrongGameFile); var fileLoadHandler = new FileLoadHandler(database, gameFile1, gameProfile); var currentAdditionalFiles = fileLoadHandler.GetCurrentAdditionalFiles(); Assert.AreEqual(3, currentAdditionalFiles.Count()); Assert.IsTrue(currentAdditionalFiles.Exists(x => x.FileName.Equals("myhouse.wad"))); Assert.IsTrue(currentAdditionalFiles.Exists(x => x.FileName.Equals("first.wad"))); Assert.IsTrue(currentAdditionalFiles.Exists(x => x.FileName.Equals("second.wad"))); } [TestMethod] public void CalculateAdditionalFiles_IfNoIWadThenClearIWadAndSourcePortFiles() { var gameProfile = new GameFile { FileName = "the_profile.wad", SettingsFilesSourcePort = "sauce_port.zip", SettingsFilesIWAD = "eye_wad.zip" }; database.InsertGameFile(gameProfile); var gameFile1 = new GameFile { FileName = "file IGNORE.wad" }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "sauce_port IGNORE.wad" }; database.InsertGameFile(gameFile2); var gameFile3 = new GameFile { FileName = "eye_wad IGNORE.wad" }; database.InsertGameFile(gameFile3); var fileLoadHandler = new FileLoadHandler(database, gameFile1, gameProfile); fileLoadHandler.CalculateAdditionalFiles(null, null); Assert.AreEqual(0, fileLoadHandler.GetIWadFiles().Count); Assert.AreEqual(0, fileLoadHandler.GetSourcePortFiles().Count); } [TestMethod] public void CalculateAdditionalFiles_IfIWadIsGameFileThenIgnoreIWadAdditionalFiles() { // This was a bug, for an iwad like DOOM2 Doom Launcher sets the SettingsFile to DOOM2.WAD if the user loaded it. // We need to exclude this when using DOOM2 as the iwad for another file var gameProfile = new GameFile { FileName = "game-profiley.wad" }; database.InsertGameFile(gameProfile); var iwadGameFile = InsertIWadAndGameFile("iwad.zip", settingsFiles: "chooseme.zip;ignoreme.zip", settingsFilesSourcePort: "ignoreme.zip"); // Should cancel out the same file in SettingsFiles var gameFile1 = new GameFile { FileName = "chooseme.zip" }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "ignoreme.zip" }; database.InsertGameFile(gameFile2); var gameFile3 = new GameFile { FileName = "secondone.zip" }; database.InsertGameFile(gameFile3); var sourcePort1 = new SourcePortData { Name = "Floom", SettingsFiles = "secondone.zip" }; database.InsertSourcePort(sourcePort1); var fileLoadHandler = new FileLoadHandler(database, iwadGameFile, gameProfile); fileLoadHandler.CalculateAdditionalFiles(iwadGameFile, sourcePort1); Assert.AreEqual(0, fileLoadHandler.GetIWadFiles().Count); Assert.AreEqual(1, fileLoadHandler.GetSourcePortFiles().Count); Assert.AreEqual("secondone.zip", fileLoadHandler.GetSourcePortFiles().First().FileName); } [TestMethod] public void CalculateAdditionalFiles_IfIWadIsNotGameFileThenTakeIWadAdditionalFiles() { var gameProfile = new GameFile { FileName = "game-profilex.wad" }; database.InsertGameFile(gameProfile); var iwadGameFile = InsertIWadAndGameFile("iwady.zip", settingsFiles: "choosemee.zip;ignoremee.zip", settingsFilesSourcePort: "ignoremee.zip"); // Should cancel out the same file in SettingsFiles var theGameFile = new GameFile { FileName = "the-game-file.zip" }; database.InsertGameFile(theGameFile); var gameFile1 = new GameFile { FileName = "choosemee.zip" }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "ignoremee.zip" }; database.InsertGameFile(gameFile2); var gameFile3 = new GameFile { FileName = "2nd-one.zip" }; database.InsertGameFile(gameFile3); var sourcePort1 = new SourcePortData { Name = "Broom", SettingsFiles = "2nd-one.zip" }; database.InsertSourcePort(sourcePort1); var fileLoadHandler = new FileLoadHandler(database, theGameFile, gameProfile); fileLoadHandler.CalculateAdditionalFiles(iwadGameFile, sourcePort1); Assert.AreEqual(1, fileLoadHandler.GetIWadFiles().Count); Assert.AreEqual("choosemee.zip", fileLoadHandler.GetIWadFiles().First().FileName); Assert.AreEqual(1, fileLoadHandler.GetSourcePortFiles().Count); Assert.AreEqual("2nd-one.zip", fileLoadHandler.GetSourcePortFiles().First().FileName); } [TestMethod] public void CalculateAdditionalFiles_CorrectlyExcludesThePreviousResults() { // Set up first calculation var gameProfile = new GameFile { FileName = "the-game-profile.wad" }; database.InsertGameFile(gameProfile); var theGameFile = new GameFile { FileName = "GAME_FILE.zip" }; database.InsertGameFile(theGameFile); var iwadGameFile1 = InsertIWadAndGameFile("IWAD1.zip", settingsFiles: "right1.zip;right2.zip;wrong1.zip", settingsFilesSourcePort: "wrong1.zip"); // Should cancel out the same file in SettingsFiles var sourcePort1 = new SourcePortData { Name = "Broom", SettingsFiles = "sp_right3.zip" }; database.InsertSourcePort(sourcePort1); var gameFile1 = new GameFile { FileName = "right1.zip" }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "right2.zip" }; database.InsertGameFile(gameFile2); var gameFile3 = new GameFile { FileName = "sp_right3.zip" }; database.InsertGameFile(gameFile3); var gameFile4 = new GameFile { FileName = "wrong1.zip" }; database.InsertGameFile(gameFile4); var fileLoadHandler = new FileLoadHandler(database, theGameFile, gameProfile); // First calculation fileLoadHandler.CalculateAdditionalFiles(iwadGameFile1, sourcePort1); // Additional files includes the game file, plus the added ones var additionalFiles1 = fileLoadHandler.GetCurrentAdditionalFiles(); Assert.AreEqual(4, additionalFiles1.Count); Assert.IsTrue(additionalFiles1.Exists(x => x.FileName.Equals("GAME_FILE.zip"))); Assert.IsTrue(additionalFiles1.Exists(x => x.FileName.Equals("right1.zip"))); Assert.IsTrue(additionalFiles1.Exists(x => x.FileName.Equals("right2.zip"))); Assert.IsTrue(additionalFiles1.Exists(x => x.FileName.Equals("sp_right3.zip"))); // They are all new except the game file, which was already there since creation var newAdditionalFiles1 = fileLoadHandler.GetCurrentAdditionalNewFiles(); Assert.AreEqual(3, newAdditionalFiles1.Count); Assert.IsTrue(newAdditionalFiles1.Exists(x => x.FileName.Equals("right1.zip"))); Assert.IsTrue(newAdditionalFiles1.Exists(x => x.FileName.Equals("right2.zip"))); Assert.IsTrue(newAdditionalFiles1.Exists(x => x.FileName.Equals("sp_right3.zip"))); // Set up second calculation var iwadGameFile2 = InsertIWadAndGameFile("IWAD2.zip", settingsFiles: "right1.zip;NEWRIGHT.ZIP;wrong1.zip", // NEWRIGHT.ZIP instead of right2.zip settingsFilesSourcePort: "wrong1.zip"); // Should cancel out the same file in SettingsFiles var sourcePort2 = new SourcePortData { Name = "Vroom", SettingsFiles = "sp_right3.zip" }; // Same as the other one database.InsertSourcePort(sourcePort2); var gameFile5 = new GameFile { FileName = "NEWRIGHT.ZIP" }; database.InsertGameFile(gameFile5); // Second calculation fileLoadHandler.CalculateAdditionalFiles(iwadGameFile2, sourcePort2); // Now has NEWRIGHT.ZIP instead of right2.zip var additionalFiles2 = fileLoadHandler.GetCurrentAdditionalFiles(); Assert.AreEqual(4, additionalFiles2.Count); Assert.IsTrue(additionalFiles2.Exists(x => x.FileName.Equals("GAME_FILE.zip"))); Assert.IsTrue(additionalFiles2.Exists(x => x.FileName.Equals("right1.zip"))); Assert.IsTrue(additionalFiles2.Exists(x => x.FileName.Equals("NEWRIGHT.ZIP"))); Assert.IsTrue(additionalFiles2.Exists(x => x.FileName.Equals("sp_right3.zip"))); // Only NEWRIGHT.ZIP is new now var newAdditionalFiles2 = fileLoadHandler.GetCurrentAdditionalNewFiles(); Assert.AreEqual(1, newAdditionalFiles2.Count); Assert.AreEqual("NEWRIGHT.ZIP", newAdditionalFiles2.First().FileName); // Only this one is new since the first calculation } [TestMethod] public void CalculateAdditionalFiles_AdditionalFilesAreOrderedByIWadFilesThenByEarliest() { // Set up first calculation var gameProfile = new GameFile { FileName = "GAME_PROFILE.wad", SettingsFiles = "og_yes1.zip;og_yes2.zip" }; database.InsertGameFile(gameProfile); var theGameFile = new GameFile { FileName = "THE_GAME_FILE.zip" }; database.InsertGameFile(theGameFile); var gameFile1 = new GameFile { FileName = "og_yes1.zip" }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "og_yes2.zip" }; database.InsertGameFile(gameFile2); var fileLoadHandler = new FileLoadHandler(database, theGameFile, gameProfile); // First calculation fileLoadHandler.CalculateAdditionalFiles(null, null); Assert.AreEqual(3, fileLoadHandler.GetCurrentAdditionalFiles().Count()); // Set up second calculation var iwadGameFile1 = InsertIWadAndGameFile("IWAD1.zip", settingsFiles: "iw_yes3.zip;iw_yes4.zip", settingsFilesSourcePort: ""); var sourcePort1 = new SourcePortData { Name = "GLoom", SettingsFiles = "sp_yes5.zip;sp_yes6.zip" }; database.InsertSourcePort(sourcePort1); var gameFile3 = new GameFile { FileName = "iw_yes3.zip" }; database.InsertGameFile(gameFile3); var gameFile4 = new GameFile { FileName = "iw_yes4.zip" }; database.InsertGameFile(gameFile4); var gameFile5 = new GameFile { FileName = "sp_yes5.zip" }; database.InsertGameFile(gameFile5); var gameFile6 = new GameFile { FileName = "sp_yes6.zip" }; database.InsertGameFile(gameFile6); // Second calculation fileLoadHandler.CalculateAdditionalFiles(iwadGameFile1, sourcePort1); var additionalFiles1 = fileLoadHandler.GetCurrentAdditionalFiles(); Assert.AreEqual(7, additionalFiles1.Count()); // IWad files are first Assert.AreEqual("iw_yes3.zip", additionalFiles1[0].FileName); Assert.AreEqual("iw_yes4.zip", additionalFiles1[1].FileName); // The GameFile was the first original file Assert.AreEqual("THE_GAME_FILE.zip", additionalFiles1[2].FileName); // Files from the first calculation are next Assert.AreEqual("og_yes1.zip", additionalFiles1[3].FileName); Assert.AreEqual("og_yes2.zip", additionalFiles1[4].FileName); // Then the rest Assert.AreEqual("sp_yes5.zip", additionalFiles1[5].FileName); Assert.AreEqual("sp_yes6.zip", additionalFiles1[6].FileName); } [TestMethod] public void Reset_RestoresAdditionalFilesToCreationState() { // Set up creation state var gameProfile = new GameFile { FileName = "the-game-profile.wad", SettingsFiles = "file1.zip;file2.zip" }; database.InsertGameFile(gameProfile); var theGameFile = new GameFile { FileName = "the-game-file.zip" }; database.InsertGameFile(theGameFile); var gameFile1 = new GameFile { FileName = "file1.zip" }; database.InsertGameFile(gameFile1); var gameFile2 = new GameFile { FileName = "file2.zip" }; database.InsertGameFile(gameFile2); var fileLoadHandler = new FileLoadHandler(database, theGameFile, gameProfile); Assert.AreEqual(3, fileLoadHandler.GetCurrentAdditionalFiles().Count); // Set up new state var iwadGameFile1 = InsertIWadAndGameFile("the-iwad.zip", settingsFiles: "new1.zip;new2.zip", settingsFilesSourcePort: ""); var gameFile3 = new GameFile { FileName = "new1.zip" }; database.InsertGameFile(gameFile3); var gameFile4 = new GameFile { FileName = "new2.zip" }; database.InsertGameFile(gameFile4); fileLoadHandler.CalculateAdditionalFiles(iwadGameFile1, null); Assert.AreEqual(5, fileLoadHandler.GetCurrentAdditionalFiles().Count); // Reset original state fileLoadHandler.Reset(); var additionalFiles = fileLoadHandler.GetCurrentAdditionalFiles(); Assert.AreEqual(3, additionalFiles.Count()); Assert.IsTrue(additionalFiles.Exists(x => x.FileName.Equals("the-game-file.zip"))); Assert.IsTrue(additionalFiles.Exists(x => x.FileName.Equals("file1.zip"))); Assert.IsTrue(additionalFiles.Exists(x => x.FileName.Equals("file2.zip"))); } private IGameFile InsertIWadAndGameFile(String fileName, String settingsFiles, String settingsFilesSourcePort) { IGameFile iwadGameFile = new GameFile { FileName = fileName, SettingsFiles = settingsFiles, SettingsFilesSourcePort = settingsFilesSourcePort }; database.InsertGameFile(iwadGameFile); iwadGameFile = database.GetGameFile(fileName); IIWadData iwad1 = new IWadData { Name = fileName, GameFileID = iwadGameFile.GameFileID }; database.InsertIWad(iwad1); iwad1 = database.GetIWad((int)iwadGameFile.GameFileID); iwadGameFile.IWadID = iwad1.IWadID; database.UpdateGameFile(iwadGameFile); return iwadGameFile; } } } ================================================ FILE: UnitTest/Tests/TestMapLumps.cs ================================================ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Linq; using WadReader; namespace UnitTest.Tests { [TestClass] public class TestMapLumps { [TestMethod] public void TestMap() { List lumps = CreateMapSetLumps("MAP01"); List mapLumps = WadFileReader.GetMapMarkerLumps(lumps); Assert.AreEqual(1, mapLumps.Count); Assert.AreEqual("MAP01", mapLumps.First().Name); } [TestMethod] public void TestMapWithExtra() { List lumps = CreateMapSetLumps("MAP01"); lumps.Insert(0, new FileLump("JUNK")); for (int i = 0; i < 10; i++) lumps.Add(new FileLump("LUMP" + i.ToString())); List mapLumps = WadFileReader.GetMapMarkerLumps(lumps); Assert.AreEqual(1, mapLumps.Count); Assert.AreEqual("MAP01", mapLumps.First().Name); } [TestMethod] public void TestMultipleMaps() { string[] maps = new string[] { "MAP01", "MAP02", "MAP03", "MAP04", "MAP05", "MAP06" }; List lumps = new List(); Array.ForEach(maps, x => lumps.AddRange(CreateMapSetLumps(x))); List mapLumps = WadFileReader.GetMapMarkerLumps(lumps); Assert.AreEqual(maps.Length, mapLumps.Count); Assert.AreEqual(maps.Length, mapLumps.Count(x => maps.Contains(x.Name))); } [TestMethod] public void TestNonStandardMapNames() { string[] maps = new string[] { "MYMAP", "ZMAP1", "XMAP2", "ANOTHERMAP", "WHOCARES", "LOOKATME" }; List lumps = new List(); Array.ForEach(maps, x => lumps.AddRange(CreateMapSetLumps(x))); List mapLumps = WadFileReader.GetMapMarkerLumps(lumps); Assert.AreEqual(maps.Length, mapLumps.Count); Assert.AreEqual(maps.Length, mapLumps.Count(x => maps.Contains(x.Name))); } [TestMethod] public void TestMultipleMapsWithExtra() { string[] maps = new string[] { "MAP01", "MAP02", "MAP03", "MAP04", "MAP05", "MAP06" }; List lumps = new List(); foreach(var map in maps) { lumps.AddRange(CreateMapSetLumps(map)); for (int i = 0; i < 10; i++) lumps.Add(new FileLump("LUMP" + i.ToString())); } for (int i = 0; i < 10; i++) lumps.Add(new FileLump("LUMP" + i.ToString())); List mapLumps = WadFileReader.GetMapMarkerLumps(lumps); Assert.AreEqual(maps.Length, mapLumps.Count); Assert.AreEqual(maps.Length, mapLumps.Count(x => maps.Contains(x.Name))); } [TestMethod] public void TestBadMapLump() { List lumps = CreateMapSetLumps("MAP01"); lumps.Insert(2, new FileLump("BADLUMP")); List mapLumps = WadFileReader.GetMapMarkerLumps(lumps); //MAP01 should not pass, but BADLUMP will because it has at least 5 valid map lumps Assert.AreEqual(1, mapLumps.Count); Assert.AreEqual("BADLUMP", mapLumps.First().Name); } [TestMethod] public void TestBadMapLumpNoMap() { List lumps = CreateMapSetLumps("MAP01"); lumps.Insert(4, new FileLump("BADLUMP")); List mapLumps = WadFileReader.GetMapMarkerLumps(lumps); //requires at least 5 valid map lumps to pass, inserting BADLUMP in the middle will fail both Assert.AreEqual(0, mapLumps.Count); } [TestMethod] public void TestUdmf() { List lumps = CreateLumps(new string[] { "MAP01", "TEXTMAP", "ZNODES", "ENDMAP" }); List mapLumps = WadFileReader.GetMapMarkerLumps(lumps); Assert.AreEqual(1, mapLumps.Count); Assert.AreEqual("MAP01", mapLumps.First().Name); lumps.AddRange(CreateLumps(new string[] { "WHOCARES", "MAP02", "TEXTMAP", "ZNODES", "REJECT", "DIALOGUE", "ENDMAP", "JUNK" })); mapLumps = WadFileReader.GetMapMarkerLumps(lumps); Assert.AreEqual(2, mapLumps.Count); Assert.AreEqual("MAP01", mapLumps[0].Name); Assert.AreEqual("MAP02", mapLumps[1].Name); } private List CreateMapSetLumps(string mapName) { return CreateLumps(new string[] { mapName, "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", "SEGS", "SECTORS", "GL_NODES", "GL_ANY" }); } private List CreateLumps(string[] names) { List ret = new List(); Array.ForEach(names, x => ret.Add(new FileLump(x))); return ret; } } } ================================================ FILE: UnitTest/Tests/TestMapStringSyncAction.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Handlers.Sync; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; namespace UnitTest.Tests { [TestClass] public class TestMapStringSyncAction { [TestMethod] public void MapString() { var mapInfo = @" map map01 { music = ""test1"" } MAP MAP02 { music = ""test2"" } "; var frag = new MapStringSyncAction(new LauncherPath(Directory.GetCurrentDirectory())); var gameFile = new GameFile(); frag.ApplyToGameFile(gameFile, null, new string[] { mapInfo }); Assert.AreEqual("map01, MAP02", gameFile.Map); Assert.AreEqual(2, gameFile.MapCount); } } } ================================================ FILE: UnitTest/Tests/TestRecursiveArchiveReader.cs ================================================ using DoomLauncher; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Linq; using DoomLauncher.Archive; namespace UnitTest.Tests { [TestClass] public class TestRecursiveArchiveReader { [TestInitialize] public void Initialize() { TreeReader.TreeReadersCreated = 0; TreeReader.TreeReadersDisposed = 0; } private IArchiveReader GetReaderFromEntry(IArchiveEntry entry) => (entry as TreeEntry)?.GetTreeReader(); [TestMethod] public void EntriesIncludesNextLevelDown() { var tree = new Tree("wads", new Tree("wads/a"), new Tree("wads/b"), new Tree("wads/id", new Tree("wads/id/c"), new Tree("wads/id/d"))); IArchiveReader root = new TreeReader(tree); var reader = new RecursiveArchiveReader(root, GetReaderFromEntry); var entryNames = reader.Entries.Select(e => e.Name).ToList(); Assert.AreEqual(5, entryNames.Count); Assert.AreEqual("wads/a", entryNames[0]); Assert.AreEqual("wads/b", entryNames[1]); Assert.AreEqual("wads/id", entryNames[2]); Assert.AreEqual("wads/id/c", entryNames[3]); Assert.AreEqual("wads/id/d", entryNames[4]); } [TestMethod] public void EntriesRespectUnderlyingLaziness() { var tree = new Tree("fruit", new Tree("apple"), new Tree("banana"), new Tree("berries", new Tree("strawberry"), new Tree("cherry"), new Tree("ILLEGAL")), new Tree("ILLEGAL")); IArchiveReader root = new TreeReader(tree); var reader = new RecursiveArchiveReader(root, (entry) => { if (entry.Name == "ILLEGAL") throw new Exception("YOU WEREN'T SUPPOSED TO LOOK AT THIS ONE!"); else return GetReaderFromEntry(entry); }); var entryNames = reader.Entries.Take(5).Select(e => e.Name).ToList(); Assert.AreEqual(5, entryNames.Count); Assert.AreEqual("apple", entryNames[0]); Assert.AreEqual("banana", entryNames[1]); Assert.AreEqual("berries", entryNames[2]); Assert.AreEqual("strawberry", entryNames[3]); Assert.AreEqual("cherry", entryNames[4]); } [TestMethod] public void DisposeRecursiveReaderDisposesAllTheChildReaders() { var tree = new Tree("wads", // 1st TreeReader new Tree("wads/a"), new Tree("wads/b"), new Tree("wads/id", // 2nd TreeReader new Tree("wads/id/c"), new Tree("wads/id/d"))); Assert.AreEqual(0, TreeReader.TreeReadersCreated); Assert.AreEqual(0, TreeReader.TreeReadersDisposed); IArchiveReader root = new TreeReader(tree); Assert.AreEqual(1, TreeReader.TreeReadersCreated); Assert.AreEqual(0, TreeReader.TreeReadersDisposed); using (var reader = new RecursiveArchiveReader(root, GetReaderFromEntry)) { var makeItExplicit = reader.Entries.ToList(); } Assert.AreEqual(2, TreeReader.TreeReadersCreated); Assert.AreEqual(2, TreeReader.TreeReadersDisposed); } } } ================================================ FILE: UnitTest/Tests/TestSourcePort.cs ================================================ using DoomLauncher; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestSourcePort { private List CreateTestSourcePorts() { List sourcePorts = new List(); for (int i = 0; i < 10; i++) { sourcePorts.Add( new DoomLauncher.DataSources.SourcePortData() { SourcePortID = i + 1, Name = "test" + i.ToString(), Executable = "test" + i.ToString(), FileOption = "test" + i.ToString(), SettingsFiles = "test" + i.ToString(), SupportedExtensions = "test" + i.ToString(), Directory = new LauncherPath(i.ToString()), AltSaveDirectory = new LauncherPath(i.ToString()), LaunchType = i > 6 ? SourcePortLaunchType.Utility : SourcePortLaunchType.SourcePort, ExtraParameters = "test" + i.ToString() } ); } return sourcePorts; } public void Clear() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); foreach (var sourcePort in adapter.GetSourcePorts()) adapter.DeleteSourcePort(sourcePort); foreach (var utility in adapter.GetUtilities()) adapter.DeleteSourcePort(utility); } [TestMethod] public void TestSourcePortData() { Clear(); TestInsert(); TestGetSourcePorts(); TestUpdate(); TestDelete(); } public void TestInsert() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var sourcePorts = CreateTestSourcePorts(); foreach (var port in sourcePorts) adapter.InsertSourcePort(port); Assert.AreEqual(sourcePorts.Count(x => x.LaunchType == SourcePortLaunchType.SourcePort), adapter.GetSourcePorts().Count()); Assert.AreEqual(sourcePorts.Count(x => x.LaunchType == SourcePortLaunchType.Utility), adapter.GetUtilities().Count()); } public void TestGetSourcePorts() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var sourcePorts = adapter.GetSourcePorts(); var utilites = adapter.GetUtilities(); Assert.AreEqual(7, sourcePorts.Count()); Assert.AreEqual(3, utilites.Count()); List allSourcePorts = new List(); allSourcePorts.AddRange(sourcePorts); allSourcePorts.AddRange(utilites); var testSourcePorts = CreateTestSourcePorts(); foreach(var testSourcePort in testSourcePorts) { var port = allSourcePorts.First(x => x.Name.Equals(testSourcePort.Name)); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(testSourcePort, port, nameof(ISourcePortData.SourcePortID))); } } public void TestGetSourcePort() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var testSourcePorts = CreateTestSourcePorts(); foreach (var testSourcePort in testSourcePorts) { var dbPort = adapter.GetSourcePort(testSourcePort.SourcePortID); Assert.IsNotNull(dbPort); Assert.IsTrue(TestUtil.AllFieldsEqualIgnore(testSourcePort, dbPort, nameof(ISourcePortData.SourcePortID))); } } public void TestUpdate() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); List allSourcePorts = new List(); allSourcePorts.AddRange(adapter.GetSourcePorts()); allSourcePorts.AddRange(adapter.GetUtilities()); int somenumber = 100; foreach(var sourcePort in allSourcePorts) { sourcePort.Directory = new LauncherPath(sourcePort.Directory.GetPossiblyRelativePath() + somenumber.ToString()); sourcePort.Name += somenumber.ToString(); sourcePort.SettingsFiles += somenumber.ToString(); sourcePort.SupportedExtensions += somenumber.ToString(); adapter.UpdateSourcePort(sourcePort); var dbPort = adapter.GetSourcePort(sourcePort.SourcePortID); Assert.IsNotNull(dbPort); Assert.IsTrue(TestUtil.AllFieldsEqual(sourcePort, dbPort)); } } public void TestDelete() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); List allSourcePorts = new List(); allSourcePorts.AddRange(adapter.GetSourcePorts()); allSourcePorts.AddRange(adapter.GetUtilities()); int count = allSourcePorts.Count; foreach (var sourcePort in allSourcePorts) { adapter.DeleteSourcePort(sourcePort); count--; Assert.AreEqual(count, adapter.GetSourcePorts().Count() + adapter.GetUtilities().Count()); Assert.IsNull(adapter.GetSourcePort(sourcePort.SourcePortID)); } } } } ================================================ FILE: UnitTest/Tests/TestSpecificFiles.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; namespace UnitTest.Tests { [TestClass] public class TestSpecificFiles { private static readonly string ResourceFolder = "Resources"; private static readonly string[] SupportedExtensions = new string[] { ".wad", }; [TestMethod] public void ZipFileManaged() { IGameFile gameFile = CreateGameFile(Path.Combine(ResourceFolder, "pyrrhic.zip"), managed: true); string[] files = SpecificFilesForm.GetSupportedFiles(ResourceFolder, gameFile, SupportedExtensions); Assert.IsFalse(gameFile.IsDirectory()); Assert.IsFalse(gameFile.IsUnmanaged()); Assert.AreEqual(1, files.Length); Assert.AreEqual("pyrrhic_.wad", files[0]); } [TestMethod] public void ZipFileUnmanaged() { // Unmanaged zip files should still extract contents like normal string path = Path.GetFullPath(Path.Combine(ResourceFolder, "pyrrhic.zip")); IGameFile gameFile = CreateGameFile(path, managed: false); string[] files = SpecificFilesForm.GetSupportedFiles(ResourceFolder, gameFile, SupportedExtensions); Assert.IsFalse(gameFile.IsDirectory()); Assert.IsTrue(gameFile.IsUnmanaged()); Assert.AreEqual(1, files.Length); Assert.AreEqual("pyrrhic_.wad", files[0]); } [TestMethod] public void Pk3FileUnmanaged() { // A pk3 is a zip, but should not have contents extracted string path = Path.GetFullPath(Path.Combine(ResourceFolder, "testpk3.pk3")); IGameFile gameFile = CreateGameFile(path, managed: false); string[] files = SpecificFilesForm.GetSupportedFiles(ResourceFolder, gameFile, SupportedExtensions); Assert.IsFalse(gameFile.IsDirectory()); Assert.IsTrue(gameFile.IsUnmanaged()); Assert.AreEqual(1, files.Length); Assert.AreEqual(path, files[0]); } [TestMethod] public void UnmanagedFile() { // The extension doesn't matter, it's unamanged so it should be added TestUtil.ExtractResourceToDirectory("pyrrhic.zip", "Doom"); string path = Path.GetFullPath(Path.Combine("Doom", "pyrrhic_.wad")); IGameFile gameFile = CreateGameFile(path, managed: false); string[] files = SpecificFilesForm.GetSupportedFiles("Doom", gameFile, SupportedExtensions); Assert.IsFalse(gameFile.IsDirectory()); Assert.IsTrue(gameFile.IsUnmanaged()); Assert.AreEqual(1, files.Length); Assert.AreEqual(path, files[0]); } [TestMethod] public void Folder() { // Extension doesn't matter, it's an unmanaged folder so it should be added TestUtil.ExtractResourceToDirectory("pyrrhic.zip", "Doom"); string path = Path.GetFullPath("Doom"); IGameFile gameFile = CreateGameFile(path, managed: false); string[] files = SpecificFilesForm.GetSupportedFiles("Doom", gameFile, SupportedExtensions); Assert.IsTrue(gameFile.IsDirectory()); Assert.IsTrue(gameFile.IsUnmanaged()); Assert.AreEqual(1, files.Length); Assert.AreEqual(path, files[0]); } private static IGameFile CreateGameFile(string path, bool managed) { string filename = managed ? Path.GetFileName(path) : path; return new GameFile() { FileName = filename, }; } } } ================================================ FILE: UnitTest/Tests/TestStartupImageSyncAction.cs ================================================ using DoomLauncher.DataSources; using DoomLauncher.Handlers.Sync; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; namespace UnitTest.Tests { [TestClass] public class TestStartupImageSyncAction { [TestMethod] public void ApplyToGameFile_CanProcessPNGImages() { var gameFile = new GameFile { FileName = "blahblah.wad" }; byte[] pngBytes = File.ReadAllBytes(@"resources\happy.png"); Tree files = new Tree("root", new Tree("STARTUP", pngBytes)); var reader = new TreeReader(files); var syncAction = new StartupImageSyncAction(); var result = syncAction.ApplyToGameFile(gameFile, reader, null); Assert.IsTrue(result.GetTitlePic(gameFile, out _)); } [TestMethod] public void ApplyToGameFile_CanProcessPlanarImages() { var gameFile = new GameFile { FileName = "wad_with_planar.wad" }; byte[] planarBytes = File.ReadAllBytes(@"resources\planarimage.dat"); Tree files = new Tree("root", new Tree("STARTUP.dat", planarBytes)); var reader = new TreeReader(files); var syncAction = new StartupImageSyncAction(); var result = syncAction.ApplyToGameFile(gameFile, reader, null); Assert.IsTrue(result.GetTitlePic(gameFile, out _)); } [TestMethod] public void ApplyToGameFile_IgnoreStartupDotWad() { var gameFile = new GameFile { FileName = "wad_with_planar.wad" }; byte[] planarBytes = File.ReadAllBytes(@"resources\planarimage.dat"); Tree files = new Tree("root", new Tree("STARTUP.wad", planarBytes)); var reader = new TreeReader(files); var syncAction = new StartupImageSyncAction(); var result = syncAction.ApplyToGameFile(gameFile, reader, null); Assert.IsFalse(result.GetTitlePic(gameFile, out _)); } [TestMethod] public void ApplyToGameFile_IgnoresStrifeStartupImage() { var gameFile = new GameFile { FileName = "wad_with_planar.wad" }; byte[] planarBytes = File.ReadAllBytes(@"resources\planarimage.dat"); Tree files = new Tree("root", new Tree("STARTUP0", planarBytes)); var reader = new TreeReader(files); var syncAction = new StartupImageSyncAction(); var result = syncAction.ApplyToGameFile(gameFile, reader, null); Assert.IsFalse(result.GetTitlePic(gameFile, out _)); } [TestMethod] public void ApplyToGameFile_NonsenseDoesntProduceAnImage() { var gameFile = new GameFile { FileName = "wadname.wad" }; byte[] planarBytes = File.ReadAllBytes(@"resources\planarimage.dat"); Tree files = new Tree("root", new Tree("STARTUP.blah", new byte[] { 0x01, 0x02, 0x03, 0xab, 0xcd })); var reader = new TreeReader(files); var syncAction = new StartupImageSyncAction(); var result = syncAction.ApplyToGameFile(gameFile, reader, null); Assert.IsFalse(result.GetTitlePic(gameFile, out _)); } } } ================================================ FILE: UnitTest/Tests/TestStatdump.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Statistics; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.IO; namespace UnitTest.Tests { [TestClass] public class TestStatdump { private readonly List m_args = new List(); [TestMethod] public void TestStatdumpFileSingle() { string file = @"=========================================== E1M1 / MAP01 =========================================== Time: 0:22 (par: 0:30) Player 1 (Green): Kills: 5 / 6 (83%) Items: 3 / 37 (8%) Secrets: 0 / 3 (0%) "; StatdumpReader statsReader = new StatdumpReader(new GameFile() { GameFileID = 1 }, "stats.txt"); statsReader.NewStastics += statsReader_NewStastics; File.WriteAllText("stats.txt", file); statsReader.ReadNow(); Assert.AreEqual(1, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("E1M1/MAP01", m_args[0].Statistics.MapName); Assert.AreEqual(5, m_args[0].Statistics.KillCount); Assert.AreEqual(6, m_args[0].Statistics.TotalKills); Assert.AreEqual(3, m_args[0].Statistics.ItemCount); Assert.AreEqual(37, m_args[0].Statistics.TotalItems); Assert.AreEqual(0, m_args[0].Statistics.SecretCount); Assert.AreEqual(3, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(22.0f, m_args[0].Statistics.LevelTime); } [TestMethod] public void TestStatdumpFileMultiple() { string file = @"=========================================== E1M1 =========================================== Time: 0:20 (par: 0:30) Player 1 (Green): Kills: 5 / 6 (83%) Items: 8 / 37 (21%) Secrets: 1 / 3 (0%) =========================================== E1M2 =========================================== Time: 12:23 (par: 1:15) Player 1 (Green): Kills: 13 / 41 (31%) Items: 12 / 42 (2%) Secrets: 3/ 6 (0%) "; StatdumpReader statsReader = new StatdumpReader(new GameFile() { GameFileID = 1 }, "stats.txt"); statsReader.NewStastics += statsReader_NewStastics; File.WriteAllText("stats.txt", file); statsReader.ReadNow(); Assert.AreEqual(2, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("E1M1", m_args[0].Statistics.MapName); Assert.AreEqual(5, m_args[0].Statistics.KillCount); Assert.AreEqual(6, m_args[0].Statistics.TotalKills); Assert.AreEqual(8, m_args[0].Statistics.ItemCount); Assert.AreEqual(37, m_args[0].Statistics.TotalItems); Assert.AreEqual(1, m_args[0].Statistics.SecretCount); Assert.AreEqual(3, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(20.0f, m_args[0].Statistics.LevelTime); Assert.AreEqual("E1M2", m_args[1].Statistics.MapName); Assert.AreEqual(13, m_args[1].Statistics.KillCount); Assert.AreEqual(41, m_args[1].Statistics.TotalKills); Assert.AreEqual(12, m_args[1].Statistics.ItemCount); Assert.AreEqual(42, m_args[1].Statistics.TotalItems); Assert.AreEqual(3, m_args[1].Statistics.SecretCount); Assert.AreEqual(6, m_args[1].Statistics.TotalSecrets); Assert.AreEqual(743.0f, m_args[1].Statistics.LevelTime); } [TestMethod] public void TestStatdumpFileZero() { string file = @"=========================================== MAP30 =========================================== Time: 0:21 (par: 3:00) Player 1 (Green): Kills: 0 Items: 1 / 6 (16%) Secrets: 0 "; StatdumpReader statsReader = new StatdumpReader(new GameFile() { GameFileID = 1 }, "stats.txt"); statsReader.NewStastics += statsReader_NewStastics; File.WriteAllText("stats.txt", file); statsReader.ReadNow(); Assert.AreEqual(1, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("MAP30", m_args[0].Statistics.MapName); Assert.AreEqual(0, m_args[0].Statistics.KillCount); Assert.AreEqual(0, m_args[0].Statistics.TotalKills); Assert.AreEqual(1, m_args[0].Statistics.ItemCount); Assert.AreEqual(6, m_args[0].Statistics.TotalItems); Assert.AreEqual(0, m_args[0].Statistics.SecretCount); Assert.AreEqual(0, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(21.0f, m_args[0].Statistics.LevelTime); } [TestMethod] public void TestStatdumpFileKills() { string file = @"=========================================== MAP11 =========================================== Time: 6:39 (par: 3:30) Player 1 (Green): Kills: 78 / 71 (109%) Items: 21 / 21 (100%) Secrets: 3 / 3 (100%) "; StatdumpReader statsReader = new StatdumpReader(new GameFile() { GameFileID = 1 }, "stats.txt"); statsReader.NewStastics += statsReader_NewStastics; File.WriteAllText("stats.txt", file); statsReader.ReadNow(); Assert.AreEqual(1, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); //Check that the kill count does not exceed total kills Assert.AreEqual("MAP11", m_args[0].Statistics.MapName); Assert.AreEqual(71, m_args[0].Statistics.KillCount); Assert.AreEqual(71, m_args[0].Statistics.TotalKills); Assert.AreEqual(21, m_args[0].Statistics.ItemCount); Assert.AreEqual(21, m_args[0].Statistics.TotalItems); Assert.AreEqual(3, m_args[0].Statistics.SecretCount); Assert.AreEqual(3, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(399.0f, m_args[0].Statistics.LevelTime); } [TestMethod] public void TestStatdumpNegative() { string file = @"=========================================== MAP11 =========================================== Time: 88:18 (par: 0:00) Player 1 (Green): Kills: 620 / 601 (-5%) Items: 502 / 551 (-27%) Secrets: 10 / 11 (90%) "; StatdumpReader statsReader = new StatdumpReader(new GameFile() { GameFileID = 1 }, "stats.txt"); statsReader.NewStastics += statsReader_NewStastics; File.WriteAllText("stats.txt", file); statsReader.ReadNow(); Assert.AreEqual(1, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); //Check that the kill count does not exceed total kills Assert.AreEqual("MAP11", m_args[0].Statistics.MapName); Assert.AreEqual(601, m_args[0].Statistics.KillCount); Assert.AreEqual(601, m_args[0].Statistics.TotalKills); // Total kills are capped Assert.AreEqual(502, m_args[0].Statistics.ItemCount); Assert.AreEqual(551, m_args[0].Statistics.TotalItems); Assert.AreEqual(10, m_args[0].Statistics.SecretCount); Assert.AreEqual(11, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(5298.0f, m_args[0].Statistics.LevelTime); } private void statsReader_NewStastics(object sender, DoomLauncher.NewStatisticsEventArgs e) { m_args.Add(e); } } } ================================================ FILE: UnitTest/Tests/TestStats.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestStats { private List CreateStatsData() { List stats = new List(); for (int i = 0; i < 10; i++) { stats.Add( new StatsData() { StatID = i + 1, GameFileID = i, ItemCount = i, TotalItems = i, KillCount = i, TotalKills = i, SecretCount = i, TotalSecrets = i, LevelTime = i, SourcePortID = i, SaveFile = i.ToString(), MapName = i.ToString(), RecordTime = new DateTime(2019, 1, 1, i, 0, 0), } ); } return stats; } [TestMethod] public void TestStatData() { TestInsert(); TestGet(); TestUpdate(); TestDelete(); } public void TestInsert() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var stats = CreateStatsData(); foreach (var stat in stats) adapter.InsertStats(stat); Assert.AreEqual(stats.Count, adapter.GetStats().Count()); } public void TestGet() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var testStats = CreateStatsData(); var stats = adapter.GetStats(); foreach (var testStat in testStats) { var stat = stats.First(x => x.StatID == testStat.StatID); Assert.AreEqual(testStat, stat); } } public void TestUpdate() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var stats = adapter.GetStats(); var updateStat = stats.First(); updateStat.SourcePortID = 1000; adapter.UpdateStats(updateStat); Assert.AreEqual(updateStat, adapter.GetStats().First(x => x.StatID == updateStat.StatID)); } public void TestDelete() { IDataSourceAdapter adapter = TestUtil.CreateAdapter(); var stats = adapter.GetStats(); int count = stats.Count(); var sourcePortStat = stats.First(); adapter.DeleteStats(new SourcePortData() { SourcePortID = sourcePortStat.SourcePortID }); count--; Assert.AreEqual(count, adapter.GetStats().Count()); stats = adapter.GetStats(); foreach(var stat in stats) { adapter.DeleteStats(stat.StatID); count--; Assert.AreEqual(count, adapter.GetStats().Count()); } } } } ================================================ FILE: UnitTest/Tests/TestSteamLoader.cs ================================================ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using DoomLauncher.GameStores; using System.Linq; using static DoomLauncher.GameStores.StoreGameLoader; namespace UnitTest.Tests { [TestClass] public class TestSteamLoader { [TestMethod] public void LoadFromPath_FindsExpectedWads() { // Assuming fixtures: // SteamGame.ULTIMATE_DOOM: // TestSteamLibrary1 / steamapps / common / TestDoom GameFinder finder = _ => @"Resources\TestSteamLibrary1\steamapps\common\TestDoom"; var gameStoreFiles = StoreGameLoader.LoadStoreGame(StoreGame.ULTIMATE_DOOM, @"Resources\TestSteamLibrary1\steamapps\common\TestDoom"); var iwads = gameStoreFiles.InstalledIWads; Assert.AreEqual(4, iwads.Count); Assert.IsTrue(iwads.Exists(x => x.Contains("doom.wad"))); Assert.IsTrue(iwads.Exists(x => x.Contains("doom2.wad"))); Assert.IsTrue(iwads.Exists(x => x.Contains("plutonia.wad"))); Assert.IsTrue(iwads.Exists(x => x.Contains("tnt.wad"))); var pwads = gameStoreFiles.InstalledPWads; Assert.AreEqual(4, pwads.Count); Assert.IsTrue(pwads.Exists(x => x.Contains("nerve.wad"))); Assert.IsTrue(pwads.Exists(x => x.Contains("id1.wad"))); Assert.IsTrue(pwads.Exists(x => x.Contains("masterlevels.wad"))); Assert.IsTrue(pwads.Exists(x => x.Contains("sigil.wad"))); } [TestMethod] public void LoadFromPath_ReturnsEmptyFilesIfDirectoryNotFound() { var gameStoreFiles = StoreGameLoader.LoadStoreGame(StoreGame.ULTIMATE_DOOM, @"Resources\DoesNotExist"); Assert.AreEqual(gameStoreFiles, GameStoreFiles.EMPTY); } } } ================================================ FILE: UnitTest/Tests/TestSyncLibraryHandler.cs ================================================ using DoomLauncher; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using DoomLauncher.Handlers.Sync; using System.Collections.Generic; using DoomLauncher.Interfaces; namespace UnitTest.Tests { [TestClass] public class TestSyncLibraryHandler { private static readonly string s_filedir = "TestSyncDir"; private static readonly string s_tempdir = "TestSyncDirTemp"; private IDataSourceAdapter database; [TestInitialize] public void Init() { database = TestUtil.CreateAdapter(); Cleanup(); Directory.CreateDirectory(s_filedir); Directory.CreateDirectory(s_tempdir); } [TestCleanup] public void Cleanup() { var gameFiles = database.GetGameFiles(); gameFiles.ToList().ForEach(x => database.DeleteGameFile(x)); if (Directory.Exists(s_filedir)) Directory.Delete(s_filedir, true); if (Directory.Exists(s_tempdir)) Directory.Delete(s_tempdir, true); } [TestMethod] public void TestSyncSingleFile() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); Assert.AreEqual(0, database.GetGameFilesCount()); string file = "uroburos.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); var syncResult = handler.SyncManyFiles(new string[] { "uroburos.zip" }); Assert.AreEqual(1, syncResult.AddedGameFiles.Count); Assert.AreEqual(file, syncResult.AddedGameFiles[0].FileName); Assert.AreEqual(1, database.GetGameFilesCount()); var gameFile = database.GetGameFiles().First(); Assert.AreEqual(file, gameFile.FileName); Assert.AreEqual("MAP01", gameFile.Map); Assert.IsFalse(string.IsNullOrEmpty(gameFile.Title)); Assert.IsFalse(string.IsNullOrEmpty(gameFile.Author)); Assert.IsFalse(string.IsNullOrEmpty(gameFile.Description)); Assert.IsNotNull(gameFile.ReleaseDate); Assert.IsNotNull(gameFile.Downloaded); } [TestMethod] public void TestSyncMultiFile() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); Assert.AreEqual(0, database.GetGameFilesCount()); string[] files = new string[] { "uroburos.zip", "pyrrhic.zip" }; Array.ForEach(files, x => File.Copy(Path.Combine("Resources", x), Path.Combine(s_filedir, x))); var syncResult = handler.SyncManyFiles(files); Assert.AreEqual(2, syncResult.AddedGameFiles.Count); Assert.AreEqual(2, database.GetGameFilesCount()); var gameFiles = database.GetGameFiles(); var gameFile = gameFiles.First(x => x.FileName == files[0]); Assert.AreEqual("uroburos.zip", gameFile.FileName); Assert.AreEqual("MAP01", gameFile.Map); Assert.IsFalse(string.IsNullOrEmpty(gameFile.Title)); Assert.IsFalse(string.IsNullOrEmpty(gameFile.Author)); Assert.IsFalse(string.IsNullOrEmpty(gameFile.Description)); Assert.IsNotNull(gameFile.ReleaseDate); Assert.IsNotNull(gameFile.Downloaded); gameFile = gameFiles.First(x => x.FileName == files[1]); Assert.AreEqual("pyrrhic.zip", gameFile.FileName); Assert.AreEqual("MAP01, MAP02, MAP03, MAP04, MAP05, MAP06, MAP07, MAP08, MAP09, MAP10, MAP11, MAP12, MAP13, MAP14, MAP15", gameFile.Map); Assert.IsFalse(string.IsNullOrEmpty(gameFile.Title)); Assert.IsFalse(string.IsNullOrEmpty(gameFile.Author)); Assert.IsFalse(string.IsNullOrEmpty(gameFile.Description)); Assert.IsNotNull(gameFile.ReleaseDate); Assert.IsNotNull(gameFile.Downloaded); } [TestMethod] public void TestMapInfo() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); Assert.AreEqual(0, database.GetGameFilesCount()); string file = "joymaps1.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); handler.SyncManyFiles(new string[] { file }); Assert.AreEqual(1, database.GetGameFilesCount()); var gameFile = database.GetGameFiles().First(); Assert.AreEqual("MAP01, MAP02, MAP03, MAP04, MAP05, MAP06, MAP07, MAP08, MAP09, MAP10, MAP11, MAP12, MAP13, MAP14, MAP15", gameFile.Map); } [TestMethod] public void TestMapInfoInclude() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); Assert.AreEqual(0, database.GetGameFilesCount()); string file = "mapinfo.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); handler.SyncManyFiles(new string[] { file }); Assert.AreEqual(1, database.GetGameFilesCount()); var gameFile = database.GetGameFiles().First(); string maps = "E1M1, E1M2, E1M3, E1M4, E1M5, E1M6, E1M7, E1M8, E1M9, E2M1, E2M2, E2M3, E2M4, E2M5, E2M6, E2M7, E2M8, E2M9, E3M1, E3M2, E3M3, E3M4, E3M5, E3M6, E3M7, E3M8, E3M9, E4M1, E4M2, E4M3, E4M4, E4M5, E4M6, E4M7, E4M8, E4M9, MAP01, MAP02, MAP03, MAP04, MAP05, MAP06, MAP07, MAP08, MAP09, MAP10, MAP11, MAP12, MAP13, MAP14, MAP15, MAP16, MAP17, MAP18, MAP19, MAP20, MAP21, MAP22, MAP23, MAP24, MAP25, MAP26, MAP27, MAP28, MAP29, MAP30, MAP31, MAP32, NV_MAP01, NV_MAP02, NV_MAP03, NV_MAP04, NV_MAP05, NV_MAP06, NV_MAP07, NV_MAP08, NV_MAP09, ML_MAP01, ML_MAP02, ML_MAP03, ML_MAP04, ML_MAP05, ML_MAP06, ML_MAP07, ML_MAP08, ML_MAP09, ML_MAP10, ML_MAP11, ML_MAP12, ML_MAP13, ML_MAP14, ML_MAP15, ML_MAP16, ML_MAP17, ML_MAP18, ML_MAP19, ML_MAP20, ML_MAP21, TEST00, TN_MAP01, TN_MAP02, TN_MAP03, TN_MAP04, TN_MAP05, TN_MAP06, TN_MAP07, TN_MAP08, TN_MAP09, TN_MAP10, TN_MAP11, TN_MAP12, TN_MAP13, TN_MAP14, TN_MAP15, TN_MAP16, TN_MAP17, TN_MAP18, TN_MAP19, TN_MAP20, TN_MAP21, TN_MAP22, TN_MAP23, TN_MAP24, TN_MAP25, TN_MAP26, TN_MAP27, TN_MAP28, TN_MAP29, TN_MAP30, TN_MAP31, TN_MAP32, PL_MAP01, PL_MAP02, PL_MAP03, PL_MAP04, PL_MAP05, PL_MAP06, PL_MAP07, PL_MAP08, PL_MAP09, PL_MAP10, PL_MAP11, PL_MAP12, PL_MAP13, PL_MAP14, PL_MAP15, PL_MAP16, PL_MAP17, PL_MAP18, PL_MAP19, PL_MAP20, PL_MAP21, PL_MAP22, PL_MAP23, PL_MAP24, PL_MAP25, PL_MAP26, PL_MAP27, PL_MAP28, PL_MAP29, PL_MAP30, PL_MAP31, PL_MAP32"; Assert.AreEqual(maps, gameFile.Map); } [TestMethod] public void TestNonDoom64FileIsntDoom64() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); string file = "mapinfo.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); handler.SyncManyFiles(new string[] { file }); var gameFile = database.GetGameFiles().First(); Assert.IsFalse(gameFile.IsDoom64); } [TestMethod] public void TestDoom64FileIsDoom64() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); string file = "mapinfo_doom64.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); handler.SyncManyFiles(new string[] { file }); var gameFile = database.GetGameFiles().First(); Assert.IsTrue(gameFile.IsDoom64); } [TestMethod] public void TestMapsMultiFile() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); Assert.AreEqual(0, database.GetGameFilesCount()); string file = "pyrrhicmaps.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); handler.SyncManyFiles(new string[] { file }); Assert.AreEqual(1, database.GetGameFilesCount()); var gameFile = database.GetGameFiles().First(); //each map (MAP01, MAP02, and MAP03) is it's own wad, make sure they are all found Assert.AreEqual("MAP01, MAP02, MAP03", gameFile.Map); } [TestMethod] public void UnmanagedWadGetsMapString() { string file = "simple.wad"; string fullPathFile = Path.Combine(Directory.GetCurrentDirectory(), s_filedir, file); SyncLibraryHandler handler = CreateSyncLibraryHandler(false, FileManagement.Unmanaged); File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); var syncResult = handler.SyncManyFiles(new string[] { fullPathFile }); Assert.AreEqual(1, syncResult.AddedGameFiles.Count); Assert.AreEqual("MAP01", syncResult.AddedGameFiles[0].Map); } [TestMethod] public void TestSyncUpdate() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); Assert.AreEqual(0, database.GetGameFilesCount()); string file = "joymaps1.zip"; File.Copy(Path.Combine("Resources", "joymaps1.zip"), Path.Combine(s_filedir, "joymaps1.zip")); var syncResult = handler.SyncManyFiles(new string[] { "joymaps1.zip" }); Assert.AreEqual(1, syncResult.AddedGameFiles.Count); Assert.AreEqual(1, database.GetGameFilesCount()); var gameFile = database.GetGameFiles().First(); Assert.AreEqual("MAP01, MAP02, MAP03, MAP04, MAP05, MAP06, MAP07, MAP08, MAP09, MAP10, MAP11, MAP12, MAP13, MAP14, MAP15", gameFile.Map); Assert.AreEqual("The Joy of Mapping #1", gameFile.Title); Assert.AreEqual("Jimmy & Various", gameFile.Author); Assert.IsTrue(gameFile.Description.StartsWith("This was a livestreamed communal mapping session")); Assert.AreEqual(gameFile.ReleaseDate, DateTime.Parse("8/1/2016", CultureInfo.InvariantCulture)); File.Copy(Path.Combine("Resources", "uroburos.zip"), Path.Combine(s_filedir, "joymaps1.zip"), true); var syncResult2 = handler.SyncManyFiles(new string[] { "joymaps1.zip" }); Assert.AreEqual(0, syncResult2.AddedGameFiles.Count); Assert.AreEqual(1, syncResult2.UpdatedGameFiles.Count); Assert.AreEqual("joymaps1.zip", syncResult2.UpdatedGameFiles[0].FileName); Assert.AreEqual(1, database.GetGameFilesCount()); gameFile = database.GetGameFiles().First(); Assert.AreEqual(gameFile.FileName, file); Assert.AreEqual("MAP01", gameFile.Map); Assert.AreEqual("Uroburos", gameFile.Title); Assert.AreEqual("hobomaster22", gameFile.Author); Assert.IsTrue(gameFile.Description.StartsWith("A 1on1 map")); Assert.AreEqual(gameFile.ReleaseDate, DateTime.Parse("3/5/2005", CultureInfo.InvariantCulture)); } [TestMethod] public void TestInUse() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); Assert.AreEqual(0, database.GetGameFilesCount()); string file = "uroburos.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); var syncResult = SyncResult.EMPTY; using (var reader = File.OpenWrite(Path.Combine(s_filedir, file))) //lock file { syncResult = handler.SyncManyFiles(new string[] { file }); } Assert.AreEqual(1, database.GetGameFilesCount()); Assert.AreEqual(1, syncResult.InvalidFiles.Count); Assert.AreEqual(file, syncResult.InvalidFiles[0].FileName); } [TestMethod] public void TestCorruptZip() { SyncLibraryHandler handler = CreateSyncLibraryHandler(); Assert.AreEqual(0, database.GetGameFilesCount()); string file = "bad.zip"; File.WriteAllText(Path.Combine(s_filedir, file), "bad data"); var syncResult = handler.SyncManyFiles(new string[] { file }); Assert.AreEqual(1, database.GetGameFilesCount()); Assert.AreEqual(1, syncResult.InvalidFiles.Count); Assert.AreEqual(file, syncResult.InvalidFiles[0].FileName); } [TestMethod] public void TestDoomImageTitlepicWad() { SyncLibraryHandler handler = CreateSyncLibraryHandler(true); string file = "uroburos.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); var syncResult = handler.SyncManyFiles(new string[] { file }); Assert.AreEqual(1, syncResult.AddedGameFiles.Count); bool hasTitlepic = syncResult.GetTitlePic(syncResult.AddedGameFiles[0], out Image titlepic); Assert.IsTrue(hasTitlepic); Assert.AreEqual(320, titlepic.Width); Assert.AreEqual(200, titlepic.Height); } [TestMethod] public void TestLargeDoomImageTitlepicWad() { SyncLibraryHandler handler = CreateSyncLibraryHandler(true); string file = "pyrrhic.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); var syncResult = handler.SyncManyFiles(new string[] { file }); Assert.AreEqual(1, syncResult.AddedGameFiles.Count); bool hasTitlepic = syncResult.GetTitlePic(syncResult.AddedGameFiles[0], out Image titlepic); Assert.IsTrue(hasTitlepic); Assert.AreEqual(640, titlepic.Width); Assert.AreEqual(400, titlepic.Height); } [TestMethod] public void TestPngTitlepicPk3() { SyncLibraryHandler handler = CreateSyncLibraryHandler(true); string file = "joymaps1.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); var syncResult = handler.SyncManyFiles(new string[] { file }); Assert.AreEqual(1, syncResult.AddedGameFiles.Count); bool hasTitlepic = syncResult.GetTitlePic(syncResult.AddedGameFiles[0], out Image titlepic); Assert.IsTrue(hasTitlepic); Assert.AreEqual(320, titlepic.Width); Assert.AreEqual(200, titlepic.Height); } [TestMethod] public void TestTilepicZMapInfo() { SyncLibraryHandler handler = CreateSyncLibraryHandler(true); string file = "zmapinfo.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); var syncResult = handler.SyncManyFiles(new string[] { file }); Assert.AreEqual(1, syncResult.AddedGameFiles.Count); bool hasTitlepic = syncResult.GetTitlePic(syncResult.AddedGameFiles[0], out Image titlepic); Assert.IsTrue(hasTitlepic); Assert.AreEqual(478, titlepic.Width); Assert.AreEqual(313, titlepic.Height); } [TestMethod] public void TestMultipleTitlepicFiles() { SyncLibraryHandler handler = CreateSyncLibraryHandler(true); string file = "pyrrhic.zip"; File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file)); string file2 = "joymaps1.zip"; File.Copy(Path.Combine("Resources", file2), Path.Combine(s_filedir, file2)); var syncResult = handler.SyncManyFiles(new string[] { file, file2 }); Assert.AreEqual(2, syncResult.AddedGameFiles.Count); bool hasTitlepic = syncResult.GetTitlePic(syncResult.AddedGameFiles[0], out Image pyrrhicTitlepic); Assert.IsTrue(hasTitlepic); Assert.AreEqual(640, pyrrhicTitlepic.Width); Assert.AreEqual(400, pyrrhicTitlepic.Height); hasTitlepic = syncResult.GetTitlePic(syncResult.AddedGameFiles[1], out Image joymapsTitlepic); Assert.IsTrue(hasTitlepic); Assert.AreEqual(320, joymapsTitlepic.Width); Assert.AreEqual(200, joymapsTitlepic.Height); } [TestMethod] public void UnmanagedFiles() { string file = "simple.wad"; string file1 = "file1.wad"; string file2 = "file2.wad"; string fullPathFile1 = Path.Combine(Directory.GetCurrentDirectory(), s_filedir, file1); string fullPathFile2 = Path.Combine(Directory.GetCurrentDirectory(), s_filedir, file2); SyncLibraryHandler handler = CreateSyncLibraryHandler(false, FileManagement.Unmanaged); File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file1)); File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file2)); // Change directory to force ignore relative paths string dir = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(s_filedir); var syncResult = handler.SyncManyFiles(new string[] { fullPathFile1, fullPathFile2 }); Assert.AreEqual(2, syncResult.AddedGameFiles.Count); Directory.SetCurrentDirectory(dir); Assert.AreEqual(fullPathFile1, syncResult.AddedGameFiles[0].FileName); Assert.AreEqual(fullPathFile2, syncResult.AddedGameFiles[1].FileName); } [TestMethod] public void UnmanagedRelativeFiles() { string file = "simple.wad"; string file1 = "file1.wad"; string file2 = "file2.wad"; string fullPathFile1 = Path.Combine(Directory.GetCurrentDirectory(), s_filedir, file1); string fullPathFile2 = Path.Combine(Directory.GetCurrentDirectory(), s_filedir, file2); SyncLibraryHandler handler = CreateSyncLibraryHandler(false, FileManagement.Unmanaged); File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file1)); File.Copy(Path.Combine("Resources", file), Path.Combine(s_filedir, file2)); var syncResult = handler.SyncManyFiles(new string[] { fullPathFile1, fullPathFile2 }); Assert.AreEqual(2, syncResult.AddedGameFiles.Count); Assert.AreEqual(Path.Combine(s_filedir, file1), syncResult.AddedGameFiles[0].FileName); Assert.AreEqual(Path.Combine(s_filedir, file2), syncResult.AddedGameFiles[1].FileName); } [TestMethod] public void WadLumpWithBadChars() { string file1 = "pathtest.zip"; SyncLibraryHandler handler = CreateSyncLibraryHandler(false, FileManagement.Managed); File.Copy(Path.Combine("Resources", file1), Path.Combine(s_filedir, file1)); var syncResult = handler.SyncManyFiles(new string[] { file1 }); Assert.AreEqual(1, syncResult.AddedGameFiles.Count); Assert.AreEqual(0, syncResult.InvalidFiles.Count); var addedFile = syncResult.AddedGameFiles[0]; Assert.AreEqual(addedFile.Map, "MAP01"); } private SyncLibraryHandler CreateSyncLibraryHandler(bool pullTitlepic = false, FileManagement fileManagement = FileManagement.Managed) { var directories = new DirectoriesConfiguration { GameFileDirectory = new LauncherPath(s_filedir), TempDirectory = new LauncherPath(s_tempdir) }; var dateParseFormats = new string[] { "dd/M/yy", "dd/MM/yyyy", "dd MMMM yyyy" }; var syncActions = new List() { new TextFileSyncAction(new IdGamesTextFileParser(dateParseFormats).Parse), new Doom64SyncAction(database), new MapStringSyncAction(directories.TempDirectory) }; if (pullTitlepic) syncActions.Add(new TitlePicSyncAction(database, DataCache.Instance.DefaultPalette, DataCache.Instance.HexenPalette, DataCache.Instance.HereticPalette)); return new SyncLibraryHandler(database, CreateDirectoryAdapater(), directories, fileManagement, syncActions); } private static DirectoryDataSourceAdapter CreateDirectoryAdapater() { return new DirectoryDataSourceAdapter(new LauncherPath("TestSyncDir")); } } } ================================================ FILE: UnitTest/Tests/TestTextFileSyncAction.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Handlers.Sync; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; namespace UnitTest.Tests { [TestClass] public class TestTextFileSyncAction { [TestMethod] public void ApplyToGameFile_ChoosesHighestQualityAnswers() { IGameFile gameFile = new GameFile() { FileName = "banana.wad" }; Tree files = new Tree("root", new Tree("child1.txt"), new Tree("child2.txt")); var syncAction = new TextFileSyncAction(str => { switch (str) { case "child1.txt": return new IdGamesTextInfo("Child Mod", "Radley Bobbikins", DateTime.Parse("4/3/2025"), null, "DOOM2"); case "child2.txt": return new IdGamesTextInfo("Wrong name", null, null, "A fine mod.", null); default: throw new Exception($"Unexpected entry {str}"); } }); syncAction.ApplyToGameFile(gameFile, new TreeReader(files), new string[0]); // Child1's fields are preferred, because it is higher quality Assert.AreEqual("Child Mod", gameFile.Title); Assert.AreEqual("Radley Bobbikins", gameFile.Author); Assert.AreEqual(DateTime.Parse("4/3/2025"), gameFile.ReleaseDate); // Falls through to Child2 where missing from Child1 Assert.AreEqual("A fine mod.", gameFile.Description); } [TestMethod] public void ApplyToGameFile_IgnoresNonTextFiles() { IGameFile gameFile = new GameFile() { FileName = "something.wad" }; Tree files = new Tree("root", new Tree("lowqual.txt"), new Tree("highqual1.bin")); var syncAction = new TextFileSyncAction(str => { switch (str) { case "lowqual.txt": return new IdGamesTextInfo("Low quality title", null, null, null, null); case "highqual1.bin": return new IdGamesTextInfo("High Quality Mod", "Ernest Wallop", null, "Lots of description", "DOOM2"); default: throw new Exception($"Unexpected entry {str}"); } }); syncAction.ApplyToGameFile(gameFile, new TreeReader(files), new string[0]); // The high quality file got ignored because it isn't .txt Assert.IsTrue(string.IsNullOrEmpty(gameFile.Author)); Assert.IsNull(gameFile.ReleaseDate); Assert.IsTrue(string.IsNullOrEmpty(gameFile.Description)); // Low quality text file got used Assert.AreEqual("Low quality title", gameFile.Title); } [TestMethod] public void ApplyToGameFile_ChecksWADINFO() { IGameFile gameFile = new GameFile() { FileName = "potato.wad" }; Tree files = new Tree("root", new Tree("WADINFO"), new Tree("wadinfo")); var syncAction = new TextFileSyncAction(str => { switch (str) { case "WADINFO": return new IdGamesTextInfo("Wadinfo title", null, null, null, "HERETIC"); case "wadinfo": return new IdGamesTextInfo(null, "Wadinfo author", null, "WadInfo description", null); default: throw new Exception($"Unexpected entry {str}"); } }); syncAction.ApplyToGameFile(gameFile, new TreeReader(files), new string[0]); Assert.AreEqual("Wadinfo title", gameFile.Title); Assert.AreEqual("Wadinfo author", gameFile.Author); Assert.AreEqual("WadInfo description", gameFile.Description); } [TestMethod] public void ApplyToGameFile_NullOrEmptyDataDoesntOverwriteGameFile() { IGameFile gameFile = new GameFile() { FileName = "imps.wad", Author = "Fredericus", Title = "Too Many Imps", ReleaseDate = DateTime.Parse("1/7/2021"), Description = "A wad with too many imps", }; Tree files = new Tree("root", new Tree("empty.txt")); var syncAction = new TextFileSyncAction(str => new IdGamesTextInfo(null, null, null, null, null)); syncAction.ApplyToGameFile(gameFile, new TreeReader(files), new string[0]); Assert.AreEqual("Too Many Imps", gameFile.Title); Assert.AreEqual("Fredericus", gameFile.Author); Assert.AreEqual(DateTime.Parse("1/7/2021"), gameFile.ReleaseDate); Assert.AreEqual("A wad with too many imps", gameFile.Description); } } } ================================================ FILE: UnitTest/Tests/TestUpdateInfo.cs ================================================ using DoomLauncher; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Threading.Tasks; namespace UnitTest.Tests { [TestClass] public class TestUpdateInfo { [TestMethod] public async Task TestGetVersion() { ApplicationUpdate applicationUpdate = new ApplicationUpdate(TimeSpan.FromSeconds(30)); Version version = new Version("1.0.0.0"); ApplicationUpdateInfo info = await applicationUpdate.GetUpdateApplicationInfo(version); if (info != null) { Assert.IsTrue(info.Version > version); Assert.IsTrue(info.DownloadUrl.EndsWith(".zip")); info = await applicationUpdate.GetUpdateApplicationInfo(info.Version); Assert.IsNull(info); } } } } ================================================ FILE: UnitTest/Tests/TestUtil.cs ================================================ using System; using DoomLauncher; using DoomLauncher.Interfaces; using System.IO; using System.IO.Compression; using System.Linq; using Newtonsoft.Json; namespace UnitTest.Tests { static class TestUtil { public static IDataSourceAdapter CreateAdapter() { string dataSource = Path.Combine(Directory.GetCurrentDirectory(), "DoomLauncher.sqlite"); return new DbDataSourceAdapter(new SqliteDatabaseAdapter(), string.Format(@"Data Source={0}", dataSource)); } public static void CleanDatabase(IDataSourceAdapter adapter) { var dataAccess = ((DbDataSourceAdapter)adapter).DataAccess; dataAccess.ExecuteNonQuery("delete from GameProfiles"); dataAccess.ExecuteNonQuery("delete from GameFiles"); } public static bool AllFieldsEqualIgnore(T obj1, T obj2, params string[] ignore) { return AllFieldsEqual(obj1, obj2, ignore); } public static bool AllFieldsEqual(T obj1, T obj2, string[] ignore = null) { var properties = typeof(T).GetProperties(); foreach (var prop in properties) { if (ignore != null && ignore.Contains(prop.Name)) continue; var value = prop.GetValue(obj1); if (value == null) { if (prop.GetValue(obj2) != null) { throw new Exception( $"AllFieldsEqual failure: Expected {prop.Name} to be null but was {prop.GetValue(obj2)}\n{GetObjectJson(obj1)}\n{GetObjectJson(obj2)}"); return false; } } else { if (!value.Equals(prop.GetValue(obj2))) { throw new Exception( $"AllFieldsEqual failure: Expected {prop.Name} to be {value} but was {prop.GetValue(obj2)}\n{GetObjectJson(obj1)}\n{GetObjectJson(obj2)}"); return false; } } } return true; } private static string GetObjectJson(object obj) => JsonConvert.SerializeObject(obj); public static void CopyResourceFile(string filename) { DeleteResourceFile(filename); File.Copy(Path.Combine("Resources", filename), filename); } public static void DeleteResourceFile(string filename) { if (File.Exists(filename)) File.Delete(filename); } public static void ExtractResourceToDirectory(string filename, string directory) { if (Directory.Exists(directory)) Directory.Delete(directory, true); string file = Path.Combine("Resources", filename); using (ZipArchive za = ZipFile.OpenRead(file)) za.ExtractToDirectory(directory); } } } ================================================ FILE: UnitTest/Tests/TestWadReader.cs ================================================ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; using System.IO.Compression; using System.Linq; using WadReader; namespace UnitTest.Tests { [TestClass] public class TestWadReader { [TestMethod] public void ReadWadEntries() { string wadfile = "uroburos.wad"; var zip = ZipFile.OpenRead(Path.Combine("Resources", "uroburos.zip")); zip.Entries.First(x => x.FullName == wadfile).ExtractToFile(wadfile, true); using (FileStream fs = File.OpenRead(wadfile)) { WadFileReader reader = new WadFileReader(fs); var lumps = reader.ReadLumps(); Assert.IsTrue(reader.IsValid); Assert.AreEqual(WadType.PWAD, reader.WadType); Assert.AreEqual(15, lumps.Count); Assert.AreEqual("D_RUNNIN", lumps[0].Name); Assert.AreEqual("WOLF1", lumps[1].Name); Assert.AreEqual("RSKY1", lumps[2].Name); Assert.AreEqual("TITLEPIC", lumps[3].Name); Assert.AreEqual("MAP01", lumps[4].Name); Assert.AreEqual("THINGS", lumps[5].Name); Assert.AreEqual("LINEDEFS", lumps[6].Name); Assert.AreEqual("SIDEDEFS", lumps[7].Name); Assert.AreEqual("VERTEXES", lumps[8].Name); Assert.AreEqual("SEGS", lumps[9].Name); Assert.AreEqual("SSECTORS", lumps[10].Name); Assert.AreEqual("NODES", lumps[11].Name); Assert.AreEqual("SECTORS", lumps[12].Name); Assert.AreEqual("REJECT", lumps[13].Name); Assert.AreEqual("BLOCKMAP", lumps[14].Name); Assert.AreEqual(25257, lumps[0].ReadData(fs).Length); //D_RUNNIN Assert.AreEqual(66888, lumps[3].ReadData(fs).Length); //TITLEPIC Assert.AreEqual(0, lumps[4].ReadData(fs).Length); //MAP01 Assert.AreEqual(6082, lumps[14].ReadData(fs).Length); //BLOCKMAP } } [TestMethod] public void ReadInvalidWadHeaderType() { using (FileStream fs = File.OpenRead(Path.Combine("Resources", "invalid1.wad"))) { WadFileReader reader = new WadFileReader(fs); Assert.IsFalse(reader.IsValid); Assert.AreEqual(WadType.Unknown, reader.WadType); Assert.AreEqual(0, reader.ReadLumps().Count); } } [TestMethod] public void ReadInvalidWadHeaderLumps() { using (FileStream fs = File.OpenRead(Path.Combine("Resources", "invalid2.wad"))) { WadFileReader reader = new WadFileReader(fs); Assert.IsFalse(reader.IsValid); Assert.AreEqual(WadType.PWAD, reader.WadType); Assert.AreEqual(0, reader.ReadLumps().Count); } } } } ================================================ FILE: UnitTest/Tests/TestZdlParser.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using DoomLauncher.Interfaces; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; namespace UnitTest.Tests { [TestClass] public class TestZdlParser { [TestMethod] public void TestZdlParserFile() { string file = @"[zdl.save] port=gzdoom iwad=C:/ZDL/DOOM2.WAD extra=extra file0=C:/ZDL/pinnacle.zip file1=C:/ZDL/bd21testApr25.zip skill=4 warp=MAP01 [zdl.general] conflib=sunrise "; ZdlParser parser = new ZdlParser(CreateSourcePorts(), CreateIWads()); IGameFile[] gameFiles = parser.Parse(file); Assert.AreEqual(2, gameFiles.Length); Assert.AreEqual("C:/ZDL/pinnacle.zip", gameFiles[0].FileName); Assert.AreEqual("MAP01", gameFiles[0].SettingsMap); Assert.AreEqual("4", gameFiles[0].SettingsSkill); Assert.AreEqual("extra", gameFiles[0].SettingsExtraParams); Assert.AreEqual("C:/ZDL/bd21testApr25.zip", gameFiles[1].FileName); } private IEnumerable CreateIWads() { return new IIWadData[] { new IWadData { FileName = "DOOM2.WAD" }, new IWadData { FileName = "DOOM.WAD" }, new IWadData { FileName = "PLUTONIA.WAD" } }; } private IEnumerable CreateSourcePorts() { return new ISourcePortData[] { new SourcePortData { Name = "GZDoom" }, new SourcePortData { Name = "PrBoom" }, new SourcePortData { Name = "Chocolate Doom" } }; } } } ================================================ FILE: UnitTest/Tests/TestZdoomSave.cs ================================================ using DoomLauncher.SaveGame; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace UnitTest.Tests { [TestClass] public class TestZdoomSave { [TestMethod] public void TestZdoomSaveBinary() { string save1 = "zdoomsave_v1.zds"; TestUtil.CopyResourceFile(save1); ZDoomSaveGameReader reader = new ZDoomSaveGameReader(save1); Assert.AreEqual("Autosave Aug 21 13:30", reader.GetName()); } [TestMethod] public void TestZdoomSaveJson() { string save1 = "zdoomsave_v2.zds"; TestUtil.CopyResourceFile(save1); ZDoomSaveGameReader reader = new ZDoomSaveGameReader(save1); Assert.AreEqual("Autosave Aug 21 13:12", reader.GetName()); } [TestMethod] public void TestZdoomSaveJson_3_5() { string save1 = "zdoomsave_v3.zds"; TestUtil.CopyResourceFile(save1); ZDoomSaveGameReader reader = new ZDoomSaveGameReader(save1); Assert.AreEqual("Autosave Aug 28 06:25", reader.GetName()); } } } ================================================ FILE: UnitTest/Tests/TestZdoomStats.cs ================================================ using DoomLauncher; using DoomLauncher.DataSources; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.IO; using System.Linq; namespace UnitTest.Tests { [TestClass] public class TestZdoomStats { private readonly List m_args = new List(); [TestMethod] public void TestZdoomBinary() { string save1 = "zdoomsave_v1.zds"; TestUtil.DeleteResourceFile(save1); ZDoomStatsReader statsReader = CreateStatsReader(); statsReader.NewStastics += StatsReader_NewStastics; statsReader.Start(); TestUtil.CopyResourceFile(save1); statsReader.Stop(); statsReader.ReadNow(); Assert.AreEqual(2, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("e1m1", m_args[0].Statistics.MapName.ToLower()); Assert.AreEqual(5, m_args[0].Statistics.KillCount); Assert.AreEqual(6, m_args[0].Statistics.TotalKills); Assert.AreEqual(0, m_args[0].Statistics.ItemCount); Assert.AreEqual(0, m_args[0].Statistics.TotalItems); Assert.AreEqual(1, m_args[0].Statistics.SecretCount); Assert.AreEqual(3, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(25.22857f, m_args[0].Statistics.LevelTime); Assert.AreEqual("e1m2", m_args[1].Statistics.MapName.ToLower()); Assert.AreEqual(22, m_args[1].Statistics.KillCount); Assert.AreEqual(41, m_args[1].Statistics.TotalKills); Assert.AreEqual(0, m_args[1].Statistics.ItemCount); Assert.AreEqual(0, m_args[1].Statistics.TotalItems); Assert.AreEqual(2, m_args[1].Statistics.SecretCount); Assert.AreEqual(6, m_args[1].Statistics.TotalSecrets); Assert.AreEqual(79.77143f, m_args[1].Statistics.LevelTime); } [TestMethod] public void TestZdoomJson() { //this is the pre 3.5 json version that did not include items string save1 = "zdoomsave_v2.zds"; TestUtil.DeleteResourceFile(save1); ZDoomStatsReader statsReader = CreateStatsReader(); statsReader.NewStastics += StatsReader_NewStastics; statsReader.Start(); TestUtil.CopyResourceFile(save1); statsReader.Stop(); statsReader.ReadNow(); Assert.AreEqual(2, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("e1m1", m_args[0].Statistics.MapName.ToLower()); Assert.AreEqual(5, m_args[0].Statistics.KillCount); Assert.AreEqual(6, m_args[0].Statistics.TotalKills); Assert.AreEqual(0, m_args[0].Statistics.ItemCount); Assert.AreEqual(0, m_args[0].Statistics.TotalItems); Assert.AreEqual(1, m_args[0].Statistics.SecretCount); Assert.AreEqual(3, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(34.2857132f, m_args[0].Statistics.LevelTime); Assert.AreEqual(3, m_args[0].Statistics.Skill); Assert.AreEqual("e1m2", m_args[1].Statistics.MapName.ToLower()); Assert.AreEqual(25, m_args[1].Statistics.KillCount); Assert.AreEqual(41, m_args[1].Statistics.TotalKills); Assert.AreEqual(0, m_args[1].Statistics.ItemCount); Assert.AreEqual(0, m_args[1].Statistics.TotalItems); Assert.AreEqual(2, m_args[1].Statistics.SecretCount); Assert.AreEqual(6, m_args[1].Statistics.TotalSecrets); Assert.AreEqual(132.65715f, m_args[1].Statistics.LevelTime); Assert.AreEqual(3, m_args[0].Statistics.Skill); //Give the reader the existing statistics, and let it re-read the save. It should not fire the events to prevent duplicates TestUtil.DeleteResourceFile(save1); statsReader = new ZDoomStatsReader(new GameFile() { GameFileID = 1 }, Directory.GetCurrentDirectory(), m_args.Select(x => x.Statistics).ToArray()); m_args.Clear(); statsReader.NewStastics += StatsReader_NewStastics; statsReader.Start(); statsReader.ReadNow(); File.Copy(Path.Combine("Resources", save1), save1); Assert.AreEqual(0, m_args.Count); } [TestMethod] public void TestZdoomJson_3_5() { //This is to test the 3.5 save format that includes items string save1 = "zdoomsave_v3.zds"; TestUtil.DeleteResourceFile(save1); ZDoomStatsReader statsReader = CreateStatsReader(); statsReader.NewStastics += StatsReader_NewStastics; statsReader.Start(); TestUtil.CopyResourceFile(save1); statsReader.Stop(); statsReader.ReadNow(); Assert.AreEqual(2, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("e1m1", m_args[0].Statistics.MapName.ToLower()); Assert.AreEqual(5, m_args[0].Statistics.KillCount); Assert.AreEqual(6, m_args[0].Statistics.TotalKills); Assert.AreEqual(15, m_args[0].Statistics.ItemCount); Assert.AreEqual(37, m_args[0].Statistics.TotalItems); Assert.AreEqual(1, m_args[0].Statistics.SecretCount); Assert.AreEqual(3, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(40.2f, m_args[0].Statistics.LevelTime); Assert.AreEqual(3, m_args[0].Statistics.Skill); Assert.AreEqual("e1m2", m_args[1].Statistics.MapName.ToLower()); Assert.AreEqual(21, m_args[1].Statistics.KillCount); Assert.AreEqual(41, m_args[1].Statistics.TotalKills); Assert.AreEqual(8, m_args[1].Statistics.ItemCount); Assert.AreEqual(42, m_args[1].Statistics.TotalItems); Assert.AreEqual(2, m_args[1].Statistics.SecretCount); Assert.AreEqual(6, m_args[1].Statistics.TotalSecrets); Assert.AreEqual(83.37143f, m_args[1].Statistics.LevelTime); Assert.AreEqual(3, m_args[0].Statistics.Skill); //Give the reader the existing statistics, and let it re-read the save. It should not fire the events to prevent duplicates TestUtil.DeleteResourceFile(save1); statsReader = new ZDoomStatsReader(new GameFile() { GameFileID = 1 }, Directory.GetCurrentDirectory(), m_args.Select(x => x.Statistics).ToArray()); m_args.Clear(); statsReader.NewStastics += StatsReader_NewStastics; statsReader.Start(); statsReader.ReadNow(); File.Copy(Path.Combine("Resources", save1), save1); Assert.AreEqual(0, m_args.Count); } [TestMethod] public void TestZdoomJson_3_7() { // Test the 3.7 save format that changed cvars for pulling skill string save1 = "zdoomsave_v4.zds"; TestUtil.DeleteResourceFile(save1); ZDoomStatsReader statsReader = CreateStatsReader(); statsReader.NewStastics += StatsReader_NewStastics; statsReader.Start(); TestUtil.CopyResourceFile(save1); statsReader.Stop(); statsReader.ReadNow(); Assert.AreEqual(1, m_args.Count); Assert.AreEqual(0, statsReader.Errors.Length); Assert.AreEqual("e1m1", m_args[0].Statistics.MapName.ToLower()); Assert.AreEqual(21, m_args[0].Statistics.KillCount); Assert.AreEqual(29, m_args[0].Statistics.TotalKills); Assert.AreEqual(10, m_args[0].Statistics.ItemCount); Assert.AreEqual(38, m_args[0].Statistics.TotalItems); Assert.AreEqual(0, m_args[0].Statistics.SecretCount); Assert.AreEqual(3, m_args[0].Statistics.TotalSecrets); Assert.AreEqual(39.8857154846191f, m_args[0].Statistics.LevelTime); Assert.AreEqual(4, m_args[0].Statistics.Skill); //Give the reader the existing statistics, and let it re-read the save. It should not fire the events to prevent duplicates TestUtil.DeleteResourceFile(save1); statsReader = new ZDoomStatsReader(new GameFile() { GameFileID = 1 }, Directory.GetCurrentDirectory(), m_args.Select(x => x.Statistics).ToArray()); m_args.Clear(); statsReader.NewStastics += StatsReader_NewStastics; statsReader.Start(); statsReader.ReadNow(); File.Copy(Path.Combine("Resources", save1), save1); Assert.AreEqual(0, m_args.Count); } private static ZDoomStatsReader CreateStatsReader() { return new ZDoomStatsReader(new GameFile() { GameFileID = 1 }, Directory.GetCurrentDirectory(), new IStatsData[] { }); } private void StatsReader_NewStastics(object sender, NewStatisticsEventArgs e) { m_args.Add(e); } } } ================================================ FILE: UnitTest/UnitTest.csproj ================================================  Debug AnyCPU {2E593D49-B240-49BE-9E9D-5DD1250EEDF3} Library Properties UnitTest UnitTest v4.8 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages False UnitTest true full false bin\Debug\ DEBUG;TRACE prompt 4 pdbonly true bin\Release\ TRACE prompt 4 ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll ..\packages\SevenZipSharp.0.64\lib\SevenZipSharp.dll ..\packages\SharpCompress.0.31.0\lib\net461\SharpCompress.dll ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.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.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll ..\packages\System.Text.Encoding.CodePages.5.0.0\lib\net461\System.Text.Encoding.CodePages.dll False {938b529a-9afd-4ae0-9f0f-5cb9527ad2f1} DoomLauncher {15a9a8fc-096e-4458-ab7f-02386740cf4e} WadReader PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest PreserveNewest Always PreserveNewest PreserveNewest PreserveNewest Always PreserveNewest PreserveNewest False False False False if not exist $(TargetDir)x86 mkdir $(TargetDir)x86 xcopy /y /d "$(SolutionDir)DoomLauncher\bin\$(Configuration)\x86\*.*" "$(TargetDir)x86" if not exist $(TargetDir)x64 mkdir $(TargetDir)x64 xcopy /y /d "$(SolutionDir)DoomLauncher\bin\$(Configuration)\x64\*.*" "$(TargetDir)x64" 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: UnitTest/app.config ================================================  ================================================ FILE: UnitTest/packages.config ================================================  ================================================ FILE: WadReader/DoomImage/DoomImage.cs ================================================ using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; namespace WadReader { public enum ImageType { Palette, Argb } public class DoomImage { public const ushort TransparentIndex = 0xFF00; public readonly Bitmap Bitmap; public readonly ImageType ImageType; public readonly int OffsetX; public readonly int OffsetY; public int Width { get; private set; } public int Height { get; private set; } public DoomImage(Bitmap bitmap, ImageType imageType, int offsetX, int offsetY) { Bitmap = EnsureExpectedFormat(bitmap); ImageType = imageType; Width = Bitmap.Width; Height = Bitmap.Height; OffsetX = offsetX; OffsetY = offsetY; } public DoomImage(int width, int height, ImageType imageType, int offsetX, int offsetY, Color? fillColor = null) { Width = Math.Max(width, 1); Height = Math.Max(height, 1); Bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); ImageType = imageType; OffsetX = offsetX; OffsetY = offsetY; Fill(fillColor ?? Color.Transparent); } private static Bitmap EnsureExpectedFormat(Bitmap bitmap) { if (bitmap.PixelFormat == PixelFormat.Format32bppArgb) return bitmap; Bitmap copy = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb); using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(copy)) g.DrawImage(bitmap, new Rectangle(0, 0, copy.Width, copy.Height)); return copy; } public static DoomImage FromArgbBytes(int w, int h, byte[] argb, int offsetX = 0, int offsetY = 0) { int numBytes = w * h * 4; if (argb.Length != numBytes || w <= 0 || h <= 0) return null; Bitmap bitmap = new Bitmap(w, h, PixelFormat.Format32bppArgb); Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); BitmapData metadata = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat); Marshal.Copy(argb, 0, metadata.Scan0, numBytes); bitmap.UnlockBits(metadata); return new DoomImage(bitmap, ImageType.Argb, offsetX, offsetY); } public static DoomImage FromPaletteIndices(int width, int height, ushort[] indices, int offsetX, int offsetY) { if (width <= 0 || height <= 0 || indices.Length != width * height) return null; int numBytes = width * height * 4; byte[] paletteData = new byte[numBytes]; int argbIndex = 0; for (int i = 0; i < indices.Length; i++) { ushort index = indices[i]; paletteData[argbIndex] = (byte)~(index >> 8); paletteData[argbIndex + 3] = (byte)index; argbIndex += 4; } Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); BitmapData metadata = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat); Marshal.Copy(paletteData, 0, metadata.Scan0, numBytes); bitmap.UnlockBits(metadata); return new DoomImage(bitmap, ImageType.Palette, offsetX, offsetY); } public void Fill(Color color) { using (SolidBrush b = new SolidBrush(color)) using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(Bitmap)) g.FillRectangle(b, 0, 0, Bitmap.Width, Bitmap.Height); } public DoomImage PaletteToArgb(Palette palette) { if (ImageType == ImageType.Argb) return this; int numBytes = Width * Height * 4; byte[] paletteBytes = new byte[numBytes]; byte[] argbBytes = new byte[numBytes]; Rectangle rect = new Rectangle(0, 0, Bitmap.Width, Bitmap.Height); BitmapData metadata = Bitmap.LockBits(rect, ImageLockMode.ReadWrite, Bitmap.PixelFormat); Marshal.Copy(metadata.Scan0, paletteBytes, 0, numBytes); Bitmap.UnlockBits(metadata); Color[] colors = palette.DefaultLayer; int offset = 0; for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { // The first of the four bytes is the alpha, and if we have // alpha, then we have to write the RGB. Otherwise, it is // already set to transparent so our job would be done. if (paletteBytes[offset] != 0) { int index = paletteBytes[offset + 3]; Color color = colors[index]; // Apparently since it reads it as a 32-bit word, then // we need to write it in BGRA format. argbBytes[offset] = color.B; argbBytes[offset + 1] = color.G; argbBytes[offset + 2] = color.R; argbBytes[offset + 3] = 255; } offset += 4; } } DoomImage image = FromArgbBytes(Width, Height, argbBytes, OffsetX, OffsetY); if (image != null) return image; return new DoomImage(1, 1, ImageType.Argb, 0, 0); } } } ================================================ FILE: WadReader/DoomImage/Palette.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; namespace WadReader { public class Palette { public static readonly int NumColors = 256; public static readonly int ColorComponents = 3; public static readonly int BytesPerLayer = NumColors * ColorComponents; private static Palette DefaultPalette; private readonly List layers; public int Count => layers.Count; public Color[] DefaultLayer => layers[0]; private Palette(List paletteLayers) { layers = paletteLayers; } public static Palette From(byte[] data) { if (data.Length == 0 || data.Length % BytesPerLayer != 0) return null; List paletteLayers = new List(); for (int layer = 0; layer < data.Length / BytesPerLayer; layer++) { int offset = layer * BytesPerLayer; Span layerSpan = new Span(data, offset, BytesPerLayer); paletteLayers.Add(PaletteLayerFrom(layerSpan)); } return new Palette(paletteLayers); } private static Color[] PaletteLayerFrom(Span data) { Color[] paletteColors = new Color[NumColors]; int offset = 0; for (int i = 0; i < BytesPerLayer; i += ColorComponents) paletteColors[offset++] = Color.FromArgb(255, data[i], data[i + 1], data[i + 2]); return paletteColors; } public Color[] Layer(int index) => layers[index]; public static Palette GetDefaultPalette() { if (DefaultPalette != null) return DefaultPalette; byte[] data = new byte[NumColors * ColorComponents]; for (int i = 0; i < NumColors; i++) { data[i] = (byte)i; data[i + 1] = (byte)i; data[i + 2] = (byte)i; } Palette palette = From(data); if (palette == null) throw new NullReferenceException("Failed to create the default palette, shouldn't be possible"); DefaultPalette = palette; return palette; } } } ================================================ FILE: WadReader/DoomImage/PaletteReaders.cs ================================================ using System.IO; using System.Linq; namespace WadReader { public static class PaletteReaders { public static bool LikelyFlat(byte[] data) { switch (data.Length) { case 64 * 64: return true; case 128 * 128: return true; case 256 * 256: return true; case 320 * 200: // TITLE images use this size return true; default: return false; } } public static bool LikelyColumn(byte[] data) { if (data.Length < 16) return false; BinaryReader reader = new BinaryReader(new MemoryStream(data)); int width = reader.ReadInt16(); int height = reader.ReadInt16(); int offsetX = reader.ReadInt16(); int offsetY = reader.ReadInt16(); if (InvalidColumnImageDimensions(data, width, height, offsetX, offsetY)) return false; return LastColumnValid(reader, width); } public static DoomImage ReadFlat(byte[] data) { if (!FlatDimension(data.Length, out int width, out int height)) return null; ushort[] indices = data.Select(x => (ushort)x).ToArray(); return DoomImage.FromPaletteIndices(width, height, indices, 0, 0); } public static DoomImage ReadColumn(byte[] data) { try { BinaryReader reader = new BinaryReader(new MemoryStream(data)); int width = reader.ReadInt16(); int height = reader.ReadInt16(); int xOff = reader.ReadInt16(); int yOff = reader.ReadInt16(); int[] offsets = new int[width]; for (int i = 0; i < width; i++) offsets[i] = reader.ReadInt32(); ushort[] indices = new ushort[width * height]; for (int i = 0; i < indices.Length; i++) indices[i] = DoomImage.TransparentIndex; for (int col = 0; col < width; col++) { reader.BaseStream.Seek(offsets[col], SeekOrigin.Begin); int offset = 0; while (true) { int rowStart = reader.ReadByte(); if (rowStart == 0xFF) break; int indicesCount = reader.ReadByte(); reader.BaseStream.Seek(1, SeekOrigin.Current); // Skip dummy. byte[] paletteIndices = reader.ReadBytes(indicesCount); reader.BaseStream.Seek(1, SeekOrigin.Current); // Skip dummy. // Tall patch support, since we are writing up the column we expect rowStart to be greater than the last // If it's smaller or equal then add to the offset to support images greater than 254 in height if (rowStart <= offset) offset += rowStart; else offset = rowStart; int indicesOffset = (offset * width) + col; for (int i = 0; i < paletteIndices.Length; i++) { if (indicesOffset >= indices.Length) break; indices[indicesOffset] = paletteIndices[i]; indicesOffset += width; } } } return DoomImage.FromPaletteIndices(width, height, indices, xOff, yOff); } catch { return null; } } private static bool FlatDimension(int length, out int width, out int height) { if (length == 64 * 64) { width = height = 64; } else if (length == 128 * 128) { width = height = 128; } else if (length == 256 * 256) { width = height = 256; } else if (length == 320 * 200) { width = 320; height = 200; } else { width = height = 0; return false; } return true; } private static bool LargerThanMaxColumnDataSize(byte[] data, int width, int height) { // This is an upper bound on the worst case for a column. Suppose // a column has a constant pixel/no-pixel alternating sequence. // That means we will get h/2 'posts' (or h/2 + 1 if odd, so we'll // go with that since it covers all cases). int maxPosts = (height / 2) + 1; // Each post is made up of a 'header' + 'length' + 2 dummy bytes + // the length of bytes. Since each length would be 1 'index pixel', // then the largest size it can be is 5 bytes. This means we have // 5 * max posts. We add 1 to the end because the last byte has to // be the 0xFF magic number to end the column. int maxBytesPerColumn = (5 * maxPosts) + 1; int headerSize = 8 - (width * 4); return data.Length - headerSize > width * maxBytesPerColumn; } private static bool InvalidColumnImageDimensions(byte[] data, int width, int height, int offsetX, int offsetY) { return width <= 0 || width >= 4096 || height <= 0 || height >= 4096 || offsetX < -2048 || offsetX > 2048 || offsetY < -2048 || offsetY > 2048 || LargerThanMaxColumnDataSize(data, width, height); } private static bool LastColumnValid(BinaryReader reader, int width) { if (reader.BaseStream.Length > reader.BaseStream.Position + width * 4) return false; reader.BaseStream.Seek((width - 1) * 4, SeekOrigin.Current); int offset = reader.ReadInt32(); if (offset < 0 || offset >= reader.BaseStream.Length) return false; reader.BaseStream.Seek(offset, SeekOrigin.Begin); return reader.BaseStream.Length + 1 <= reader.BaseStream.Length; } } } ================================================ FILE: WadReader/FileLump.cs ================================================ using System; using System.IO; using System.Runtime.InteropServices; namespace WadReader { public class FileLump { private string m_name; public int Length => m_filelump.size; public FileLump(Stream stream) { m_filelump = Util.ReadStuctureFromStream(stream); } public FileLump(string name) { m_name = name; } public string Name { get { if (m_name == null) { int index = Array.IndexOf(m_filelump.name, (byte)0); if (index > 8 || index < 0) index = 8; m_name = System.Text.Encoding.UTF8.GetString(m_filelump.name, 0, index); } return m_name; } } public byte[] ReadData(FileStream fs) { fs.Seek(m_filelump.filepos, SeekOrigin.Begin); byte[] ret = new byte[m_filelump.size]; fs.Read(ret, 0, ret.Length); return ret; } public void ReadData(FileStream fs, byte[] data, int offset, int length) { fs.Seek(m_filelump.filepos, SeekOrigin.Begin); fs.Read(data, offset, length); } public static int FileLumpByteSize { get { return Marshal.SizeOf(typeof(filelump_t)); } } private readonly filelump_t m_filelump; private struct filelump_t { public filelump_t(int i) //useless constructor, removes annoying unused variable warnings { filepos = size = i; name = new byte[8]; } public int filepos; public int size; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] name; } } } ================================================ FILE: WadReader/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.InteropServices; // 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("WadReader")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("WadReader")] [assembly: AssemblyCopyright("")] [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("68aa5a5a-4e3a-483b-acc2-c3962c3b4590")] // 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("3.8.1.0")] [assembly: AssemblyFileVersion("3.8.1.0")] ================================================ FILE: WadReader/Util.cs ================================================ using System.IO; using System.Runtime.InteropServices; namespace WadReader { static class Util { public static T ReadStuctureFromStream(Stream stream) { byte[] bytes = new byte[Marshal.SizeOf(typeof(T))]; stream.Read(bytes, 0, bytes.Length); GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); T obj = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(),typeof(T)); handle.Free(); return obj; } } } ================================================ FILE: WadReader/WadFileReader.cs ================================================ using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; namespace WadReader { public class WadFileReader { public readonly bool IsValid; private static readonly string[] s_mapData = new string[] { "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP", "BEHAVIOR", "TEXTMAP", "ZNODES", "ENDMAP", "DIALOGUE", "SCRIPTS", }; private static readonly string[] s_importantMapData = new string[] { "BLOCKMAP", "VERTEXES", "SECTORS", "SIDEDEFS", "LINEDEFS", "SSECTORS", "NODES", "SEGS" }; private static readonly string s_mapdataRegex = @"^GL_\S+"; private readonly Stream m_stream; public WadFileReader(Stream stream) { m_stream = stream; WadHeader = new WadHeader(stream); IsValid = CheckIsValid(); } private bool CheckIsValid() { if (WadHeader.WadType == WadType.Unknown) return false; int lumpSize = WadHeader.LumpCount * FileLump.FileLumpByteSize; if (lumpSize < 0 || lumpSize > m_stream.Length) return false; return true; } public WadType WadType { get { return WadHeader.WadType; } } public List ReadLumps() { if (!IsValid) return new List(); return WadHeader.ReadLumps(); } public static List GetMapMarkerLumps(List lumps) { List ret = new List(); HashSet mapLumps = new HashSet(s_mapData); HashSet important = new HashSet(s_importantMapData); int mapLumpCount = 0, mapMarkerIndex = 0; for (int i = 0; i < lumps.Count; i++) { if (IsMapData(mapLumps, lumps[i]) && i > 0) { bool isUdmf = false; mapMarkerIndex = i - 1; while (i < lumps.Count && IsMapData(mapLumps, lumps[i])) { if (lumps[i].Name == "ENDMAP") isUdmf = true; if (important.Contains(lumps[i].Name)) mapLumpCount++; i++; } if (isUdmf || mapLumpCount > 4) ret.Add(lumps[mapMarkerIndex]); mapLumpCount = 0; i--; } } return ret; } private static bool IsMapData(HashSet mapLumps, FileLump lump) { return mapLumps.Contains(lump.Name) || Regex.IsMatch(lump.Name, s_mapdataRegex); } private WadHeader WadHeader { get; set; } } } ================================================ FILE: WadReader/WadHeader.cs ================================================ using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; namespace WadReader { public class WadHeader { private const int s_iwad = 1145132873; private const int s_pwad = 1145132880; public WadHeader(Stream stream) { if (stream.Length < Marshal.SizeOf(typeof(wadinfo_t))) { WadType = WadType.Unknown; } else { m_stream = stream; m_wadinfo = Util.ReadStuctureFromStream(m_stream); switch (m_wadinfo.identification) { case s_iwad: WadType = WadType.IWAD; break; case s_pwad: WadType = WadType.PWAD; break; default: WadType = WadType.Unknown; break; } } } public List ReadLumps() { List lumps = new List(m_wadinfo.numlumps); try { m_stream.Seek(m_wadinfo.infotableofs, SeekOrigin.Begin); for (int i = 0; i < m_wadinfo.numlumps; i++) lumps.Add(new FileLump(m_stream)); } catch { //failed, nothing to do } return lumps; } public readonly WadType WadType; public int LumpCount => m_wadinfo.numlumps; private readonly wadinfo_t m_wadinfo; private readonly Stream m_stream; private struct wadinfo_t { public wadinfo_t(int i) //useless constructor, removes annoying unused variable warnings { identification = numlumps = infotableofs = i; } public int identification; public int numlumps; public int infotableofs; }; } } ================================================ FILE: WadReader/WadReader.csproj ================================================  Debug AnyCPU {15A9A8FC-096E-4458-AB7F-02386740CF4E} Library Properties WadReader WadReader v4.8 512 true full false bin\Debug\ DEBUG;TRACE prompt 4 false pdbonly true bin\Release\ TRACE prompt 4 false ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.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 ================================================ FILE: WadReader/WadType.cs ================================================ namespace WadReader { public enum WadType { PWAD, IWAD, Unknown } } ================================================ FILE: WadReader/packages.config ================================================  ================================================ FILE: WpfControlLibrary/AssemblyInfo.cs ================================================ using System.Windows; [assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located //(used if a resource is not found in the page, // or application resource dictionaries) ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located //(used if a resource is not found in the page, // app, or any theme specific resource dictionaries) )] ================================================ FILE: WpfControlLibrary/WpfComboBox.xaml ================================================ ================================================ FILE: WpfControlLibrary/WpfComboBox.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 WpfControlLibrary { /// /// Interaction logic for UserControl1.xaml /// public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); } } } ================================================ FILE: WpfControlLibrary/WpfControlLibrary.csproj ================================================  net6.0-windows enable true ================================================ FILE: appveyor.yml ================================================ version: '{build}' configuration: Release platform: Any CPU image: Visual Studio 2022 before_build: - cmd: nuget restore build_script: - ps: | $env:DevenvPath = 'C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE' - '"%DevenvPath%\devenv.com" DoomLauncher.sln /build Release /project Setup\Setup.vdproj /projectconfig Release /Out errorFile.txt' - msbuild DoomLauncher.sln /target:UnitTest /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" test: on after_build: - '%APPVEYOR_BUILD_FOLDER%\DoomLauncherRelease\bin\Release\DoomLauncherRelease.exe %APPVEYOR_BUILD_FOLDER%' artifacts: path: ReleaseBuild/DoomLauncher*.zip name: BuiltZip