diff --git a/FirebirdRepairBase.dpr b/FirebirdRepairBase.dpr index 8674868..cdaec90 100644 --- a/FirebirdRepairBase.dpr +++ b/FirebirdRepairBase.dpr @@ -5,16 +5,17 @@ uses Windows, SysUtils, Dialogs, - main in 'main.pas' {frmMain} , - struck in 'struck.pas', + main in 'main.pas' {frmMain}, + uStructs in 'uStructs.pas', uCommon in 'uCommon.pas', - uDatabase in 'uDatabase.pas', - uHeaderPage in 'uHeaderPage.pas', - uPag in 'uPag.pas', uTipPage in 'uTipPage.pas', uDataPage in 'uDataPage.pas', uGenPage in 'uGenPage.pas', - uPoiner in 'uPoiner.pas'; + uPointer in 'uPointer.pas', + uPageAnalyzer in 'uPageAnalyzer.pas', + uBtreePage in 'uBtreePage.pas', + uDatabaseStats in 'uDatabaseStats.pas', + uFlagManager in 'uFlagManager.pas'; {$R *.res} @@ -23,7 +24,7 @@ begin Application.Initialize; Application.Title := 'FirebirdRepairBase'; Application.CreateForm(TfrmMain, frmMain); - Application.Run; + Application.Run; except on E: Exception do begin diff --git a/FirebirdRepairBase.dproj b/FirebirdRepairBase.dproj index ea77526..6f9a953 100644 --- a/FirebirdRepairBase.dproj +++ b/FirebirdRepairBase.dproj @@ -4,11 +4,12 @@ FirebirdRepairBase.dpr True Debug - 5121 + 1 Application VCL - 18.8 + 20.3 Win32 + FirebirdRepairBase true @@ -39,18 +40,6 @@ Base true - - true - Cfg_2 - true - true - - - true - Cfg_2 - true - true - true Cfg_2 @@ -81,7 +70,7 @@ CompanyName=formeo;FileDescription=Utilite for reparing FB Databases;FileVersion=0.1.0.35;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=Repare Firebird DB;ProductVersion=0.1.0.11;Comments= - Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) Debug true CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName) @@ -112,12 +101,6 @@ false true - - Debug - - - Debug - true PerMonitorV2 @@ -129,19 +112,16 @@
frmMain
- + - - - - - - Cfg_2 - Base - + + + + + Base @@ -149,6 +129,10 @@ Cfg_1 Base + + Cfg_2 + Base + Delphi.Personality.12 @@ -163,14 +147,971 @@ - True - True True False + + + + FirebirdRepairBase.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-anydpi-v21 + 1 + + + res\drawable-anydpi-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values-v31 + 1 + + + res\values-v31 + 1 + + + + + res\values-v35 + 1 + + + res\values-v35 + 1 + + + + + res\drawable-anydpi-v26 + 1 + + + res\drawable-anydpi-v26 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-anydpi-v33 + 1 + + + res\drawable-anydpi-v33 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-night-v21 + 1 + + + res\values-night-v21 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable-anydpi-v24 + 1 + + + res\drawable-anydpi-v24 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-night-anydpi-v21 + 1 + + + res\drawable-night-anydpi-v21 + 1 + + + + + res\drawable-anydpi-v31 + 1 + + + res\drawable-anydpi-v31 + 1 + + + + + res\drawable-night-anydpi-v31 + 1 + + + res\drawable-night-anydpi-v31 + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + + + + + + + + + + + 12 + diff --git a/FirebirdRepairBase.res b/FirebirdRepairBase.res index 5f77887..3bc1eaf 100644 Binary files a/FirebirdRepairBase.res and b/FirebirdRepairBase.res differ diff --git a/main.dfm b/main.dfm index 08c46bd..43dea48 100644 --- a/main.dfm +++ b/main.dfm @@ -5070,14 +5070,12 @@ object frmMain: TfrmMain 000000000000FFF300000FE30000018200000004000000080000001000008021 0000C0030000E0070000F0030000F0010000E0010000E1010000C3830000E7C7 0000FFE70000} - OldCreateOrder = False Position = poScreenCenter OnClose = FormClose OnShow = FormShow DesignSize = ( 1114 764) - PixelsPerInch = 96 TextHeight = 13 object pnl1: TPanel Left = 0 diff --git a/main.pas b/main.pas index e9cd91f..eceb210 100644 --- a/main.pas +++ b/main.pas @@ -4,25 +4,10 @@ interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, - Dialogs, StdCtrls, FileCtrl, ComCtrls, struck, DB, ExtCtrls, + Dialogs, StdCtrls, FileCtrl, ComCtrls, uStructs, DB, ExtCtrls, Menus, uCommon, uDatabase, XPMan, uDataPage, Grids, DBGrids, DBClient, - DBCtrls, Spin; - -type - PDpg_repeat = ^TDpg_rpt; - - trhd1 = record - rhd_transaction: SLONG; - rhd_b_page: SLONG; - rhd_b_line: USHORT; - rhd_flags: USHORT; - rhd_format: UCHAR; - end; - - Trhd_page = record - fix_data: trhd1; - rhd_data: array [0 .. (MAX_PAGE_SIZE - sizeof(trhd1))] of UCHAR; - end; + DBCtrls, Spin, Vcl.Buttons, + uPageAnalyzer, uDatabaseStats, uFlagManager; type TfrmMain = class(TForm) @@ -116,37 +101,54 @@ TfrmMain = class(TForm) procedure btnGotoPageClick(Sender: TObject); private - { Private declarations } + FPageAnalyzer: TPageAnalyzer; + FDatabaseStats: TDatabaseStats; + FFlagManager: TFlagManager; + FCurrentDBFileName: string; procedure clearUserGui; procedure checkDB; + procedure UpdateFlagCheckboxesFromManager; public end; var frmMain: TfrmMain; - new_header_page: THdrPage; - page_size_curr: integer; - RDatabase: TFBDatabase; implementation -uses uPag; - {$R *.dfm} procedure TfrmMain.btnOpenClick(Sender: TObject); begin if dlgOpenDB.Execute then begin - RDatabase := TFBDatabase.Create(dlgOpenDB.FileName); - lbNameDB.Caption := RDatabase.NameDB; - btnOpen.Enabled := False; - checkDB; + // Освобождаем предыдущую БД, если была + btnClearDBClick(Sender); // Вызовем старую логику очистки + + try + // Создаем анализатор + FPageAnalyzer := TPageAnalyzer.Create(dlgOpenDB.FileName); + // Создаем статистик + FDatabaseStats := TDatabaseStats.Create(FPageAnalyzer); + // Создаем менеджер флагов + FFlagManager := TFlagManager.Create(dlgOpenDB.FileName); + // Сохраняем имя файла + FCurrentDBFileName := dlgOpenDB.FileName; + + lbNameDB.Caption := FPageAnalyzer.FileName; + btnOpen.Enabled := False; + clearUserGui; // Очищаем UI перед анализом + checkDB; // Запускаем анализ через FDatabaseStats + except + on E: Exception do + begin + btnClearDBClick(Sender); // Очищаем, если возникла ошибка + raise; // Повторно выбрасываем исключение + end; + end; end; end; - - procedure TfrmMain.mniN1Click(Sender: TObject); begin if dlgSave1.Execute then @@ -155,197 +157,166 @@ procedure TfrmMain.mniN1Click(Sender: TObject); procedure TfrmMain.btnSeekClick(Sender: TObject); var - TypePage: integer; + PageNum: Int64; + PageInfo: TPageInfo; begin + // Блокируем элементы на время операции edtPageNumber.Enabled := False; btnSeek.Enabled := False; cbbTypePage.Enabled := False; edtCheckSum.Enabled := False; btnReWrite.Enabled := False; - TypePage := RDatabase.typeCurrPage(StrToIntDef(edtPageNumber.text, 0)); - if TypePage <= 0 then - cbbTypePage.text := 'Unknown' - else - cbbTypePage.ItemIndex := TypePage - 1; - edtCheckSum.text := - IntToStr(RDatabase.typePageChecksum(StrToIntDef(edtPageNumber.text, 0))); - - edtPageNumber.Enabled := true; - btnSeek.Enabled := true; - cbbTypePage.Enabled := true; - edtCheckSum.Enabled := true; - btnReWrite.Enabled := true; + try + PageNum := StrToIntDef(edtPageNumber.text, 0); + // Проверяем, в пределах ли номер страницы + if (PageNum < 0) or (PageNum > FPageAnalyzer.GetLastPageNumber) then + begin + cbbTypePage.text := 'Unknown (Out of bounds)'; + edtCheckSum.text := 'N/A'; + Exit; + end; + + PageInfo := FPageAnalyzer.GetPageInfo(PageNum); + + cbbTypePage.text := PageInfo.TypeStr; + edtCheckSum.text := IntToStr(PageInfo.Header.pag_checksum); + + finally + // Восстанавливаем доступность элементов + edtPageNumber.Enabled := true; + btnSeek.Enabled := true; + cbbTypePage.Enabled := true; + edtCheckSum.Enabled := true; + btnReWrite.Enabled := true; + end; end; procedure TfrmMain.btnReWriteClick(Sender: TObject); var - TypePage: ShortInt; - + PageNum: Int64; + NewChecksum: Integer; + NewType: Integer; + PageInfo: TPageInfo; + NewPageBuffer: TBytes; + FileStream: TFileStream; begin - // ReWriting page - if Application.MessageBox('Do You Want to Rewrite Database Page?', + // ReWriting page - ВНИМАНИЕ: Это ОПАСНАЯ операция! + if Application.MessageBox('Do You Want to Rewrite Database Page? THIS IS DANGEROUS!', 'ReWriting Page', MB_OKCANCEL + MB_ICONQUESTION) = 6 then begin - TypePage := RDatabase.typeCurrPage(StrToIntDef(edtPageNumber.text, 0)); - if RDatabase.ReWritePage(TypePage, StrToIntDef(edtPageNumber.text, 0), - StrToIntDef(edtCheckSum.text, 12345), StrToIntDef(edtNextTip.text, 0), - cbbTypePage.ItemIndex - 1) then - Application.MessageBox('Data ReWrited!', 'Information', - MB_OK + MB_ICONWARNING) - else - Application.MessageBox('Data Does Not ReWrited!', 'Information', - MB_OK + MB_ICONERROR); + PageNum := StrToIntDef(edtPageNumber.text, 0); + NewChecksum := StrToIntDef(edtCheckSum.text, 12345); + NewType := cbbTypePage.ItemIndex - 1; // Или как-то иначе, в зависимости от cbbTypePage + + try + // Получаем текущую страницу + PageInfo := FPageAnalyzer.GetPageInfo(PageNum); + NewPageBuffer := Copy(PageInfo.Buffer); // Копируем текущий буфер + + // Модифицируем заголовок в буфере + TPag(NewPageBuffer[0]).pag_checksum := UShort(NewChecksum); + if (NewType >= 0) and (NewType <= $FF) then // Проверяем диапазон + TPag(NewPageBuffer[0]).pag_type := SChar(NewType); + + // Записываем изменённый буфер обратно в файл + FileStream := TFileStream.Create(FPageAnalyzer.FileName, fmOpenReadWrite or fmShareExclusive); + try + FileStream.Seek(PageNum * FPageAnalyzer.PageSize, soFromBeginning); + if FileStream.Write(NewPageBuffer[0], FPageAnalyzer.PageSize) <> FPageAnalyzer.PageSize then + raise Exception.Create('Error writing page.'); + finally + FileStream.Free; + end; + + Application.MessageBox('Page ReWritten!', 'Information', MB_OK + MB_ICONWARNING); + except + on E: Exception do + begin + Application.MessageBox(PChar('Error rewriting page: ' + E.Message), 'Error', MB_OK + MB_ICONERROR); + end; + end; end end; +// btnGetDataClick: оставлен пустым или с комментарием, как и раньше procedure TfrmMain.btnGetDataClick(Sender: TObject); -var - i, DB, pCurrSize: integer; - DataData: TDataPage; begin - { pCurrSize:=getCurrPageSize(dlgOpenDB.FileName); - try - db := fileopen(dlgOpenDB.FileName, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, 745754*pCurrSize, 0); - i := fileread(db, DataData,pCurrSize); - - finally - FileClose(db ); - end; - pbData.Min:=0; - pbData.Max:=length(DataData.dpg_repeat)-1; - for i :=0 to length(DataData.dpg_repeat)-1 - do - begin - pbData.Position:=pbData.Position+1; - // mmoData.Text:= mmoData.Text+' '+ Chr(StrToInt('$'+ByteToHex(DataData.dpg_repeat[i]))); - Application.ProcessMessages; - // lstData.Items.Add(ByteToHex(DataData.rhd_data[i])); - // lstData.Items.Add(Chr(StrToInt('$'+ByteToHex(DataData.rhd_data[i])))); - end; - ShowMessage('Finished'); } + // Эта функция требует доработки или удаления + // Может быть использована для вывода сырых байтов страницы + lstLog.Items.Add('btnGetDataClick: Not implemented.'); end; +// btnPIPClick: оставлен пустым или с комментарием, как и раньше procedure TfrmMain.btnPIPClick(Sender: TObject); -var - FS: TFileStream; - HeaderPage: THdrPage; - DB, firstPointerPage, i: integer; - pPage: TPointer_page; - DPage: TPointer_page; - pCurrSize: integer; - DataData: TDataPage; - DataData2: Trhd_page; begin - { firstPointerPage:=-1; - pCurrSize:=getCurrPageSize(dlgOpenDB.FileName); - try - FS := TFileStream.Create(dlgOpenDB.FileName, fmOpenRead or fmShareDenyNone); - FS.Read(HeaderPage, pCurrSize); - finally - FS.Free; - end; - firstPointerPage:= HeaderPage.fix_data.hdr_pages; - try - db := fileopen(dlgOpenDB.FileName, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, firstPointerPage*pCurrSize, 0); - i := fileread(db, pPage, pCurrSize); - finally - FileClose(db ); - end; - - lstLog.Items.Add('Readed pointer Page '); - lstLog.Items.Add('RDB$PAGES состоит из '+inttostr(pPage.fix_data.ppg_count) + ' страниц' ); - - try - db := fileopen(dlgOpenDB.FileName, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, (5*pCurrSize), 0); - i := fileread(db, DataData, pCurrSize); - finally - FileClose(db); - end; - - try - db := fileopen(dlgOpenDB.FileName, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, (1878*pCurrSize)+4524, 0); - i := fileread(db, DataData2, 28); - finally - FileClose(db); - end; - pbData.Min:=0; - pbData.Max:=length(DataData2.rhd_data)-1; - for i :=0 to length(DataData2.rhd_data)-1 - do - begin - pbData.Position:=pbData.Position+1; - mmoData.Text:= mmoData.Text+' '+ Chr(StrToInt('$'+ByteToHex(DataData2.rhd_data[i]))); - Application.ProcessMessages; - // lstData.Items.Add(ByteToHex(DataData.rhd_data[i])); - // lstData.Items.Add(Chr(StrToInt('$'+ByteToHex(DataData.rhd_data[i])))); - end; - ShowMessage('Finished'); - { /* for i :=0 to 100 - do - begin - lstLog.Items.Add(ByteToHex(DataData.rhd_data[i])); - lstLog.Items.Add(Chr(StrToInt('$'+ByteToHex(DataData.rhd_data[i])))); - end; */ } - + // Эта функция требует доработки или удаления + lstLog.Items.Add('btnPIPClick: Not implemented.'); end; procedure TfrmMain.btnClearDBClick(Sender: TObject); begin - if Assigned(RDatabase) then - RDatabase.Free; + // Освобождаем ресурсы + FPageAnalyzer.Free; + FDatabaseStats.Free; + FFlagManager.Free; + FPageAnalyzer := nil; + FDatabaseStats := nil; + FFlagManager := nil; + FCurrentDBFileName := ''; + btnOpen.Enabled := true; + clearUserGui; end; procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction); begin - if Assigned(RDatabase) then - RDatabase.Free; + btnClearDBClick(Sender); // Очищаем ресурсы при закрытии формы end; procedure TfrmMain.btnGetHeaderFlagsClick(Sender: TObject); var - i: integer; + Flags: TDatabaseFlags; begin - // Hdr_flags: Two bytes, unsigned. Bytes 0x2a and 0x2b on the page. The database flags. - // lstPages.Items.Clear; - edtFlags.text := ''; - edtFlags.text := RDatabase.GetHeaderFlags; - if edtFlags.text[1] = '1' then - lstDBFlags.Items.Add('This file is an active shadow file'); - if edtFlags.text[2] = '1' then - lstDBFlags.Items.Add('The database is in forced writes mode'); - if edtFlags.text[4] = '1' then - lstDBFlags.Items.Add('Dont calculate checksums'); - if edtFlags.text[5] = '1' then - lstDBFlags.Items.Add('Dont reserve space for record versions in pages'); - - { https://firebirdsql.org/manual/fbint-page-1.html - hdr_active_shadow 0x01 (bit 0) This file is an active shadow file. - hdr_force_write 0x02 (bit 1) The database is in forced writes mode. - Unused 0x04 (bit 2) Was previously for short term journalling, no longer used. - Unused 0x08 (bit 3) Was previously for long term journalling, no longer used. - hdr_no_checksums 0x10 (bit 4) Don't calculate checksums. - hdr_no_reserve 0x20 (bit 5) Don'r reserve space for record versions in pages. - Unused 0x40 (bit 6) Was used to indicate that the shared cache file was disabled. - hdr_shutdown_mask (bit one of two) 0x1080 (bits 7 and 12) Used with bit 12 (see below) to indicate the database shutdown mode. - hdr_sql_dialect_3 0x100 (bit 8) If set, the database is using SQL dialect 3. - hdr_read_only 0x200 (bit 9) Database is in read only mode. - hdr_backup_mask 0xC00 (bits 10 and 11) Indicates the current backup mode. - hdr_shutdown_mask (bit two of two) 0x1080 (bits 7 and 12) Used with bit 7 (see above) to indicate the database shutdown mode - } - if edtFlags.text[9] = '1' then - lstDBFlags.Items.Add('Dialect 3'); - if edtFlags.text[10] = '1' then - lstDBFlags.Items.Add('Read Only'); + lstDBFlags.Items.Clear; // Очищаем перед добавлением + edtFlags.text := ''; // Очищаем поле + + if not Assigned(FFlagManager) then + begin + lstDBFlags.Items.Add('No database loaded.'); + Exit; + end; + + try + Flags := FFlagManager.GetFlags; + + // Формируем строку для отображения (например, бинарное представление или просто список) + // Для простоты, выведем список активных флагов + edtFlags.text := 'Active flags: '; + if Flags.ActiveShadow then edtFlags.text := edtFlags.text + 'ActiveShadow, '; + if Flags.ForceWrite then edtFlags.text := edtFlags.text + 'ForceWrite, '; + if Flags.NoChecksums then edtFlags.text := edtFlags.text + 'NoChecksums, '; + if Flags.NoReserve then edtFlags.text := edtFlags.text + 'NoReserve, '; + if Flags.SqlDialect3 then edtFlags.text := edtFlags.text + 'SqlDialect3, '; + if Flags.ReadOnly then edtFlags.text := edtFlags.text + 'ReadOnly, '; + // Убираем последнюю запятую + if edtFlags.text.EndsWith(', ') then + edtFlags.text := edtFlags.text.Remove(edtFlags.text.Length - 2); + + // Или можно вывести все флаги с указанием статуса + lstDBFlags.Items.Add(Format('Active Shadow: %s', [BoolToStr(Flags.ActiveShadow, True)])); + lstDBFlags.Items.Add(Format('Force Write: %s', [BoolToStr(Flags.ForceWrite, True)])); + lstDBFlags.Items.Add(Format('No Checksums: %s', [BoolToStr(Flags.NoChecksums, True)])); + lstDBFlags.Items.Add(Format('No Reserve: %s', [BoolToStr(Flags.NoReserve, True)])); + lstDBFlags.Items.Add(Format('SQL Dialect 3: %s', [BoolToStr(Flags.SqlDialect3, True)])); + lstDBFlags.Items.Add(Format('Read Only: %s', [BoolToStr(Flags.ReadOnly, True)])); + + except + on E: Exception do + begin + lstDBFlags.Items.Add('Error getting flags: ' + E.Message); + end; + end; end; procedure TfrmMain.clearUserGui; @@ -354,215 +325,74 @@ procedure TfrmMain.clearUserGui; edtFlags.text := ''; lstLog.Items.Clear; edtPageNumber.Enabled := False; - btnSeek.Enabled := False + btnSeek.Enabled := False; end; - procedure TfrmMain.checkDB; var - FS: TFileStream; - FileSize: Int64; - HeaderPage: THdrPage; - CountPIPPage: integer; - CountTIPPage: integer; - CountPointerPage: integer; - CountDataPage: integer; - CountIndexRootPage: integer; - CountBlobPage: integer; - CountGeneratorPage: integer; - CountWriteAheadLogPage: integer; - CountUnknownPage: integer; - CountRelations: integer; - NumRead, i: integer; - pages: tippage; - DataPage: TDataPage; - Relations: TStringList; + PageStats: TPageTypeStats; + TxStats: TTransactionStats; + RelStats: TRelationStatsArray; + i: Integer; begin - if not Assigned(RDatabase) then + if not Assigned(FDatabaseStats) then exit; - Relations := TStringList.Create; - Relations.Sorted := True; - Relations.Duplicates := dupIgnore; - lstLog.Items.Clear; - dsPages.Close; - lstLog.Items.Add('Data base: ' + RDatabase.NameDB); - lstLog.Items.Add('Database size: ' + IntToStr(RDatabase.DBFileSize)); - if RDatabase.Curr_page_size = 0 then - Application.MessageBox('Database page size is 0. ERROR!', 'Wrong page size', - MB_OK + MB_ICONERROR); - lstLog.Items.Add('Count Database Pages: ' + - floatToStr(Round(RDatabase.DBFileSize / RDatabase.Curr_page_size))); - lstLog.Items.Add('Database page size: ' + IntToStr(RDatabase.Curr_page_size)); - lstLog.Items.Add('Type pages detecting: '); - CountPIPPage := 0; - CountTIPPage := 0; - CountPointerPage := 0; - CountDataPage := 0; - CountIndexRootPage := 0; - CountBlobPage := 0; - CountGeneratorPage := 0; - CountWriteAheadLogPage := 0; - CountUnknownPage := 0; - CountRelations := 0; - pbDetectedPage.Min := 0; - pbDetectedPage.Max := Round(RDatabase.DBFileSize / RDatabase.Curr_page_size); - pbDetectedPage.Position := 1; - dsPages.CreateDataSet; - i := 1; - while not RDatabase.EmptyDataPage do - begin - DataPage := RDatabase.GetNextDataPage; - case DataPage.fix_data.pagHdr_Header.pag_type of - 1: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Header page'; - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Post; - lstLog.Items.Add('First pointer page number: '+inttostr(RDatabase.GetHeaderPage.fix_data.hdr_pages)); - end; - 2: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Page Inventory Page (PIP)'; - inc(CountPIPPage); - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Post; - end; - 3: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Page Inventory Page (TIP)'; - inc(CountTIPPage); - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Post; - end; - 4: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Pointer Page'; - inc(CountPointerPage); - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Post; - end; - 5: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Data Page'; - inc(CountDataPage); - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Fields[4].AsInteger := DataPage.fix_data.dpg_relation; - Relations.Add(intTostr(DataPage.fix_data.dpg_relation)); - dsPages.Post; - end; - 6: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Index Root Page'; - inc(CountIndexRootPage); - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Post; - end; - 7: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Index B-Tree Page'; - inc(CountBlobPage); - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Post; - end; - 8: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Blob Page'; - inc(CountBlobPage); - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Post; - end; - 9: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Generator Page'; - inc(CountGeneratorPage); - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Post; - end; - 10: - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Write Ahead Log page'; - inc(CountWriteAheadLogPage); - dsPages.Fields[2].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Fields[3].AsInteger := - DataPage.fix_data.pagHdr_Header.pag_checksum; - dsPages.Post; - end; - else - begin - dsPages.Insert; - dsPages.Fields[0].AsInteger := i; - dsPages.Fields[1].AsString := ' Unknown page'; - inc(CountUnknownPage); - dsPages.Fields[2].AsInteger := DataPage.fix_data.pagHdr_Header.pag_type; - dsPages.Post; - end + + try + lstLog.Items.Clear; + dsPages.Close; + dsPages.CreateDataSet; + + lstLog.Items.Add('Database: ' + FPageAnalyzer.FileName); + lstLog.Items.Add('Database size: ' + IntToStr(FPageAnalyzer.FileSize)); + lstLog.Items.Add('Database page size: ' + IntToStr(FPageAnalyzer.PageSize)); + lstLog.Items.Add('Scanning pages...'); + + // Вычисляем статистику через FDatabaseStats + FDatabaseStats.CalculateStats; + + // Получаем результаты + PageStats := FDatabaseStats.PageStats; + TxStats := FDatabaseStats.TransactionStats; + RelStats := FDatabaseStats.RelationStats; + + // Обновляем прогресс бар (для наглядности, хотя теперь это быстро) + pbDetectedPage.Min := 0; + pbDetectedPage.Max := 100; // Условно + pbDetectedPage.Position := 100; + + // Выводим результаты подсчета + lstLog.Items.Add('--- Page Type Summary ---'); + lstLog.Items.Add('Count Header Pages: ' + IntToStr(PageStats.HeaderPages)); + lstLog.Items.Add('Count Page Inventory Pages (PIP): ' + IntToStr(PageStats.PipPages)); + lstLog.Items.Add('Count Transaction Inventory Pages (TIP): ' + IntToStr(PageStats.TipPages)); + lstLog.Items.Add('Count Pointer Pages: ' + IntToStr(PageStats.PointerPages)); + lstLog.Items.Add('Count Data Pages: ' + IntToStr(PageStats.DataPages)); + lstLog.Items.Add('Count Index Root Pages: ' + IntToStr(PageStats.IndexRootPages)); + lstLog.Items.Add('Count Index B-Tree Pages: ' + IntToStr(PageStats.IndexBtreePages)); // Исправлено + lstLog.Items.Add('Count Blob Pages: ' + IntToStr(PageStats.BlobPages)); + lstLog.Items.Add('Count Generator Pages: ' + IntToStr(PageStats.GeneratorPages)); + lstLog.Items.Add('Count Write Ahead Log Pages: ' + IntToStr(PageStats.WalPages)); + lstLog.Items.Add('Count Unknown Pages: ' + IntToStr(PageStats.UnknownPages)); + + lstLog.Items.Add('--- Transaction Summary ---'); + lstLog.Items.Add('Count Active Transactions: ' + IntToStr(TxStats.Active)); + lstLog.Items.Add('Count Limbo Transactions: ' + IntToStr(TxStats.Limbo)); + lstLog.Items.Add('Count Dead Transactions: ' + IntToStr(TxStats.Dead)); + lstLog.Items.Add('Count Committed Transactions: ' + IntToStr(TxStats.Committed)); + lstLog.Items.Add('Total Transactions: ' + IntToStr(TxStats.Total)); + + lstLog.Items.Add('--- Relation Summary ---'); + lstLog.Items.Add('Count Relations: ' + IntToStr(Length(RelStats))); + for i := 0 to High(RelStats) do + begin + lstLog.Items.Add(Format(' Relation ID: %d, Page Count: %d, Record Fragments Count: %d', + [RelStats[i].RelationID, RelStats[i].PageCount, RelStats[i].RecordCount])); end; - inc(i); - pbDetectedPage.Position := i; - Application.ProcessMessages; + + finally + dsPages.Close; end; - lstLog.Items.Add('Count Page Inventory Pages (PIP) ' + - IntToStr(CountPIPPage)); - lstLog.Items.Add('Count Page Inventory Pages (TIP) ' + - IntToStr(CountTIPPage)); - lstLog.Items.Add('Count Pointer Page ' + IntToStr(CountPointerPage)); - lstLog.Items.Add('Count Data Pages ' + IntToStr(CountDataPage)); - lstLog.Items.Add('Count Index Root Pages ' + IntToStr(CountIndexRootPage)); - lstLog.Items.Add('Count Blob Pages ' + IntToStr(CountBlobPage)); - lstLog.Items.Add('Count Generator Pages ' + IntToStr(CountGeneratorPage)); - lstLog.Items.Add('Count Write Ahead Log Pages ' + - IntToStr(CountWriteAheadLogPage)); - lstLog.Items.Add('Count Unknown Pages ' + IntToStr(CountUnknownPage)); - lstLog.Items.Add('Count Relations ' + IntToStr(Relations.Count)); - FreeAndNil(Relations) end; procedure TfrmMain.FormShow(Sender: TObject); @@ -572,107 +402,139 @@ procedure TfrmMain.FormShow(Sender: TObject); pgcServices.pages[0].Show; end; -// Write flag function procedure TfrmMain.btnWriteFlagsClick(Sender: TObject); var - flags, resString: string; - s: Byte; - i: integer; + NewFlags: TDatabaseFlags; begin - flags := RDatabase.GetHeaderFlags; - if flags[1] = '1' then - lstDBFlags.Items.Add('This file is an active shadow file'); - if flags[2] = '1' then - lstDBFlags.Items.Add('The database is in forced writes mode'); - if flags[4] = '1' then - lstDBFlags.Items.Add('Dont calculate checksums'); - if flags[5] = '1' then - lstDBFlags.Items.Add('Dont reserve space for record versions in pages'); - if flags[9] = '1' then - lstDBFlags.Items.Add('Dialect 3'); - if flags[10] = '1' then - lstDBFlags.Items.Add('Read Only'); - if chkSetFW.Checked then - flags[2] := '1' - else - flags[2] := '0'; - if chkReadOnly.Checked then - flags[10] := '1' - else - flags[10] := '0'; - RDatabase.SetHeaderFlags(flags); + if not Assigned(FFlagManager) then + begin + Application.MessageBox('No database loaded.', 'Error', MB_OK + MB_ICONERROR); + Exit; + end; + + try + NewFlags := FFlagManager.GetFlags; + + // Обновляем флаги в соответствии с чекбоксами + NewFlags.ForceWrite := chkSetFW.Checked; + NewFlags.ReadOnly := chkReadOnly.Checked; + // Добавьте другие флаги по необходимости + + // Устанавливаем флаги через FFlagManager + FFlagManager.SetFlags(NewFlags); + + Application.MessageBox('Header flags updated!', 'Information', MB_OK + MB_ICONINFORMATION); + + UpdateFlagCheckboxesFromManager; + except + on E: Exception do + begin + Application.MessageBox(PChar('Error setting flags: ' + E.Message), 'Error', MB_OK + MB_ICONERROR); + end; + end; end; -procedure TfrmMain.tsFlagsShow(Sender: TObject); +procedure TfrmMain.UpdateFlagCheckboxesFromManager; var - flags: string; + CurrentFlags: TDatabaseFlags; begin - { flags:=RDatabase.GetHeaderFlags; - if edtFlags.Text[10] = '1' then chkReadOnly.Checked:=True; - if edtFlags.Text[2] = '1' then chkSetFW.Checked:=True; } + if not Assigned(FFlagManager) then Exit; + try + CurrentFlags := FFlagManager.GetFlags; + chkSetFW.Checked := CurrentFlags.ForceWrite; + chkReadOnly.Checked := CurrentFlags.ReadOnly; + except + on E: Exception do + begin + lstDBFlags.Items.Add('Error updating flags UI: ' + E.Message); + end; + end; end; +// tsFlagsShow - теперь использует UpdateFlagCheckboxesFromManager +procedure TfrmMain.tsFlagsShow(Sender: TObject); +begin + // Обновляем чекбоксы при показе вкладки + UpdateFlagCheckboxesFromManager; +end; + +// pgcServicesChange - обновляет MaxValue для sePosition procedure TfrmMain.pgcServicesChange(Sender: TObject); -var - flags: TCaption; begin - if tsFlags.Showing then - begin - if Assigned(RDatabase) then - begin - flags := RDatabase.GetHeaderFlags; - if flags[10] = '1' then - chkReadOnly.Checked := true; - if flags[2] = '1' then - chkSetFW.Checked := true; - end - end; if tsGenerateNewPage.Showing then - if Assigned(RDatabase) then + if Assigned(FPageAnalyzer) then begin - sePosition.MaxValue := - Round(RDatabase.DBFileSize / RDatabase.Curr_page_size); + sePosition.MaxValue := FPageAnalyzer.GetLastPageNumber; end; end; procedure TfrmMain.btnGenerateClick(Sender: TObject); begin - if RDatabase.GenerateNewPagePosition(cbbTypePageGen.ItemIndex, - sePosition.Value) = 1 then - Application.MessageBox('Page Added!', 'Information', MB_OK + MB_ICONWARNING) + // Эта функция требует доработки, возможно, через FPageAnalyzer или TFBDatabase + lstLog.Items.Add('btnGenerateClick: Not implemented via new modules yet.'); + // Если TFBDatabase.Create нужен для генерации, используйте его или добавьте логику в FPageAnalyzer + // Пример: + // if FCurrentDBFileName <> '' then + // begin + // var TempDB := TFBDatabase.Create(FCurrentDBFileName); + // try + // if TempDB.GenerateNewPagePosition(cbbTypePageGen.ItemIndex, sePosition.Value) = 1 then + // Application.MessageBox('Page Added!', 'Information', MB_OK + MB_ICONWARNING); + // finally + // TempDB.Free; + // end; + // end; end; procedure TfrmMain.btnGotoPageClick(Sender: TObject); var - DataPage: TDataPage; - i: integer; + PageNum: Int64; + PageInfo: TPageInfo; + PageFragments: TRecordFragmentsArray; + i, j: integer; begin - DataPage := RDatabase.GetDataPage(StrToIntDef(edtNPage.text, 0)); - if DataPage.fix_data.pagHdr_Header.pag_type <> 5 then + PageNum := StrToIntDef(edtNPage.text, 0); + + if (PageNum < 0) or (PageNum > FPageAnalyzer.GetLastPageNumber) then begin - Application.MessageBox('It is not DataPage, please select DataPage', - 'Information', MB_OK + MB_ICONWARNING); - exit; + Application.MessageBox('Page number out of bounds.', 'Error', MB_OK + MB_ICONERROR); + Exit; end; + try + PageInfo := FPageAnalyzer.GetPageInfo(PageNum); + + if PageInfo.Header.pag_type <> $05 then // 0x05 = Data Page + begin + Application.MessageBox('It is not a Data Page, please select a Data Page.', + 'Information', MB_OK + MB_ICONWARNING); + exit; + end; + btnGotoPage.Enabled := False; - DataPage := RDatabase.GetDataPage(StrToIntDef(edtNPage.text, 0)); - pbDataProgress.Min := 0; - pbDataProgress.Max := length(DataPage.dpg_repeat) - 1; + mmoData.Clear; // Очищаем Memo перед выводом + + // Используем функцию из uDataPage для извлечения фрагментов + PageFragments := ExtractDataFragments(PageInfo.Buffer, PageInfo.Size); - for i := 0 to length(DataPage.dpg_repeat)-1 do - begin - pbDataProgress.Position:=pbDataProgress.Position+1; - // mmoData.Text:= mmoData.Text+' '+ StrToInt('$'+ByteToHex(DataPage.dpg_repeat[i].dpg_offset)); - Application.ProcessMessages; + pbDataProgress.Min := 0; + pbDataProgress.Max := Length(PageFragments) - 1; // Количество фрагментов - // mmoData.Text:= mmoData.Text+' '+ByteToHex(DataPage.rhd_data[i]) ; - mmoData.Text:= mmoData.Text+' '+Chr(StrToInt('$'+ByteToHex(DataPage.dpg_repeat[i].dpg_offset))) ; - {lstData.Items.Add(ByteToHex(DataData.rhd_data[i])); - lstData.Items.Add(Chr(StrToInt('$'+ByteToHex(DataData.rhd_data[i]))));} + for i := 0 to High(PageFragments) do // Используем High для массива + begin + pbDataProgress.Position := i; + mmoData.Lines.Add('Fragment ' + IntToStr(i) + ', Offset: ' + IntToStr(PageFragments[i].Offset)); + var hexData: string := ''; + for j := 0 to High(PageFragments[i].Data) do + begin + hexData := hexData + IntToHex(PageFragments[i].Data[j], 2) + ' '; + end; + mmoData.Lines.Add(' Data (Hex): ' + Trim(hexData)); - end; + Application.ProcessMessages; + end; + mmoData.Lines.Add('--- End of Page Data ---'); finally btnGotoPage.Enabled := True; end; diff --git a/struck.pas b/struck.pas deleted file mode 100644 index a7854e4..0000000 --- a/struck.pas +++ /dev/null @@ -1,233 +0,0 @@ -unit struck; - -interface - -uses - SysUtils, Classes; - -const - cMAX_PAGE_SIZE = 32768; - -type - SChar = Shortint; - SShort = Smallint; - UShort = Word; - SLong = Longint; - ULong = LongWord; - - TPag = packed record - - pag_type: SChar; - - pag_flags: SChar; - - pag_checksum: UShort; - - pag_generation: ULong; - - pag_seqno: ULong; - - pg_offset: ULong; - - end; - - THdr = packed record - - hdr_header: TPag; - - hdr_page_size: UShort; - - hdr_ods_version: UShort; - - hdr_pages: SLong; - - hdr_next_page: ULong; - - hdr_oldest_transaction: SLong; - - hdr_oldest_active: SLong; - - hdr_next_transaction: SLong; - - hdr_sequence: UShort; - - hdr_flags: UShort; - - hdr_creation_date: array [0 .. 1] of SLong; - - hdr_attachment_id: SLong; - - hdr_shadow_count: SLong; - - hdr_implementation: SShort; - - hdr_ods_minor: UShort; - - hdr_ods_minor_original: UShort; - - hdr_end: UShort; - - hdr_page_buffers: ULong; - - hdr_bumped_transaction: SLong; - - hdr_oldest_snapshot: SLong; - - hdr_misc: array [0 .. 3] of SLong; - - end; - - THdrPage = packed record - - fix_data: THdr; - - var_data: array [0 .. cMAX_PAGE_SIZE - 1 - SizeOf(THdr)] of Byte; - - end; - - - - // Not IB related - - EGDBError = class(Exception); - - PGDBFile = ^TGDBFileInfo; - - TGDBFileInfo = record - Header: THdr; - Filename: ShortString; - ContinuationFile: ShortString; - FirstLogicalPage: LongWord; - LastLogicalPage: LongWord; - TotalPages: LongWord; - end; - - TGDBInfo = class - private - FList: TList; - - FFilename: string; - - procedure GetDBFiles; - - function GetItem(I: Integer): TGDBFileInfo; - - protected - - public - - constructor Create(const AFilename: string); - - destructor Destroy; override; - - function Count: Integer; - - property Items[I: Integer]: TGDBFileInfo read GetItem; default; - - end; - -implementation - -{ TGDBInfo } - -function TGDBInfo.Count: Integer; -begin - Result := FList.Count; -end; - -constructor TGDBInfo.Create(const AFilename: string); -begin - inherited Create; - FList := TList.Create; - FFilename := AFilename; - GetDBFiles; -end; - -destructor TGDBInfo.Destroy; -var - I: Integer; -begin - for I := Count - 1 downto 0 do - begin - FreeMem(FList[I]); - FList.Delete(I); - end; - inherited; -end; - -procedure TGDBInfo.GetDBFiles; -var - FS: TFileStream; - HeaderPage: THdrPage; - NewFile: PGDBFile; - CurrentFilename: ShortString; - FilenameSize: Byte; - StartPage: LongWord; - SourceDir: string; - DataOffset: Integer; -begin - if not FileExists(FFilename) then - raise EGDBError.Create('File does not exist - ' + FFilename); - - SourceDir := ExtractFilePath(FFilename); - if SourceDir = '' then - SourceDir := IncludeTrailingBackSlash(GetCurrentDir); - StartPage := 0; - CurrentFilename := SourceDir + ExtractFilename(FFilename); - repeat - FS := TFileStream.Create(CurrentFilename, fmOpenRead or fmShareDenyNone); - try - GetMem(NewFile, SizeOf(TGDBFileInfo)); - FS.Read(HeaderPage, SizeOf(HeaderPage)); - Move(HeaderPage, NewFile.Header, SizeOf(THdr)); - DataOffset := 0; - // Format of var_data is repeated - // 1 = Root file name - // 2 = Journal server - // 3 = Continuation file (this is the one we want) - // 4 = Last logical page - // 5 = Unlicensed accesses - // 6 = Sweep interval - // 7 = Replay logging file - // 11= Shared cache file - - while HeaderPage.var_data[DataOffset] <> 3 do - begin - if HeaderPage.var_data[DataOffset + 1] = 0 then - Break; - Inc(DataOffset, HeaderPage.var_data[DataOffset + 1] + 2); - if DataOffset > HeaderPage.fix_data.hdr_page_size - - SizeOf(HeaderPage.fix_data) then - raise EGDBError.Create('Continuation'); - end; - FilenameSize := HeaderPage.var_data[DataOffset + 1]; - NewFile.Filename := CurrentFilename; - SetLength(NewFile.ContinuationFile, FilenameSize); - if FilenameSize > 0 then - Move(HeaderPage.var_data[DataOffset + 2], NewFile.ContinuationFile[1], - FilenameSize); - NewFile.FirstLogicalPage := StartPage; - Move(HeaderPage.var_data[DataOffset + FilenameSize + 4], - NewFile.LastLogicalPage, SizeOf(LongWord)); - NewFile.TotalPages := NewFile.LastLogicalPage - NewFile.FirstLogicalPage; - Inc(StartPage, NewFile.TotalPages); - FList.Add(NewFile); - CurrentFilename := NewFile.ContinuationFile; - if CurrentFilename = '' then - begin - NewFile.LastLogicalPage := 0; - NewFile.TotalPages := 0; - Break; - end; - finally - FS.Free; - end; - until False; -end; - -function TGDBInfo.GetItem(I: Integer): TGDBFileInfo; -begin - Result := PGDBFile(FList[I])^; -end; - -end. diff --git a/uCommon.pas b/uCommon.pas index d6918a9..a55df4d 100644 --- a/uCommon.pas +++ b/uCommon.pas @@ -3,125 +3,70 @@ interface uses - Classes, Windows, SysUtils; + Classes, Windows, SysUtils, uStructs; const MAX_PAGE_SIZE = 32768; MIN_PAGE_SIZE = 1024; type - SChar = Shortint; - SShort = Smallint; - UShort = Word; - SLong = Longint; - ULong = LongWord; - - TPag = packed record - pag_type: SChar; - pag_flags: SChar; - pag_checksum: UShort; - pag_generation: ULong; - pag_seqno: ULong; - pg_offset: ULong; + trhd1 = record + rhd_transaction: SLong; + rhd_b_page: SLong; + rhd_b_line: UShort; + rhd_flags: UShort; + rhd_format: UChar; // Используем UChar из uStructs + end; + + // Возможно, нестандартная структура, используем TPag из uStructs, UChar из uStructs + Trhd_page = record + fix_data: trhd1; + rhd_data: array [0 .. (MAX_PAGE_SIZE - sizeof(trhd1))] of UChar; // Ошибка размера, как и в старом коде end; + // --- ИСПРАВЛЕНО: Используем TPag из uStructs --- tip = packed record - tip_header: TPag; + tip_header: TPag; // Используем TPag из uStructs tip_next: SLong; end; - tippage = packed record + // --- ИСПРАВЛЕНО: Используем TPag из uStructs --- + // Массив tip_transactions должен быть динамическим при чтении страницы + // Эта структура определяет *фиксированную* часть + tippage_fixed_part = packed record fix_data: tip; - tip_transactions: array [0 .. (4096 - sizeof(tip))] of UCHAR; - end; - - THdr = packed record - hdr_header: TPag; - hdr_page_size: UShort; - hdr_ods_version: UShort; - hdr_pages: SLong; - hdr_next_page: ULong; - hdr_oldest_transaction: SLong; - hdr_oldest_active: SLong; - hdr_next_transaction: SLong; - hdr_sequence: UShort; - hdr_flags: UShort; - hdr_creation_date: array [0 .. 1] of SLong; - hdr_attachment_id: SLong; - hdr_shadow_count: SLong; - hdr_implementation: SShort; - hdr_ods_minor: UShort; - hdr_ods_minor_original: UShort; - hdr_end: UShort; - hdr_page_buffers: ULong; - hdr_bumped_transaction: SLong; - hdr_oldest_snapshot: SLong; - hdr_misc: array [0 .. 3] of SLong; - end; - - THdrPage = packed record - fix_data: THdr; - var_data: array [0 .. (MAX_PAGE_SIZE - sizeof(THdr))] of UCHAR; + // tip_transactions: array [0 .. N-1] of UCHAR; // Добавляется динамически end; + // --- ИСПРАВЛЕНО: Используем TPag из uStructs --- Tgenerator_page = record - gpg_header: TPag; + gpg_header: TPag; // Используем TPag из uStructs gpg_sequence: ULong; // Sequence number - // Generator vector end; - Tgnrtr_page = record + // --- ИСПРАВЛЕНО: Используем TPag из uStructs --- + // Массив gpg_values должен быть динамическим при чтении страницы + Tgnrtr_page_fixed_part = record fix_data1: Tgenerator_page; // Sequence number - gpg_values: array [0 .. (MAX_PAGE_SIZE - sizeof(Tgenerator_page))] of Int64; - // Generator vector - end; - - PDpg_repeat = ^TDpg_rpt; - - TDpg_rpt = record - dpg_offset: Word; - dpg_length: Word; - end; - - TData_Page = record - pagHdr_Header: TPag; - dpg_sequence: Longint; - dpg_relation: Word; - dpg_count: Word; - end; - - TDataPage = record - fix_data: TData_Page; - dpg_repeat: array [0 .. (MAX_PAGE_SIZE - sizeof(TData_Page))] of TDpg_rpt; - end; - - trhd1 = record - rhd_transaction: SLong; - rhd_b_page: SLong; - rhd_b_line: UShort; - rhd_flags: UShort; - rhd_format: UCHAR; - end; - - Trhd_page = record - fix_data: trhd1; - rhd_data: array [0 .. (MAX_PAGE_SIZE - sizeof(trhd1))] of UCHAR; + // gpg_values: array [0 .. N-1] of Int64; // Добавляется динамически end; + // --- ИСПРАВЛЕНО: Используем TPag из uStructs --- Tpnr_page = record - pp_header: TPag; + pp_header: TPag; // Используем TPag из uStructs ppg_sequence: SLong; ppg_next: SLong; ppg_count: UShort; ppg_relation: UShort; ppg_min_space: UShort; ppg_max_space: UShort; - end; - TPointer_page = record + // --- ИСПРАВЛЕНО: Используем TPag из uStructs --- + // Массив ppg_page должен быть динамическим при чтении страницы + TPointer_page_fixed_part = record fix_data: Tpnr_page; - ppg_page: array [0 .. (MAX_PAGE_SIZE - sizeof(Tpnr_page))] of SLong; + // ppg_page: array [0 .. N-1] of SLong; // Добавляется динамически end; function getCurrPageSize(const fileName: string): Integer; @@ -287,10 +232,11 @@ function GetFileSizeBase(const AFileName: String): Int64; end; end; + procedure CopyDatabaseFile(ADatabaseOriginalPath: string; ADatabaseCopyPath: String); const - BUFFER_SIZE = 10240; // 10KB + BUFFER_SIZE = 10240; var FromFile, ToFile: TFileStream; Buffer: array [0 .. BUFFER_SIZE - 1] of byte; @@ -301,7 +247,6 @@ procedure CopyDatabaseFile(ADatabaseOriginalPath: string; FromFile := TFileStream.Create(ADatabaseOriginalPath, fmOpenRead or fmShareDenyNone); try - // Создадим или перезапишем целевой файл if FileExists(ADatabaseCopyPath) then ToFile := TFileStream.Create(ADatabaseCopyPath, fmOpenReadWrite or fmShareDenyWrite) @@ -315,6 +260,7 @@ procedure CopyDatabaseFile(ADatabaseOriginalPath: string; NumRead := FromFile.Read(Buffer[0], BUFFER_SIZE); while NumRead > 0 do begin + ToFile.Write(Buffer[0], NumRead); CopiedSize := CopiedSize + NumRead; NumRead := FromFile.Read(Buffer[0], BUFFER_SIZE); end; diff --git a/uDataPage.pas b/uDataPage.pas index e0fbbc4..39c1d3b 100644 --- a/uDataPage.pas +++ b/uDataPage.pas @@ -1,45 +1,114 @@ unit uDataPage; -{ -Dpg_header: The page starts with a standard page header. In this page type, the pag_flags byte is used as follows: - Bit 0 - dpg_orphan. Setting this bit indicates that this page is an orphan - it has no entry in the pointer page for this relation. This may indicate a possible database corruption. - Bit 1 - dpg_full. Setting this bit indicates that the page is full up. This will be also seen in the bitmap array on the corresponding pointer page for this table. - Bit 2 - dpg_large. Setting this bit indicates that a large object is stored on this page. This will be also seen in the bitmap array on the corresponding pointer page for this table. - -Dpg_sequence: Four bytes, signed. Offset 0x10 on the page. This field holds the sequence number for this page in the list of pages assigned to this table within the database. The first page of any table has sequence zero. -Dpg_relation: Two bytes, unsigned. Offset 0x12 on the page. The relation number for this table. This corresponds to RDB$RELATIONS.RDB$RELATION_ID. -Dpg_count: Two bytes, unsigned. Offset 0x14 on the page. The number of records (or record fragments) on this page. In other words, the number of entries in the dpg_rpt array. -Dpg_rpt: This is an array of two byte unsigned values. The array begins at offset 0x18 on the page and counts upwards from the low address to the higher address as each new record fragment is added. - The two fields in this array are: -Dpg_offset: Two bytes, unsigned. The offset on the page where the record fragment starts. If the value here is zero and the length is zero, then this is an unused array entry. The offset is from the start address of the page. For example, if the offset is 0x0fc8 and this is a database with a 4Kb page size, and the page in question is page 0xcd (205 decimal) then we have the offset of 0xcdfc8 because 0xcd000 is the actual address (in the database file) of the start of the page. -Dpg_length: Two bytes, unsigned. The length of this record fragment in byte } - interface -uses uPag, Windows; +uses + Windows, SysUtils, uStructs, Math; const MAX_PAGE_SIZE = 32768; MIN_PAGE_SIZE = 1024; type - TDpg_rpt = record + TDpg_rpt = packed record dpg_offset: Word; dpg_length: Word; end; - TData_Page = record - pagHdr_Header: TPag; - dpg_sequence: Longint; - dpg_relation: Word; - dpg_count: Word; + + TData_Page_Fixed_Part = packed record + pagHdr_Header: TPag; // 0x00 - Стандартный заголовок (16 байт) + dpg_sequence: Longint; // 0x10 - Номер в последовательности (4 байта) + dpg_relation: Word; // 0x14 - ID отношения (2 байта) + dpg_count: Word; // 0x16 - Количество фрагментов (2 байта) end; - TDataPage = record - fix_data: TData_Page; - dpg_repeat: array [0 .. (MAX_PAGE_SIZE - sizeof(TData_Page))] of TDpg_rpt; + TRecordFragment = record + Offset: Word; + Data: TBytes; // Используем динамический массив байтов end; + TRecordFragmentsArray = array of TRecordFragment; +function ExtractDataFragments(const PageBuffer: TBytes; PageSize: Integer): TRecordFragmentsArray; + +// Функция для получения фиксированной части заголовка Data Page из буфера +function GetDataPageHeader(const PageBuffer: TBytes): TData_Page_Fixed_Part; implementation +function GetDataPageHeader(const PageBuffer: TBytes): TData_Page_Fixed_Part; +begin + if Length(PageBuffer) < SizeOf(TData_Page_Fixed_Part) then + raise Exception.Create('Page buffer too small for Data Page header.'); + + if PageBuffer[0] <> $05 then // pag_type == 0x05 + raise Exception.Create('Not a Data Page.'); + + // Используем Move для копирования байтов в структуру + Move(PageBuffer[0], Result, SizeOf(TData_Page_Fixed_Part)); +end; + +function ExtractDataFragments(const PageBuffer: TBytes; PageSize: Integer): TRecordFragmentsArray; +var + Header: TData_Page_Fixed_Part; + Fragments: TRecordFragmentsArray; + i, Count: Integer; // Тип для Count + ActualCount: Integer; // Тип для ActualCount + DpgRepeatStartOffset: Integer; + DataAreaStartOffset: Integer; // Новое: начало области данных (после dpg_repeat) + FragmentEntryOffset: Integer; + FragmentRec: TDpg_rpt; + FragmentStart, FragmentEnd: Integer; + FragmentLength: Word; + MaxPossibleCount: Integer; // Новое: максимальное возможное количество записей +begin + Result := nil; // Инициализация результата + + // Проверка на минимальный размер и соответствие размера буфера + if (PageSize < MIN_PAGE_SIZE) or (PageSize > MAX_PAGE_SIZE) or + (Length(PageBuffer) <> PageSize) then + begin + Exit; // Возвращаем nil, если размер неверен + end; + + // Получаем фиксированную часть заголовка + Header := GetDataPageHeader(PageBuffer); + + Count := Header.dpg_count; + + DpgRepeatStartOffset := SizeOf(TData_Page_Fixed_Part); // = 0x18 + + + MaxPossibleCount := (PageSize - DpgRepeatStartOffset) div SizeOf(TDpg_rpt); + + + ActualCount := IfThen(Count < MaxPossibleCount, Count, MaxPossibleCount); + + DataAreaStartOffset := DpgRepeatStartOffset + (ActualCount * SizeOf(TDpg_rpt)); + + SetLength(Fragments, ActualCount); + + for i := 0 to ActualCount - 1 do + begin + FragmentEntryOffset := DpgRepeatStartOffset + (i * SizeOf(TDpg_rpt)); + Move(PageBuffer[FragmentEntryOffset], FragmentRec, SizeOf(TDpg_rpt)); + FragmentStart := FragmentRec.dpg_offset; + FragmentLength := FragmentRec.dpg_length; + if (FragmentLength > 0) and + (FragmentStart >= DataAreaStartOffset) and + (FragmentStart <= Cardinal(PageSize - FragmentLength)) then + begin + Fragments[i].Offset := FragmentStart; + SetLength(Fragments[i].Data, FragmentLength); + Move(PageBuffer[FragmentStart], Fragments[i].Data[0], FragmentLength); + end + else + begin + Fragments[i].Offset := FragmentStart; + SetLength(Fragments[i].Data, 0); + end; + end; + + Result := Fragments; +end; + end. diff --git a/uDatabase.pas b/uDatabase.pas deleted file mode 100644 index b36413a..0000000 --- a/uDatabase.pas +++ /dev/null @@ -1,396 +0,0 @@ -unit uDatabase; - -interface - -uses classes, Sysutils, uCommon, uHeaderPage, uTipPage, uDataPage, uPoiner, - uGenPage; - -const - MAX_PAGE_SIZE = 32768; - MIN_PAGE_SIZE = 1024; - -type - TFBDatabase = class(TObject) - private - _NameDB: string; - _page_size_curr: integer; - HeaderPage: THdrPage; - _EmptyDataPage: Boolean; - _CurrentPageNumber: int64; - function GetCount: integer; - function GetNameDB: string; - function GetCurrPageSizeDB: integer; - procedure SetCurrPage; - public - constructor Create(nameDB: string); - destructor Destroy; override; - procedure AddValue(Value: string); - procedure Clear; - function DBFileSize: int64; - function typeCurrPage(PageNum: int64): integer; - function typePageChecksum(PageNum: int64): integer; - function GetNextDataPage: TDataPage; - function GetDataPage(PageNum: int64): TDataPage; - function EmptyDataPage: Boolean; - function GenerateNewPage(const PageType: Smallint): int64; - function GenerateNewPagePosition(const PageType: Smallint; - Position: int64): int64; - function ReWritePage(typePagePrew: Shortint; PageNumber: int64; - Checksum: integer; TipeNext: integer; typePage: integer): Boolean; - function GetHeaderFlags: string; - procedure SetHeaderFlags(const flags: string); - function GetHeaderPage: THdrPage; - property Count: integer read GetCount; - property nameDB: string read GetNameDB; - property Curr_page_size: integer read GetCurrPageSizeDB; - end; - -implementation - -uses uPag; - -{ TDatabase } - -procedure TFBDatabase.AddValue(Value: string); -begin - -end; - -procedure TFBDatabase.Clear; -begin - -end; - -constructor TFBDatabase.Create(nameDB: string); -begin - _NameDB := nameDB; - _EmptyDataPage := false; - _CurrentPageNumber := 0; - SetCurrPage; -end; - -function TFBDatabase.DBFileSize: int64; -begin - result := GetFileSizeBase(_NameDB); -end; - -destructor TFBDatabase.Destroy; -begin - - inherited; -end; - -function TFBDatabase.EmptyDataPage: Boolean; -begin - result := _EmptyDataPage; -end; - -function TFBDatabase.GenerateNewPage(const PageType: Smallint): int64; -var - FS: TFileStream; - DataPage: TDataPage; - TipPage: TTipPage; - PointerPage: TPointer_page; - GeneratorPage: TGenerator_page; -begin - if PageType = 0 then - begin - FillChar(TipPage, SizeOf(TipPage), 0); - try - FS := TFileStream.Create(_NameDB, fmOpenReadWrite or fmShareDenyNone); - FS.Position := FS.Size; - FS.Write(TipPage, SizeOf(TipPage)); - result := round(FS.Size / _page_size_curr); - finally - FS.Free; - end; - end; -end; - -function TFBDatabase.GenerateNewPagePosition(const PageType: Smallint; - Position: int64): int64; -var - FromFile, ToFile: TFileStream; - Buffer: array [0 .. 8192 - 1] of byte; - NumRead: Longint; - FileSize, CopiedSize: int64; - TipPage: TTipPage; -begin - FillChar(TipPage, SizeOf(TipPage), 0); - FileSize := GetFileSizeBase(_NameDB); - FromFile := TFileStream.Create(_NameDB, fmOpenRead or fmShareDenyNone); - try - if FileExists('new_base.FDB') then - ToFile := TFileStream.Create('new_base.FDB', fmOpenReadWrite or - fmShareDenyWrite) - else - ToFile := TFileStream.Create('new_base.FDB', fmCreate); - try - CopiedSize := 0; - ToFile.Size := FileSize + _page_size_curr; - ToFile.Position := 0; - FromFile.Position := 0; - NumRead := FromFile.Read(Buffer[0], _page_size_curr); - ToFile.Write(Buffer[0], _page_size_curr); - while NumRead > 0 do - begin - CopiedSize := CopiedSize + NumRead; - if CopiedSize = Position * _page_size_curr then - ToFile.Write(TipPage, SizeOf(TipPage)); - NumRead := FromFile.Read(Buffer[0], _page_size_curr); - ToFile.Write(TipPage, SizeOf(TipPage)) - end; - finally - FreeAndNil(ToFile); - end; - finally - FreeAndNil(FromFile); - end; - DeleteFile(_NameDB); - RenameFile('new_base.FDB', _NameDB); - result := 1; -end; - -function TFBDatabase.GetCount: integer; -begin - -end; - -function TFBDatabase.GetCurrPageSizeDB: integer; -begin - result := _page_size_curr; -end; - -// Get DataPage by number -function TFBDatabase.GetDataPage(PageNum: int64): TDataPage; -var - i, db: integer; - ResDataPage: TDataPage; -begin - FillChar(ResDataPage, SizeOf(ResDataPage), 0); - try - PageNum := PageNum - 1; - db := fileopen(_NameDB, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, PageNum * _page_size_curr, 0); - i := fileread(db, ResDataPage, _page_size_curr); - result := ResDataPage; - finally - FileClose(db); - end; -end; - -function TFBDatabase.GetHeaderPage: THdrPage; -var - FS: TFileStream; -begin - try - FS := TFileStream.Create(_NameDB, fmOpenRead or fmShareDenyNone); - FS.Read(HeaderPage, _page_size_curr); - result := HeaderPage; - finally - FS.Free; - end; -end; - -function TFBDatabase.GetHeaderFlags: string; -var - FS: TFileStream; - s, resString: string; - i: integer; -begin - try - FS := TFileStream.Create(_NameDB, fmOpenRead or fmShareDenyNone); - FS.Read(HeaderPage, _page_size_curr); - s := IntToBin2(HeaderPage.fix_data.hdr_flags); - resString := ''; - For i := Length(s) downto 1 do - resString := resString + s[i]; - result := resString; - finally - FS.Free; - end; -end; - -function TFBDatabase.GetNameDB: string; -begin - result := _NameDB; - -end; - -// get nex DataPage Iterator pattern -function TFBDatabase.GetNextDataPage: TDataPage; -var - NumRead: integer; - db, i, countByte: integer; - sizeStream: int64; - DataPage: TDataPage; - FS: TFileStream; -begin - FillChar(DataPage, SizeOf(DataPage), 0); - try - FS := TFileStream.Create(_NameDB, fmOpenRead or fmShareDenyNone); - FS.Seek(_CurrentPageNumber, 0); - countByte := FS.Read(DataPage, _page_size_curr); - sizeStream := FS.Size; - _CurrentPageNumber := _CurrentPageNumber + _page_size_curr; - if sizeStream = _CurrentPageNumber then - _EmptyDataPage := True; - result := DataPage; - finally - FS.Free; - end; -end; - -// rewrite database page with ne data -function TFBDatabase.ReWritePage(typePagePrew: Shortint; PageNumber: int64; - Checksum: integer; TipeNext: integer; typePage: integer): Boolean; -var - db, i: integer; - DataPage: TDataPage; - TipPage: TTipPage; -begin - try - // TIP - if typePagePrew = 3 then - begin - FillChar(TipPage, SizeOf(TipPage), 0); - try - db := fileopen(_NameDB, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, PageNumber * _page_size_curr, 0); - i := fileread(db, TipPage, _page_size_curr); - finally - FileClose(db); - end; - TipPage.fix_data.tip_header.pag_type := typePage; - TipPage.fix_data.tip_header.pag_checksum := Checksum; - TipPage.fix_data.tip_next := TipeNext; - - try - db := fileopen(_NameDB, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, PageNumber * _page_size_curr, 0); - i := filewrite(db, TipPage, _page_size_curr); - finally - FileClose(db); - end; - result := True; - end; - // data - if typePagePrew <> 3 then - begin - FillChar(DataPage, SizeOf(DataPage), 0); - try - db := fileopen(_NameDB, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, PageNumber * _page_size_curr, 0); - i := fileread(db, DataPage, _page_size_curr); - finally - FileClose(db); - end; - DataPage.fix_data.pagHdr_Header.pag_type := typePage; - DataPage.fix_data.pagHdr_Header.pag_checksum := Checksum; - try - db := fileopen(_NameDB, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, PageNumber * _page_size_curr, 0); - i := filewrite(db, DataPage, _page_size_curr); - finally - FileClose(db); - end; - result := True; - end; - except - result := false; - end; -end; - -procedure TFBDatabase.SetCurrPage; -var - FS: TFileStream; - DPage: TDataPage; -begin - try - FS := TFileStream.Create(_NameDB, fmOpenRead or fmShareDenyNone); - - FS.Read(HeaderPage, MIN_PAGE_SIZE); - _page_size_curr := HeaderPage.fix_data.hdr_page_size; - finally - FS.Free; - end; - FillChar(HeaderPage, SizeOf(HeaderPage), 0); - // HeaderPage:=nil; - try - FS := TFileStream.Create(_NameDB, fmOpenRead or fmShareDenyNone); - FS.Read(HeaderPage, _page_size_curr); - _page_size_curr := HeaderPage.fix_data.hdr_page_size; - finally - FS.Free; - end; - -end; - -procedure TFBDatabase.SetHeaderFlags(const flags: string); -var - FS: TFileStream; - s, resString: string; - i, db: integer; -begin - FillChar(HeaderPage, SizeOf(HeaderPage), 0); - resString := ''; - For i := Length(flags) downto 1 do - resString := resString + flags[i]; - try - FS := TFileStream.Create(_NameDB, fmOpenRead or fmShareDenyNone); - FS.Read(HeaderPage, _page_size_curr); - - finally - FS.Free; - end; - - HeaderPage.fix_data.hdr_flags := BinToInt(resString); - try - db := fileopen(_NameDB, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, 0 * _page_size_curr, 0); - i := filewrite(db, HeaderPage, _page_size_curr); - finally - FileClose(db); - end; -end; - -function TFBDatabase.typeCurrPage(PageNum: int64): integer; -var - db, i: integer; - DataPage: TDataPage; -begin - try - PageNum := PageNum - 1; - db := fileopen(_NameDB, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, PageNum * _page_size_curr, 0); - i := fileread(db, DataPage, _page_size_curr); - result := DataPage.fix_data.pagHdr_Header.pag_type; - finally - FileClose(db); - end; -end; - -function TFBDatabase.typePageChecksum(PageNum: int64): integer; -var - db, i: integer; - DataPage: TDataPage; -begin - try - db := fileopen(_NameDB, fmOpenReadWrite + fmShareExclusive); - fileseek(db, 0, 0); - fileseek(db, PageNum * _page_size_curr, 0); - i := fileread(db, DataPage, _page_size_curr); - result := DataPage.fix_data.pagHdr_Header.pag_checksum; - finally - FileClose(db); - end; -end; - -end. diff --git a/uGenPage.pas b/uGenPage.pas index f2cc818..8b0bd6d 100644 --- a/uGenPage.pas +++ b/uGenPage.pas @@ -2,7 +2,7 @@ interface -uses uPag, Windows; +uses uStructs, Windows; const MAX_PAGE_SIZE = 32768; @@ -17,14 +17,12 @@ interface TGenerator_page = record gpg_header: TPag; - gpg_sequence: ULong; // Sequence number - // Generator vector + gpg_sequence: ULong; end; Tgnrtr_page = record - fix_data1: TGenerator_page; // Sequence number + fix_data1: TGenerator_page; gpg_values: array [0 .. (MAX_PAGE_SIZE - sizeof(TGenerator_page))] of Int64; - // Generator vector end; implementation diff --git a/uHeaderPage.pas b/uHeaderPage.pas index aacffeb..91849be 100644 --- a/uHeaderPage.pas +++ b/uHeaderPage.pas @@ -16,28 +16,29 @@ interface ULong = LongWord; THdr = packed record - hdr_header: TPag; - hdr_page_size: UShort; - hdr_ods_version: UShort; - hdr_pages: SLong; - hdr_next_page: ULong; - hdr_oldest_transaction: SLong; - hdr_oldest_active: SLong; - hdr_next_transaction: SLong; - hdr_sequence: UShort; - hdr_flags: UShort; - hdr_creation_date: array [0 .. 1] of SLong; - hdr_attachment_id: SLong; - hdr_shadow_count: SLong; - hdr_implementation: SShort; - hdr_ods_minor: UShort; - hdr_ods_minor_original: UShort; - hdr_end: UShort; - hdr_page_buffers: ULong; - hdr_bumped_transaction: SLong; - hdr_oldest_snapshot: SLong; - hdr_misc: array [0 .. 3] of SLong; - end; + hdr_header: TPag; + hdr_page_size: UShort; + hdr_ods_version: UShort; + hdr_pages: SLong; + hdr_next_page: ULong; + hdr_oldest_transaction: SLong; + hdr_oldest_active: SLong; + hdr_next_transaction: SLong; + hdr_sequence: UShort; + hdr_flags: UShort; + hdr_creation_date: array [0 .. 1] of SLong; + hdr_attachment_id: SLong; + hdr_shadow_count: SLong; + hdr_implementation: SShort; + hdr_ods_minor: UShort; + hdr_ods_minor_original: UShort; + hdr_end: UShort; + hdr_page_buffers: ULong; + hdr_bumped_transaction: SLong; + hdr_oldest_snapshot: SLong; + hdr_backup_pages: SLong; + hdr_misc: array [0 .. 2] of SLong; +end; THdrPage = packed record fix_data: THdr; diff --git a/uPag.pas b/uPag.pas deleted file mode 100644 index 4406dd2..0000000 --- a/uPag.pas +++ /dev/null @@ -1,23 +0,0 @@ -unit uPag; - -interface - -type - SChar = Shortint; - SShort = Smallint; - UShort = Word; - SLong = Longint; - ULong = LongWord; - - TPag = packed record - pag_type: SChar; - pag_flags: SChar; - pag_checksum: UShort; - pag_generation: ULong; - pag_seqno: ULong; - pg_offset: ULong; - end; - -implementation - -end. diff --git a/uPoiner.pas b/uPoiner.pas deleted file mode 100644 index ce81234..0000000 --- a/uPoiner.pas +++ /dev/null @@ -1,36 +0,0 @@ -unit uPoiner; - -interface - -uses uPag, Windows; - -const - MAX_PAGE_SIZE = 32768; - MIN_PAGE_SIZE = 1024; - -type - SChar = Shortint; - SShort = Smallint; - UShort = Word; - SLong = Longint; - ULong = LongWord; - - Tpnr_page = record - pp_header: Tpag; - ppg_sequence: SLong; - ppg_next: SLong; - ppg_count: UShort; - ppg_relation: UShort; - ppg_min_space: UShort; - ppg_max_space: UShort; - - end; - - TPointer_page = record - fix_data: Tpnr_page; - ppg_page: array [0 .. (MAX_PAGE_SIZE - sizeof(Tpnr_page))] of SLong; - end; - -implementation - -end. diff --git a/uTipPage.pas b/uTipPage.pas index 8bd881d..8504a22 100644 --- a/uTipPage.pas +++ b/uTipPage.pas @@ -1,30 +1,169 @@ unit uTipPage; -{ - separate unit for TIP page - -} interface -uses uPag, Windows; +uses + Windows, SysUtils, uStructs, Math; type - SChar = Shortint; - SShort = Smallint; - UShort = Word; - SLong = Longint; - ULong = LongWord; - - tip = packed record - tip_header: Tpag; + + TTIPHeader = packed record + tip_header: TPag; tip_next: SLong; end; - TTipPage = packed record - fix_data: tip; - tip_transactions: array [0 .. (4096 - sizeof(tip))] of UCHAR; + TTransactionState = (tsActive, tsLimbo, tsDead, tsCommitted, tsUnknown); + TTransactionInfo = record + ID: ULong; + State: TTransactionState; + end; + + + TTransactionInfoArray = array of TTransactionInfo; + + + TTIPAnalyzer = class + private + FPageSize: Integer; + FPageNumber: ULong; + FTipHeader: TTIPHeader; + FTipData: TBytes; + FNextTipPageNum: SLong; + + function GetStateFromBits(Bits: Byte): TTransactionState; + function GetStartTransactionID: ULong; + function GetMaxTransactionID: ULong; + + public + + constructor Create(const PageBuffer: TBytes; PageSize: Integer; PageNum: ULong); + function GetTransactionInfo(StartTxID: ULong = ULong(-1); EndTxID: ULong = ULong(-1)): TTransactionInfoArray; + property NextTipPageNumber: SLong read FNextTipPageNum; + property StartTransactionID: ULong read GetStartTransactionID; + property MaxTransactionID: ULong read GetMaxTransactionID; + end; implementation +constructor TTIPAnalyzer.Create(const PageBuffer: TBytes; PageSize: Integer; PageNum: ULong); +var + ExpectedMinSize: Integer; +begin + if (PageSize < MIN_PAGE_SIZE) or (PageSize > MAX_PAGE_SIZE) or + (Length(PageBuffer) <> PageSize) then + raise Exception.Create('Invalid page size or buffer size for TIP page.'); + + ExpectedMinSize := SizeOf(TTIPHeader); + if Length(PageBuffer) < ExpectedMinSize then + raise Exception.Create('Page buffer too small for TIP header.'); + + + Move(PageBuffer[0], FTipHeader, SizeOf(TTIPHeader)); + + if FTipHeader.tip_header.pag_type <> $03 then + raise Exception.Create('Not a Transaction Inventory Page (TIP).'); + + FPageSize := PageSize; + FPageNumber := PageNum; + FNextTipPageNum := FTipHeader.tip_next; + + SetLength(FTipData, FPageSize - ExpectedMinSize); + if Length(FTipData) > 0 then + Move(PageBuffer[ExpectedMinSize], FTipData[0], Length(FTipData)); +end; + +function TTIPAnalyzer.GetStateFromBits(Bits: Byte): TTransactionState; +begin + case (Bits and $03) of + $00: Result := tsActive; + $01: Result := tsLimbo; + $02: Result := tsDead; + $03: Result := tsCommitted; + else + Result := tsUnknown; + end; +end; + +function TTIPAnalyzer.GetTransactionInfo(StartTxID, EndTxID: ULong): TTransactionInfoArray; +var + StartIdx, EndIdx, TxID: ULong; + RelStartIdx, RelEndIdx: ULong; + ByteIdx, BitPairIdx: Integer; + ByteValue: Byte; + State: TTransactionState; + InfoList: TTransactionInfoArray; + InfoCount: Integer; + MaxPossibleRelTxID: ULong; +begin + Result := nil; + MaxPossibleRelTxID := GetMaxTransactionID - GetStartTransactionID; + if StartTxID = ULong(-1) then + StartIdx := GetStartTransactionID + else + StartIdx := StartTxID; + + if EndTxID = ULong(-1) then + EndTxID := GetMaxTransactionID; + + + if (EndIdx < GetStartTransactionID) or (StartIdx > GetMaxTransactionID) then + Exit; + + + StartIdx := Max(StartIdx, GetStartTransactionID); + EndIdx := Min(EndIdx, GetMaxTransactionID); + + + RelStartIdx := StartIdx - GetStartTransactionID; + RelEndIdx := EndIdx - GetStartTransactionID; + + if RelStartIdx > MaxPossibleRelTxID then + Exit; + + + SetLength(InfoList, RelEndIdx - RelStartIdx + 1); + InfoCount := 0; + + TxID := RelStartIdx; + while TxID <= RelEndIdx do + begin + + ByteIdx := (TxID div 4); + BitPairIdx := (TxID mod 4) * 2; + + if ByteIdx >= Length(FTipData) then + Break; + + + ByteValue := FTipData[ByteIdx]; + State := GetStateFromBits(ByteValue shr BitPairIdx); + + InfoList[InfoCount].ID := TxID + GetStartTransactionID; + InfoList[InfoCount].State := State; + Inc(InfoCount); + + Inc(TxID); + end; + + SetLength(Result, InfoCount); + if InfoCount > 0 then + Move(InfoList[0], Result[0], InfoCount * SizeOf(TTransactionInfo)); +end; + + +function TTIPAnalyzer.GetStartTransactionID: ULong; +begin + Result := FPageNumber * ULong((FPageSize - SizeOf(TTIPHeader)) * 4); +end; + + +function TTIPAnalyzer.GetMaxTransactionID: ULong; +var + TransactionsPerPage: ULong; +begin + TransactionsPerPage := ULong((FPageSize - SizeOf(TTIPHeader)) * 4); + Result := GetStartTransactionID + TransactionsPerPage - 1; +end; + end.