├── .github ├── dependabot.yml └── workflows │ ├── build-msix-sideload.yml │ ├── release.yml │ └── wiki.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── images │ ├── AppLogo-512.png │ ├── github-internals-viewer-repository-open-graph.png │ ├── logo.svg │ └── readme │ │ └── allocation-map-pages-and-extents.png └── internalsviewer.com │ ├── .vitepress │ ├── cache │ │ └── deps │ │ │ ├── _metadata.json │ │ │ ├── package.json │ │ │ ├── vitepress___@vue_devtools-api.js │ │ │ ├── vitepress___@vueuse_core.js │ │ │ └── vue.js │ ├── config.mts │ └── theme │ │ ├── index.ts │ │ └── style.css │ ├── .vscode │ └── settings.json │ ├── docs │ ├── components │ │ └── MarkerKey.vue │ ├── concepts │ │ └── page-header.md │ ├── introduction │ │ ├── background.md │ │ ├── connecting.md │ │ ├── database-view.md │ │ ├── getting-started.md │ │ ├── installation.md │ │ ├── page-viewer.md │ │ └── permissions.md │ ├── logo.svg │ ├── reference │ │ ├── compression.md │ │ ├── data-records.md │ │ ├── index-records.md │ │ └── page-header.md │ ├── technical-details │ │ ├── general-internals.md │ │ └── index.md │ └── tutorial │ │ ├── 0-introduction.md │ │ ├── 1-connecting-and-allocations.md │ │ ├── 2-viewing-pages.md │ │ ├── 3-indexes.md │ │ └── images │ │ ├── 01-after-initial-insert.png │ │ ├── 01-allocated-extent.png │ │ ├── 01-connect-to-database.png │ │ ├── 01-database-refresh-button.png │ │ ├── 01-decoded-data.png │ │ ├── 01-entry-points.png │ │ ├── 01-initial-view.png │ │ ├── 01-open-page.png │ │ ├── 01-page-row.png │ │ ├── 01-page-slots-more-rows.png │ │ ├── 01-page-viewer.pdn │ │ ├── 01-page-viewer.png │ │ └── 01-tooltip.png │ ├── index.md │ ├── package-lock.json │ └── package.json └── src ├── .editorconfig ├── InternalsViewer.Internals.Metadata.SourceGenerators ├── AttributeGenerator.cs ├── InternalsViewer.Internals.Metadata.SourceGenerators.csproj ├── MetadataLoaderSourceGenerator.cs └── TypeFinder.cs ├── InternalsViewer.Internals.Tests.VerificationTool ├── Helpers │ └── ConnectionStringHelper.cs ├── Indexes │ └── IndexVerification.cs ├── InternalsViewer.Internals.Tests.VerificationTool.csproj ├── Models │ └── DatabaseIndexRow.cs ├── Program.cs └── Services │ ├── IndexVerificationService.cs │ ├── ObjectPageListService.cs │ ├── ObjectService.cs │ └── TableVerificationService.cs ├── InternalsViewer.Internals.Tests ├── GlobalUsings.cs ├── Helpers │ ├── ConnectionStringHelper.cs │ ├── DatabaseDumper.cs │ ├── FilePageReader.cs │ ├── ServiceHelper.cs │ ├── TestHelpers.cs │ ├── TestLogger.cs │ └── TestServiceHost.cs ├── IntegrationTests │ ├── Providers │ │ ├── Metadata │ │ │ ├── AllocationUnitProviderTests.cs │ │ │ ├── FileProviderTests.cs │ │ │ ├── IndexStructureProviderTests.cs │ │ │ ├── ProviderTestBase.cs │ │ │ └── TableStructureProviderTests.cs │ │ └── Server │ │ │ └── BufferPoolInfoProviderTests.cs │ ├── Readers │ │ ├── BackupReaderTests.cs │ │ ├── QueryPageReaderTests.cs │ │ └── RecordReaderTests.cs │ ├── Services │ │ ├── Indexes │ │ │ └── IndexServiceTests.cs │ │ └── Loaders │ │ │ └── Engine │ │ │ └── MetadataLoaderTests.cs │ └── Test Data │ │ └── TestDatabase │ │ ├── CreateDatabase.sql │ │ └── TestDatabase.mdf ├── InternalsViewer.Internals.Tests.csproj └── UnitTests │ ├── Converters │ ├── DataConverterTests.cs │ ├── DateTimeConverterTests.cs │ ├── NumberDataConverterTests.cs │ └── StringDataConverterTests.cs │ ├── Engine │ └── Parsers │ │ ├── LogSequenceNumberParserTests.cs │ │ ├── PageAddressParserTests.cs │ │ └── PfsByteParserTests.cs │ ├── Helpers │ └── IdHelperTests.cs │ ├── Loaders │ ├── CdDataRecordLoaderTests.cs │ ├── CompressionInfoLoaderTests.cs │ └── DictionaryLoaderTests.cs │ ├── Pages │ └── PfsByteTests.cs │ ├── Readers │ ├── Headers │ │ └── PageHeaderParserTests.cs │ └── RecordReaderTests.cs │ ├── Services │ ├── BufferPoolServiceTests.cs │ ├── Loaders │ │ └── Records │ │ │ ├── Fields │ │ │ ├── LobOverflowFieldLoaderTests.cs │ │ │ ├── LobPointerFieldLoaderTests.cs │ │ │ └── LobRootFieldLoaderTests.cs │ │ │ ├── FixedVarDataRecordLoaderTests.cs │ │ │ └── FixedVarIndexRecordLoaderTests.cs │ └── Pages │ │ ├── Loaders │ │ ├── DatabaseServiceTests.cs │ │ └── TestPageHeader.cs │ │ └── Parsers │ │ ├── AllocationPageParserTests.cs │ │ ├── BootPageParserTests.cs │ │ ├── DataPageParserTests.cs │ │ ├── IamPageParserTests.cs │ │ ├── IndexPageParserTests.cs │ │ ├── LobPageParserTests.cs │ │ ├── PageParserTestsBase.cs │ │ └── PfsPageParserTests.cs │ └── Test Data │ └── Test Pages │ ├── TestDatabase_1_100_IamPage.page │ ├── TestDatabase_1_1_PfsPage.page │ ├── TestDatabase_1_25520_DataPage.page │ ├── TestDatabase_1_2_GamPage.page │ ├── TestDatabase_1_3_SGamPage.page │ ├── TestDatabase_1_6_DcmPage.page │ ├── TestDatabase_1_7_BcmPage.page │ └── TestDatabase_1_9_BootPage.page ├── InternalsViewer.Internals ├── Annotations │ ├── DataStructure.cs │ ├── DataStructureItem.cs │ ├── DataStructureItemAttribute.cs │ └── ItemType.cs ├── Connections │ ├── Backup │ │ ├── BackupConnectionFactory.cs │ │ ├── BackupConnectionType.cs │ │ └── BackupConnectionTypeConfig.cs │ ├── ConnectionTypeConfig.cs │ ├── File │ │ ├── FileConnectionFactory.cs │ │ ├── FileConnectionType.cs │ │ └── FileConnectionTypeConfig.cs │ └── Server │ │ ├── ServerConnectionConfig.cs │ │ ├── ServerConnectionFactory.cs │ │ └── ServerConnectionType.cs ├── Converters │ ├── CompressedDataConverter.cs │ ├── DataConverter.cs │ ├── DataEncoders.cs │ ├── DateTimeConverters.cs │ └── Decoder │ │ ├── DataDecoder.cs │ │ └── DecodeResult.cs ├── Engine │ ├── Address │ │ ├── LogSequenceNumber.cs │ │ ├── PageAddress.cs │ │ └── RowIdentifier.cs │ ├── Allocation │ │ ├── AllocationChain.cs │ │ ├── Enums │ │ │ ├── ChainType.cs │ │ │ ├── PfsByte.cs │ │ │ └── SpaceFree.cs │ │ ├── IamChain.cs │ │ └── PfsChain.cs │ ├── Database │ │ ├── AllocationUnit.cs │ │ ├── BufferPool.cs │ │ ├── DatabaseFile.cs │ │ ├── DatabaseSource.cs │ │ ├── DatabaseSummary.cs │ │ ├── Enums │ │ │ ├── AllocationUnitType.cs │ │ │ ├── DatabaseState.cs │ │ │ └── IndexType.cs │ │ └── FileType.cs │ ├── Indexes │ │ └── IndexNode.cs │ ├── Pages │ │ ├── AllocationPage.cs │ │ ├── AllocationUnitPage.cs │ │ ├── BootPage.cs │ │ ├── EmptyPage.cs │ │ ├── Enums │ │ │ └── PageType.cs │ │ ├── FileHeaderPage.cs │ │ ├── IamPage.cs │ │ ├── LobPage.cs │ │ ├── Page.cs │ │ ├── PageData.cs │ │ ├── PageHeader.cs │ │ └── PfsPage.cs │ ├── Parsers │ │ ├── LogSequenceNumberParser.cs │ │ ├── PageAddressParser.cs │ │ └── PfsByteParser.cs │ └── Records │ │ ├── Blob │ │ ├── BlobPointers │ │ │ ├── BlobChildLink.cs │ │ │ ├── BlobField.cs │ │ │ ├── BlobFieldType.cs │ │ │ ├── OverflowField.cs │ │ │ ├── PointerField.cs │ │ │ └── RootField.cs │ │ ├── BlobRecord.cs │ │ └── BlobType.cs │ │ ├── CdRecordType │ │ ├── ColumnDescriptor.cs │ │ ├── ColumnDescriptorFlag.cs │ │ ├── CompressedDataRecord.cs │ │ ├── CompressedRecordField.cs │ │ ├── CompressedRecordType.cs │ │ ├── CompressionInfo.cs │ │ ├── CompressionInfoStructure.cs │ │ ├── CompressionType.cs │ │ ├── Dictionary.cs │ │ └── DictionaryEntry.cs │ │ ├── Data │ │ ├── DataRecord.cs │ │ └── SparseVector.cs │ │ ├── Field.cs │ │ ├── FixedVarRecordType │ │ ├── FixedVarRecord.cs │ │ └── FixedVarRecordField.cs │ │ ├── Index │ │ ├── IndexRecord.cs │ │ ├── IndexTypes.cs │ │ └── NodeType.cs │ │ ├── Record.cs │ │ ├── RecordField.cs │ │ ├── RecordHelpers.cs │ │ └── RecordType.cs ├── Extensions │ ├── ByteExtensions.cs │ └── EnumerableExtensions.cs ├── GlobalUsings.cs ├── Helpers │ ├── History.cs │ ├── IdHelpers.cs │ ├── PageHelpers.cs │ ├── SqlTypeHelpers.cs │ └── StringHelpers.cs ├── Interfaces │ ├── Connections │ │ ├── IConnectionProvider.cs │ │ ├── IConnectionType.cs │ │ └── IConnectionTypeFactory.cs │ ├── Engine │ │ └── IAllocationChain.cs │ ├── MetadataProviders │ │ ├── IBufferPoolInfoProvider.cs │ │ └── ITransactionLogProvider.cs │ ├── Readers │ │ ├── IPageReader.cs │ │ └── Internals │ │ │ └── IRecordReader.cs │ └── Services │ │ ├── Loaders │ │ ├── Chains │ │ │ ├── IAllocationChainService.cs │ │ │ ├── IIamChainService.cs │ │ │ └── IPfsChainService.cs │ │ ├── Engine │ │ │ ├── IDatabaseService.cs │ │ │ └── IMetadataLoader.cs │ │ └── Pages │ │ │ ├── IPageLoader.cs │ │ │ ├── IPageParser.cs │ │ │ └── IPageService.cs │ │ └── Records │ │ └── IRecordService.cs ├── InternalsViewer.Internals.csproj ├── Metadata │ ├── ColumnAttribute.cs │ ├── Helpers │ │ └── StructureExtensions.cs │ ├── HobtEntryPoint.cs │ ├── Internals │ │ ├── InternalMetadata.cs │ │ ├── Tables │ │ │ ├── InternalAllocationUnit.cs │ │ │ ├── InternalColumn.cs │ │ │ ├── InternalColumnLayout.cs │ │ │ ├── InternalEntityObject.cs │ │ │ ├── InternalFile.cs │ │ │ ├── InternalIndex.cs │ │ │ ├── InternalIndexColumn.cs │ │ │ ├── InternalObject.cs │ │ │ └── InternalRowSet.cs │ │ ├── TypeInfo.cs │ │ └── TypeInfoExtensions.cs │ ├── Structures │ │ ├── ColumnStructure.cs │ │ ├── IndexColumnStructure.cs │ │ ├── IndexStructure.cs │ │ ├── Structure.cs │ │ ├── StructureType.cs │ │ └── TableStructure.cs │ └── TransactionLogEntry.cs ├── Providers │ ├── Metadata │ │ ├── AllocationUnitProvider.cs │ │ ├── FileProvider.cs │ │ ├── IndexStructureProvider.cs │ │ └── TableStructureProvider.cs │ └── Server │ │ └── BufferPoolInfoProvider.cs ├── Readers │ ├── Internals │ │ └── RecordReader.cs │ └── Pages │ │ ├── DataFilePageReader.cs │ │ ├── PageReader.cs │ │ └── QueryPageReader.cs ├── ServiceRegistration.cs ├── Services │ ├── BufferPoolService.cs │ ├── Indexes │ │ └── IndexService.cs │ ├── Loaders │ │ ├── Chains │ │ │ ├── AllocationChainService.cs │ │ │ ├── IamChainService.cs │ │ │ └── PfsChainService.cs │ │ ├── Compression │ │ │ ├── CompressionInfoLoader.cs │ │ │ └── DictionaryLoader.cs │ │ ├── Engine │ │ │ ├── DatabaseService.cs │ │ │ ├── InternalTableConstants.cs │ │ │ └── MetadataLoader.cs │ │ └── Records │ │ │ ├── BlobFixedVarRecordLoader.cs │ │ │ ├── Cd │ │ │ └── CdDataRecordLoader.cs │ │ │ ├── Fields │ │ │ ├── LobOverflowFieldLoader.cs │ │ │ ├── LobPointerFieldLoader.cs │ │ │ └── LobRootFieldLoader.cs │ │ │ ├── FixedVar │ │ │ ├── FixedVarDataRecordLoader.cs │ │ │ ├── FixedVarIndexRecordLoader.cs │ │ │ └── FixedVarRecordLoader.cs │ │ │ └── SparseVectorLoader.cs │ ├── Pages │ │ ├── Loaders │ │ │ └── PageLoader.cs │ │ ├── PageService.cs │ │ └── Parsers │ │ │ ├── AllocationPageParser.cs │ │ │ ├── BootPageParser.cs │ │ │ ├── DataPageParser.cs │ │ │ ├── EmptyPageParser.cs │ │ │ ├── FileHeaderPageParser.cs │ │ │ ├── IamPageParser.cs │ │ │ ├── IndexPageParser.cs │ │ │ ├── LobPageParser.cs │ │ │ ├── PageHeaderParser.cs │ │ │ ├── PageParser.cs │ │ │ └── PfsPageParser.cs │ └── Records │ │ └── RecordService.cs ├── SqlCommands.cs └── TransactionLog │ ├── LogData.cs │ └── LogMonitor.cs ├── InternalsViewer.UI.App ├── Activation │ ├── ActivationHandler.cs │ ├── DefaultActivationHandler.cs │ └── IActivationHandler.cs ├── App.xaml ├── App.xaml.cs ├── Assets │ ├── AppIcon16.bmp │ ├── AppIcon16.png │ ├── AppLogo-16.svg │ ├── AppLogo-512.png │ ├── AppLogo.png │ ├── AppLogo.svg │ ├── Bookmark.svg │ ├── Database.svg │ ├── DatabaseFile.svg │ ├── Index32.png │ ├── InternalsViewer.ico │ ├── TabIcons │ │ ├── Database.png │ │ ├── Database16.png │ │ ├── Database32.png │ │ ├── DatabaseTabIcon.svg │ │ ├── IndexTabIcon.svg │ │ ├── Page.png │ │ ├── Page16.png │ │ ├── Page32.png │ │ └── PageTabIcon.svg │ └── Untitled.png ├── Controls │ ├── Allocation │ │ ├── AllocationControl.xaml │ │ ├── AllocationControl.xaml.cs │ │ ├── AllocationLayerGrid.xaml │ │ ├── AllocationLayerGrid.xaml.cs │ │ ├── AllocationRenderer.cs │ │ └── PfsRenderer.cs │ ├── Connections │ │ ├── BackupFileConnectionControl.xaml │ │ ├── BackupFileConnectionControl.xaml.cs │ │ ├── ConnectTile.xaml │ │ ├── ConnectTile.xaml.cs │ │ ├── HeaderTile.xaml │ │ └── HeaderTile.xaml.cs │ ├── CopyButton.xaml │ ├── CopyButton.xaml.cs │ ├── CopyTextBlock.xaml │ ├── CopyTextBlock.xaml.cs │ ├── CursorUserControl.cs │ ├── ExceptionDialog.xaml │ ├── ExceptionDialog.xaml.cs │ ├── Index │ │ ├── IndexControl.xaml │ │ ├── IndexControl.xaml.cs │ │ ├── RecordGrid.xaml │ │ └── RecordGrid.xaml.cs │ ├── Page │ │ ├── HexViewControl.xaml │ │ ├── HexViewControl.xaml.cs │ │ ├── LabelTextBox.xaml │ │ ├── LabelTextBox.xaml.cs │ │ ├── MarkerDataTable.xaml │ │ ├── MarkerDataTable.xaml.cs │ │ ├── MarkerTreeView.xaml │ │ └── MarkerTreeView.xaml.cs │ ├── PageAddressTextBox.xaml │ ├── PageAddressTextBox.xaml.cs │ ├── PasswordDialog.xaml │ └── PasswordDialog.xaml.cs ├── GlobalUsings.cs ├── Helpers │ ├── CollectionExtensions.cs │ ├── ColourHelpers.cs │ ├── ConnectionHelper.cs │ ├── Converters │ │ ├── ColorToSolidColorBrushValueConverter.cs │ │ ├── PageAddressToStringConverter.cs │ │ └── RecordValueConverter.cs │ ├── FileHelpers.cs │ ├── LayoutHelpers.cs │ ├── RuntimeHelper.cs │ └── Selectors │ │ ├── MarkerTemplateSelector.cs │ │ └── SlotTemplateSelector.cs ├── Images │ ├── LargeTile.scale-100.png │ ├── LargeTile.scale-125.png │ ├── LargeTile.scale-150.png │ ├── LargeTile.scale-200.png │ ├── LargeTile.scale-400.png │ ├── SmallTile.scale-100.png │ ├── SmallTile.scale-125.png │ ├── SmallTile.scale-150.png │ ├── SmallTile.scale-200.png │ ├── SmallTile.scale-400.png │ ├── SplashScreen.scale-100.png │ ├── SplashScreen.scale-125.png │ ├── SplashScreen.scale-150.png │ ├── SplashScreen.scale-200.png │ ├── SplashScreen.scale-400.png │ ├── Square150x150Logo.scale-100.png │ ├── Square150x150Logo.scale-125.png │ ├── Square150x150Logo.scale-150.png │ ├── Square150x150Logo.scale-200.png │ ├── Square150x150Logo.scale-400.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-16.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-24.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-256.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-32.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-48.png │ ├── Square44x44Logo.altform-unplated_targetsize-16.png │ ├── Square44x44Logo.altform-unplated_targetsize-24.png │ ├── Square44x44Logo.altform-unplated_targetsize-256.png │ ├── Square44x44Logo.altform-unplated_targetsize-32.png │ ├── Square44x44Logo.altform-unplated_targetsize-48.png │ ├── Square44x44Logo.scale-100.png │ ├── Square44x44Logo.scale-125.png │ ├── Square44x44Logo.scale-150.png │ ├── Square44x44Logo.scale-200.png │ ├── Square44x44Logo.scale-400.png │ ├── Square44x44Logo.targetsize-16.png │ ├── Square44x44Logo.targetsize-24.png │ ├── Square44x44Logo.targetsize-256.png │ ├── Square44x44Logo.targetsize-32.png │ ├── Square44x44Logo.targetsize-48.png │ ├── StoreLogo.scale-100.png │ ├── StoreLogo.scale-125.png │ ├── StoreLogo.scale-150.png │ ├── StoreLogo.scale-200.png │ ├── StoreLogo.scale-400.png │ ├── Wide310x150Logo.scale-100.png │ ├── Wide310x150Logo.scale-125.png │ ├── Wide310x150Logo.scale-150.png │ ├── Wide310x150Logo.scale-200.png │ └── Wide310x150Logo.scale-400.png ├── InternalsViewer.UI.App.csproj ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Messages │ ├── ConnectMessages.cs │ ├── ExceptionMessage.cs │ ├── NavigateMessage.cs │ └── OpenPageMessage.cs ├── Models │ ├── AllocationLayer.cs │ ├── AllocationUnit.cs │ ├── Connections │ │ ├── RecentConnection.cs │ │ └── ServerConnectionSettings.cs │ ├── DatabaseFile.cs │ ├── ExtentAllocation.cs │ ├── Index │ │ ├── IndexPageModel.cs │ │ ├── IndexRecordFieldModel.cs │ │ └── IndexRecordModel.cs │ ├── MarkStyle.cs │ ├── Marker.cs │ ├── Page │ │ └── DecodePair.cs │ ├── PageBookmark.cs │ ├── PageSlot.cs │ └── SettingsOptions.cs ├── Package.StoreAssociation.xml ├── Package.appxmanifest ├── Properties │ ├── PublishProfiles │ │ ├── win-x64.pubxml │ │ └── win-x86.pubxml │ └── launchSettings.json ├── Services │ ├── Markers │ │ ├── MarkStyleProvider.cs │ │ └── MarkerBuilder.cs │ └── SettingsService.cs ├── Styles │ ├── CommonStyles.xaml │ └── Markers │ │ ├── MarkerStylesDark.xaml │ │ └── MarkerStylesLight.xaml ├── ViewModels │ ├── Allocation │ │ ├── AllocationLayerBuilder.cs │ │ ├── AllocationLayerGridViewModel.cs │ │ ├── AllocationOverViewModel.cs │ │ └── CalibrationBuilder.cs │ ├── Connections │ │ ├── BackupFileConnectionViewModel.cs │ │ ├── ConnectFileViewModel.cs │ │ └── ConnectServerViewModel.cs │ ├── Database │ │ └── DatabaseTabViewModel.cs │ ├── Index │ │ └── IndexTabViewModel.cs │ ├── MainViewModel.cs │ ├── Page │ │ ├── HexControlViewModel.cs │ │ └── PageTabViewModel.cs │ └── Tabs │ │ ├── TabType.cs │ │ └── TabViewModel.cs ├── Views │ ├── Connect │ │ ├── ConnectBackupPage.xaml │ │ ├── ConnectBackupPage.xaml.cs │ │ ├── ConnectFilePage.xaml │ │ ├── ConnectFilePage.xaml.cs │ │ ├── ConnectServerPage.xaml │ │ ├── ConnectServerPage.xaml.cs │ │ ├── ConnectStartPage.xaml │ │ ├── ConnectStartPage.xaml.cs │ │ ├── ConnectView.xaml │ │ └── ConnectView.xaml.cs │ ├── DatabaseView.xaml │ ├── DatabaseView.xaml.cs │ ├── IndexView.xaml │ ├── IndexView.xaml.cs │ ├── PageView.xaml │ ├── PageView.xaml.cs │ ├── SettingsPage.xaml │ └── SettingsPage.xaml.cs ├── app.manifest └── appsettings.json ├── InternalsViewer.ndproj ├── InternalsViewer.sln ├── InternalsViewer.sln.DotSettings └── nuget.config /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "nuget" 4 | directory: "/" # Location of package manifests 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build application and create GitHub release 2 | on: 3 | push: 4 | tags: [ 'v*.*.*' ] 5 | workflow_dispatch: 6 | 7 | jobs: 8 | build: 9 | uses: ./.github/workflows/build-msix-sideload.yml 10 | secrets: inherit 11 | 12 | release: 13 | needs: build 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v2 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Download artifacts 23 | uses: actions/download-artifact@v4 24 | with: 25 | path: artifacts 26 | 27 | - name: List downloaded files 28 | run: ls -R artifacts 29 | 30 | - name: Zip x86 package 31 | run: | 32 | zip -r artifacts/internals-viewer-msix-x86.zip artifacts/msix-package-x86/ 33 | 34 | - name: Zip x64 package 35 | run: | 36 | zip -r artifacts/internals-viewer-msix-x64.zip artifacts/msix-package-x64/ 37 | 38 | - name: Create release 39 | run: | 40 | gh release create ${{ github.ref }} artifacts/internals-viewer-*.zip 41 | env: 42 | GITHUB_TOKEN: ${{ github.TOKEN }} 43 | shell: bash 44 | -------------------------------------------------------------------------------- /.github/workflows/wiki.yml: -------------------------------------------------------------------------------- 1 | name: Deploy static content to Pages 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | concurrency: 15 | group: 'pages' 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | deploy: 20 | environment: 21 | name: github-pages 22 | url: ${{ steps.deployment.outputs.page_url }} 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | - name: Set up Node 28 | uses: actions/setup-node@v3 29 | with: 30 | node-version: 18 31 | cache: 'npm' 32 | cache-dependency-path: ./docs/internalsviewer.com/package-lock.json 33 | 34 | - name: Install dependencies 35 | run: npm install 36 | working-directory: ./docs/internalsviewer.com/ 37 | 38 | - name: Build 39 | run: npm run docs:build 40 | working-directory: ./docs/internalsviewer.com/ 41 | 42 | - name: Setup Pages 43 | uses: actions/configure-pages@v3 44 | - name: Upload artifact 45 | uses: actions/upload-pages-artifact@v2 46 | with: 47 | # Upload dist repository 48 | path: './docs/internalsviewer.com/.vitepress/dist' 49 | - name: Deploy to GitHub Pages 50 | id: deployment 51 | uses: actions/deploy-pages@v2 52 | -------------------------------------------------------------------------------- /docs/images/AppLogo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/images/AppLogo-512.png -------------------------------------------------------------------------------- /docs/images/github-internals-viewer-repository-open-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/images/github-internals-viewer-repository-open-graph.png -------------------------------------------------------------------------------- /docs/images/readme/allocation-map-pages-and-extents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/images/readme/allocation-map-pages-and-extents.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/.vitepress/cache/deps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /docs/internalsviewer.com/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import { h } from 'vue' 3 | import type { Theme } from 'vitepress' 4 | import DefaultTheme from 'vitepress/theme' 5 | import './style.css' 6 | 7 | export default { 8 | extends: DefaultTheme, 9 | Layout: () => { 10 | return h(DefaultTheme.Layout, null, { 11 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 12 | }) 13 | }, 14 | enhanceApp({ app, router, siteData }) { 15 | // ... 16 | } 17 | } satisfies Theme 18 | -------------------------------------------------------------------------------- /docs/internalsviewer.com/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "colour", 4 | "VARCHAR" 5 | ] 6 | } -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/components/MarkerKey.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/concepts/page-header.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/concepts/page-header.md -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/introduction/background.md: -------------------------------------------------------------------------------- 1 | # Background 2 | 3 | In 2006 I started looking into SQL Server internals to get a better understanding of what was going on inside of a database. SQL Server isn't a black box where what happens inside is a mystery. There's a huge amount of information on what it is doing but to understand it requires knowledge about the internal architecture. What is a clustered index? What is a heap? What is the difference between a DATETIME and SMALLDATETIME? What is the effect on my table structure if a field is NULL vs NOT NULL, or CHAR vs VARCHAR? 4 | 5 | To get answers to these questions and to learn about internals if you want to see it and experiment you have to start digging around using system views and undocumented commands. 6 | 7 | -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/introduction/connecting.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/introduction/connecting.md -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/introduction/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | outline: deep 3 | --- 4 | 5 | # Getting started 6 | 7 | ## Requirements 8 | 9 | Internals Viewer will run on Windows 10/11 (version 17763.0 or higher). 10 | 11 | SQL Server 2022 or 2019 is required when connecting to a SQL Server database or file. 12 | 13 | `sysadmin` permissions are required for a SQL Server connection, see [Permissions](/docs/introduction/permissions). 14 | 15 | ## Installation 16 | 17 | The easiest way to install Internals Viewer is to get it from the Microsoft Store. 18 | 19 | 21 | 22 | 23 | 24 | To install manually see [Installation](/docs/introduction/installation.md) 25 | 26 | ## Connecting 27 | 28 | When you open the application the Start page will bring up the different options to connect. 29 | 30 | - Click on Connect to SQL Server 31 | - Set Instance Name to the name of the SQL Server 32 | - Choose either Active Directory Integrated or SQL Password for the Authentication type 33 | - For Database either type in the name of the database or expand the drop down list to see a list of databases on the server 34 | - Click Connect 35 | 36 | -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/introduction/installation.md: -------------------------------------------------------------------------------- 1 | # Manual Installation 2 | 3 | The releases on GitHub are built from its source code. 4 | 5 | The application is packaged as an .msix file. Windows will only install a package that has been signed. The version published in the Microsoft Store is signed with a Microsoft certificate as it has been through a verification process. 6 | 7 | The version on Github uses a self-signing certificate that needs to be installed first before the application is installed. 8 | 9 | The script Install.ps1 installs the certificate and then installs the .msix package. 10 | 11 | Steps: 12 | 13 | 1. Download the latest release artifacts from the [Releases](https://github.com/danny-sg/internals-viewer/releases) page 14 | 2. Extract the files to a folder and navigate to `\internals-viewer-msix-platform\artifacts\msix-package-platform\InternalsViewer.UI.App_version\` 15 | 3. Run `powershell -ExecutionPolicy Bypass -File Install.ps1` 16 | - You will be prompted to install the certificate. Accept prompts to continue. -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/introduction/page-viewer.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/introduction/page-viewer.md -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/introduction/permissions.md: -------------------------------------------------------------------------------- 1 | # Permissions 2 | 3 | `sysadmin` permissions are required for a SQL Server connection. 4 | 5 | Permissions are not required to read .mdf or .bak files other than file read permissions. 6 | 7 | Internals Viewer uses the `DBCC PAGE` command to connect to online databases. This is the only command used as everything is done from the page data. `DBCC PAGE` requires `syadmin` permissions due to the nature of the command - it can read anything in the database regardless of permissions. 8 | -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/reference/compression.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Compression 6 | 7 | ## Compression Info 8 | 9 | |Key|Name|Description| 10 | |---|----|-----------| 11 | |00|Header 12 | |00|Page Modification Count 13 | |00|Size 14 | |00|Length 15 | |00|Anchor Record 16 | |00|Dictionary 17 | 18 | ## Dictionary 19 | 20 | |Key|Name|Description| 21 | |---|----|-----------| 22 | |00|Entry Count 23 | |00|Dictionary Entry Offset Array 24 | |00|Dictionary Value 25 | -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/reference/index-records.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Data Records 6 | 7 | ## FixedVar Format 8 | 9 | |Key|Name|Description| 10 | |---|----|-----------| 11 | |00|Status Bits A 12 | |00|Null Bitmap 13 | |00|Column Count 14 | |00|Variable Length Column Count 15 | |00|Variable Length Column Offset Array 16 | |00|Fixed Length Value 17 | |00|Variable Length Value 18 | |00|Uniquifier 19 | |00|RID 20 | |00|Down Page Pointer 21 | -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/technical-details/general-internals.md: -------------------------------------------------------------------------------- 1 | # Undocumented DBCC Commands 2 | 3 | List all DBCC commands: 4 | 5 | ```sql 6 | DBCC TRACEON(2588) 7 | DBCC HELP ('?') 8 | 9 | -- Then DBCC HELP for the command syntax, e.g. 10 | DBCC HELP ('READPAGE') 11 | ``` 12 | 13 | Reference - https://www.sqlskills.com/blogs/paul/dbcc-writepage/ 14 | 15 | # System Base Tables 16 | 17 | See - https://learn.microsoft.com/en-us/sql/relational-databases/system-tables/system-base-tables 18 | 19 | If you connect to SQL Server using a [Dedicated Administrator Connection (DAC)](https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/diagnostic-connection-for-database-administrators) you can query these tables. 20 | 21 | To connect on a DAC connection prefix the server name with `admin:`. 22 | 23 | Only one DAC connection is allowed at a time. 24 | 25 | # sp_helptext 26 | 27 | The system stored procedures, views, DMVs etc. have underlying SQL that can be viewed using `sp_helptext`. 28 | 29 | For example `sys.objects` is a view based on a hidden base table, `sys.objects$`. -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/technical-details/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/technical-details/index.md -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/0-introduction.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | In this tutorial we'll create a database from scratch. We'll run DDL (Data Definition Language) SQL to create objects, then run DML (Data Manipulation Language) SQL to insert and modify data to see how we can view it and the effects on the database storage internals. 4 | 5 | This tutorial will cover: 6 | 7 | - Part 1 8 | - Connecting to a database 9 | - Viewing object allocations in the database 10 | - How to find object entry points 11 | 12 | - Part 2 13 | - Viewing pages and records 14 | - How to view records 15 | - How pages are linked together 16 | 17 | - Part 3 18 | - Navigating indexes 19 | - Heaps, clustered indexes, and non-clustered indexes -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/2-viewing-pages.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/2-viewing-pages.md -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/3-indexes.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/3-indexes.md -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-after-initial-insert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-after-initial-insert.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-allocated-extent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-allocated-extent.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-connect-to-database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-connect-to-database.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-database-refresh-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-database-refresh-button.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-decoded-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-decoded-data.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-entry-points.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-entry-points.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-initial-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-initial-view.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-open-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-open-page.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-page-row.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-page-row.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-page-slots-more-rows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-page-slots-more-rows.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-page-viewer.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-page-viewer.pdn -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-page-viewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-page-viewer.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/docs/tutorial/images/01-tooltip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-tooltip.png -------------------------------------------------------------------------------- /docs/internalsviewer.com/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | hero: 5 | name: "Internals Viewer" 6 | text: "Tutorials and documentation" 7 | # tagline: My great project tagline 8 | actions: 9 | - theme: brand 10 | text: Getting started 11 | link: /docs/introduction/getting-started 12 | - theme: alt 13 | text: Tutorial 14 | link: /docs/tutorial/0-introduction 15 | 16 | --- 17 | 18 | -------------------------------------------------------------------------------- /docs/internalsviewer.com/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "docs:dev": "vitepress dev", 4 | "docs:build": "vitepress build", 5 | "docs:preview": "vitepress preview" 6 | }, 7 | "devDependencies": { 8 | "vitepress": "^1.0.0-rc.41" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Metadata.SourceGenerators/AttributeGenerator.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace InternalsViewer.Internals.Metadata.SourceGenerators; 4 | 5 | [Generator] 6 | public class AttributeGenerator : IIncrementalGenerator 7 | { 8 | /// 9 | public void Initialize(IncrementalGeneratorInitializationContext context) 10 | { 11 | context.RegisterPostInitializationOutput(i => 12 | { 13 | var attributeSource = @"// 14 | #nullable enable 15 | 16 | using System; 17 | using System.Data; 18 | using System.Diagnostics.CodeAnalysis; 19 | 20 | #pragma warning disable CS9113 21 | 22 | namespace InternalsViewer.Internals.Generators; 23 | 24 | [ExcludeFromCodeCoverage] 25 | [AttributeUsage(AttributeTargets.Class)] 26 | public class InternalsMetadataAttribute : Attribute; 27 | 28 | [ExcludeFromCodeCoverage] 29 | [AttributeUsage(AttributeTargets.Property)] 30 | public class InternalsMetadataColumnAttribute(string Name, int ColumnId, SqlDbType DataType, int DataLength, short LeafOffset, short NullBit) 31 | : Attribute; 32 | "; 33 | 34 | i.AddSource("InternalsMetadataAttribute.g.cs", attributeSource); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Metadata.SourceGenerators/InternalsViewer.Internals.Metadata.SourceGenerators.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | true 6 | Latest 7 | Debug;Release;Installer 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Metadata.SourceGenerators/TypeFinder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Microsoft.CodeAnalysis; 4 | using Microsoft.CodeAnalysis.CSharp.Syntax; 5 | 6 | namespace InternalsViewer.Internals.Metadata.SourceGenerators; 7 | 8 | public class TypeFinder : ISyntaxReceiver 9 | { 10 | public List LoadTypes { get; } = new(); 11 | 12 | public void OnVisitSyntaxNode(SyntaxNode syntaxNode) 13 | { 14 | if (syntaxNode is RecordDeclarationSyntax type) 15 | { 16 | if (type.AttributeLists 17 | .SelectMany(s=>s.Attributes) 18 | .Any(a => a.GetText().ToString().EndsWith("InternalsMetadata"))) 19 | { 20 | LoadTypes.Add(type); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests.VerificationTool/Helpers/ConnectionStringHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace InternalsViewer.Internals.Tests.VerificationTool.Helpers; 4 | 5 | internal class ConnectionStringHelper 6 | { 7 | public static string GetConnectionString(string name) 8 | { 9 | var builder = new ConfigurationBuilder().AddUserSecrets(); 10 | 11 | var configuration = builder.Build(); 12 | 13 | return configuration.GetConnectionString(name) ?? string.Empty; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests.VerificationTool/Indexes/IndexVerification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace InternalsViewer.Internals.Tests.VerificationTool.Indexes; 8 | 9 | internal class IndexVerification 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests.VerificationTool/InternalsViewer.Internals.Tests.VerificationTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | ec4f112d-d125-4a6d-83ad-34a84213b825 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using InternalsViewer.Internals.Engine.Address; 2 | global using InternalsViewer.Internals.Engine.Allocation.Enums; 3 | global using InternalsViewer.Internals.Engine.Pages; 4 | global using InternalsViewer.Internals.Metadata.Structures; 5 | global using Xunit; 6 | global using Xunit.Abstractions; -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/Helpers/ConnectionStringHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace InternalsViewer.Internals.Tests.Helpers; 4 | internal class ConnectionStringHelper 5 | { 6 | public static string GetConnectionString(string name) 7 | { 8 | var builder = new ConfigurationBuilder().AddUserSecrets(); 9 | 10 | var configuration = builder.Build(); 11 | 12 | return configuration.GetConnectionString(name) ?? string.Empty; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/Helpers/TestHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Tests.Helpers; 2 | 3 | public static class TestHelpers 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/Helpers/TestServiceHost.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | 3 | namespace InternalsViewer.Internals.Tests.Helpers; 4 | 5 | internal class TestServiceHost 6 | { 7 | private readonly IHost host; 8 | 9 | public TestServiceHost() 10 | { 11 | host = GetHost(); 12 | } 13 | 14 | private IHost GetHost() 15 | { 16 | return 17 | Host 18 | .CreateDefaultBuilder() 19 | .UseContentRoot(AppContext.BaseDirectory) 20 | .ConfigureServices((context, services) => 21 | { 22 | services.RegisterServices(); 23 | }).Build(); 24 | } 25 | 26 | internal T GetService() where T : class 27 | { 28 | var service = host.Services.GetService(typeof(T)); 29 | 30 | return service as T; 31 | } 32 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/IntegrationTests/Providers/Metadata/AllocationUnitProviderTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Providers.Metadata; 2 | 3 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Providers.Metadata; 4 | 5 | public class AllocationUnitProviderTests(ITestOutputHelper testOutput) : ProviderTestBase(testOutput) 6 | { 7 | [Fact] 8 | public async Task Can_Get_AllocationUnits() 9 | { 10 | var metadata = await GetMetadata(); 11 | 12 | var allocationUnits = AllocationUnitProvider.GetAllocationUnits(metadata); 13 | 14 | Assert.NotEmpty(allocationUnits); 15 | } 16 | 17 | [Theory] 18 | [InlineData(72057594060734464)] 19 | public async Task Can_Get_AllocationUnit(long allocationUnitId) 20 | { 21 | var metadata = await GetMetadata(); 22 | 23 | var source = metadata.AllocationUnits.First(a=> a.AllocationUnitId == allocationUnitId); 24 | var result = AllocationUnitProvider.GetAllocationUnit(metadata, source); 25 | 26 | Assert.NotNull(result); 27 | } 28 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/IntegrationTests/Providers/Metadata/FileProviderTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Providers.Metadata; 2 | 3 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Providers.Metadata; 4 | 5 | public class FileProviderTests(ITestOutputHelper testOutput) : ProviderTestBase(testOutput) 6 | { 7 | [Fact] 8 | public async Task Can_Load_And_Parse_Metadata() 9 | { 10 | var metadata = await GetMetadata(); 11 | 12 | var files = FileProvider.GetFiles(metadata); 13 | 14 | Assert.NotEmpty(files); 15 | } 16 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/IntegrationTests/Providers/Metadata/IndexStructureProviderTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Metadata.Helpers; 2 | using InternalsViewer.Internals.Providers.Metadata; 3 | 4 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Providers.Metadata; 5 | 6 | public class IndexStructureProviderTests(ITestOutputHelper testOutput) : ProviderTestBase(testOutput) 7 | { 8 | [Theory] 9 | //[InlineData(72057594054049792)] 10 | //[InlineData(72057594054115328)] 11 | [InlineData(72057594054049792)] 12 | public async Task Can_Get_IndexStructure(long allocationUnitId) 13 | { 14 | var metadata = await GetMetadata(); 15 | 16 | var structure = IndexStructureProvider.GetIndexStructure(metadata, allocationUnitId); 17 | 18 | TestOutput.WriteLine(structure.ToDetailString()); 19 | 20 | Assert.NotEmpty(structure.Columns); 21 | } 22 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/IntegrationTests/Providers/Server/BufferPoolInfoProviderTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Providers.Server; 2 | using InternalsViewer.Internals.Tests.Helpers; 3 | 4 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Providers.Server; 5 | 6 | public class BufferPoolInfoProviderTests 7 | { 8 | private static BufferPoolInfoProvider GetProvider() 9 | { 10 | var connectionString = ConnectionStringHelper.GetConnectionString("local"); 11 | 12 | var provider = new BufferPoolInfoProvider(connectionString); 13 | 14 | return provider; 15 | } 16 | 17 | [Fact] 18 | public async Task Can_Get_Buffer_Pool_Entries() 19 | { 20 | var provider = GetProvider(); 21 | 22 | var bufferPoolEntries = await provider.GetBufferPoolEntries("AdventureWorks2022"); 23 | 24 | Assert.NotNull(bufferPoolEntries.Clean); 25 | Assert.NotNull(bufferPoolEntries.Dirty); 26 | } 27 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/IntegrationTests/Readers/QueryPageReaderTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Connections.File; 2 | using InternalsViewer.Internals.Engine.Database; 3 | using InternalsViewer.Internals.Engine.Pages.Enums; 4 | using InternalsViewer.Internals.Readers.Pages; 5 | using InternalsViewer.Internals.Tests.Helpers; 6 | 7 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Readers; 8 | 9 | public class QueryPageReaderTests(ITestOutputHelper testOutputHelper) 10 | { 11 | public ITestOutputHelper TestOutputHelper { get; set; } = testOutputHelper; 12 | 13 | [Fact] 14 | public async Task Can_Read_Database_Page() 15 | { 16 | var connectionString = ConnectionStringHelper.GetConnectionString("local"); 17 | 18 | var reader = new QueryPageReader(connectionString); 19 | 20 | var result = await reader.Read("TestDatabase", new PageAddress(1, 1)); 21 | 22 | Assert.NotNull(result); 23 | } 24 | 25 | [Fact] 26 | public async Task Can_Read_Mdf_Page() 27 | { 28 | var service = ServiceHelper.CreatePageService(TestOutputHelper); 29 | 30 | var connection = FileConnectionFactory.Create(c => c.Filename = "./IntegrationTests/Test Data/TestDatabase.mdf"); 31 | 32 | var result = await service.GetPage(new DatabaseSource(connection), 33 | new PageAddress(1, 9)); 34 | 35 | Assert.Equal(PageType.Boot, result.PageHeader.PageType); 36 | } 37 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/IntegrationTests/Services/Indexes/IndexServiceTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Services.Indexes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using InternalsViewer.Internals.Connections.File; 8 | using InternalsViewer.Internals.Engine.Database; 9 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Engine; 10 | using InternalsViewer.Internals.Services.Records; 11 | using InternalsViewer.Internals.Tests.Helpers; 12 | using InternalsViewer.Internals.Readers.Internals; 13 | using InternalsViewer.Internals.Services.Loaders.Engine; 14 | using Microsoft.Extensions.Logging; 15 | 16 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Services.Indexes; 17 | 18 | public class IndexServiceTests(ITestOutputHelper testOutput) 19 | { 20 | public ITestOutputHelper TestOutput { get; } = testOutput; 21 | 22 | [Fact] 23 | public async Task Can_Get_Index_Nodes() 24 | { 25 | var serviceHost = new TestServiceHost(); 26 | 27 | var service = serviceHost.GetService(); 28 | 29 | var connection = FileConnectionFactory.Create(c => c.Filename = "./IntegrationTests/Test Data/TestDatabase/TestDatabase.mdf"); 30 | 31 | var databaseService = serviceHost.GetService(); 32 | 33 | var database = await databaseService.LoadAsync("TestDatabase", connection); 34 | 35 | var result = await service.GetNodes(database, new PageAddress(1,1688)); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/IntegrationTests/Test Data/TestDatabase/TestDatabase.mdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.Internals.Tests/IntegrationTests/Test Data/TestDatabase/TestDatabase.mdf -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Engine/Parsers/LogSequenceNumberParserTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Parsers; 2 | 3 | namespace InternalsViewer.Internals.Tests.UnitTests.Engine.Parsers; 4 | 5 | public class LogSequenceNumberParserTests 6 | { 7 | [Theory] 8 | [InlineData("0:0:0", 0, 0, 0)] 9 | [InlineData("(0:0:0)", 0, 0, 0)] 10 | [InlineData("1:2:3", 1, 2, 3)] 11 | [InlineData("(1:2:3)", 1, 2, 3)] 12 | [InlineData("(53:24384:559)", 53, 24384, 559)] 13 | [InlineData("00000035:000075B8:0002", 53, 30136, 2)] 14 | public void Can_Parse_Log_Sequence_Number(string value, int virtualLogFile, int fileOffset, int recordSequence) 15 | { 16 | var logSequenceNumber = LogSequenceNumberParser.Parse(value); 17 | 18 | Assert.Equal(virtualLogFile, logSequenceNumber.VirtualLogFile); 19 | Assert.Equal(fileOffset, logSequenceNumber.FileOffset); 20 | Assert.Equal(recordSequence, logSequenceNumber.RecordSequence); 21 | } 22 | 23 | [Theory] 24 | [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0, 0, 0)] 25 | public void Can_Parse_Binary_Log_Sequence_Number(byte[] value, int virtualLogFile, int fileOffset, int recordSequence) 26 | { 27 | var logSequenceNumber = LogSequenceNumberParser.Parse(value); 28 | 29 | Assert.Equal(virtualLogFile, logSequenceNumber.VirtualLogFile); 30 | Assert.Equal(fileOffset, logSequenceNumber.FileOffset); 31 | Assert.Equal(recordSequence, logSequenceNumber.RecordSequence); 32 | } 33 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Engine/Parsers/PageAddressParserTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Parsers; 2 | 3 | namespace InternalsViewer.Internals.Tests.UnitTests.Engine.Parsers; 4 | 5 | public class PageAddressParserTests 6 | { 7 | [Theory] 8 | [InlineData("1:9", 1, 9)] 9 | [InlineData("(1:9)", 1, 9)] 10 | [InlineData("(01:09)", 1, 9)] 11 | [InlineData("2:1000", 2, 1000)] 12 | [InlineData("0x000000000000", 0, 0)] 13 | [InlineData("0x521A00000100", 1, 6738)] 14 | [InlineData("0x140000000100", 1, 20)] 15 | [InlineData("0x111111111111", 4369, 286331153)] 16 | [InlineData("0xDF0200000100", 1, 735)] 17 | public void Can_Parse_Page_Address(string value, int expectedFileId, int expectedPageId) 18 | { 19 | var pageAddress = PageAddressParser.Parse(value); 20 | 21 | Assert.Equal(expectedFileId, pageAddress.FileId); 22 | Assert.Equal(expectedPageId, pageAddress.PageId); 23 | } 24 | 25 | [Theory] 26 | [InlineData("1")] 27 | [InlineData("1-10")] 28 | [InlineData("1:ABC")] 29 | [InlineData("0x00")] 30 | public void Invalid_Format_Throws_Exception(string value) 31 | { 32 | Assert.Throws(() => PageAddressParser.Parse(value)); 33 | } 34 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Helpers/IdHelperTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Helpers; 2 | 3 | namespace InternalsViewer.Internals.Tests.UnitTests.Helpers; 4 | 5 | public class IdHelperTests 6 | { 7 | [Theory] 8 | [InlineData(97, 256, 72057594044284928)] 9 | [InlineData(7, 2, 562949953880064)] 10 | [InlineData(7, 0, 458752)] 11 | public void Can_Get_AllocationUnitId_From_ObjectId_IndexId(int objectId, int indexId, long expected) 12 | { 13 | var result = IdHelpers.GetAllocationUnitId(objectId, indexId); 14 | 15 | Assert.Equal(expected, result); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Readers/RecordReaderTests.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Tests.UnitTests.Readers; 2 | public class RecordReaderTests 3 | { 4 | //[Fact] 5 | //public Task Can_Read_Table() 6 | //{ 7 | 8 | //} 9 | } 10 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/BufferPoolServiceTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.MetadataProviders; 2 | using InternalsViewer.Internals.Services; 3 | using Moq; 4 | 5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services; 6 | 7 | public class BufferPoolServiceTests 8 | { 9 | [Fact] 10 | public async Task Can_Get_Buffer_Pool() 11 | { 12 | var provider = new Mock(); 13 | 14 | var results = 15 | ( 16 | Clean: new List { new(1, 100), new(1, 102) }, 17 | Dirty: new List { new(1, 200), new(1, 202) } 18 | ); 19 | 20 | provider.Setup(p => p.GetBufferPoolEntries("TestDatabase")) 21 | .ReturnsAsync(results); 22 | 23 | var service = new BufferPoolService(provider.Object); 24 | 25 | var bufferPool = await service.GetBufferPool("TestDatabase"); 26 | 27 | Assert.Equivalent(results.Clean, bufferPool.CleanPages); 28 | Assert.Equivalent(results.Dirty, bufferPool.DirtyPages); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Loaders/Records/Fields/LobOverflowFieldLoaderTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 2 | using InternalsViewer.Internals.Helpers; 3 | using InternalsViewer.Internals.Services.Loaders.Records.Fields; 4 | 5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Loaders.Records.Fields; 6 | 7 | public class LobOverflowFieldLoaderTests 8 | { 9 | private const string TestData = "02 00 00 00 01 00 00 00 23 48 00 00 40 1F 00 00 4D 03 00 00 01 00 00 00"; 10 | 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// DBCC Page output: 16 | /// 17 | /// Column3 = [BLOB Inline Root] Slot 0 Column 3 Offset 0x1f51 Length 24 Length (physical) 24 18 | /// 19 | /// Level = 0 Unused = 0 UpdateSeq = 1 20 | /// TimeStamp = 1210253312 Type = 2 21 | /// Link 0 22 | /// 23 | /// Size = 8000 RowId = (1:845:0) 24 | /// 25 | [Fact] 26 | public void Can_Load_Overflow_Field() 27 | { 28 | var data = TestData.ToByteArray(); 29 | 30 | var field = LobOverflowFieldLoader.Load(data, 0); 31 | 32 | Assert.Equal(BlobFieldType.RowOverflow, field.PointerType); 33 | Assert.Equal(0, field.Level); 34 | Assert.Equal((uint)1210253312, field.Timestamp); 35 | Assert.Equal(1, field.UpdateSeq); 36 | Assert.Equal(8000, field.Length); 37 | Assert.Equal(new RowIdentifier(1, 845, 0), field.Links[0].RowIdentifier); 38 | } 39 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Loaders/Records/Fields/LobPointerFieldLoaderTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 2 | using InternalsViewer.Internals.Helpers; 3 | using InternalsViewer.Internals.Services.Loaders.Records.Fields; 4 | 5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Loaders.Records.Fields; 6 | 7 | public class LobPointerFieldLoaderTests 8 | { 9 | private const string TestData = "00 00 35 F2 00 00 00 00 10 23 00 00 01 00 01 00"; 10 | 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// DBCC Page output: 16 | /// 17 | /// Col3 = [Textpointer] Slot 0 Column 3 Offset 0x15 Length 16 Length (physical) 16 18 | /// 19 | /// TextTimeStamp = 4063559680 RowId = (1:8976:1) 20 | /// 21 | /// 22 | [Fact] 23 | public void Can_Load_Pointer_Field() 24 | { 25 | var data = TestData.ToByteArray(); 26 | 27 | var field = LobPointerFieldLoader.Load(data, 0); 28 | 29 | Assert.Equal(BlobFieldType.LobPointer, field.PointerType); 30 | Assert.Equal(4063559680, field.Timestamp); 31 | Assert.Equal(new RowIdentifier(1, 8976, 1), field.Links[0].RowIdentifier); 32 | } 33 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Loaders/Records/Fields/LobRootFieldLoaderTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 2 | using InternalsViewer.Internals.Helpers; 3 | using InternalsViewer.Internals.Services.Loaders.Records.Fields; 4 | 5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Loaders.Records.Fields; 6 | 7 | public class LobRootFieldLoaderTests 8 | { 9 | private const string TestData = "04 00 00 00 01 00 00 00 29 00 00 00 40 1F 00 00 58 03 00 00 01 00 00 00"; 10 | 11 | /// 12 | /// 13 | /// 14 | /// 15 | /// DBCC Page output: 16 | /// 17 | /// Column4 = [BLOB Inline Root] Slot 0 Column 4 Offset 0x43 Length 24 Length (physical) 24 18 | /// 19 | /// Level = 0 Unused = 0 UpdateSeq = 1 20 | /// TimeStamp = 2686976 Type = 4 21 | /// Link 0 22 | /// 23 | /// Size = 8000 RowId = (1:856:0) 24 | /// 25 | [Fact] 26 | public void Can_Load_Root_Field() 27 | { 28 | var data = TestData.ToByteArray(); 29 | 30 | var field = LobRootFieldLoader.Load(data, 0); 31 | 32 | Assert.Equal(BlobFieldType.LobRoot, field.PointerType); 33 | Assert.Equal(0, field.Level); 34 | Assert.Equal((uint)2686976, field.Timestamp); 35 | Assert.Equal(1, field.UpdateSeq); 36 | 37 | Assert.Equal(new RowIdentifier(1, 856, 0), field.Links[0].RowIdentifier); 38 | Assert.Equal(8000, field.Links[0].Length); 39 | } 40 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Loaders/TestPageHeader.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Loaders; 2 | 3 | public class TestPageHeader 4 | { 5 | public static Dictionary HeaderValues { get; set; } = new() 6 | { 7 | {"m_slotCnt", "1"}, 8 | {"m_freeCnt", "2"}, 9 | {"m_freeData", "3"}, 10 | {"m_type", "8"}, 11 | {"m_level", "0"}, 12 | {"pminlen", "1"}, 13 | {"Metadata: IndexId", "1"}, 14 | {"Metadata: AllocUnitId", "6488064"}, 15 | {"Metadata: ObjectId", "99"}, 16 | {"Metadata: PartitionId", "0"}, 17 | {"m_reservedCnt", "1"}, 18 | {"m_xactReserved", "2"}, 19 | {"m_tornBits", "-273531820"}, 20 | {"m_pageId", "(1:2)"}, 21 | {"m_lsn", "(53:29745:7)"}, 22 | {"m_flagBits", "0x200"}, 23 | {"m_prevPage", "(0:0)"}, 24 | {"m_nextPage", "(0:0)"} 25 | }; 26 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/AllocationPageParserTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Pages.Enums; 2 | using InternalsViewer.Internals.Services.Pages.Parsers; 3 | 4 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers; 5 | 6 | public class AllocationPageParserTests(ITestOutputHelper testOutput) 7 | : PageParserTestsBase(testOutput) 8 | { 9 | [Theory] 10 | [InlineData(AllocationPage.FirstGamPage, PageType.Gam)] 11 | [InlineData(AllocationPage.FirstSgamPage, PageType.Sgam)] 12 | [InlineData(AllocationPage.FirstDcmPage, PageType.Dcm)] 13 | [InlineData(AllocationPage.FirstBcmPage, PageType.Bcm)] 14 | public async Task Can_Parse_Allocation_Page(int pageId, PageType pageType) 15 | { 16 | var pageData = await GetPageData(new PageAddress(1, pageId)); 17 | 18 | var parser = new AllocationPageParser(); 19 | 20 | var allocationPage = parser.Parse(pageData); 21 | 22 | Assert.True(allocationPage.PageHeader.PageType == pageType); 23 | } 24 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/IamPageParserTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Pages.Enums; 2 | using InternalsViewer.Internals.Services.Pages.Parsers; 3 | 4 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers; 5 | 6 | public class IamPageParserTests(ITestOutputHelper testOutput) 7 | : PageParserTestsBase(testOutput) 8 | { 9 | [Theory] 10 | [InlineData(100)] 11 | public async Task Can_Parse_Iam_Page(int pageId) 12 | { 13 | var pageData = await GetPageData(new PageAddress(1, pageId)); 14 | 15 | var parser = new IamPageParser(); 16 | 17 | var page = parser.Parse(pageData); 18 | 19 | Assert.Equal(PageType.Iam, page.PageHeader.PageType); 20 | Assert.Equal(new PageAddress(1, 0), page.StartPage); 21 | 22 | Assert.Equal(new PageAddress(1, 99), page.SinglePageSlots[0]); 23 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[1]); 24 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[2]); 25 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[3]); 26 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[4]); 27 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[5]); 28 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[6]); 29 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[7]); 30 | } 31 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/IndexPageParserTests.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers; 2 | 3 | public class IndexPageParserTests(ITestOutputHelper testOutput) 4 | : PageParserTestsBase(testOutput) 5 | { 6 | public async Task Can_Parse_Index_Page() 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/LobPageParserTests.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers; 2 | 3 | public class LobPageParserTests(ITestOutputHelper testOutput) 4 | : PageParserTestsBase(testOutput) 5 | { 6 | public async Task Can_Parse_Lob_Page() 7 | { 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/PageParserTestsBase.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Database; 2 | using InternalsViewer.Internals.Services.Pages.Loaders; 3 | using InternalsViewer.Internals.Tests.Helpers; 4 | 5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers; 6 | 7 | public abstract class PageParserTestsBase(ITestOutputHelper testOutput) 8 | { 9 | public ITestOutputHelper TestOutput { get; } = testOutput; 10 | 11 | protected DatabaseSource Database { get; set; } 12 | 13 | protected async Task GetPageData(PageAddress pageAddress) 14 | { 15 | var filePath = "./UnitTests/Test Data/Test Pages/"; 16 | 17 | var reader = new FilePageReader(filePath); 18 | 19 | var pageLoader = new PageLoader(); 20 | 21 | return await pageLoader.Load(Database, pageAddress); 22 | } 23 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/PfsPageParserTests.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Services.Pages.Parsers; 2 | 3 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers; 4 | 5 | public class PfsPageParserTests(ITestOutputHelper testOutput) 6 | : PageParserTestsBase(testOutput) 7 | { 8 | [Fact] 9 | public async Task Can_Parse_Pfs_Page() 10 | { 11 | var pageData = await GetPageData(new PageAddress(1, 1)); 12 | 13 | var parser = new PfsPageParser(); 14 | 15 | var page = parser.Parse(pageData); 16 | 17 | Assert.Equal(new PfsByte { IsAllocated = true, PageSpaceFree = SpaceFree.OneHundredPercent, IsIam = false, IsMixed = false }, 18 | page.PfsBytes[0]); 19 | 20 | Assert.Equal(new PfsByte { IsAllocated = true, PageSpaceFree = SpaceFree.OneHundredPercent, IsIam = false, IsMixed = false }, 21 | page.PfsBytes[1]); 22 | 23 | Assert.Equal(new PfsByte { IsAllocated = true, PageSpaceFree = SpaceFree.Empty, IsIam = true, IsMixed = true }, 24 | page.PfsBytes[100]); 25 | } 26 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Annotations/DataStructureItem.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Annotations; 2 | 3 | /// 4 | /// Data Structure annotations that can be made against a Data Structure object 5 | /// 6 | public class DataStructureItem 7 | { 8 | public ItemType ItemType { get; set; } 9 | 10 | public int Offset { get; set; } = -1; 11 | 12 | public int Length { get; set; } = -1; 13 | 14 | public string Name { get; set; } = string.Empty; 15 | 16 | public int Index { get; set; } = -1; 17 | 18 | public string Prefix { get; set; } = string.Empty; 19 | 20 | public List Tags { get; set; } = new(); 21 | 22 | public bool IsVisible { get; set; } = true; 23 | } 24 | 25 | public class PropertyItem : DataStructureItem 26 | { 27 | public string PropertyName { get; set; } = string.Empty; 28 | } 29 | 30 | public class ValueItem : DataStructureItem 31 | { 32 | public object? Value { get; set; } 33 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Annotations/DataStructureItemAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Annotations; 2 | 3 | /// 4 | /// Custom attribute to store mark properties 5 | /// 6 | [AttributeUsage(AttributeTargets.Property)] 7 | public class DataStructureItemAttribute(ItemType itemType, string description) 8 | : Attribute 9 | { 10 | public string Name { get; set; } = description; 11 | 12 | public ItemType ItemType { get; set; } = itemType; 13 | 14 | public DataStructureItemAttribute(ItemType itemType) 15 | : this(itemType, string.Empty) 16 | { 17 | } 18 | 19 | public bool IsVisible { get; set; } = true; 20 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/Backup/BackupConnectionFactory.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.Connections; 2 | 3 | namespace InternalsViewer.Internals.Connections.Backup; 4 | 5 | public class BackupConnectionFactory : IConnectionTypeFactory 6 | { 7 | public string Identifier => "Backup"; 8 | 9 | public static IConnectionType Create(Action configDelegate) 10 | { 11 | var config = new BackupConnectionTypeConfig(); 12 | 13 | configDelegate(config); 14 | 15 | throw new NotImplementedException(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/Backup/BackupConnectionType.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.Connections; 2 | using InternalsViewer.Internals.Interfaces.Readers; 3 | 4 | namespace InternalsViewer.Internals.Connections.Backup; 5 | 6 | public class BackupConnectionType(IPageReader pageReader, string name): IConnectionType 7 | { 8 | public string Identifier => "Backup"; 9 | 10 | public string Name { get; set; } = name; 11 | 12 | public IPageReader PageReader { get; } = pageReader; 13 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/Backup/BackupConnectionTypeConfig.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Connections.Backup; 2 | 3 | public class BackupConnectionTypeConfig() : ConnectionTypeConfig 4 | { 5 | public string Filename { get; set; } = string.Empty; 6 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/ConnectionTypeConfig.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Connections; 2 | 3 | public abstract class ConnectionTypeConfig; -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/File/FileConnectionFactory.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.Connections; 2 | using InternalsViewer.Internals.Readers.Pages; 3 | 4 | namespace InternalsViewer.Internals.Connections.File; 5 | 6 | public class FileConnectionFactory : IConnectionTypeFactory 7 | { 8 | public string Identifier => "File"; 9 | 10 | public static IConnectionType Create(Action configDelegate) 11 | { 12 | var config = new FileConnectionTypeConfig(); 13 | 14 | configDelegate(config); 15 | 16 | var name = System.IO.Path.GetFileNameWithoutExtension(config.Filename); 17 | 18 | return new FileConnectionType(new DataFilePageReader(config.Filename), name); 19 | } 20 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/File/FileConnectionType.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.Connections; 2 | using InternalsViewer.Internals.Interfaces.Readers; 3 | 4 | namespace InternalsViewer.Internals.Connections.File; 5 | 6 | public class FileConnectionType(IPageReader pageReader, string name) : IConnectionType 7 | { 8 | public IPageReader PageReader { get; } = pageReader; 9 | 10 | public string Name { get; set; } = name; 11 | 12 | public string Identifier => "File"; 13 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/File/FileConnectionTypeConfig.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Connections.File; 2 | 3 | public class FileConnectionTypeConfig: ConnectionTypeConfig 4 | { 5 | public string Filename { get; set; } = string.Empty; 6 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/Server/ServerConnectionConfig.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Connections.Server; 2 | 3 | public class ServerConnectionConfig : ConnectionTypeConfig 4 | { 5 | public string ConnectionString { get; set; } = string.Empty; 6 | 7 | public string Name { get; set; } = string.Empty; 8 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/Server/ServerConnectionFactory.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.Connections; 2 | using InternalsViewer.Internals.Readers.Pages; 3 | using Microsoft.Data.SqlClient; 4 | 5 | namespace InternalsViewer.Internals.Connections.Server; 6 | 7 | public class ServerConnectionFactory : IConnectionTypeFactory 8 | { 9 | public string Identifier => "Server"; 10 | 11 | public static IConnectionType Create(Action config) 12 | { 13 | var c = new ServerConnectionConfig(); 14 | 15 | config.Invoke(c); 16 | var connectionStringBuilder = new SqlConnectionStringBuilder(c.ConnectionString); 17 | 18 | var name = connectionStringBuilder.InitialCatalog ?? c.Name; 19 | 20 | return new ServerConnectionType(new QueryPageReader(c.ConnectionString), name); 21 | } 22 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Connections/Server/ServerConnectionType.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.Connections; 2 | using InternalsViewer.Internals.Interfaces.Readers; 3 | 4 | namespace InternalsViewer.Internals.Connections.Server; 5 | 6 | public class ServerConnectionType(IPageReader pageReader, string name) : IConnectionType 7 | { 8 | public string Identifier => "Server"; 9 | 10 | public string Name { get; set; } = name; 11 | 12 | public IPageReader PageReader { get; } = pageReader; 13 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Converters/Decoder/DecodeResult.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Converters.Decoder; 2 | 3 | public record DecodeResult(string DataType, string Value) 4 | { 5 | public string DataType { get; set; } = DataType; 6 | 7 | public string Value { get; set; } = Value; 8 | } 9 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Address/LogSequenceNumber.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Address; 2 | 3 | public readonly record struct LogSequenceNumber(int VirtualLogFile, int FileOffset, short RecordSequence) 4 | { 5 | public const int Size = sizeof(int) + sizeof(int) + sizeof(short); 6 | 7 | public override string ToString() => $"({VirtualLogFile}:{FileOffset}:{RecordSequence})"; 8 | 9 | public string ToBinaryString() 10 | { 11 | return $"{VirtualLogFile:X8}:{FileOffset:X8}:{RecordSequence:X4}"; 12 | } 13 | 14 | public decimal ToDecimal() 15 | { 16 | return decimal.Parse($"{VirtualLogFile}{FileOffset:0000000000}{RecordSequence:00000}"); 17 | } 18 | 19 | public decimal ToDecimalFileOffsetOnly() 20 | { 21 | return decimal.Parse($"{VirtualLogFile}{FileOffset:0000000000}"); 22 | } 23 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Address/PageAddress.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace InternalsViewer.Internals.Engine.Address; 4 | 5 | /// 6 | /// Page Address that gives a unique address for a page in a database 7 | /// 8 | public record struct PageAddress(short FileId, int PageId) 9 | { 10 | public static readonly PageAddress Empty = new(); 11 | 12 | /// 13 | /// Size of a Page Address is 6 bytes (2 byte File Id + 4 byte Page Id) 14 | /// 15 | public const int Size = sizeof(short) + sizeof(int); 16 | 17 | /// 18 | /// File Id for the Page Address 19 | /// 20 | public short FileId { get; } = FileId; 21 | 22 | /// 23 | /// Page Id for the Page Address 24 | /// 25 | public int PageId { get; } = PageId; 26 | 27 | /// 28 | /// Extent (block of 8 pages) for the Page Address 29 | /// 30 | public readonly int Extent => (PageId - 1) / 8; 31 | 32 | public override string ToString() 33 | { 34 | return string.Format(CultureInfo.CurrentCulture, "({0}:{1})", FileId, PageId); 35 | } 36 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Allocation/Enums/ChainType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Allocation.Enums; 2 | 3 | /// 4 | /// Allocation chain type 5 | /// 6 | public enum ChainType 7 | { 8 | /// 9 | /// If the next page in the chain is based on a bitmap interval 10 | /// 11 | Interval, 12 | /// 13 | /// If the next page in the chain is based on a linked list in the Page Header 14 | /// 15 | Linked 16 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Allocation/Enums/SpaceFree.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Allocation.Enums; 2 | 3 | public enum SpaceFree : byte 4 | { 5 | Empty = 0x00, 6 | FiftyPercent = 0x01, // 1%-50% 7 | EightyPercent = 0x02, // 51%-80% 8 | NinetyFivePercent = 0x03, // 81%-95% 9 | OneHundredPercent = 0x04 // 96-100% 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Allocation/PfsChain.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Allocation.Enums; 2 | using InternalsViewer.Internals.Engine.Pages; 3 | 4 | namespace InternalsViewer.Internals.Engine.Allocation; 5 | 6 | /// 7 | /// PFS (Page Free Space) Page Chain 8 | /// 9 | /// 10 | /// Collection of the PFS Pages in a database. 11 | /// 12 | /// Each PFS page covers 8088 pages = 64MB meaning the number of PFS pages per file = (file size / 64MB) 13 | /// 14 | /// 15 | /// 16 | public class PfsChain 17 | { 18 | public List PfsPages { get; } = new(); 19 | 20 | /// 21 | /// Gets the PFS status for a given page 22 | /// 23 | public PfsByte GetPageStatus(int page) 24 | { 25 | // How many pages into the PFS chain is the page 26 | var pfsPageIndex = page / PfsPage.PfsInterval; 27 | 28 | // Where in the byte map is the page 29 | var pfsByteIndex = page % PfsPage.PfsInterval; 30 | 31 | // Check the PFS byte exists 32 | var result = PfsPages.Count > pfsPageIndex && PfsPages[pfsPageIndex].PfsBytes.Count > pfsByteIndex 33 | ? PfsPages[pfsPageIndex].PfsBytes[pfsByteIndex] 34 | : PfsByte.Unknown; 35 | 36 | return result; 37 | } 38 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Database/BufferPool.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | 3 | namespace InternalsViewer.Internals.Engine.Database; 4 | 5 | /// 6 | /// Set of pages in the server's buffer bool 7 | /// 8 | public record BufferPool(List CleanPages, List DirtyPages) 9 | { 10 | /// 11 | /// Gets the clean page addresses. 12 | /// 13 | public List CleanPages { get; } = CleanPages; 14 | 15 | /// 16 | /// Gets the dirty page addresses. 17 | /// 18 | public List DirtyPages { get; } = DirtyPages; 19 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Database/DatabaseFile.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace InternalsViewer.Internals.Engine.Database; 3 | 4 | /// 5 | /// SQL Server Database File 6 | /// 7 | /// 8 | public record DatabaseFile(short FileId) 9 | { 10 | public short FileId { get; set; } = FileId; 11 | 12 | public short FileGroupId { get; set; } 13 | 14 | public string Name { get; set; } = string.Empty; 15 | 16 | public FileType FileType { get; set; } 17 | 18 | public string PhysicalName { get; set; } = string.Empty; 19 | 20 | public long TotalPages { get; set; } 21 | 22 | public long UsedPages { get; set; } 23 | 24 | public float TotalMb => TotalPages * 512 / 1024F; 25 | 26 | public float UsedMb => UsedPages * 512 / 1024F; 27 | 28 | public string FileName => PhysicalName[(PhysicalName.LastIndexOf('\\') + 1)..]; 29 | 30 | public int Size { get; set; } 31 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Database/DatabaseSummary.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Database.Enums; 2 | 3 | namespace InternalsViewer.Internals.Engine.Database; 4 | 5 | public class DatabaseSummary 6 | { 7 | public short DatabaseId { get; set; } 8 | 9 | public string Name { get; set; } = string.Empty; 10 | 11 | public DatabaseState State { get; set; } 12 | 13 | public byte CompatibilityLevel { get; set; } 14 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Database/Enums/AllocationUnitType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Database.Enums; 2 | 3 | /// 4 | /// Allocation Unit Types 5 | /// 6 | /// 7 | public enum AllocationUnitType : byte 8 | { 9 | Dropped = 0, 10 | 11 | /// 12 | /// In-row data (all data types, except LOB data types) 13 | /// 14 | InRowData = 1, 15 | 16 | /// 17 | /// Large object (LOB) data (text, ntext, image, xml, large value types, and CLR user-defined types) 18 | /// 19 | LargeObjectData = 2, 20 | 21 | RowOverflowData = 3 22 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Database/Enums/DatabaseState.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Database.Enums; 2 | 3 | /// 4 | /// Database States 5 | /// 6 | /// 7 | public enum DatabaseState : byte 8 | { 9 | Online = 0, 10 | Restoring = 1, 11 | Recovering = 2, 12 | RecoveryPending = 3, 13 | Suspect = 4, 14 | Emergency = 5, 15 | Offline = 6, 16 | Copying = 7, 17 | OfflineSecondary = 10 18 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Database/Enums/IndexType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Database.Enums; 2 | 3 | /// 4 | /// Index Types 5 | /// 6 | /// 7 | public enum IndexType : byte 8 | { 9 | Heap = 0, 10 | Clustered = 1, 11 | NonClustered = 2, 12 | Xml = 3, 13 | Spatial = 4, 14 | ClusteredColumnStore = 5, 15 | NonClusteredColumnStore = 6, 16 | NonClusteredHash = 7 17 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Database/FileType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Database; 2 | 3 | /// 4 | /// File Types 5 | /// 6 | /// 7 | /// Source: SELECT * FROM sys.syspalvalues ft WHERE ft.class = 'DBFT' 8 | /// 9 | public enum FileType : byte 10 | { 11 | Rows = 0, 12 | Log = 1, 13 | Filestream = 2, 14 | FilestreamLog = 3, 15 | FullText = 4 16 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Indexes/IndexNode.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using InternalsViewer.Internals.Engine.Pages.Enums; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace InternalsViewer.Internals.Engine.Indexes; 10 | 11 | public class IndexNode(PageAddress pageAddress) 12 | { 13 | public PageAddress PageAddress { get; set; } = pageAddress; 14 | 15 | public PageType PageType { get; set; } 16 | 17 | public List Parents { get; set; } = new(); 18 | 19 | public List Children { get; set; } = new(); 20 | 21 | public PageAddress NextPage { get; set; } 22 | 23 | public PageAddress PreviousPage { get; set; } 24 | 25 | public int Level { get; set; } 26 | 27 | public int Ordinal { get; set; } 28 | 29 | public int IndexLevel { get; set; } 30 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/AllocationPage.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Pages; 2 | 3 | /// 4 | /// Allocation Page containing an allocation bitmap (IAM, GAM, SGAM, DCM, BCM) 5 | /// 6 | /// 7 | /// Allocation Pages are used to track the allocation of pages and extents within a database file 8 | /// 9 | public class AllocationPage : Page 10 | { 11 | /// 12 | /// Interval between allocation pages 13 | /// 14 | /// 15 | /// 1 bit per extent representing allocation status of the extent 16 | /// 17 | /// 63,904 bits = 7,988 bytes = 1 page (8,192 bytes) less page header/overhead. 18 | /// 19 | public const int AllocationInterval = 63904; 20 | 21 | public const int AllocationArrayOffset = 194; 22 | 23 | public const int SinglePageSlotOffset = 142; 24 | 25 | public const int SlotCount = 8; 26 | 27 | public const int FirstGamPage = 2; 28 | 29 | public const int FirstSgamPage = 3; 30 | 31 | public const int FirstDcmPage = 6; 32 | 33 | public const int FirstBcmPage = 7; 34 | 35 | /// 36 | /// Allocation bitmap 37 | /// 38 | public bool[] AllocationMap { get; } = new bool[AllocationInterval]; 39 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/AllocationUnitPage.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Database; 2 | using InternalsViewer.Internals.Engine.Records.CdRecordType; 3 | 4 | namespace InternalsViewer.Internals.Engine.Pages; 5 | 6 | public abstract class AllocationUnitPage 7 | : Page 8 | { 9 | /// 10 | /// If page compression is active 11 | /// 12 | /// 13 | /// Page Compression can be turned on against the object, but not all pages will necessarily be compressed, for example if the page 14 | /// is not full it will use row compression only. 15 | /// 16 | /// The type flag bit 0x80 (128 / bit 8) determines if the page is compressed. 17 | /// 18 | public bool IsPageCompressed => (PageHeader.TypeFlagBits & 0x80) != 0; 19 | 20 | /// 21 | /// CI (Compression Information) structure for compressed data/index pages 22 | /// 23 | public CompressionInfo? CompressionInfo { get; set; } 24 | 25 | public AllocationUnit AllocationUnit { get; set; } = AllocationUnit.Unknown; 26 | } 27 | 28 | public class IndexPage 29 | : AllocationUnitPage; 30 | 31 | public class DataPage 32 | : AllocationUnitPage; -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/BootPage.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | 3 | namespace InternalsViewer.Internals.Engine.Pages; 4 | 5 | /// 6 | /// Boot Page 7 | /// 8 | public class BootPage : Page 9 | { 10 | public static PageAddress BootPageAddress = new(1, 9); 11 | 12 | public string DatabaseName { get; set; } = string.Empty; 13 | 14 | public int DatabaseId { get; set; } 15 | 16 | public short CreatedVersion { get; set; } 17 | 18 | public short CurrentVersion { get; set; } 19 | 20 | /// 21 | /// First page for the Allocation Units (sys.sysallocunits) table 22 | /// 23 | /// 24 | /// dbi_firstSysIndexes in the DBCC PAGE output 25 | /// 26 | public PageAddress FirstAllocationUnitsPage { get; set; } 27 | 28 | public int Status { get; set; } 29 | 30 | public DateTime CreatedDateTime { get; set; } 31 | 32 | public int CompatibilityLevel { get; set; } 33 | 34 | /// 35 | /// Last checkpoint LSN. 36 | /// 37 | public LogSequenceNumber CheckpointLsn { get; set; } 38 | 39 | public long MaxLogSpaceUsed { get; set; } 40 | 41 | public int Collation { get; set; } 42 | 43 | public long NextAllocationUnitId { get; set; } 44 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/EmptyPage.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Pages.Enums; 2 | 3 | namespace InternalsViewer.Internals.Engine.Pages; 4 | 5 | public class EmptyPage : Page 6 | { 7 | public EmptyPage() 8 | { 9 | PageHeader.PageType = PageType.None; 10 | } 11 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/Enums/PageType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Pages.Enums; 2 | 3 | /// 4 | /// Page Type 5 | /// 6 | public enum PageType 7 | { 8 | None = 0, 9 | FileHeader = 15, 10 | Data = 1, 11 | Index = 2, 12 | Lob3 = 3, 13 | Lob4 = 4, 14 | Sort = 7, 15 | Gam = 8, 16 | Sgam = 9, 17 | Iam = 10, 18 | Pfs = 11, 19 | Boot = 13, 20 | Dcm = 16, 21 | Bcm = 17 22 | } 23 | 24 | public enum AllocationUnitPageFlags 25 | { 26 | AllocNonLogged = 0x20, 27 | HasChecksum = 0x200, 28 | VersionInfo = 0x2000, 29 | AddBeg = 0x4000, 30 | AddEnd = 0x8000, 31 | HasFreeSlot = 0x10000, 32 | PgAligned4 = 0x2, 33 | FixedLengthRow = 0x4 34 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/FileHeaderPage.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Pages; 2 | 3 | public class FileHeaderPage : DataPage; -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/LobPage.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Pages; 2 | 3 | public class LobPage : Page 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/Page.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Pages; 2 | 3 | public abstract class Page : PageData; -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/PageData.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | using InternalsViewer.Internals.Engine.Address; 3 | using InternalsViewer.Internals.Engine.Database; 4 | 5 | namespace InternalsViewer.Internals.Engine.Pages; 6 | 7 | /// 8 | /// Page Data 9 | /// 10 | public class PageData : DataStructure 11 | { 12 | /// 13 | /// Page size is 8192 bytes/8KB 14 | /// 15 | public const int Size = 8192; 16 | 17 | /// 18 | /// Database the page belongs to 19 | /// 20 | public DatabaseSource Database { get; init; } = null!; 21 | 22 | /// 23 | /// Page Address in the format File Id : Page Id 24 | /// 25 | public PageAddress PageAddress { get; init; } 26 | 27 | /// 28 | /// Raw page data 29 | /// 30 | public byte[] Data { get; set; } = new byte[Size]; 31 | 32 | /// 33 | /// Page Header 34 | /// 35 | public PageHeader PageHeader { get; init; } = new(); 36 | 37 | /// 38 | /// Table/Array containing the data offset of each row in the page 39 | /// 40 | public List OffsetTable { get; init; } = new(); 41 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Pages/PfsPage.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using InternalsViewer.Internals.Engine.Address; 3 | using InternalsViewer.Internals.Engine.Allocation.Enums; 4 | 5 | namespace InternalsViewer.Internals.Engine.Pages; 6 | 7 | /// 8 | /// PFS (Page Free Space) page 9 | /// 10 | /// 11 | /// Information about page allocation and free space available on pages. 12 | /// 13 | public class PfsPage : Page 14 | { 15 | /// 16 | /// Interval between PFS pages = 8088 bytes/pages (1 byte = 1 pfs entry) 17 | /// 18 | public const int PfsInterval = 8088; 19 | 20 | /// 21 | /// PFS bytes collection 22 | /// 23 | public List PfsBytes { get; set; } = new(); 24 | 25 | public override string ToString() 26 | { 27 | var sb = new StringBuilder(); 28 | 29 | for (var i = 0; i <= PfsBytes.Count - 1; i++) 30 | { 31 | sb.AppendFormat("{0,-14}{1}", new PageAddress(1, i), PfsBytes[i]); 32 | sb.Append(Environment.NewLine); 33 | } 34 | 35 | return sb.ToString(); 36 | } 37 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/BlobChildLink.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | using InternalsViewer.Internals.Engine.Address; 3 | 4 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 5 | 6 | public class BlobChildLink : DataStructure 7 | { 8 | public BlobChildLink() 9 | { 10 | } 11 | 12 | public BlobChildLink(RowIdentifier rowIdentifier, int offset, int length) 13 | { 14 | RowIdentifier = rowIdentifier; 15 | Offset = offset; 16 | Length = length; 17 | } 18 | 19 | [DataStructureItem(ItemType.Rid)] 20 | public RowIdentifier? RowIdentifier { get; set; } 21 | 22 | [DataStructureItem(ItemType.BlobChildOffset)] 23 | public int Offset { get; set; } 24 | 25 | [DataStructureItem(ItemType.BlobChildLength)] 26 | public int Length { get; set; } 27 | 28 | public override string ToString() 29 | { 30 | return $"Page: {RowIdentifier} Offset: {Offset} Length: {Length}"; 31 | } 32 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/BlobField.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using InternalsViewer.Internals.Annotations; 3 | 4 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 5 | 6 | /// 7 | /// BLOB internal field 8 | /// 9 | public class BlobField : Field 10 | { 11 | /// 12 | /// Timestamp used by DBCC CHECKTABLE 13 | /// 14 | [DataStructureItem(ItemType.Timestamp)] 15 | public uint Timestamp { get; set; } 16 | 17 | public List Links { get; set; } = new(); 18 | 19 | [DataStructureItem(ItemType.Rid)] 20 | public BlobChildLink[] LinksArray => Links.ToArray(); 21 | 22 | [DataStructureItem(ItemType.PointerType)] 23 | public BlobFieldType PointerType { get; set; } 24 | 25 | public int Offset { get; set; } 26 | 27 | public override string ToString() 28 | { 29 | var sb = new StringBuilder(); 30 | 31 | sb.AppendLine(Timestamp.ToString()); 32 | 33 | foreach (var b in Links) 34 | { 35 | sb.AppendLine(b.ToString()); 36 | } 37 | 38 | return sb.ToString(); 39 | } 40 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/BlobFieldType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 2 | 3 | public enum BlobFieldType 4 | { 5 | LobPointer = 0x00, 6 | LobRoot = 0x04, 7 | RowOverflow = 0x02 8 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/OverflowField.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | 3 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 4 | 5 | /// 6 | /// Row Overflow field 7 | /// 8 | public class OverflowField : BlobField 9 | { 10 | [DataStructureItem(ItemType.OverflowLevel)] 11 | public byte Level { get; set; } 12 | 13 | [DataStructureItem(ItemType.OverflowLength)] 14 | public int Length { get; set; } 15 | 16 | [DataStructureItem(ItemType.Unused)] 17 | public byte Unused { get; set; } 18 | 19 | /// 20 | /// Update seq (used by optimistic concurrency control for cursors) 21 | /// 22 | [DataStructureItem(ItemType.UpdateSeq)] 23 | public short UpdateSeq { get; set; } 24 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/PointerField.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 2 | 3 | public class PointerField : BlobField; -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/RootField.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | 3 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 4 | 5 | public class RootField : BlobField 6 | { 7 | [DataStructureItem(ItemType.SlotCount)] 8 | public int SlotCount { get; set; } 9 | 10 | [DataStructureItem(ItemType.Level)] 11 | public byte Level { get; set; } 12 | 13 | [DataStructureItem(ItemType.Unused)] 14 | public byte Unused { get; set; } 15 | 16 | [DataStructureItem(ItemType.UpdateSeq)] 17 | public short UpdateSeq { get; set; } 18 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Blob/BlobType.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace InternalsViewer.Internals.Engine.Records.Blob; 3 | 4 | public enum BlobType : byte 5 | { 6 | Data = 3, 7 | Internal = 2, 8 | LargeRoot = 5, 9 | SmallRoot = 0 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/CdRecordType/ColumnDescriptor.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | using InternalsViewer.Internals.Helpers; 3 | 4 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType; 5 | 6 | public class ColumnDescriptor(byte value) : DataStructure 7 | { 8 | public ColumnDescriptorFlag Value { get; } = (ColumnDescriptorFlag)value; 9 | 10 | public int Size => Value switch 11 | { 12 | ColumnDescriptorFlag.Null => 0, 13 | ColumnDescriptorFlag.ZeroByteShort => 0, 14 | ColumnDescriptorFlag.OneByteShort => 1, 15 | ColumnDescriptorFlag.TwoByteShort => 2, 16 | ColumnDescriptorFlag.ThreeByteShort => 3, 17 | ColumnDescriptorFlag.FourByteShort => 4, 18 | ColumnDescriptorFlag.FiveByteShort => 5, 19 | ColumnDescriptorFlag.SixByteShort => 6, 20 | ColumnDescriptorFlag.SevenByteShort => 7, 21 | ColumnDescriptorFlag.EightByteShort => 8, 22 | ColumnDescriptorFlag.Long => 0, 23 | ColumnDescriptorFlag.BitTrue => 0, 24 | ColumnDescriptorFlag.PageSymbol => 1, 25 | _ => 0 26 | }; 27 | 28 | public override string ToString() 29 | { 30 | return Value.ToString().SplitCamelCase(); 31 | } 32 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/CdRecordType/ColumnDescriptorFlag.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType; 2 | 3 | public enum ColumnDescriptorFlag : byte 4 | { 5 | Null = 0, 6 | ZeroByteShort = 1, 7 | OneByteShort = 2, 8 | TwoByteShort = 3, 9 | ThreeByteShort = 4, 10 | FourByteShort = 5, 11 | FiveByteShort = 6, 12 | SixByteShort = 7, 13 | SevenByteShort = 8, 14 | EightByteShort = 9, 15 | Long = 10, 16 | BitTrue = 11, 17 | PageSymbol = 12 18 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/CdRecordType/CompressedRecordType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType; 2 | 3 | public enum CompressedRecordType 4 | { 5 | Primary = 0, 6 | GhostEmpty = 1, 7 | Forwarding = 2, 8 | GhostData = 3, 9 | Forwarded = 4, 10 | GhostForwarded = 5, 11 | Index = 6, 12 | GhostIndex = 7 13 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/CdRecordType/CompressionInfo.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | 3 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType; 4 | 5 | /// 6 | /// Compression Info record 7 | /// 8 | /// 9 | /// The CI record is a record added for pages compressed using the PAGE compression type. 10 | /// 11 | /// It has the following structure: 12 | /// 13 | /// - Header 14 | /// - Page Modification Count 15 | /// - Offsets (Length = CI Record Size, Size = CI Record Size) 16 | /// - Anchor Record 17 | /// - Dictionary 18 | /// 19 | public class CompressionInfo(int offset) : DataStructure 20 | { 21 | [DataStructureItem(ItemType.PageModificationCount)] 22 | public short PageModificationCount { get; set; } 23 | 24 | [DataStructureItem(ItemType.Size)] 25 | public short Size { get; set; } 26 | 27 | [DataStructureItem(ItemType.Length)] 28 | public ushort Length { get; set; } 29 | 30 | [DataStructureItem(ItemType.Header)] 31 | public byte Header { get; set; } 32 | 33 | public static ushort SlotOffset => 96; 34 | 35 | [DataStructureItem(ItemType.AnchorRecord)] 36 | public CompressedDataRecord? AnchorRecord { get; set; } 37 | 38 | [DataStructureItem(ItemType.CompressionDictionary)] 39 | public Dictionary? CompressionDictionary { get; set; } 40 | 41 | public bool HasAnchorRecord { get; set; } 42 | 43 | public bool HasDictionary { get; set; } 44 | 45 | public int Offset { get; set; } = offset; 46 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/CdRecordType/CompressionInfoStructure.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType; 2 | 3 | public enum CompressionInfoStructure 4 | { 5 | None, 6 | Header, 7 | Anchor, 8 | Dictionary 9 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/CdRecordType/CompressionType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType; 2 | 3 | /// 4 | /// 2008 Page compression type 5 | /// 6 | [Flags] 7 | public enum CompressionType : byte 8 | { 9 | /// 10 | /// No compression (default) 11 | /// 12 | None = 0, 13 | /// 14 | /// Row level compressions 15 | /// 16 | Row = 1, 17 | /// 18 | /// Page level compression 19 | /// 20 | Page = 2, 21 | 22 | Columnstore = 3, 23 | 24 | ColumnstoreArchive = 4, 25 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/CdRecordType/Dictionary.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using InternalsViewer.Internals.Annotations; 3 | 4 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType; 5 | 6 | public class Dictionary(int offset) : DataStructure 7 | { 8 | public int Offset { get; } = offset; 9 | 10 | [DataStructureItem(ItemType.DictionaryEntries)] 11 | public DictionaryEntry[] DictionaryEntries { get; set; } = Array.Empty(); 12 | 13 | [DataStructureItem(ItemType.DictionaryEntryCount)] 14 | public int EntryCount { get; set; } 15 | 16 | [DataStructureItem(ItemType.DictionaryColumnOffsets)] 17 | public ushort[] EntryOffsets { get; set; } = Array.Empty(); 18 | 19 | public override string ToString() 20 | { 21 | var sb = new StringBuilder(); 22 | 23 | sb.AppendLine($"Dictionary at {Offset}"); 24 | sb.AppendLine($"Entry count = {EntryCount}"); 25 | sb.AppendLine($"Entry offset = {EntryOffsets}"); 26 | sb.AppendLine(); 27 | sb.AppendLine($"Dictionary entries"); 28 | sb.AppendLine(); 29 | 30 | foreach (var entry in DictionaryEntries) 31 | { 32 | sb.AppendLine(entry.ToString()); 33 | } 34 | 35 | return sb.ToString(); 36 | } 37 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/CdRecordType/DictionaryEntry.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | 3 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType; 4 | 5 | public class DictionaryEntry(int symbol, ushort offset, byte[] data) : DataStructure 6 | { 7 | [DataStructureItem(ItemType.DictionarySymbol)] 8 | public int Symbol { get; } = symbol; 9 | 10 | [DataStructureItem(ItemType.DictionaryValue)] 11 | public byte[] Data { get; } = data; 12 | 13 | public ushort Offset { get; } = offset; 14 | 15 | public override string ToString() 16 | { 17 | return $"Dictionary Entry = Symbol {Symbol}, offset {Offset} length {Data.Length}"; 18 | } 19 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Data/DataRecord.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | using InternalsViewer.Internals.Converters; 3 | using InternalsViewer.Internals.Engine.Address; 4 | using InternalsViewer.Internals.Engine.Records.FixedVarRecordType; 5 | 6 | namespace InternalsViewer.Internals.Engine.Records.Data; 7 | 8 | public class DataRecord : FixedVarRecord 9 | { 10 | public SparseVector? SparseVector { get; set; } 11 | 12 | [DataStructureItem(ItemType.StatusBitsB)] 13 | public string StatusBitsBDescription => ""; 14 | 15 | [DataStructureItem(ItemType.ForwardingStub)] 16 | public RowIdentifier? ForwardingStub { get; set; } 17 | 18 | public RowIdentifier? RowIdentifier { get; set; } 19 | 20 | public T? GetValue(string columnName) 21 | { 22 | var field = Fields.FirstOrDefault(f => f.Name.ToLower() == columnName.ToLower()); 23 | 24 | if (field == null) 25 | { 26 | throw new ArgumentException($"Column {columnName} not found"); 27 | } 28 | 29 | if (field.Data.Length == 0) 30 | { 31 | return default; 32 | } 33 | 34 | return DataConverter.GetValue(field.Data, 35 | field.ColumnStructure.DataType, 36 | field.ColumnStructure.Precision, 37 | field.ColumnStructure.Scale); 38 | } 39 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Field.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | 3 | namespace InternalsViewer.Internals.Engine.Records; 4 | 5 | public class Field : DataStructure; -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Index/IndexRecord.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | using InternalsViewer.Internals.Engine.Address; 3 | using InternalsViewer.Internals.Engine.Records.FixedVarRecordType; 4 | 5 | namespace InternalsViewer.Internals.Engine.Records.Index; 6 | 7 | public class IndexRecord : FixedVarRecord 8 | { 9 | /// 10 | /// Down page pointer to the next page in the index 11 | /// 12 | [DataStructureItem(ItemType.DownPagePointer)] 13 | public PageAddress DownPagePointer { get; set; } 14 | 15 | /// 16 | /// RID (Row Identifier) the index is pointing to 17 | /// 18 | [DataStructureItem(ItemType.Rid)] 19 | public RowIdentifier? Rid { get; set; } 20 | 21 | public bool IncludeKey { get; set; } 22 | 23 | public NodeType NodeType { get; set; } 24 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Index/IndexTypes.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace InternalsViewer.Internals.Engine.Records.Index; 3 | 4 | [Flags] 5 | public enum IndexTypes 6 | { 7 | Heap = 1, 8 | Clustered = 2, 9 | NonClustered = 4, 10 | Leaf = 8, 11 | Node = 16, 12 | TableClustered = 32, 13 | NonClusteredLeaf = NonClustered | Leaf 14 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Index/NodeType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Engine.Records.Index; 2 | 3 | public enum NodeType 4 | { 5 | Node, 6 | Leaf, 7 | Root, 8 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/Record.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | 3 | namespace InternalsViewer.Internals.Engine.Records; 4 | 5 | /// 6 | /// Database Record Structure 7 | /// 8 | public abstract class Record : DataStructure 9 | { 10 | public int Slot { get; set; } 11 | 12 | public ushort Offset { get; set; } 13 | 14 | public List Fields { get; } = new(); 15 | 16 | public RecordField[] FieldsArray => Fields.ToArray(); 17 | 18 | [DataStructureItem(ItemType.ColumnCount)] 19 | public short ColumnCount { get; set; } 20 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/RecordField.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Metadata.Structures; 2 | 3 | namespace InternalsViewer.Internals.Engine.Records; 4 | 5 | public abstract class RecordField(ColumnStructure columnStructure) : Field 6 | { 7 | public ColumnStructure ColumnStructure { get; } = columnStructure; 8 | 9 | /// 10 | /// Length of the field (in bytes) 11 | /// 12 | public int Length { get; set; } 13 | 14 | /// 15 | /// Offset of the field in the row 16 | /// 17 | public int Offset { get; set; } 18 | 19 | /// 20 | /// Raw data for the field 21 | /// 22 | public byte[] Data { get; set; } = Array.Empty(); 23 | 24 | public string Name => ColumnStructure.ColumnName; 25 | 26 | /// 27 | /// String representation of the field value 28 | /// 29 | public abstract string Value { get; } 30 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/RecordHelpers.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace InternalsViewer.Internals.Engine.Records; 4 | 5 | public static class RecordHelpers 6 | { 7 | /// 8 | /// Flips the high bit if set 9 | /// 10 | public static ushort DecodeOffset(ushort value) 11 | { 12 | if ((value | 0x8000) == value) 13 | { 14 | return Convert.ToUInt16(value ^ 0x8000); 15 | } 16 | 17 | return value; 18 | } 19 | 20 | /// 21 | /// Get an offset array from a record 22 | /// 23 | /// An array of 2-byte integers representing a start offset in the page 24 | public static ushort[] GetOffsetArray(byte[] record, int size, int offset) 25 | { 26 | var offsetArray = new ushort[size]; 27 | 28 | for (var i = 0; i < size; i++) 29 | { 30 | offsetArray[i] = BitConverter.ToUInt16(record, offset); 31 | 32 | offset += sizeof(ushort); 33 | } 34 | 35 | return offsetArray; 36 | } 37 | 38 | public static string GetArrayString(ushort[] array) 39 | { 40 | var sb = new StringBuilder(); 41 | 42 | foreach (var offset in array) 43 | { 44 | if (sb.Length > 0) 45 | { 46 | sb.Append(", "); 47 | } 48 | 49 | sb.AppendFormat("{0} - 0x{0:X}", offset); 50 | } 51 | 52 | return sb.ToString(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Engine/Records/RecordType.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace InternalsViewer.Internals.Engine.Records; 3 | 4 | public enum RecordType 5 | { 6 | Primary = 0, 7 | Forwarded = 1, 8 | ForwardingStub = 2, 9 | Index = 3, 10 | Blob = 4, 11 | GhostIndex = 5, 12 | GhostData = 6, 13 | GhostRecordVersion = 7 14 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Extensions/ByteExtensions.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Helpers; 2 | using System.Collections; 3 | using System.Text; 4 | 5 | namespace InternalsViewer.Internals.Extensions; 6 | 7 | public static class ByteExtensions 8 | { 9 | /// 10 | /// Extension method to return a binary/bitmap string for a given byte 11 | /// 12 | public static string ToBinaryString(this byte input) 13 | { 14 | var bitArray = new BitArray(new[] { input }); 15 | 16 | var stringBuilder = new StringBuilder(); 17 | 18 | for (var i = 0; i < bitArray.Length; i++) 19 | { 20 | stringBuilder.Append(bitArray[i] ? "1" : "0"); 21 | } 22 | 23 | return stringBuilder.ToString(); 24 | } 25 | 26 | /// 27 | /// Extension method to return a hex string for a given byte 28 | /// 29 | public static string ToHexString(this byte input) 30 | { 31 | return StringHelpers.ToHexString(input); 32 | } 33 | 34 | /// 35 | /// Extension method to return a hex string for a given array of bytes 36 | /// 37 | public static string ToHexString(this byte[] input) 38 | { 39 | return StringHelpers.ToHexString(input); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace InternalsViewer.Internals.Extensions; 8 | 9 | public static class EnumerableExtensions 10 | { 11 | public static void AddIf(this List collection, T item, bool condition) 12 | { 13 | if (condition) 14 | { 15 | collection.Add(item); 16 | } 17 | } 18 | 19 | public static T? Pop(this List collection, Func predicate) 20 | { 21 | if (collection.Count(predicate) > 0) 22 | { 23 | var result = collection.Where(predicate).First(); 24 | 25 | collection.Remove(result); 26 | 27 | return result; 28 | } 29 | 30 | return default; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using System; 4 | global using System.Collections.Generic; 5 | global using System.Linq; 6 | global using System.Threading.Tasks; 7 | global using Microsoft.Extensions.Logging; -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Helpers/History.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Helpers; 2 | 3 | public class History 4 | { 5 | private Stack BackStack { get; } = new(); 6 | 7 | private Stack ForwardStack { get; } = new(); 8 | 9 | public void Add(T item) 10 | { 11 | switch (BackStack.Count) 12 | { 13 | case > 0 when BackStack.Peek()!.Equals(item): 14 | return; 15 | case > 0: 16 | ForwardStack.Clear(); 17 | break; 18 | } 19 | 20 | BackStack.Push(item); 21 | } 22 | 23 | public T? Back() 24 | { 25 | if (BackStack.Count > 1) 26 | { 27 | ForwardStack.Push(BackStack.Pop()); 28 | 29 | return BackStack.Peek(); 30 | } 31 | 32 | return default; 33 | } 34 | 35 | public T? Forward() 36 | { 37 | if (ForwardStack.Count > 0) 38 | { 39 | var item = ForwardStack.Pop(); 40 | 41 | BackStack.Push(item); 42 | 43 | return item; 44 | } 45 | 46 | return default; 47 | } 48 | 49 | public bool CanGoBack() => BackStack.Count > 1; 50 | 51 | public bool CanGoForward() => ForwardStack.Count > 0; 52 | } 53 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Helpers/IdHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Helpers; 2 | 3 | public class IdHelpers 4 | { 5 | /// 6 | /// Gets the Allocation Unit Id from the Object Id and Index Id 7 | /// 8 | /// 9 | /// Taken from 10 | /// 11 | public static long GetAllocationUnitId(int objectId, int indexId) 12 | { 13 | return (long)indexId << 48 | (long)objectId << 16; 14 | } 15 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Connections/IConnectionProvider.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.Readers; 2 | 3 | namespace InternalsViewer.Internals.Interfaces.Connections; 4 | 5 | public interface IConnectionProvider 6 | { 7 | IPageReader PageReader { get; } 8 | 9 | string Name { get; } 10 | } 11 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Connections/IConnectionType.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.Readers; 2 | 3 | namespace InternalsViewer.Internals.Interfaces.Connections; 4 | 5 | public interface IConnectionType 6 | { 7 | IPageReader PageReader { get; } 8 | 9 | string Identifier { get; } 10 | 11 | string Name { get; set; } 12 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Connections/IConnectionTypeFactory.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Connections; 2 | 3 | namespace InternalsViewer.Internals.Interfaces.Connections; 4 | 5 | public interface IConnectionTypeFactory where TConfig : ConnectionTypeConfig 6 | { 7 | static abstract IConnectionType Create(Action configDelegate); 8 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Engine/IAllocationChain.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using InternalsViewer.Internals.Engine.Pages; 3 | 4 | namespace InternalsViewer.Internals.Interfaces.Engine; 5 | 6 | public interface IAllocationChain where T: AllocationPage 7 | { 8 | bool IsExtentAllocated(int extent, short fileId, bool isInverted); 9 | 10 | PageAddress[] SinglePageSlots { get; } 11 | 12 | List Pages { get; } 13 | } 14 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/MetadataProviders/IBufferPoolInfoProvider.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | 3 | namespace InternalsViewer.Internals.Interfaces.MetadataProviders; 4 | 5 | public interface IBufferPoolInfoProvider 6 | { 7 | Task<(List Clean, List Dirty)> GetBufferPoolEntries(string databaseName); 8 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/MetadataProviders/ITransactionLogProvider.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Metadata; 2 | 3 | namespace InternalsViewer.Internals.Interfaces.MetadataProviders; 4 | 5 | public interface ITransactionLogProvider 6 | { 7 | Task Checkpoint(); 8 | 9 | Task> GetTransactionLog(); 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Readers/IPageReader.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | 3 | namespace InternalsViewer.Internals.Interfaces.Readers; 4 | 5 | public interface IPageReader 6 | { 7 | Task Read(string name, PageAddress pageAddress); 8 | } 9 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Readers/Internals/IRecordReader.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using InternalsViewer.Internals.Engine.Database; 3 | using InternalsViewer.Internals.Engine.Records.Data; 4 | using InternalsViewer.Internals.Metadata.Structures; 5 | 6 | namespace InternalsViewer.Internals.Interfaces.Readers.Internals; 7 | 8 | public interface IRecordReader 9 | { 10 | Task> Read(DatabaseSource database, PageAddress startPage, TableStructure structure); 11 | } 12 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Services/Loaders/Chains/IAllocationChainService.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using InternalsViewer.Internals.Engine.Allocation; 3 | using InternalsViewer.Internals.Engine.Database; 4 | using InternalsViewer.Internals.Engine.Pages.Enums; 5 | 6 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Chains; 7 | 8 | public interface IAllocationChainService 9 | { 10 | Task LoadChain(DatabaseSource database, short fileId, PageType pageType); 11 | 12 | Task LoadChain(DatabaseSource database, PageAddress startPageAddress); 13 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Services/Loaders/Chains/IIamChainService.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using InternalsViewer.Internals.Engine.Allocation; 3 | using InternalsViewer.Internals.Engine.Database; 4 | 5 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Chains; 6 | 7 | public interface IIamChainService 8 | { 9 | Task LoadChain(DatabaseSource database, PageAddress startPageAddress); 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Services/Loaders/Chains/IPfsChainService.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Allocation; 2 | using InternalsViewer.Internals.Engine.Database; 3 | 4 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Chains; 5 | 6 | public interface IPfsChainService 7 | { 8 | Task LoadChain(DatabaseSource databaseDetail, short fileId); 9 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Services/Loaders/Engine/IDatabaseService.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Database; 2 | using InternalsViewer.Internals.Interfaces.Connections; 3 | 4 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Engine; 5 | 6 | public interface IDatabaseService 7 | { 8 | Task LoadAsync(string name, IConnectionType connection); 9 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Services/Loaders/Engine/IMetadataLoader.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Database; 2 | using InternalsViewer.Internals.Metadata.Internals; 3 | 4 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Engine; 5 | 6 | public interface IMetadataLoader 7 | { 8 | Task Load(DatabaseSource database); 9 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Services/Loaders/Pages/IPageLoader.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using InternalsViewer.Internals.Engine.Database; 3 | using InternalsViewer.Internals.Engine.Pages; 4 | 5 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 6 | 7 | public interface IPageLoader 8 | { 9 | Task Load(DatabaseSource database, PageAddress pageAddress); 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Services/Loaders/Pages/IPageParser.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Pages; 2 | using InternalsViewer.Internals.Engine.Pages.Enums; 3 | 4 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 5 | 6 | public interface IPageParser 7 | { 8 | PageType[] SupportedPageTypes { get; } 9 | 10 | Page Parse(PageData page); 11 | } 12 | 13 | public interface IPageParser : IPageParser where T : Page, new() 14 | { 15 | new T Parse(PageData page); 16 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Services/Loaders/Pages/IPageService.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using InternalsViewer.Internals.Engine.Database; 3 | using InternalsViewer.Internals.Engine.Pages; 4 | 5 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 6 | 7 | public interface IPageService 8 | { 9 | Task GetPage(DatabaseSource database, PageAddress pageAddress); 10 | 11 | Task GetPage(DatabaseSource database, PageAddress pageAddress) where T : Page; 12 | } 13 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Interfaces/Services/Records/IRecordService.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Pages; 2 | using InternalsViewer.Internals.Engine.Records.CdRecordType; 3 | using InternalsViewer.Internals.Engine.Records.Data; 4 | using InternalsViewer.Internals.Engine.Records.Index; 5 | 6 | namespace InternalsViewer.Internals.Interfaces.Services.Records; 7 | 8 | public interface IRecordService 9 | { 10 | List GetDataRecords(DataPage page); 11 | 12 | List GetCompressedDataRecords(DataPage page); 13 | 14 | List GetIndexRecords(IndexPage page); 15 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/ColumnAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | 3 | namespace InternalsViewer.Internals.Metadata; 4 | 5 | [AttributeUsage(AttributeTargets.Property)] 6 | public class ColumnAttribute : Attribute 7 | { 8 | /// 9 | /// Name of the column. 10 | /// 11 | public string ColumnName { get; set; } = string.Empty; 12 | 13 | /// 14 | /// Column id. 15 | /// 16 | public int ColumnId { get; set; } 17 | 18 | /// 19 | /// Data type 20 | /// 21 | public SqlDbType DataType { get; set; } 22 | 23 | /// 24 | /// Data length 25 | /// 26 | public short DataLength { get; set; } = 0; 27 | 28 | /// 29 | /// Leaf offset for fixed length fields 30 | /// 31 | public short LeafOffset { get; set; } 32 | 33 | /// 34 | /// 1-based index for the column in the null bitmap. 35 | /// 36 | public short NullBitIndex { get; set; } 37 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/HobtEntryPoint.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | 3 | namespace InternalsViewer.Internals.Metadata; 4 | 5 | /// 6 | /// The entry point pages for a HOBT 7 | /// 8 | /// See https://learn.microsoft.com/en-us/sql/relational-databases/pages-and-extents-architecture-guide 9 | /// 10 | public struct HobtEntryPoint 11 | { 12 | /// 13 | /// Initializes a new instance of the struct. 14 | /// 15 | public HobtEntryPoint(PageAddress firstIam, PageAddress rootPage, PageAddress firstPage, int partitionNumber) 16 | { 17 | FirstIam = firstIam; 18 | RootPage = rootPage; 19 | FirstPage = firstPage; 20 | PartitionNumber = partitionNumber; 21 | } 22 | 23 | /// 24 | /// First IAM page address. 25 | /// 26 | /// 27 | /// The first page in the HOBTs allocation IAM chain 28 | /// 29 | public PageAddress FirstIam { get; set; } 30 | 31 | /// 32 | /// Index root page address. 33 | /// 34 | /// 35 | /// The root page of the b-tree (index) 36 | /// 37 | public PageAddress RootPage { get; set; } 38 | 39 | /// 40 | /// First page address 41 | /// 42 | /// 43 | /// The first page at the leaf/heap level 44 | /// 45 | public PageAddress FirstPage { get; set; } 46 | 47 | /// 48 | /// Partition number. 49 | /// 50 | public int PartitionNumber { get; set; } 51 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/Internals/Tables/InternalIndexColumn.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | using InternalsViewer.Internals.Generators; 3 | 4 | namespace InternalsViewer.Internals.Metadata.Internals.Tables; 5 | 6 | /// 7 | /// sys.sysiscols 8 | /// 9 | [InternalsMetadata] 10 | public record InternalIndexColumn 11 | { 12 | [InternalsMetadataColumn("idmajor", 1, SqlDbType.Int, 4, 4, 1)] 13 | public int ObjectId { get; set; } 14 | 15 | [InternalsMetadataColumn("idminor", 2, SqlDbType.Int, 4, 8, 2)] 16 | public int IndexId { get; set; } 17 | 18 | [InternalsMetadataColumn("subid", 3, SqlDbType.Int, 4, 12, 3)] 19 | public int IndexColumnId { get; set; } 20 | 21 | [InternalsMetadataColumn("status", 4, SqlDbType.Int, 4, 16, 4)] 22 | public int Status { get; set; } 23 | 24 | [InternalsMetadataColumn("intprop", 5, SqlDbType.Int, 4, 20, 5)] 25 | public int ColumnId { get; set; } 26 | 27 | [InternalsMetadataColumn("tinyprop1", 6, SqlDbType.TinyInt, 1, 24, 6)] 28 | public byte KeyOrdinal { get; set; } 29 | 30 | [InternalsMetadataColumn("tinyprop2", 7, SqlDbType.TinyInt, 1, 25, 7)] 31 | public byte PartitionOrdinal { get; set; } 32 | 33 | [InternalsMetadataColumn("tinyprop3", 8, SqlDbType.TinyInt, 1, 26, 8)] 34 | public byte TinyProp3 { get; set; } 35 | 36 | [InternalsMetadataColumn("tinyprop4", 9, SqlDbType.TinyInt, 1, 27, 9)] 37 | public byte ColumnStoreOrderOrdinal { get; set; } 38 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/Internals/TypeInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | 3 | namespace InternalsViewer.Internals.Metadata.Internals; 4 | 5 | /// 6 | /// Parses the ti (Type Info) field of the sys.sysallocunits table 7 | /// 8 | public record TypeInfo 9 | { 10 | public byte Scale { get; set; } 11 | 12 | public byte Precision { get; set; } 13 | 14 | public short MaxLength { get; set; } 15 | 16 | public short MaxInRowLength { get; set; } 17 | 18 | public SqlDbType DataType { get; set; } 19 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/Structures/IndexColumnStructure.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Metadata.Structures; 2 | 3 | public record IndexColumnStructure : ColumnStructure 4 | { 5 | public bool IsIncludeColumn { get; set; } 6 | 7 | public bool IsIndexKey { get; set; } 8 | 9 | public int IndexColumnId { get; set; } 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/Structures/IndexStructure.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Metadata.Structures; 2 | 3 | public record IndexStructure(long AllocationUnitId) 4 | : Structure(AllocationUnitId) 5 | { 6 | public bool IsUnique { get; set; } 7 | 8 | public bool HasFilter { get; set; } 9 | 10 | public TableStructure? TableStructure { get; set; } 11 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/Structures/Structure.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Database.Enums; 2 | using InternalsViewer.Internals.Engine.Records.CdRecordType; 3 | 4 | namespace InternalsViewer.Internals.Metadata.Structures; 5 | 6 | public abstract record Structure(long AllocationUnitId) where T : ColumnStructure 7 | { 8 | public int ObjectId { get; set; } 9 | 10 | public int IndexId { get; set; } 11 | 12 | public long AllocationUnitId { get; set; } = AllocationUnitId; 13 | 14 | public long PartitionId { get; set; } 15 | 16 | public List Columns { get; set; } = new(); 17 | 18 | public bool HasSparseColumns => Columns.Any(c => c.IsSparse); 19 | 20 | public IndexType IndexType { get; set; } 21 | 22 | public CompressionType CompressionType { get; set; } 23 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/Structures/StructureType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Metadata.Structures; 2 | 3 | /// 4 | /// HOBT structure type 5 | /// 6 | public enum StructureType 7 | { 8 | /// 9 | /// Heap - table without a clustered index 10 | /// 11 | Heap, 12 | /// 13 | /// B-Tree (Index) 14 | /// 15 | BTree 16 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/Structures/TableStructure.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Metadata.Structures; 2 | 3 | public record TableStructure(long AllocationUnitId) 4 | : Structure(AllocationUnitId) 5 | { 6 | public int ColumnCount => Columns.Count; 7 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Metadata/TransactionLogEntry.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Metadata; 2 | 3 | public class TransactionLogEntry 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Providers/Metadata/FileProvider.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Database; 2 | using InternalsViewer.Internals.Metadata.Internals; 3 | using InternalsViewer.Internals.Metadata.Internals.Tables; 4 | 5 | namespace InternalsViewer.Internals.Providers.Metadata; 6 | 7 | /// 8 | /// Provider responsible for providing file information from the metadata collection 9 | /// 10 | public static class FileProvider 11 | { 12 | public static List GetFiles(InternalMetadata metadata) 13 | { 14 | return metadata.Files.Select(GetFile).Where(f => f.FileType == FileType.Rows).ToList(); 15 | } 16 | 17 | private static DatabaseFile GetFile(InternalFile source) 18 | { 19 | var fileId = (short)(source.FileId & 0x7fff); 20 | 21 | var file = new DatabaseFile(fileId) 22 | { 23 | FileGroupId = (short)source.FileGroupId, 24 | Size = source.Size, 25 | FileType = (FileType)source.FileType, 26 | Name = source.LogicalName, 27 | PhysicalName = source.PhysicalName, 28 | }; 29 | 30 | return file; 31 | } 32 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Readers/Pages/DataFilePageReader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using InternalsViewer.Internals.Engine.Address; 3 | using InternalsViewer.Internals.Engine.Pages; 4 | using InternalsViewer.Internals.Interfaces.Readers; 5 | 6 | namespace InternalsViewer.Internals.Readers.Pages; 7 | 8 | /// 9 | /// Page Reader for SQL Server data files 10 | /// 11 | /// 12 | public class DataFilePageReader(string path) : PageReader, IPageReader 13 | { 14 | private string FilePath { get; } = path; 15 | 16 | /// 17 | /// Reads a page from a SQL Server data file 18 | /// 19 | /// 20 | /// SQL Server data files (MDF/LDF) are stored in 8 KB (8192 bytes) pages, so a page is located in the file at location 21 | /// (Page Id * 8192) 22 | /// 23 | /// The file has to be detached/not attached to SQL Server to be read as it will be locked by the SQL Server process. 24 | /// 25 | public async Task Read(string name, PageAddress pageAddress) 26 | { 27 | var offset = pageAddress.PageId * PageData.Size; 28 | 29 | var data = new byte[PageData.Size]; 30 | 31 | await using var file = File.OpenRead(FilePath); 32 | 33 | file.Seek(offset, SeekOrigin.Begin); 34 | 35 | _ = await file.ReadAsync(data, 0, PageData.Size); 36 | 37 | return data; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Readers/Pages/PageReader.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using InternalsViewer.Internals.Engine.Pages; 3 | 4 | namespace InternalsViewer.Internals.Readers.Pages; 5 | 6 | /// 7 | /// Abstract class for reading pages from hex dump 8 | /// 9 | public abstract class PageReader 10 | { 11 | protected static int ReadData(string currentRow, int offset, byte[] data) 12 | { 13 | var currentData = currentRow.Substring(20, 44).Replace(" ", string.Empty); 14 | 15 | for (var i = 0; i < currentData.Length; i += 2) 16 | { 17 | var byteString = currentData.Substring(i, 2); 18 | 19 | if (!byteString.Contains("†") && !byteString.Contains(".") && offset < PageData.Size) 20 | { 21 | if (byte.TryParse(byteString, 22 | NumberStyles.HexNumber, 23 | CultureInfo.InvariantCulture, 24 | out data[offset])) 25 | { 26 | offset++; 27 | } 28 | } 29 | } 30 | 31 | return offset; 32 | } 33 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/BufferPoolService.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Interfaces.MetadataProviders; 2 | using InternalsViewer.Internals.Engine.Database; 3 | 4 | namespace InternalsViewer.Internals.Services; 5 | 6 | /// 7 | /// Service responsible for getting the Buffer Pool 8 | /// 9 | /// 10 | /// From Books Online: 11 | /// 12 | /// Buffer pool 13 | /// 14 | /// Also called buffer cache. The buffer pool is a global resource shared by all databases for their cached data pages. 15 | /// 16 | public class BufferPoolService(IBufferPoolInfoProvider provider) 17 | { 18 | public IBufferPoolInfoProvider Provider { get; } = provider; 19 | 20 | public async Task GetBufferPool(string databaseName) 21 | { 22 | var (clean, dirty) = await Provider.GetBufferPoolEntries(databaseName); 23 | 24 | var bufferPool = new BufferPool(clean, dirty); 25 | 26 | return bufferPool; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Loaders/Engine/InternalTableConstants.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.Internals.Services.Loaders.Engine; 2 | 3 | public class InternalTableConstants 4 | { 5 | public static readonly (int ObjectId, int IndexId) ColumnLayoutsId = (3, 0); 6 | 7 | public static readonly (int ObjectId, int IndexId) ColumnsId = (41, 1); 8 | 9 | public static readonly (int ObjectId, int IndexId) RowSetId = (5, 0); 10 | 11 | public static readonly (int ObjectId, int IndexId) AllocationUnitsId = (7, 0); 12 | 13 | public static readonly (int ObjectId, int IndexId) FilesId = (24, 1); 14 | 15 | public static readonly (int ObjectId, int IndexId) ObjectsId = (34, 1); 16 | 17 | public static readonly (int ObjectId, int IndexId) EntitiesId = (64, 1); 18 | 19 | public static readonly (int ObjectId, int IndexId) IndexesId = (54, 1); 20 | 21 | public static readonly (int ObjectId, int IndexId) IndexColumnsId = (55, 1); 22 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Loaders/Records/Fields/LobPointerFieldLoader.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using InternalsViewer.Internals.Engine.Records.Blob.BlobPointers; 3 | 4 | namespace InternalsViewer.Internals.Services.Loaders.Records.Fields; 5 | 6 | public class LobPointerFieldLoader 7 | { 8 | public const int RowIdOffset = 8; 9 | 10 | public static PointerField Load(byte[] data, int offset) 11 | { 12 | var field = new PointerField(); 13 | 14 | field.MarkProperty("PointerType", offset, sizeof(byte)); 15 | 16 | field.PointerType = (BlobFieldType)data[0]; 17 | 18 | field.MarkProperty("Timestamp", offset + sizeof(byte), sizeof(int)); 19 | 20 | field.Timestamp = BitConverter.ToUInt32(data, 0); 21 | 22 | LoadLinks(field, data, offset); 23 | 24 | return field; 25 | } 26 | 27 | private static void LoadLinks(PointerField field, byte[] data, int offset) 28 | { 29 | field.Links = new List(); 30 | 31 | var rowIdData = new byte[8]; 32 | Array.Copy(data, RowIdOffset, rowIdData, 0, 8); 33 | 34 | field.MarkProperty("LinksArray", string.Empty, 0); 35 | 36 | var rowId = new RowIdentifier(rowIdData); 37 | 38 | var link = new BlobChildLink(rowId, 0, 0); 39 | 40 | link.MarkProperty("RowIdentifier", offset + RowIdOffset, 8); 41 | 42 | field.Links.Add(link); 43 | } 44 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Pages/Parsers/AllocationPageParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using InternalsViewer.Internals.Engine.Pages; 3 | using InternalsViewer.Internals.Engine.Pages.Enums; 4 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 5 | 6 | namespace InternalsViewer.Internals.Services.Pages.Parsers; 7 | 8 | /// 9 | /// Parser for Allocation pages 10 | /// 11 | public class AllocationPageParser : PageParser, IPageParser 12 | { 13 | public PageType[] SupportedPageTypes => new[] { PageType.Gam, PageType.Sgam, PageType.Bcm, PageType.Dcm }; 14 | 15 | Page IPageParser.Parse(PageData page) 16 | { 17 | return Parse(page); 18 | } 19 | 20 | public AllocationPage Parse(PageData page) 21 | { 22 | var allocationPage = CopyToPageType(page); 23 | 24 | return Parse(allocationPage); 25 | } 26 | 27 | private static AllocationPage Parse(AllocationPage page) 28 | { 29 | var allocationData = new byte[AllocationPage.AllocationInterval / 8]; 30 | 31 | Array.Copy(page.Data, 32 | AllocationPage.AllocationArrayOffset, 33 | allocationData, 34 | 0, 35 | allocationData.Length); 36 | 37 | var bitArray = new BitArray(allocationData); 38 | 39 | bitArray.CopyTo(page.AllocationMap, 0); 40 | 41 | return page; 42 | } 43 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Pages/Parsers/DataPageParser.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Database; 2 | using InternalsViewer.Internals.Engine.Pages; 3 | using InternalsViewer.Internals.Engine.Pages.Enums; 4 | using InternalsViewer.Internals.Engine.Records.CdRecordType; 5 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 6 | using InternalsViewer.Internals.Services.Loaders.Compression; 7 | 8 | namespace InternalsViewer.Internals.Services.Pages.Parsers; 9 | 10 | /// 11 | /// Parser for Data pages 12 | /// 13 | public class DataPageParser(CompressionInfoLoader compressionInfoLoader) : PageParser, IPageParser 14 | { 15 | private CompressionInfoLoader CompressionInfoLoader { get; } = compressionInfoLoader; 16 | 17 | public PageType[] SupportedPageTypes => new[] { PageType.Data }; 18 | 19 | Page IPageParser.Parse(PageData page) 20 | { 21 | return Parse(page); 22 | } 23 | 24 | public DataPage Parse(PageData page) 25 | { 26 | var dataPage = CopyToPageType(page); 27 | 28 | dataPage.AllocationUnit = dataPage.Database 29 | .AllocationUnits 30 | .FirstOrDefault(a => a.AllocationUnitId == dataPage.PageHeader.AllocationUnitId) 31 | ?? AllocationUnit.Unknown; 32 | 33 | if(dataPage.AllocationUnit.CompressionType == CompressionType.Page && dataPage.IsPageCompressed) 34 | { 35 | dataPage.CompressionInfo = CompressionInfoLoader.Load(dataPage, CompressionInfo.SlotOffset); 36 | } 37 | 38 | return dataPage; 39 | } 40 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Pages/Parsers/EmptyPageParser.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Pages; 2 | using InternalsViewer.Internals.Engine.Pages.Enums; 3 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 4 | 5 | namespace InternalsViewer.Internals.Services.Pages.Parsers; 6 | 7 | public class EmptyPageParser : PageParser, IPageParser 8 | { 9 | public PageType[] SupportedPageTypes => new[] { PageType.None }; 10 | 11 | Page IPageParser.Parse(PageData page) 12 | { 13 | return Parse(page); 14 | } 15 | 16 | public EmptyPage Parse(PageData page) 17 | { 18 | var emptyPage = CopyToPageType(page); 19 | 20 | return emptyPage; 21 | } 22 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Pages/Parsers/FileHeaderPageParser.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Pages; 2 | using InternalsViewer.Internals.Engine.Pages.Enums; 3 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 4 | 5 | namespace InternalsViewer.Internals.Services.Pages.Parsers; 6 | 7 | public class FileHeaderPageParser: PageParser, IPageParser 8 | { 9 | public PageType[] SupportedPageTypes => new[] { PageType.FileHeader }; 10 | 11 | public FileHeaderPage Parse(PageData page) 12 | { 13 | var fileHeaderPage = CopyToPageType(page); 14 | 15 | return fileHeaderPage; 16 | } 17 | 18 | Page IPageParser.Parse(PageData page) 19 | { 20 | return Parse(page); 21 | } 22 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Pages/Parsers/IndexPageParser.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Database; 2 | using InternalsViewer.Internals.Engine.Pages; 3 | using InternalsViewer.Internals.Engine.Pages.Enums; 4 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 5 | 6 | namespace InternalsViewer.Internals.Services.Pages.Parsers; 7 | 8 | /// 9 | /// Parser for Index pages 10 | /// 11 | public class IndexPageParser : PageParser, IPageParser 12 | { 13 | public PageType[] SupportedPageTypes => new[] { PageType.Index }; 14 | 15 | Page IPageParser.Parse(PageData page) 16 | { 17 | return Parse(page); 18 | } 19 | 20 | public IndexPage Parse(PageData page) 21 | { 22 | var indexPage = CopyToPageType(page); 23 | 24 | indexPage.AllocationUnit = indexPage.Database 25 | .AllocationUnits 26 | .FirstOrDefault(a => a.AllocationUnitId == indexPage.PageHeader.AllocationUnitId) 27 | ?? AllocationUnit.Unknown; 28 | 29 | return indexPage; 30 | } 31 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Pages/Parsers/LobPageParser.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Pages; 2 | using InternalsViewer.Internals.Engine.Pages.Enums; 3 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 4 | 5 | namespace InternalsViewer.Internals.Services.Pages.Parsers; 6 | 7 | /// 8 | /// Parser for LOB (Large Object Data) pages 9 | /// 10 | public class LobPageParser : PageParser, IPageParser 11 | { 12 | public PageType[] SupportedPageTypes => new[] { PageType.Lob3, PageType.Lob4 }; 13 | 14 | Page IPageParser.Parse(PageData page) 15 | { 16 | return Parse(page); 17 | } 18 | 19 | public LobPage Parse(PageData page) 20 | { 21 | var lobPage = CopyToPageType(page); 22 | 23 | return lobPage; 24 | } 25 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Pages/Parsers/PageParser.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Pages; 2 | 3 | namespace InternalsViewer.Internals.Services.Pages.Parsers; 4 | 5 | public abstract class PageParser 6 | { 7 | protected T CopyToPageType(PageData pageData) where T : Page, new() 8 | { 9 | var page = new T 10 | { 11 | PageHeader = pageData.PageHeader, 12 | Database = pageData.Database, 13 | Data = pageData.Data, 14 | PageAddress = pageData.PageAddress, 15 | OffsetTable = pageData.OffsetTable, 16 | }; 17 | 18 | return page; 19 | } 20 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/Services/Pages/Parsers/PfsPageParser.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Allocation.Enums; 2 | using InternalsViewer.Internals.Engine.Pages; 3 | using InternalsViewer.Internals.Engine.Pages.Enums; 4 | using InternalsViewer.Internals.Engine.Parsers; 5 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages; 6 | 7 | namespace InternalsViewer.Internals.Services.Pages.Parsers; 8 | 9 | /// 10 | /// Parser for PFS (Page Free Space) pages 11 | /// 12 | public class PfsPageParser: PageParser, IPageParser 13 | { 14 | private const int PfsOffset = 100; // PFS byte array starts at offset 100 15 | private const int PfsSize = 8088; // PFS byte array is 8088 bytes/pages 16 | 17 | public PageType[] SupportedPageTypes => new[] { PageType.Pfs }; 18 | 19 | Page IPageParser.Parse(PageData page) 20 | { 21 | return Parse(page); 22 | } 23 | 24 | public PfsPage Parse(PageData pageData) 25 | { 26 | var page = CopyToPageType(pageData); 27 | 28 | var pfsBytes = GetPfsBytes(page); 29 | 30 | page.PfsBytes = pfsBytes; 31 | 32 | return page; 33 | } 34 | 35 | /// 36 | /// Loads the PFS bytes collection from the page data 37 | /// 38 | private static List GetPfsBytes(PageData page) 39 | { 40 | var pfsData = new byte[PfsSize]; 41 | 42 | Array.Copy(page.Data, PfsOffset, pfsData, 0, PfsSize); 43 | 44 | return pfsData.Select(PfsByteParser.Parse).ToList(); 45 | } 46 | } -------------------------------------------------------------------------------- /src/InternalsViewer.Internals/TransactionLog/LogData.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using InternalsViewer.Internals.Engine.Pages; 3 | using InternalsViewer.Internals.Helpers; 4 | 5 | namespace InternalsViewer.Internals.TransactionLog; 6 | 7 | /// 8 | /// Log Data containing fragment of a page at a particular offset 9 | /// 10 | public class LogData 11 | { 12 | public ushort Offset { get; set; } 13 | 14 | public ushort Slot { get; set; } 15 | 16 | public byte[] Data { get; set; } = Array.Empty(); 17 | 18 | public LogSequenceNumber LogSequenceNumber { get; set; } 19 | 20 | public override string ToString() 21 | { 22 | return string.Format("LSN: {0} Slot: {1} Offset: {2} Data: {3}", 23 | LogSequenceNumber, 24 | Slot, 25 | Offset, 26 | StringHelpers.ToHexString(Data)); 27 | } 28 | 29 | /// 30 | /// Merges the data into a page 31 | /// 32 | /// The target page. 33 | public Page MergeData(Page page) 34 | { 35 | var dataOffset = page.OffsetTable[Slot] + Offset; 36 | 37 | var pageData = new byte[PageData.Size]; 38 | 39 | Array.Copy(page.Data, pageData, PageData.Size); 40 | 41 | Array.Copy(Data, 0, pageData, dataOffset, Data.Length); 42 | 43 | page.Data = pageData; 44 | 45 | return page; 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Activation/ActivationHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace InternalsViewer.UI.App.Activation; 4 | 5 | // Extend this class to implement new ActivationHandlers. See DefaultActivationHandler for an example. 6 | // https://github.com/microsoft/TemplateStudio/blob/main/docs/WinUI/activation.md 7 | public abstract class ActivationHandler : IActivationHandler 8 | where T : class 9 | { 10 | // Override this method to add the logic for whether to handle the activation. 11 | protected virtual bool CanHandleInternal(T args) => true; 12 | 13 | // Override this method to add the logic for your activation handler. 14 | protected abstract Task HandleInternalAsync(T args); 15 | 16 | public bool CanHandle(object args) => args is T && CanHandleInternal((args as T)!); 17 | 18 | public async Task HandleAsync(object args) => await HandleInternalAsync((args as T)!); 19 | } 20 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Activation/DefaultActivationHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace InternalsViewer.UI.App.Activation; 4 | 5 | public class DefaultActivationHandler : ActivationHandler 6 | { 7 | public DefaultActivationHandler() 8 | { 9 | } 10 | 11 | protected override bool CanHandleInternal(LaunchActivatedEventArgs args) 12 | { 13 | return false; 14 | } 15 | 16 | protected override async Task HandleInternalAsync(LaunchActivatedEventArgs args) 17 | { 18 | await Task.CompletedTask; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Activation/IActivationHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace InternalsViewer.UI.App.Activation; 4 | 5 | public interface IActivationHandler 6 | { 7 | bool CanHandle(object args); 8 | 9 | Task HandleAsync(object args); 10 | } 11 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/AppIcon16.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/AppIcon16.bmp -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/AppIcon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/AppIcon16.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/AppLogo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/AppLogo-512.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/AppLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/AppLogo.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/AppLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/Index32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/Index32.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/InternalsViewer.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/InternalsViewer.ico -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/TabIcons/Database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Database.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/TabIcons/Database16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Database16.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/TabIcons/Database32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Database32.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/TabIcons/Page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Page.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/TabIcons/Page16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Page16.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/TabIcons/Page32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Page32.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Assets/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/Untitled.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/Connections/BackupFileConnectionControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using InternalsViewer.UI.App.ViewModels.Connections; 3 | 4 | namespace InternalsViewer.UI.App.Controls.Connections; 5 | 6 | public sealed partial class BackupFileConnectionControl 7 | { 8 | public BackupFileConnectionViewModel ViewModel => (BackupFileConnectionViewModel)DataContext; 9 | 10 | public BackupFileConnectionControl() 11 | { 12 | InitializeComponent(); 13 | 14 | DataContext = new BackupFileConnectionViewModel(); 15 | } 16 | 17 | private void OpenButton_Click(object sender, RoutedEventArgs e) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/CopyButton.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Controls; 2 | using Microsoft.UI.Xaml.Media.Animation; 3 | 4 | namespace InternalsViewer.UI.App.Controls; 5 | 6 | public sealed class CopyButton : Button 7 | { 8 | public CopyButton() 9 | { 10 | DefaultStyleKey = typeof(CopyButton); 11 | } 12 | 13 | private void CopyButton_Click(object sender, RoutedEventArgs e) 14 | { 15 | if (GetTemplateChild("CopyToClipboardSuccessAnimation") is Storyboard storyBoard) 16 | { 17 | storyBoard.Begin(); 18 | } 19 | } 20 | 21 | protected override void OnApplyTemplate() 22 | { 23 | Click -= CopyButton_Click; 24 | 25 | base.OnApplyTemplate(); 26 | 27 | Click += CopyButton_Click; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/CopyTextBlock.xaml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/CopyTextBlock.xaml.cs: -------------------------------------------------------------------------------- 1 | using Windows.ApplicationModel.DataTransfer; 2 | 3 | namespace InternalsViewer.UI.App.Controls; 4 | 5 | public sealed partial class CopyTextBlock 6 | { 7 | public string Value 8 | { 9 | get { return (string)GetValue(ValueProperty); } 10 | set { SetValue(ValueProperty, value); } 11 | } 12 | 13 | private static readonly DependencyProperty ValueProperty = 14 | DependencyProperty.Register(nameof(Value), typeof(string), typeof(CopyTextBlock), new PropertyMetadata(string.Empty)); 15 | 16 | public CopyTextBlock() 17 | { 18 | InitializeComponent(); 19 | } 20 | 21 | private void CopyButton_Click(object sender, RoutedEventArgs e) 22 | { 23 | var value = (sender as CopyButton)?.Tag.ToString() ?? string.Empty; 24 | 25 | var package = new DataPackage(); 26 | 27 | package.SetText(value); 28 | 29 | Clipboard.SetContent(package); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/CursorUserControl.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Input; 2 | using Microsoft.UI.Xaml.Controls; 3 | 4 | namespace InternalsViewer.UI.App.Controls; 5 | 6 | /// 7 | /// User control with a public method for Change Cursor 8 | /// 9 | public class CursorUserControl: UserControl 10 | { 11 | protected void ChangeCursor(InputCursor cursor) 12 | { 13 | ProtectedCursor = cursor; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/ExceptionDialog.xaml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 17 | Error details: 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/ExceptionDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Controls; 2 | using System.Text; 3 | using Windows.ApplicationModel.DataTransfer; 4 | 5 | namespace InternalsViewer.UI.App.Controls; 6 | 7 | public sealed partial class ExceptionDialog 8 | { 9 | public string Message 10 | { 11 | get { return (string)GetValue(MessageProperty); } 12 | set { SetValue(MessageProperty, value); } 13 | } 14 | 15 | public static readonly DependencyProperty MessageProperty = 16 | DependencyProperty.Register(nameof(Message), typeof(string), typeof(ExceptionDialog), new PropertyMetadata(null)); 17 | 18 | public string StackTrace 19 | { 20 | get { return (string)GetValue(StackTraceProperty); } 21 | set { SetValue(StackTraceProperty, value); } 22 | } 23 | 24 | public static readonly DependencyProperty StackTraceProperty = 25 | DependencyProperty.Register(nameof(Title), typeof(string), typeof(ExceptionDialog), new PropertyMetadata(null)); 26 | 27 | public ExceptionDialog() 28 | { 29 | InitializeComponent(); 30 | } 31 | 32 | private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) 33 | { 34 | var text = new StringBuilder(); 35 | 36 | text.AppendLine(Message); 37 | text.AppendLine(); 38 | text.AppendLine(StackTrace); 39 | 40 | var package = new DataPackage(); 41 | 42 | package.SetText(text.ToString()); 43 | 44 | Clipboard.SetContent(package); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/Page/LabelTextBox.xaml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/Page/LabelTextBox.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.UI.App.Controls.Page; 2 | public sealed partial class LabelTextBox 3 | { 4 | public LabelTextBox() 5 | { 6 | InitializeComponent(); 7 | 8 | DataContext = this; 9 | } 10 | 11 | public string Label 12 | { 13 | get { return (string)GetValue(LabelProperty); } 14 | set { SetValue(LabelProperty, value); } 15 | } 16 | 17 | public static readonly DependencyProperty LabelProperty = DependencyProperty 18 | .Register(nameof(Label), 19 | typeof(string), 20 | typeof(LabelTextBox), 21 | PropertyMetadata.Create(() => "Label")); 22 | 23 | public string Text 24 | { 25 | get { return (string)GetValue(TextProperty); } 26 | set { SetValue(TextProperty, value); } 27 | } 28 | 29 | public static readonly DependencyProperty TextProperty = DependencyProperty 30 | .Register(nameof(Text), 31 | typeof(string), 32 | typeof(LabelTextBox), 33 | PropertyMetadata.Create(() => "TextBox")); 34 | 35 | public double LabelWidth 36 | { 37 | get { return (double)GetValue(LabelWidthProperty); } 38 | set { SetValue(LabelWidthProperty, value); } 39 | } 40 | 41 | public static readonly DependencyProperty LabelWidthProperty = DependencyProperty 42 | .Register(nameof(LabelWidth), 43 | typeof(double), 44 | typeof(LabelTextBox), 45 | PropertyMetadata.Create(() => 100)); 46 | } 47 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/PasswordDialog.xaml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Controls/PasswordDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.UI.App.Controls; 2 | 3 | public sealed partial class PasswordDialog 4 | { 5 | public string Password 6 | { 7 | get { return (string)GetValue(PasswordProperty); } 8 | set { SetValue(PasswordProperty, value); } 9 | } 10 | 11 | public static readonly DependencyProperty PasswordProperty = 12 | DependencyProperty.Register(nameof(Password), typeof(string), typeof(PasswordDialog), new PropertyMetadata(null)); 13 | 14 | public PasswordDialog() 15 | { 16 | InitializeComponent(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | // Global using directives 2 | 3 | global using Microsoft.UI.Xaml; -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | 5 | namespace InternalsViewer.UI.App.Helpers; 6 | 7 | public static class CollectionExtensions 8 | { 9 | public static ObservableCollection ToObservableCollection(this IEnumerable source) 10 | { 11 | if (source == null) 12 | { 13 | throw new ArgumentNullException(nameof(source)); 14 | } 15 | 16 | return new ObservableCollection(source); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/ConnectionHelper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Data.SqlClient; 2 | 3 | namespace InternalsViewer.UI.App.Helpers; 4 | 5 | internal static class ConnectionHelper 6 | { 7 | public static string SetPassword(string connectionString, string result) 8 | { 9 | var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString); 10 | 11 | connectionStringBuilder.Password = result; 12 | 13 | return connectionStringBuilder.ToString(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/Converters/ColorToSolidColorBrushValueConverter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Data; 2 | using System; 3 | using System.Drawing; 4 | using Microsoft.UI.Xaml.Media; 5 | 6 | namespace InternalsViewer.UI.App.Helpers.Converters; 7 | 8 | public class ColorToSolidColorBrushValueConverter : IValueConverter 9 | { 10 | /// 11 | /// Converts a Color (System.Drawing or Windows.UI) to a SolidColorBrush 12 | /// 13 | public object? Convert(object? value, Type targetType, object parameter, string language) 14 | { 15 | if (value is null) 16 | { 17 | return null; 18 | } 19 | 20 | if (value is Color drawingColour) 21 | { 22 | return new SolidColorBrush(Windows.UI.Color.FromArgb(drawingColour.A, 23 | drawingColour.R, 24 | drawingColour.G, 25 | drawingColour.B)); 26 | } 27 | 28 | if (value is Windows.UI.Color windowsColour) 29 | { 30 | return new SolidColorBrush(windowsColour); 31 | } 32 | 33 | throw new InvalidOperationException(@"Type cannot be converted - {value.GetType()}"); 34 | } 35 | 36 | public object ConvertBack(object value, Type targetType, object parameter, string language) 37 | { 38 | throw new NotImplementedException(); 39 | } 40 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/Converters/PageAddressToStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using InternalsViewer.Internals.Engine.Parsers; 3 | using Microsoft.UI.Xaml.Data; 4 | 5 | namespace InternalsViewer.UI.App.Helpers.Converters; 6 | 7 | public class PageAddressToStringConverter: IValueConverter 8 | { 9 | public object? Convert(object? value, Type targetType, object parameter, string language) 10 | { 11 | 12 | return value?.ToString() ?? string.Empty; 13 | } 14 | 15 | public object? ConvertBack(object? value, Type targetType, object parameter, string language) 16 | { 17 | if (value?.ToString() is null) 18 | { 19 | return null; 20 | } 21 | 22 | var stringValue = value.ToString(); 23 | 24 | if (PageAddressParser.TryParse(stringValue!, out var pageAddress)) 25 | { 26 | return pageAddress; 27 | } 28 | 29 | return null; 30 | } 31 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/Converters/RecordValueConverter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Data; 2 | using System; 3 | using System.Linq; 4 | using InternalsViewer.UI.App.Models.Index; 5 | 6 | namespace InternalsViewer.UI.App.Helpers.Converters; 7 | 8 | internal class RecordValueConverter: IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, string language) 11 | { 12 | if (value is not IndexRecordModel record) 13 | { 14 | return string.Empty; 15 | } 16 | 17 | var field = record.Fields.FirstOrDefault(f => f.Name == parameter.ToString()); 18 | 19 | return field?.Value ?? $"{ parameter} not found"; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, string language) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/FileHelpers.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using System.Text.Json; 4 | using System.Threading.Tasks; 5 | 6 | namespace InternalsViewer.UI.App.Helpers; 7 | 8 | public static class FileHelpers 9 | { 10 | public static async Task ReadFile(string folderPath, string fileName) 11 | { 12 | var path = Path.Combine(folderPath, fileName); 13 | 14 | if (File.Exists(path)) 15 | { 16 | var json = await File.ReadAllTextAsync(path); 17 | 18 | return JsonSerializer.Deserialize(json); 19 | } 20 | 21 | return default; 22 | } 23 | 24 | public static async Task SaveFile(string folderPath, string fileName, T content) 25 | { 26 | if (!Directory.Exists(folderPath)) 27 | { 28 | Directory.CreateDirectory(folderPath); 29 | } 30 | 31 | var fileContent = JsonSerializer.Serialize(content); 32 | 33 | await File.WriteAllTextAsync(Path.Combine(folderPath, fileName), fileContent, Encoding.UTF8); 34 | } 35 | 36 | public static void DeleteFile(string folderPath, string? filename) 37 | { 38 | if (filename != null && File.Exists(Path.Combine(folderPath, filename))) 39 | { 40 | File.Delete(Path.Combine(folderPath, filename)); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/LayoutHelpers.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Media; 2 | 3 | namespace InternalsViewer.UI.App.Helpers; 4 | 5 | public static class LayoutHelpers 6 | { 7 | public static T? FindParent(DependencyObject? source) where T : DependencyObject 8 | { 9 | var target = source; 10 | 11 | while (target != null && target is not T) 12 | { 13 | target = VisualTreeHelper.GetParent(target); 14 | } 15 | 16 | return target as T; 17 | } 18 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/RuntimeHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Text; 3 | 4 | namespace InternalsViewer.UI.App.Helpers; 5 | 6 | public static class RuntimeHelper 7 | { 8 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 9 | private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder? packageFullName); 10 | 11 | public static bool IsMsix 12 | { 13 | get 14 | { 15 | var length = 0; 16 | 17 | return GetCurrentPackageFullName(ref length, null) != 15700L; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/Selectors/MarkerTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Annotations; 2 | using InternalsViewer.UI.App.Models; 3 | using Microsoft.UI.Xaml.Controls; 4 | using System.Linq; 5 | 6 | namespace InternalsViewer.UI.App.Helpers.Selectors; 7 | 8 | public class MarkerTemplateSelector : DataTemplateSelector 9 | { 10 | public DataTemplate DefaultTemplate { get; set; } = null!; 11 | 12 | public DataTemplate PointerTemplate { get; set; } = null!; 13 | 14 | protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) 15 | { 16 | var marker = item as Marker; 17 | 18 | var pointerTypes = new[] 19 | { 20 | ItemType.DownPagePointer, 21 | ItemType.Rid, 22 | ItemType.HeaderPageAddress, 23 | ItemType.NextPage, 24 | ItemType.PreviousPage, 25 | }; 26 | 27 | if (pointerTypes.Any(p => p == marker?.Type)) 28 | { 29 | return PointerTemplate; 30 | } 31 | 32 | return DefaultTemplate; 33 | } 34 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Helpers/Selectors/SlotTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.UI.App.Models; 2 | using Microsoft.UI.Xaml.Controls; 3 | 4 | namespace InternalsViewer.UI.App.Helpers.Selectors; 5 | 6 | internal class SlotTemplateSelector : DataTemplateSelector 7 | { 8 | public DataTemplate SlotTemplate { get; set; } = null!; 9 | 10 | public DataTemplate ItemTemplate { get; set; } = null!; 11 | 12 | protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) 13 | { 14 | var slot = item as PageSlot; 15 | 16 | if (slot?.Index < 0) 17 | { 18 | return ItemTemplate; 19 | } 20 | 21 | return SlotTemplate; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/LargeTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-100.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/LargeTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-125.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/LargeTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-150.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/LargeTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-200.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/LargeTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-400.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SmallTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-100.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SmallTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-125.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SmallTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-150.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SmallTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-200.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SmallTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-400.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SplashScreen.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-100.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SplashScreen.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-125.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SplashScreen.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-150.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/SplashScreen.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-400.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-100.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-125.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-150.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-400.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-16.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-24.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-256.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-32.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-48.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-100.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-125.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-150.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-400.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-16.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-24.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-256.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-32.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-48.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/StoreLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-100.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/StoreLogo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-125.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/StoreLogo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-150.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/StoreLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-200.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/StoreLogo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-400.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-100.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-125.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-150.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-400.png -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Messages/ConnectMessages.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging.Messages; 2 | using InternalsViewer.UI.App.Models.Connections; 3 | 4 | namespace InternalsViewer.UI.App.Messages; 5 | 6 | public class ConnectServerMessage(string connectionString, RecentConnection recent) : AsyncRequestMessage 7 | { 8 | public string ConnectionString { get; } = connectionString; 9 | 10 | public RecentConnection Recent { get; } = recent; 11 | 12 | public bool IsPasswordRequired { get; set; } 13 | } 14 | 15 | public class ConnectFileMessage(string filename, RecentConnection recent) : AsyncRequestMessage 16 | { 17 | public string Filename { get; } = filename; 18 | 19 | public RecentConnection Recent { get; } = recent; 20 | } 21 | 22 | public class ConnectBackupMessage(string filename, RecentConnection recent) : AsyncRequestMessage 23 | { 24 | public string Filename { get; } = filename; 25 | 26 | public RecentConnection Recent { get; set; } = recent; 27 | } 28 | 29 | public class ConnectRecentMessage(RecentConnection recent) : AsyncRequestMessage 30 | { 31 | public RecentConnection Recent { get; set; } = recent; 32 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Messages/ExceptionMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommunityToolkit.Mvvm.Messaging.Messages; 3 | 4 | namespace InternalsViewer.UI.App.Messages; 5 | 6 | public class ExceptionMessage(Exception exception) : AsyncRequestMessage 7 | { 8 | public Exception Exception { get; } = exception; 9 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Messages/NavigateMessage.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging.Messages; 2 | 3 | namespace InternalsViewer.UI.App.Messages; 4 | 5 | public class NavigateMessage(string target) : ValueChangedMessage(target); -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Messages/OpenPageMessage.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging.Messages; 2 | using InternalsViewer.Internals.Engine.Address; 3 | using InternalsViewer.Internals.Engine.Database; 4 | 5 | namespace InternalsViewer.UI.App.Messages; 6 | 7 | public class OpenPageRequest(DatabaseSource database, PageAddress pageAddress) 8 | { 9 | public PageAddress PageAddress { get; } = pageAddress; 10 | 11 | public DatabaseSource Database { get; } = database; 12 | 13 | public ushort? Slot { get; set; } 14 | } 15 | 16 | public class OpenIndexRequest(DatabaseSource database, PageAddress rootPage) 17 | { 18 | public DatabaseSource Database { get; } = database; 19 | 20 | public PageAddress RootPageAddress { get; } = rootPage; 21 | } 22 | 23 | public class OpenPageMessage(OpenPageRequest request) : AsyncRequestMessage 24 | { 25 | public OpenPageRequest Request { get; } = request; 26 | } 27 | 28 | public class OpenIndexMessage(OpenIndexRequest request) : AsyncRequestMessage 29 | { 30 | public OpenIndexRequest Request { get; } = request; 31 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/AllocationUnit.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace InternalsViewer.UI.App.Models; 4 | 5 | public class AllocationUnit: ObservableObject 6 | { 7 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/Connections/RecentConnection.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System; 3 | 4 | namespace InternalsViewer.UI.App.Models.Connections; 5 | 6 | public partial class RecentConnection : ObservableObject 7 | { 8 | [ObservableProperty] 9 | private string name = string.Empty; 10 | 11 | [ObservableProperty] 12 | private string connectionType = string.Empty; 13 | 14 | [ObservableProperty] 15 | private string value = string.Empty; 16 | 17 | [ObservableProperty] 18 | private string id = Guid.NewGuid().ToString(); 19 | 20 | public bool IsPasswordRequired { get; init; } 21 | } 22 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/Connections/ServerConnectionSettings.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.UI.App.Models.Connections; 2 | 3 | public class ServerConnectionSettings 4 | { 5 | public string InstanceName { get; init; } = string.Empty; 6 | 7 | public int AuthenticationType { get; init; } 8 | 9 | public string DatabaseName { get; init; } = string.Empty; 10 | 11 | public string UserId { get; init; } = string.Empty; 12 | } 13 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/DatabaseFile.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using InternalsViewer.UI.App.ViewModels.Database; 3 | 4 | namespace InternalsViewer.UI.App.Models; 5 | 6 | public partial class DatabaseFile(DatabaseTabViewModel parent) : ObservableObject 7 | { 8 | [ObservableProperty] 9 | private short fileId; 10 | 11 | [ObservableProperty] 12 | private int size; 13 | 14 | public DatabaseTabViewModel Parent { get; } = parent; 15 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/ExtentAllocation.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.UI.App.Models; 2 | 3 | public class ExtentAllocation(short fileId, int extentId) 4 | { 5 | public int ExtentId { get; } = extentId; 6 | 7 | public short FileId { get; } = fileId; 8 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/Index/IndexPageModel.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | 3 | namespace InternalsViewer.UI.App.Models.Index; 4 | 5 | public class IndexPageModel 6 | { 7 | public PageAddress PageAddress { get; set; } 8 | 9 | public PageAddress NextPage { get; set; } 10 | 11 | public PageAddress PreviousPage { get; set; } 12 | 13 | public string Name { get; set; } = string.Empty; 14 | 15 | public int Slots { get; set; } 16 | 17 | public int Level { get; set; } 18 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/Index/IndexRecordFieldModel.cs: -------------------------------------------------------------------------------- 1 | using System.Data; 2 | 3 | namespace InternalsViewer.UI.App.Models.Index; 4 | 5 | public class IndexRecordFieldModel 6 | { 7 | public string Name { get; set; } = string.Empty; 8 | 9 | public string Value { get; set; } = string.Empty; 10 | 11 | public SqlDbType DataType { get; set; } 12 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/Index/IndexRecordModel.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace InternalsViewer.UI.App.Models.Index; 9 | 10 | public class IndexRecordModel 11 | { 12 | public int Slot { get; set; } 13 | 14 | public PageAddress DownPagePointer { get; set; } = PageAddress.Empty; 15 | 16 | public RowIdentifier? RowIdentifier { get; set; } = RowIdentifier.Empty; 17 | 18 | public List Fields { get; set; } = new(); 19 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/MarkStyle.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI; 2 | using Microsoft.UI.Xaml.Media; 3 | 4 | namespace InternalsViewer.UI.App.Models; 5 | 6 | public class MarkStyle 7 | { 8 | /// 9 | /// Gets or sets the fore colour. 10 | /// 11 | /// The fore colour. 12 | public SolidColorBrush ForeColour { get; set; } = new(Colors.Black); 13 | 14 | /// 15 | /// Gets or sets the back colour. 16 | /// 17 | /// The back colour. 18 | public SolidColorBrush BackColour { get; set; } = new(Colors.Transparent); 19 | 20 | /// 21 | /// Gets or sets the alternate back colour. 22 | /// 23 | /// The alternate back colour. 24 | public SolidColorBrush AlternateBackColour { get; set; } = new(Colors.Transparent); 25 | 26 | public string Name { get; set; } = string.Empty; 27 | 28 | public int? Ordinal { get; set; } 29 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/Marker.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using InternalsViewer.Internals.Annotations; 5 | using InternalsViewer.UI.App.Services.Markers; 6 | using Windows.UI; 7 | 8 | namespace InternalsViewer.UI.App.Models; 9 | 10 | public class Marker: DependencyObject 11 | { 12 | public string Name { get; set; } = string.Empty; 13 | 14 | public int StartPosition { get; set; } 15 | 16 | public int EndPosition { get; set; } 17 | 18 | public int Length => EndPosition - StartPosition; 19 | 20 | public Color BackColour { get; set; } 21 | 22 | public Color AlternateBackColour { get; set; } 23 | 24 | public Color ForeColour { get; set; } 25 | 26 | public string Value { get; set; } = string.Empty; 27 | 28 | public bool IsNull { get; set; } 29 | 30 | public bool IsVisible { get; set; } = true; 31 | 32 | public MarkerType MarkerType { get; set; } 33 | 34 | public ItemType Type { get; set; } 35 | 36 | public int? Ordinal { get; set; } 37 | 38 | public List Tags { get; set; } = new(); 39 | 40 | public static Marker? GetMarkerAtPosition(int startPosition, int endPosition, List markers) 41 | { 42 | return markers.FirstOrDefault(marker => marker.StartPosition >= startPosition & marker.EndPosition <= endPosition); 43 | } 44 | 45 | public ObservableCollection Children { get; set; } = new(); 46 | 47 | public bool HasKey { get; set; } 48 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/Page/DecodePair.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.UI.App.Models.Page; 2 | 3 | internal class DecodePair 4 | { 5 | public string DataType { get; set; } = string.Empty; 6 | 7 | public string Value { get; set; } = string.Empty; 8 | } 9 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/PageBookmark.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.Internals.Engine.Address; 2 | 3 | namespace InternalsViewer.UI.App.Models; 4 | 5 | public record PageBookmark 6 | { 7 | public string DatabaseName { get; set; } = string.Empty; 8 | 9 | public PageAddress PageAddress { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/PageSlot.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.UI.App.Models; 2 | 3 | public class PageSlot 4 | { 5 | public short Index { get; init; } 6 | 7 | public ushort Offset { get; init; } 8 | 9 | public string Description { get; init; } = string.Empty; 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Models/SettingsOptions.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.UI.App.Models; 2 | 3 | public class SettingsOptions 4 | { 5 | public string ApplicationDataFolder { get; set; } = "InternalsViewer/ApplicationData"; 6 | 7 | public string SettingsFile { get; set; } = "LocalSettings.json"; 8 | } 9 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Properties/PublishProfiles/win-x64.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | FileSystem 8 | x64 9 | win-x64 10 | bin\\\win-x64\publish\ 11 | true 12 | false 13 | False 14 | true 15 | Release 16 | net8.0-windows10.0.19041.0 17 | false 18 | 22 | 23 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Properties/PublishProfiles/win-x86.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | FileSystem 8 | x86 9 | win-x86 10 | bin\\\win-x86\publish\ 11 | true 12 | false 13 | False 14 | true 15 | Release 16 | net8.0-windows10.0.19041.0 17 | false 18 | 22 | 23 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Local": { 4 | "commandName": "MsixPackage", 5 | "commandLineArgs": "", /* Command line arguments to pass to the app. */ 6 | "alwaysReinstallApp": false, /* Uninstall and then reinstall the app. All information about the app state is deleted. */ 7 | "remoteDebugEnabled": false, /* Indicates that the debugger should attach to a process on a remote machine. */ 8 | "allowLocalNetworkLoopbackProperty": true, /* Allow the app to make network calls to the device it is installed on. */ 9 | "authenticationMode": "Windows", /* The authentication scheme to use when connecting to the remote machine. */ 10 | "doNotLaunchApp": false, /* Do not launch the app, but debug my code when it starts. */ 11 | "remoteDebugMachine": "", /* The name of the remote machine. */ 12 | "nativeDebugging": false /* Enable debugging for managed and native code together, also known as mixed-mode debugging. */ 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Services/Markers/MarkStyleProvider.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.WinUI.Helpers; 2 | using InternalsViewer.Internals.Annotations; 3 | using InternalsViewer.UI.App.Models; 4 | using System.Diagnostics; 5 | 6 | namespace InternalsViewer.UI.App.Services.Markers; 7 | 8 | public class MarkStyleProvider 9 | { 10 | private ResourceDictionary? ThemeDictionary { get; set; } 11 | 12 | public MarkStyleProvider() 13 | { 14 | Initialize(); 15 | } 16 | 17 | private void Initialize() 18 | { 19 | var themeListener = new ThemeListener(); 20 | 21 | var currentTheme = themeListener.CurrentTheme; 22 | 23 | ThemeDictionary = Application.Current.Resources.ThemeDictionaries[currentTheme.ToString()] as ResourceDictionary; 24 | } 25 | 26 | public MarkStyle GetDefaultMarkStyle() 27 | { 28 | object? resource = null; 29 | 30 | ThemeDictionary?.TryGetValue("DefaultMarkerStyle", out resource); 31 | 32 | var style = resource as MarkStyle ?? new MarkStyle(); 33 | 34 | return style; 35 | } 36 | 37 | public MarkStyle GetMarkStyle(ItemType itemType) 38 | { 39 | object? resource = null; 40 | 41 | ThemeDictionary?.TryGetValue($"{itemType}MarkerStyle", out resource); 42 | 43 | if(Debugger.IsAttached && resource == null) 44 | { 45 | Debugger.Break(); 46 | } 47 | 48 | if (resource == null) 49 | { 50 | ThemeDictionary?.TryGetValue("DefaultMarkerStyle", out resource); 51 | } 52 | 53 | var style = resource as MarkStyle ?? new MarkStyle(); 54 | 55 | return style; 56 | } 57 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/ViewModels/Allocation/AllocationOverViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | using InternalsViewer.Internals.Engine.Allocation.Enums; 4 | 5 | namespace InternalsViewer.UI.App.ViewModels.Allocation; 6 | 7 | public partial class AllocationOverViewModel : ObservableObject 8 | { 9 | [ObservableProperty] 10 | private bool isOpen; 11 | 12 | [ObservableProperty] 13 | private int pageId; 14 | 15 | [ObservableProperty] 16 | private int extentId; 17 | 18 | [ObservableProperty] 19 | private Color layerColour = Color.Transparent; 20 | 21 | [ObservableProperty] 22 | private string layerName = string.Empty; 23 | 24 | [ObservableProperty] 25 | private PfsByte pfsValue = PfsByte.Unknown; 26 | } 27 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/ViewModels/Connections/BackupFileConnectionViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace InternalsViewer.UI.App.ViewModels.Connections; 4 | 5 | [ObservableObject] 6 | public partial class BackupFileConnectionViewModel 7 | { 8 | [ObservableProperty] 9 | private string filename = string.Empty; 10 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/ViewModels/Connections/ConnectFileViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | using CommunityToolkit.Mvvm.ComponentModel; 5 | using CommunityToolkit.Mvvm.Input; 6 | using CommunityToolkit.Mvvm.Messaging; 7 | using InternalsViewer.UI.App.Messages; 8 | using InternalsViewer.UI.App.Models.Connections; 9 | 10 | namespace InternalsViewer.UI.App.ViewModels.Connections; 11 | 12 | public static class ConnectFileViewModelFactory 13 | { 14 | public static ConnectFileViewModel Create() => new(); 15 | } 16 | 17 | public partial class ConnectFileViewModel : ObservableValidator 18 | { 19 | [Required(AllowEmptyStrings = false)] 20 | [ObservableProperty] 21 | private string filename = string.Empty; 22 | 23 | [ObservableProperty] 24 | private bool isValid; 25 | 26 | partial void OnFilenameChanged(string value) 27 | { 28 | IsValid = File.Exists(value); 29 | } 30 | 31 | [RelayCommand] 32 | private async Task Connect() 33 | { 34 | var recent = new RecentConnection 35 | { 36 | Name = Path.GetFileName(Filename), 37 | ConnectionType = "File", 38 | Value = Filename 39 | }; 40 | 41 | var message = new ConnectFileMessage(Filename, recent); 42 | 43 | await WeakReferenceMessenger.Default.Send(message); 44 | 45 | await message.Response; 46 | } 47 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/ViewModels/Page/HexControlViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System.Collections.ObjectModel; 3 | using CommunityToolkit.Mvvm.Input; 4 | using InternalsViewer.Internals.Converters.Decoder; 5 | 6 | namespace InternalsViewer.UI.App.ViewModels.Page; 7 | 8 | [ObservableObject] 9 | public partial class HexControlViewModel 10 | { 11 | [ObservableProperty] 12 | private bool isDataTipOpen; 13 | 14 | [ObservableProperty] 15 | private ObservableCollection decodeResults = new(); 16 | 17 | [ObservableProperty] 18 | private int startOffset; 19 | 20 | [ObservableProperty] 21 | private int endOffset; 22 | 23 | [ObservableProperty] 24 | private string selectedText = string.Empty; 25 | 26 | [ObservableProperty] 27 | private string dataTipTitle = string.Empty; 28 | 29 | partial void OnSelectedTextChanged(string value) 30 | { 31 | DecodeResults = new ObservableCollection(DataDecoder.Decode(value)); 32 | 33 | IsDataTipOpen = DecodeResults.Count > 0; 34 | } 35 | 36 | partial void OnStartOffsetChanged(int value) 37 | { 38 | SetDataTipTitle(); 39 | } 40 | 41 | partial void OnEndOffsetChanged(int value) 42 | { 43 | SetDataTipTitle(); 44 | } 45 | 46 | private void SetDataTipTitle() 47 | { 48 | DataTipTitle = $" {StartOffset} (0x{StartOffset:X8}) - {EndOffset} (0x{EndOffset:X8})"; 49 | } 50 | 51 | [RelayCommand] 52 | private void CloseDataTip() 53 | { 54 | IsDataTipOpen = false; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/ViewModels/Tabs/TabType.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.UI.App.ViewModels.Tabs; 2 | 3 | public enum TabType 4 | { 5 | Database, 6 | Page, 7 | Connect 8 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/ViewModels/Tabs/TabViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | using Microsoft.UI.Dispatching; 4 | 5 | namespace InternalsViewer.UI.App.ViewModels.Tabs; 6 | 7 | public partial class TabViewModel : ObservableObject 8 | { 9 | [ObservableProperty] 10 | private string tabId = string.Empty; 11 | 12 | [ObservableProperty] 13 | private string name = string.Empty; 14 | 15 | [ObservableProperty] 16 | private bool isLoading = true; 17 | 18 | protected DispatcherQueue DispatcherQueue { get; } 19 | 20 | protected TabViewModel() 21 | { 22 | DispatcherQueue = DispatcherQueue.GetForCurrentThread(); 23 | 24 | TabId = Guid.NewGuid().ToString(); 25 | } 26 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Views/Connect/ConnectBackupPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | This functionality is not yet implemented 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Views/Connect/ConnectBackupPage.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace InternalsViewer.UI.App.Views.Connect; 2 | 3 | public sealed partial class ConnectBackupPage 4 | { 5 | public ConnectBackupPage() 6 | { 7 | InitializeComponent(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Views/Connect/ConnectFilePage.xaml.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.UI.App.ViewModels.Connections; 2 | using Microsoft.UI.Xaml.Navigation; 3 | using System; 4 | using Windows.Storage.Pickers; 5 | 6 | namespace InternalsViewer.UI.App.Views.Connect; 7 | 8 | public sealed partial class ConnectFilePage 9 | { 10 | private ConnectFileViewModel ViewModel => (ConnectFileViewModel)DataContext; 11 | 12 | public ConnectFilePage() 13 | { 14 | InitializeComponent(); 15 | } 16 | 17 | protected override void OnNavigatedTo(NavigationEventArgs e) 18 | { 19 | base.OnNavigatedTo(e); 20 | 21 | if (e.Parameter is ConnectFileViewModel viewModel) 22 | { 23 | DataContext = viewModel; 24 | } 25 | } 26 | 27 | private async void BrowseButton_Click(object sender, RoutedEventArgs e) 28 | { 29 | var openPicker = new FileOpenPicker(); 30 | 31 | var window = App.MainWindow; 32 | 33 | var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window); 34 | 35 | WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd); 36 | 37 | openPicker.ViewMode = PickerViewMode.List; 38 | 39 | openPicker.FileTypeFilter.Add(".mdf"); 40 | 41 | var file = await openPicker.PickSingleFileAsync(); 42 | 43 | if (file != null) 44 | { 45 | ViewModel.Filename = file.Path; 46 | ViewModel.IsValid = true; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Views/Connect/ConnectServerPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using InternalsViewer.UI.App.ViewModels.Connections; 2 | using Microsoft.UI.Xaml.Navigation; 3 | 4 | namespace InternalsViewer.UI.App.Views.Connect; 5 | 6 | public sealed partial class ConnectServerPage 7 | { 8 | public ConnectServerPage() 9 | { 10 | InitializeComponent(); 11 | 12 | NavigationCacheMode = NavigationCacheMode.Enabled; 13 | } 14 | 15 | private ConnectServerViewModel ViewModel => (ConnectServerViewModel)DataContext; 16 | 17 | protected override void OnNavigatedTo(NavigationEventArgs e) 18 | { 19 | base.OnNavigatedTo(e); 20 | 21 | if (e.Parameter is ConnectServerViewModel viewModel) 22 | { 23 | DataContext = viewModel; 24 | } 25 | } 26 | 27 | private async void DatabaseComboBox_DropDownOpened(object? sender, object e) 28 | { 29 | await ViewModel.RefreshDatabases(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Views/Connect/ConnectStartPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging; 2 | using InternalsViewer.UI.App.Messages; 3 | using InternalsViewer.UI.App.ViewModels; 4 | 5 | namespace InternalsViewer.UI.App.Views.Connect; 6 | 7 | public sealed partial class ConnectStartPage 8 | { 9 | public ConnectStartPage() 10 | { 11 | InitializeComponent(); 12 | } 13 | 14 | public MainViewModel ViewModel => (MainViewModel)DataContext; 15 | 16 | private void ConnectFileTile_OnClick(object? sender, RoutedEventArgs e) 17 | { 18 | WeakReferenceMessenger.Default.Send(new NavigateMessage("ConnectFilePage")); 19 | } 20 | 21 | private void ConnectSqlServerHeaderTile_Click(object sender, RoutedEventArgs e) 22 | { 23 | WeakReferenceMessenger.Default.Send(new NavigateMessage("ConnectServerPage")); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Views/SettingsPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | Settings 14 | 18 | 19 | Created by Danny Gould 20 | Copyright © 2007 - 2024 21 | 22 | https://github.com/danny-sg/internals-viewer 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/Views/SettingsPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml.Controls; 2 | 3 | // To learn more about WinUI, the WinUI project structure, 4 | // and more about our project templates, see: http://aka.ms/winui-project-info. 5 | 6 | namespace InternalsViewer.UI.App.Views; 7 | 8 | /// 9 | /// An empty page that can be used on its own or navigated to within a Frame. 10 | /// 11 | public sealed partial class SettingsPage : Page 12 | { 13 | public SettingsPage() 14 | { 15 | this.InitializeComponent(); 16 | } 17 | } -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | PerMonitorV2 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/InternalsViewer.UI.App/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "LocalSettingsOptions": { 3 | "ApplicationDataFolder": "App1/ApplicationData", 4 | "LocalSettingsFile": "LocalSettings.json" 5 | }, 6 | "Logging": { 7 | "LogLevel": { 8 | "Default": "Debug", 9 | "Microsoft.AspNetCore": "Warning" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------