From 4c2e79e27e2d581412fbc9265e5cd2074226029a Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Sun, 21 Feb 2016 11:01:35 +0000 Subject: [PATCH] biffexplorer: Copy from components/fpspreadsheet to applications git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4516 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- applications/biffexplorer/BIFFExplorer.ico | Bin 0 -> 137040 bytes applications/biffexplorer/BIFFExplorer.lpi | 224 + applications/biffexplorer/BIFFExplorer.lpr | 22 + applications/biffexplorer/BIFFExplorer.res | Bin 0 -> 27196 bytes applications/biffexplorer/beabout.lfm | 68 + applications/biffexplorer/beabout.pas | 153 + applications/biffexplorer/bebiffgrid.pas | 6699 ++++++++++++++++++++ applications/biffexplorer/bebiffutils.pas | 801 +++ applications/biffexplorer/behtml.pas | 245 + applications/biffexplorer/bemain.lfm | 1061 ++++ applications/biffexplorer/bemain.pas | 1472 +++++ applications/biffexplorer/betypes.pas | 30 + applications/biffexplorer/beutils.pas | 165 + applications/biffexplorer/mrumanager.pp | 485 ++ applications/biffexplorer/readme.txt | 20 + 15 files changed, 11445 insertions(+) create mode 100644 applications/biffexplorer/BIFFExplorer.ico create mode 100644 applications/biffexplorer/BIFFExplorer.lpi create mode 100644 applications/biffexplorer/BIFFExplorer.lpr create mode 100644 applications/biffexplorer/BIFFExplorer.res create mode 100644 applications/biffexplorer/beabout.lfm create mode 100644 applications/biffexplorer/beabout.pas create mode 100644 applications/biffexplorer/bebiffgrid.pas create mode 100644 applications/biffexplorer/bebiffutils.pas create mode 100644 applications/biffexplorer/behtml.pas create mode 100644 applications/biffexplorer/bemain.lfm create mode 100644 applications/biffexplorer/bemain.pas create mode 100644 applications/biffexplorer/betypes.pas create mode 100644 applications/biffexplorer/beutils.pas create mode 100644 applications/biffexplorer/mrumanager.pp create mode 100644 applications/biffexplorer/readme.txt diff --git a/applications/biffexplorer/BIFFExplorer.ico b/applications/biffexplorer/BIFFExplorer.ico new file mode 100644 index 0000000000000000000000000000000000000000..7ce4cfe07496f1df9eeea55620d98a4eac9c5cc0 GIT binary patch literal 137040 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 Sty-%j+CcH1<8qouNc(?VHBeUo literal 0 HcmV?d00001 diff --git a/applications/biffexplorer/BIFFExplorer.lpi b/applications/biffexplorer/BIFFExplorer.lpi new file mode 100644 index 000000000..20be0cb9f --- /dev/null +++ b/applications/biffexplorer/BIFFExplorer.lpi @@ -0,0 +1,224 @@ + + + + + + + + + + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + <XPManifest> + <DpiAware Value="True"/> + </XPManifest> + <Icon Value="0"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="3"> + <Item1 Name="Release" Default="True"/> + <Item2 Name="Debug"> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="bin\$(TargetCPU)-$(TargetOS)\BiffExplorer"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="mrumenu"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Debugging> + <DebugInfoType Value="dsDwarf2Set"/> + <UseExternalDbgSyms Value="True"/> + </Debugging> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + <Other> + <CustomOptions Value="-duse_new_ole"/> + </Other> + </CompilerOptions> + </Item2> + <Item3 Name="Debug with heaptrace"> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="mrumenu"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <IncludeAssertionCode Value="True"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <Checks> + <IOChecks Value="True"/> + <RangeChecks Value="True"/> + <OverflowChecks Value="True"/> + <StackChecks Value="True"/> + </Checks> + </CodeGeneration> + <Linking> + <Debugging> + <DebugInfoType Value="dsStabs"/> + <UseHeaptrc Value="True"/> + </Debugging> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + <Other> + <CustomOptions Value="-duse_new_ole"/> + </Other> + </CompilerOptions> + </Item3> + <SharedMatrixOptions Count="1"> + <Item1 ID="897672969650" Value="-vd"/> + </SharedMatrixOptions> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="5"> + <Item1> + <PackageName Value="KControlsLaz"/> + </Item1> + <Item2> + <PackageName Value="laz_fpspreadsheet"/> + </Item2> + <Item3> + <PackageName Value="TurboPowerIPro"/> + </Item3> + <Item4> + <PackageName Value="virtualtreeview_package"/> + </Item4> + <Item5> + <PackageName Value="LCL"/> + </Item5> + </RequiredPackages> + <Units Count="11"> + <Unit0> + <Filename Value="BIFFExplorer.lpr"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="beabout.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="AboutForm"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="beAbout"/> + </Unit1> + <Unit2> + <Filename Value="bebiffgrid.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="beBIFFGrid"/> + </Unit2> + <Unit3> + <Filename Value="bebiffutils.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="beBIFFUtils"/> + </Unit3> + <Unit4> + <Filename Value="behtml.pas"/> + <IsPartOfProject Value="True"/> + </Unit4> + <Unit5> + <Filename Value="bemain.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="MainForm"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="beMain"/> + </Unit5> + <Unit6> + <Filename Value="beutils.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="beUtils"/> + </Unit6> + <Unit7> + <Filename Value="mrumanager.pp"/> + <IsPartOfProject Value="True"/> + </Unit7> + <Unit8> + <Filename Value="bemain.lfm"/> + <IsPartOfProject Value="True"/> + </Unit8> + <Unit9> + <Filename Value="beabout.lfm"/> + <IsPartOfProject Value="True"/> + </Unit9> + <Unit10> + <Filename Value="betypes.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="beTypes"/> + </Unit10> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="mrumenu"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <CodeGeneration> + <SmartLinkUnit Value="True"/> + <Optimizations> + <OptimizationLevel Value="3"/> + </Optimizations> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + </Debugging> + <LinkSmart Value="True"/> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + <Other> + <CustomOptions Value="-duse_new_ole"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="4"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + <Item4> + <Name Value="EStreamError"/> + </Item4> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/applications/biffexplorer/BIFFExplorer.lpr b/applications/biffexplorer/BIFFExplorer.lpr new file mode 100644 index 000000000..4e9abfb96 --- /dev/null +++ b/applications/biffexplorer/BIFFExplorer.lpr @@ -0,0 +1,22 @@ +program BIFFExplorer; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, + {$ENDIF}{$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, virtualtreeview_package, laz_fpspreadsheet, kcontrolslaz, beabout, + bebiffgrid, bebiffutils, behtml, beutils, mrumanager, beMain, beTypes; + +{$R *.res} + +begin + RequireDerivedFormResource := True; + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + MainForm.BeforeRun; + Application.Run; +end. + diff --git a/applications/biffexplorer/BIFFExplorer.res b/applications/biffexplorer/BIFFExplorer.res new file mode 100644 index 0000000000000000000000000000000000000000..701ea761fe84b511a3d91ed901a8319a3984b29c GIT binary patch literal 27196 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 R`uu5S|8P9?vqs<8{|BIEYeoP7 literal 0 HcmV?d00001 diff --git a/applications/biffexplorer/beabout.lfm b/applications/biffexplorer/beabout.lfm new file mode 100644 index 000000000..adecc1421 --- /dev/null +++ b/applications/biffexplorer/beabout.lfm @@ -0,0 +1,68 @@ +object AboutForm: TAboutForm + Left = 338 + Height = 512 + Top = 153 + Width = 642 + Caption = 'About BIFF Explorer' + ClientHeight = 512 + ClientWidth = 642 + Color = clWindow + OnCreate = FormCreate + Position = poMainFormCenter + LCLVersion = '1.7' + object Panel1: TPanel + Left = 0 + Height = 66 + Top = 0 + Width = 642 + Align = alTop + BevelOuter = bvNone + ClientHeight = 66 + ClientWidth = 642 + Color = clWindow + ParentColor = False + TabOrder = 0 + object IconImage: TImage + Left = 7 + Height = 48 + Top = 8 + Width = 48 + end + object LblTitle: TLabel + Left = 69 + Height = 29 + Top = 16 + Width = 158 + Caption = 'BIFF Explorer' + Font.CharSet = ANSI_CHARSET + Font.Color = clNavy + Font.Height = -24 + Font.Name = 'Arial' + Font.Pitch = fpVariable + Font.Quality = fqDraft + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object BtnClose: TButton + Left = 559 + Height = 29 + Top = 16 + Width = 75 + Anchors = [akTop, akRight] + Cancel = True + Caption = 'Close' + Default = True + ModalResult = 1 + TabOrder = 0 + end + end + object Bevel1: TBevel + Left = 0 + Height = 6 + Top = 66 + Width = 642 + Align = alTop + Shape = bsTopLine + end +end diff --git a/applications/biffexplorer/beabout.pas b/applications/biffexplorer/beabout.pas new file mode 100644 index 000000000..105422b7c --- /dev/null +++ b/applications/biffexplorer/beabout.pas @@ -0,0 +1,153 @@ +unit beAbout; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, IpHtml, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, + ExtCtrls, StdCtrls; + +type + + { TAboutForm } + + TAboutForm = class(TForm) + Bevel1: TBevel; + BtnClose: TButton; + IconImage: TImage; + LblTitle: TLabel; + Panel1: TPanel; + procedure FormCreate(Sender: TObject); + procedure HTMLViewerHotClick(Sender: TObject); + private + { private declarations } + FHTMLViewer: TIpHtmlPanel; + function CreateCredits: String; + public + { public declarations } + end; + +var + AboutForm: TAboutForm; + +implementation + +{$R *.lfm} + +uses + LCLIntf, types, beHTML; + +{ TAboutForm } + +function TAboutForm.CreateCredits: String; +var + html: THTMLDocument; + clrs: THeaderColors = (clBlack, clBlack, clBlack, clBlack, clBlack); +begin + html := THTMLDocument.Create; + try + clrs[h3] := LblTitle.Font.Color; + clrs[h4] := LblTitle.Font.Color; + with html do begin + BeginDocument('Credits', clrs, false); + AddHeader(h3, 'Credits'); + AddHeader(h4, 'Libraries'); + BeginBulletList; + AddListItem(Hyperlink( + 'Free Pascal', + 'www.freepascal.org') + ); + AddListItem(Hyperlink( + 'Lazarus', + 'www.lazarus.freepascal.org') + ); + AddListItem(HyperLink( + 'fpspreadsheet', + 'http://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/components/fpspreadsheet/') + ); + AddListItem(Hyperlink( + 'KControls', + 'http://www.tkweb.eu/en/delphicomp/kcontrols.html') + ); + EndBulletList; + + AddEmptyLine; + + AddHeader(h4, 'Icons'); + BeginBulletList; + AddListItem(HyperLink( + 'Fugue icons', + 'http://p.yusukekamiyamane.com/') + + ' (for toolbar icons)'); + AddListItem(HyperLink( + 'Nuvola icons', + 'www.icon-king.com/projects/nuvola/') + + ' (for application icon'); + EndBulletList; + + AddEmptyLine; + + AddHeader(h4, 'Used documentation'); + BeginBulletList; + AddListItem(Hyperlink( + 'OpenOffice.org''s Documentation of the Microsoft Excel File Format', + 'http://www.openoffice.org/sc/excelfileformat.pdf') + + ' (see folder "fpspreadsheet/reference")' + ); + AddListItem(Hyperlink( + '[MS-XLS]: Excel Binary File Format (.xls) Structure', + 'http://msdn.microsoft.com/en-us/library/cc313154%28v=office.12%29.aspx' + )); + AddListItem(HyperLink( + 'Excel97-2007BinaryFileFormat(xls)Specification', + 'http://download.microsoft.com/download/0/B/E/0BE8BDD7-E5E8-422A-ABFD-4342ED7AD886/Excel97-2007BinaryFileFormat(xls)Specification.pdf' + )); + EndBulletList; + + AddEmptyLine; + + EndDocument; + + Result := Lines.Text; + end; + finally + html.Free; + end; +end; + + +procedure TAboutForm.FormCreate(Sender: TObject); +var + ico: TIcon; + sz: TSize; +begin + ico := TIcon.Create; + try + ico.Assign(Application.Icon); + sz.cx := 48; + sz.cy := 48; + ico.Current := ico.GetBestIndexForSize(sz); + IconImage.Picture.Assign(ico); + finally + ico.Free; + end; + + FHTMLViewer := TIpHtmlPanel.Create(self); + FHTMLViewer.Parent := self; + FHTMLViewer.Align := alClient; + FHTMLViewer.DefaultFontSize := 9; + FHTMLViewer.OnHotClick := @HTMLViewerHotClick; + + FHTMLViewer.SetHTMLFromStr(CreateCredits); +end; + + +procedure TAboutForm.HTMLViewerHotClick(Sender: TObject); +begin + OpenURL((Sender as TIpHtmlPanel).HotURL); +end; + + +end. + diff --git a/applications/biffexplorer/bebiffgrid.pas b/applications/biffexplorer/bebiffgrid.pas new file mode 100644 index 000000000..686c53c3a --- /dev/null +++ b/applications/biffexplorer/bebiffgrid.pas @@ -0,0 +1,6699 @@ +unit beBIFFGrid; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Controls, Grids, fpstypes, fpspreadsheet, beTypes; + +type + TBIFFBuffer = array of byte; + + TBIFFDetailsEvent = procedure(Sender: TObject; ADetails: TStrings) of object; + + TRichTextFormattingRun = packed record + FirstIndex, FontIndex: Word; + end; + TRichTextFormattingRuns = array of TRichTextFormattingRun; + + TBIFFGrid = class(TStringGrid) + private + FRecType: Word; + FBuffer: TBIFFBuffer; + FBufferIndex: LongWord; + FFormat: TsSpreadsheetFormat; + FInfo: Integer; + FTotalSST: Integer; + FCounterSST: Integer; + FPendingCharCount: Integer; + FCurrRow: Integer; + FDetails: TStrings; + FOnDetails: TBIFFDetailsEvent; + function GetStringType: String; + + procedure ShowBackup; + procedure ShowBlankCell; + procedure ShowBOF; + procedure ShowBookBool; + procedure ShowBoolCell; + procedure ShowBottomMargin; + procedure ShowCalcCount; + procedure ShowCalcMode; + procedure ShowCellAddress; + procedure ShowCellAddressRange(AFormat: TsSpreadsheetFormat); + procedure ShowClrtClient; + procedure ShowCodePage; + procedure ShowColInfo; + procedure ShowColWidth; + procedure ShowContinue; + procedure ShowCountry; + procedure ShowDateMode; + procedure ShowDBCell; + procedure ShowDefColWidth; + procedure ShowDefinedName; + procedure ShowDefRowHeight; + procedure ShowDelta; + procedure ShowDimensions; + procedure ShowDSF; + procedure ShowEOF; + procedure ShowExcel9File; + procedure ShowExternBook; + procedure ShowExternCount; + procedure ShowExternSheet; + procedure ShowFileSharing; + procedure ShowFnGroupCount; + procedure ShowFont; + procedure ShowFontColor; + procedure ShowFooter; + procedure ShowFormat; + procedure ShowFormatCount; + procedure ShowFormula; + procedure ShowFormulaTokens(ATokenBytes: Integer); + procedure ShowGCW; + procedure ShowHeader; + procedure ShowHideObj; + procedure ShowHyperLink; + procedure ShowHyperLinkTooltip; + procedure ShowInteger; + procedure ShowInterfaceEnd; + procedure ShowInterfaceHdr; + procedure ShowIteration; + procedure ShowIXFE; + procedure ShowLabelCell; + procedure ShowLabelSSTCell; + procedure ShowLeftMargin; + procedure ShowMergedCells; + procedure ShowMMS; + procedure ShowMSODrawing; + procedure ShowMulBlank; + procedure ShowMulRK; + procedure ShowNote; + procedure ShowNumberCell; + procedure ShowObj; + procedure ShowPageSetup; + procedure ShowPalette; + procedure ShowPane; + procedure ShowPassword; + procedure ShowPrecision; + procedure ShowPrintGridLines; + procedure ShowPrintHeaders; + procedure ShowProt4Rev; + procedure ShowProt4RevPass; + procedure ShowProtect; + procedure ShowRecalc; + procedure ShowRefMode; + procedure ShowRefreshAll; + procedure ShowRightMargin; + procedure ShowRK; + procedure ShowRString; + procedure ShowRow; + procedure ShowSelection; + procedure ShowSharedFormula; + procedure ShowSheet; + procedure ShowSheetPR; + procedure ShowSST; + procedure ShowStandardWidth; + procedure ShowString; + procedure ShowStyle; + procedure ShowStyleExt; + procedure ShowTabID; + procedure ShowTopMargin; + procedure ShowTXO; + procedure ShowWindow1; + procedure ShowWindow2; + procedure ShowWindowProtect; + procedure ShowWriteAccess; + procedure ShowWriteProt; + procedure ShowXF; + procedure ShowXFCRC; + procedure ShowXFEXT; + + protected + procedure Click; override; + procedure DoExtractDetails; + function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override; + function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override; + procedure ExtractString(ABufIndex: Integer; AUnicode: Boolean; + ACharCount: Integer; out AString: String; out ANumbytes: Integer); overload; + procedure ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean; + out AString: String; out ANumBytes: Integer; + out ARichTextRuns: TRichTextFormattingRuns; + out ABufIndexOfFirstRichTextRun: LongWord; + IgnoreCompressedFlag: Boolean = false); overload; + procedure ExtractString(ABufIndex: Integer; ALenbytes: Byte; AUnicode: Boolean; + out AString: String; out ANumBytes: Integer; + IgnoreCompressedFlag: Boolean=False); overload; + procedure PopulateGrid; + procedure ShowInRow(var ARow: Integer; var AOffs: LongWord; ASize: Word; + AValue,ADescr: String; ADescrOnly: Boolean = false); + procedure ShowRowColData(var ABufIndex: LongWord); + + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + procedure SetBIFFNodeData(AData: PBIFFNodeData; ABuffer: TBIFFBuffer; + AFormat: TsSpreadsheetFormat); + + published + property TabOrder; + property OnDetails: TBIFFDetailsEvent read FOnDetails write FOnDetails; + property OnSelection; + end; + +implementation + +uses + StrUtils, Math, lazutf8, + fpsutils, + beBIFFUtils; + +const + ABS_REL: array[boolean] of string = ('abs', 'rel'); + +constructor TBIFFGrid.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + ColCount := 4; + FixedCols := 0; + RowCount := 2; + Cells[0, 0] := 'Offset'; + Cells[1, 0] := 'Size'; + Cells[2, 0] := 'Value'; + Cells[3, 0] := 'Description'; + ColWidths[0] := 60; + ColWidths[1] := 60; + ColWidths[2] := 120; + ColWidths[3] := 350; + Options := Options + + [goThumbTracking, goColSizing, goTruncCellHints, goCellHints] + - [goVertLine, goSmoothScroll]; + MouseWheelOption := mwGrid; + FDetails := TStringList.Create; + FPendingCharCount := -1; +end; + + +destructor TBIFFGrid.Destroy; +begin + FDetails.Free; + inherited; +end; + + +procedure TBIFFGrid.Click; +begin + inherited; + if (FBuffer <> nil) then + DoExtractDetails; +end; + + +procedure TBIFFGrid.DoExtractDetails; +begin + if Assigned(FOnDetails) then begin + PopulateGrid; + FOnDetails(self, FDetails); + end; +end; + +function TBIFFGrid.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint + ): Boolean; +begin + Result := inherited; + Click; +end; + +function TBIFFGrid.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint + ): Boolean; +begin + Result := inherited; + Click; +end; + +{ Reads a string character array starting at ABufIndex. The string is supposed + to have ACharCount character, but less characters are read if the string + extends across the max size of a record and is continued in the next CONTINUE + record. + The string is assumed to be a UTF16 string if AUnicode=true, otherwise it is + an ansi string. } +procedure TBIFFGrid.ExtractString(ABufIndex: Integer; AUnicode: Boolean; + ACharCount: Integer; out AString: String; out ANumbytes: Integer); +var + sa: AnsiString; + sw: WideString; + n: Integer; +begin + if AUnicode then // uncompressed unicode --> 2 bytes per char + begin + if ABufIndex + ACharCount * SizeOf(WideChar) >= Length(FBuffer) then + begin + n := (Length(FBuffer) - ABufIndex) div SizeOf(WideChar); + FPendingCharCount := ACharCount - n; // number of chars to be read from subsequent CONTINUE record + end else + begin + n := ACharCount; + FPendingCharCount := 0; + end; + SetLength(sw, n); + ANumBytes := n * SizeOf(WideChar); + Move(FBuffer[ABufIndex], sw[1], ANumBytes); + AString := UTF8Encode(WideStringLEToN(sw)); + end else + begin // ansi or compressed unicode + if ABufIndex + ACharCount >= Length(FBuffer) then + begin + n := Length(FBuffer) - ABufIndex; + FPendingCharCount := ACharCount - n; // number of chars in subsequent CONTINUE record + end else + begin + n := ACharCount; + FPendingCharCount := 0; + end; + SetLength(sa, n); + ANumBytes := n; + Move(FBuffer[ABufIndex], sa[1], ANumBytes); + AString := AnsiToUTF8(sa); // to do: use code page of file + end; +end; + +procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean; + out AString: String; out ANumBytes: Integer; + IgnoreCompressedFlag: Boolean = false); +var + rtfRuns: TRichTextFormattingRuns; + rtfIndex: LongWord; +begin + ExtractString(ABufIndex, ALenbytes, AUnicode, AString, ANumBytes, + rtfRuns, rtfIndex, IgnoreCompressedFlag); +end; + +procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean; + out AString: String; out ANumBytes: Integer; + out ARichTextRuns: TRichTextFormattingRuns; + out ABufIndexOfFirstRichTextRun: LongWord; + IgnoreCompressedFlag: Boolean = false); +var + ls: Integer; // Character count of string + w: Word; + dw: DWord; + optn: Byte; + n: Integer; // Byte count in string character array + asianPhoneticBytes: DWord; + numRichRuns: Word; + offs: Integer; + rtfBufIndex: Int64; + rtfIndex: Integer; +begin + ABufIndexOfFirstRichTextRun := LongWord(-1); + SetLength(ARichTextRuns, 0); + + if Length(FBuffer) = 0 then begin + AString := ''; + ANumBytes := 0; + exit; + end; + if ALenBytes = 1 then + ls := FBuffer[ABufIndex] + else begin + Move(FBuffer[ABufIndex], w, 2); + ls := WordLEToN(w); + end; + if AUnicode then begin + offs := ALenBytes; + optn := FBuffer[ABufIndex + ALenBytes]; + inc(offs, 1); + + if optn and $08 <> 0 then // rich text + begin + Move(FBuffer[ABufIndex + offs], w, 2); + numRichRuns := WordLEToN(w); + inc(offs, 2); + end else + numRichRuns := 0; + SetLength(ARichTextRuns, numRichRuns); + + if optn and $04 <> 0 then // Asian phonetic + begin + Move(FBuffer[ABufIndex + offs], dw, 4); + AsianPhoneticBytes := DWordLEToN(dw); + inc(offs, 4); + end else + asianPhoneticBytes := 0; + + if (optn and $01 = 0) and (not IgnoreCompressedFlag) then + // compressed --> 1 byte per character + ExtractString(ABufIndex + offs, false, ls, AString, n) + else + // non-compressed unicode + ExtractString(ABufIndex + offs, true, ls, AString, n); + + ANumBytes := offs + n + numRichRuns * 4 + asianPhoneticBytes; + + rtfIndex := 0; + rtfBufIndex := ABufIndex + offs + n; + ABufIndexOfFirstRichTextRun := rtfBufIndex; + while rtfIndex < numRichRuns do begin + Move(FBuffer[rtfBufIndex], w, 2); + ARichTextRuns[rtfIndex].FirstIndex := WordLEToN(w); + Move(FBuffer[rtfBufIndex+2], w, 2); + ARichTextRuns[rtfIndex].FontIndex := WordLEToN(w); + inc(rtfIndex); + inc(rtfBufIndex, 4); + end; + end else + begin + // ansi string + SetLength(ARichTextRuns, 0); // no rich text formatting for ansi strings + ExtractString(ABufIndex + ALenBytes, false, ls, AString, n); + ANumbytes := ALenBytes + n; + end; +end; + + +function TBIFFGrid.GetStringType: String; +begin + case FFormat of + sfExcel2: Result := 'Byte'; + sfExcel5: Result := 'Byte'; + sfExcel8: Result := 'Unicode'; + end; +end; + + +procedure TBIFFGrid.PopulateGrid; +begin + FBufferIndex := 0; + FCurrRow := FixedRows; + FDetails.Clear; + case FRecType of + $0000, $0200: + ShowDimensions; + $0001, $0201: + ShowBlankCell; + $0002: + ShowInteger; + $0003, $0203: + ShowNumberCell; + $0004, $0204: + ShowLabelCell; + $0005, $0205: + ShowBoolCell; + $0006: + ShowFormula; + $0007, $0207: + ShowString; + $0008, $0208: + ShowRow; + $0009, $0209, $0409, $0809: + ShowBOF; + $000A: + ShowEOF; + $000C: + ShowCalcCount; + $000D: + ShowCalcMode; + $000E: + ShowPrecision; + $000F: + ShowRefMode; + $0010: + ShowDelta; + $0011: + ShowIteration; + $0012: + ShowProtect; + $0013: + ShowPassword; + $0014: + ShowHeader; + $0015: + ShowFooter; + $0016: + ShowExternCount; + $0017: + ShowExternSheet; + $0018, $0218: + ShowDefinedName; + $0019: + ShowWindowProtect; + $001C: + ShowNote; + $001D: + ShowSelection; + $001E, $041E: + ShowFormat; + $001F: + ShowFormatCount; + $0022: + ShowDateMode; + $0024: + ShowColWidth; + $0025, $0225: + ShowDefRowHeight; + $0026: + ShowLeftMargin; + $0027: + ShowRightMargin; + $0028: + ShowTopMargin; + $0029: + ShowBottomMargin; + $002A: + ShowPrintHeaders; + $002B: + ShowPrintGridLines; + $0031: + ShowFont; + $003C: + ShowContinue; + $003D: + ShowWindow1; + $003E, $023E: + ShowWindow2; + $0040: + ShowBackup; + $0041: + ShowPane; + $0042: + ShowCodePage; + $0043: + ShowXF; + $0044: + ShowIXFE; + $0045: + ShowFontColor; + $0055: + ShowDefColWidth; + $005B: + ShowFileSharing; + $005C: + ShowWriteAccess; + $005D: + ShowObj; + $005F: + ShowRecalc; + $007D: + ShowColInfo; + $0081: + ShowSheetPR; + $0085: + ShowSheet; + $0086: + ShowWriteProt; + $008C: + ShowCountry; + $008D: + ShowHideObj; + $0092: + ShowPalette; + $099: + ShowStandardWidth; + $00A1: + ShowPageSetup; + $00AB: + ShowGCW; + $00C1: + ShowMMS; + $009C: + ShowFnGroupCount; + $00BE: + ShowMulBlank; + $00BD: + ShowMulRK; + $00D6: + ShowRString; + $00D7: + ShowDBCell; + $00DA: + ShowBookBool; + $00E0: + ShowXF; + $00E1: + ShowInterfaceHdr; + $00E2: + ShowInterfaceEnd; + $00E5: + ShowMergedCells; + $00EC: + ShowMSODrawing; + $00FC: + ShowSST; + $00FD: + ShowLabelSSTCell; + $013D: + ShowTabID; + $0161: + ShowDSF; + $01AE: + ShowExternBook; + $01AF: + ShowProt4Rev; + $01B6: + ShowTXO; + $01B7: + ShowRefreshAll; + $01B8: + ShowHyperlink; + $01BC: + ShowProt4RevPass; + $01C0: + ShowExcel9File; + $027E: + ShowRK; + $0293: + ShowStyle; + $04BC: + ShowSharedFormula; + $0800: + ShowHyperlinkTooltip; + $087C: + ShowXFCRC; + $087D: + ShowXFEXT; + $0892: + ShowStyleExt; + $105C: + ShowClrtClient; + else + RowCount := 2; + Rows[1].Clear; + end; +end; + + +procedure TBIFFGrid.SetBIFFNodeData(AData: PBIFFNodeData; + ABuffer: TBIFFBuffer; AFormat: TsSpreadsheetFormat); +begin + if AData = nil then + exit; + FFormat := AFormat; + FRecType := AData^.RecordID; + FInfo := AData^.Tag; + SetLength(FBuffer, Length(ABuffer)); + if Length(FBuffer) > 0 then + Move(ABuffer[0], FBuffer[0], Length(FBuffer)); + PopulateGrid; + if Assigned(FOnDetails) then FOnDetails(self, FDetails); +end; + + +procedure TBIFFGrid.ShowBackup; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Save backup copy of workbook:'#13); + if w = 0 + then FDetails.Add('0 = no backup') + else FDetails.Add('1 = backup copy is saved when workbook is saved'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), + 'Save backup copy of workbook'); +end; + + +procedure TBIFFGrid.ShowBlankCell; +var + numBytes: Integer; + b: Byte = 0; + w: Word = 0; + dbl: Double; +begin + RowCount := IfThen(FFormat = sfExcel2, FixedRows + 5, FixedRows + 3); + // Offset 0: Row & Offset 2: Column + ShowRowColData(FBufferIndex); + + // Offset 4: Cell attributes (BIFF2) or XF record index (> BIFF2) + if FFormat = sfExcel2 then begin + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Cell protection and XF index:'#13); + FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F])); + case b and $40 of + 0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.'); + 1: FDetails.Add('Bit 6 = 1: Cell is locked.'); + end; + case b and $80 of + 0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.'); + 1: FDetails.Add('Bit 7 = 1: Formula is hidden.'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]), + 'Cell protection and XF index'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Indexes to format and font records:'#13); + FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f])); + FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]), + 'Indexes of format and font records'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Cell style:'#13); + case b and $07 of + 0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL'); + 1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT'); + 2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED'); + 3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT'); + 4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED'); + end; + if b and $08 = 0 + then FDetails.Add('Bit 3 = 0: Cell has NO left border') + else FDetails.Add('Bit 3 = 1: Cell has left black border'); + if b and $10 = 0 + then FDetails.Add('Bit 4 = 0: Cell has NO right border') + else FDetails.Add('Bit 4 = 1: Cell has right black border'); + if b and $20 = 0 + then FDetails.Add('Bit 5 = 0: Cell has NO top border') + else FDetails.Add('Bit 5 = 1: Cell has top black border'); + if b and $40 = 0 + then FDetails.Add('Bit 6 = 0: Cell has NO bottom border') + else FDetails.Add('Bit 6 = 1: Cell has bottom black border'); + if b and $80 = 0 + then FDetails.Add('Bit 7 = 0: Cell has NO shaded background') + else FDetails.Add('Bit 7 = 1: Cell has shaded background'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]), + 'Cell style'); + end else + begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Index of XF record'); + end; +end; + + +procedure TBIFFGrid.ShowBOF; +var + numBytes: Integer; + w: Word; + s: String; +begin + case FFormat of + sfExcel2: RowCount := FixedRows + 2; + { //Excel3 & 4 not supported by fpspreadsheet + sfExcel3, sfExcel4: RowCount := FixedRows + 3; + } + sfExcel5: RowCount := FixedRows + 4; + sfExcel8: RowCount := FixedRows + 6; + end; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('BIFF version:'#13); + case FRecType of + $0009, + $0209, + $0409: FDetails.Add('not used'); + $0809: case FFormat of + sfExcel5: FDetails.Add('$0500 = BIFF5'); + sfExcel8: FDetails.Add('$0600 = BIFF8'); + end; + else case w of + $0000: FDetails.Add('$0000 = BIFF5'); + $0200: FDetails.Add('$0200 = BIFF2'); + $0300: FDetails.Add('$0300 = BIFF3'); + $0400: FDetails.Add('$0400 = BIFF4'); + $0500: FDetails.Add('$0500 = BIFF5'); + $0600: FDetails.Add('$0600 = BIFF8'); + end; + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), + 'BIFF version'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + s := '$0010=Sheet, $0020=Chart, $0040=Macro sheet'; + if FFormat > sfExcel2 then + s := '$0005=WB globals, $0006=VB module, ' + s + ', $0100=Workspace'; + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Type of data:'#13); + FDetails.Add(Format('$%.4x = %s', [w, BofName(w)])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), + Format('Type of data (%s)', [s])); + + if FFormat > sfExcel2 then begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + { Excel3/4 not supported in fpSpreadsheet + if FFormat in [sfExcel3, sfExcel4] then + ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)), + 'not used') + else} + begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Build identifier (must not be zero)'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Build year (must not be zero)'); + end; + end; + + if FFormat = sfExcel8 then begin + numBytes := 4; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'File history flags'); + + numBytes :=4; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Lowest Excel version that can read all records of this file'); + end; +end; + + +procedure TBIFFGrid.ShowBookBool; +var + numbytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Some properties assosciated with notebook:'#13); + if w and $0001 = 0 + then FDetails.Add('Bit 0 = 0: External link values are saved.') + else FDetails.Add('Bit 0 = 1: External link values are NOT saved.'); + FDetails.Add('Bit 1: to be ignored'); + if w and $0004 = 0 + then FDetails.Add('Bit 2 = 0: Workbook does not have a mail envelope') + else FDetails.Add('Bit 2 = 1: Workbook has a mail envelope'); + if w and $0008 = 0 + then FDetails.Add('Bit 3 = 0: Mail envelope is NOT visible.') + else FDetails.Add('Bit 3 = 1: Mail envelope is visible.'); + if w and $0010 = 0 + then FDetails.Add('Bit 4 = 0: Mail envelope has NOT been initialized.') + else FDetails.Add('Bit 4 = 1: Mail envelope has been initialized.'); + case (w and $0060) shr 5 of + 0: FDetails.Add('Bits 5-6 (Update external links) = 0: Prompt user to update'); + 1: FDetails.Add('Bits 5-6 (Update external linls) = 1: Do not update, and do not prompt user.'); + 2: FDetails.Add('Bits 5-6 (Update external links) = 2: Silently update external links.'); + end; + FDetails.Add('Bit 7: undefined, must be ignored'); + if w and $0100 = 0 + then FDetails.Add('Bit 8 = 0: Do not hide borders of tables that do not contain the active cell') + else FDetails.Add('Bit 8 = 1: Hide borders of tables that do not contain the active cell'); + FDetails.Add('Bits 9-15: MUST BE zero, MUST be ignored'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), + 'Specifies some properties assosciated with a workbook'); +end; + +procedure TBIFFGrid.ShowBoolCell; +var + numBytes: Integer; + w: Word; + b: Byte; +begin + if FFormat = sfExcel2 then + RowCount := FixedRows + 7 + else + RowCount := FixedRows + 5; + + ShowRowColData(FBufferIndex); + + if FFormat = sfExcel2 then begin + numBytes := 1; + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Cell protection and XF index:'#13); + FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F])); + case b and $40 of + 0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.'); + 1: FDetails.Add('Bit 6 = 1: Cell is locked.'); + end; + case b and $80 of + 0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.'); + 1: FDetails.Add('Bit 7 = 1: Formula is hidden.'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Cell protection and XF index'); + + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Indexes to format and font records:'#13); + FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f])); + FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Indexes of format and font records'); + + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Cell style:'#13); + case b and $07 of + 0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL'); + 1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT'); + 2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED'); + 3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT'); + 4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED'); + end; + if b and $08 = 0 + then FDetails.Add('Bit 3 = 0: Cell has NO left border') + else FDetails.Add('Bit 3 = 1: Cell has left black border'); + if b and $10 = 0 + then FDetails.Add('Bit 4 = 0: Cell has NO right border') + else FDetails.Add('Bit 4 = 1: Cell has right black border'); + if b and $20 = 0 + then FDetails.Add('Bit 5 = 0: Cell has NO top border') + else FDetails.Add('Bit 5 = 1: Cell has top black border'); + if b and $40 = 0 + then FDetails.Add('Bit 6 = 0: Cell has NO bottom border') + else FDetails.Add('Bit 6 = 1: Cell has bottom black border'); + if b and $80 = 0 + then FDetails.Add('Bit 7 = 0: Cell has NO shaded background') + else FDetails.Add('Bit 7 = 1: Cell has shaded background'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]), + 'Cell style'); + end else + begin // BIFF3 - BIFF 8 + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Index of XF record'); + end; + + // boolean value + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, + Format('%d (%s)', [b, Uppercase(BoolToStr(Boolean(b), true))]), + 'Boolean value (0=FALSE, 1=TRUE)' + ); + + // bool/error flag + numBytes := 1; + b := FBuffer[FBufferIndex]; + if b = 0 then + ShowInRow(FCurrRow, FBufferIndex, numbytes, '0 (boolean value)', + 'Boolean/Error value flag (0=boolean, 1=error value)') + else + ShowInRow(FCurrRow, FBufferIndex, numbytes, '1 (error value)', + 'Boolean/Error value flag (0=boolean, 1=error value)'); +end; + +procedure TBIFFGrid.ShowBottomMargin; +var + numBytes: Integer; + dbl: Double; +begin + RowCount := FixedRows + 1; + numBytes := 8; + Move(FBuffer[FBufferIndex], dbl, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), + 'Bottom page margin in inches (IEEE 754 floating-point value, 64-bit double precision)'); +end; + + +procedure TBIFFGrid.ShowCalcCount; +var + numBytes: Word; + w: Word; +begin + RowCount := FixedRows + 1; + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Maximum number of iterations allowed in circular references'); +end; + + +procedure TBIFFGrid.ShowCalcMode; +var + numBytes: Word; + w: word; + s: String; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if w = $FFFF then + s := '–1 = automatically except for multiple table operations' + else if w = 0 then + s := '0 = manually' + else if w = 1 then + s := '1 = automatically (default)'; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), s); +end; + + +procedure TBIFFGrid.ShowCellAddress; +{ Note: The bitmask assignment to relative column/row is reversed in relation + to OpenOffice documentation in order to match with Excel files. } +var + numBytes: Word; + b: Byte; + w: Word; + r,c: Integer; + s: String; +begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); // row --> w + r := WordLEToN(w); + if FFormat = sfExcel8 then begin + numBytes := 2; + Move(FBuffer[FBufferIndex+2], w, numBytes); // column --w1 + c := WordLEToN(w); + if FCurrRow = Row then begin + FDetails.Add('RowIndex information:'#13); + FDetails.Add(Format('RowIndex = %d (%s)', [r, ABS_REL[c and $4000 <> 0]])); + end; + s := Format('%d ($%.4x)', [r, r]); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Row index'); + if FCurrRow = Row then begin + FDetails.Add('ColIndex information:'#13); + FDetails.Add(Format('Bits 0-13: ColIndex = %d (%s)', [c and $3FFF, ABS_REL[c and $8000 <> 0]])); + if c and $4000 = 0 + then FDetails.Add('Bit 14=0: absolute column index') + else FDetails.Add('Bit 14=1: relative column index'); + if c and $8000 = 0 + then FDetails.Add('Bit 15=0: absolute row index') + else FDetails.Add('Bit 15=1: relative row index'); + end; + s := Format('%d ($%.4x)', [c, c]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Column index'); + end else + begin + numbytes := 1; + Move(FBuffer[FBufferIndex+2], b, numBytes); + c := b; + if FCurrRow = Row then begin + FDetails.Add('RowIndex information:'#13); + FDetails.Add(Format('Bits 0-13: RowIndex = %d (%s)', [r and $3FFF, ABS_REL[r and $4000 <> 0]])); + if r and $4000 = 0 + then FDetails.Add('Bit 14=0: absolute column index') + else FDetails.Add('Bit 14=1: relative column index'); + if r and $8000 = 0 + then FDetails.Add('Bit 15=0: absolute row index') + else FDetails.Add('Bit 15=1: relative row index'); + end; + //s := Format('$%.4x (%d, %s)', [r, r and $3FFF, ABS_REL[r and $4000 <> 0]]); + s := Format('%d ($%.4x)', [r, r]); + ShowInRow(FCurrRow, FBufferIndex, 2, s, 'Row index'); + if FCurrRow = Row then begin + FDetails.Add('ColIndex information:'#13); + FDetails.Add(Format('ColIndex = %d (%s)', [c, ABS_REL[r and $8000 <> 0]])); + end; + //s := Format('$%.2x (%d, %s)', [c, c, ABS_REL[r and $8000 <> 0]]); + s := Format('%d ($%.4x)', [c, c]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Column index'); + end; +end; + + +procedure TBIFFGrid.ShowCellAddressRange(AFormat: TsSpreadsheetFormat); +{ Note: The bitmask assignment to relative column/row is reversed in relation + to OpenOffice documentation in order to match with Excel files. + + The spreadsheet format is passed as a parameter because some BIFF8 records + used these fields in BIFF5 format. } +var + numbytes: Word; + b: Byte; + w: Word; + r, c, r2, c2: Integer; + s: String; +begin + if AFormat = sfExcel8 then begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + r := WordLEToN(w); + if FCurrRow = Row then begin + FDetails.Add('RowIndex information:'#13); + FDetails.Add(Format('RowIndex = %d (%s)', [r, ABS_REL[c and $4000 <> 0]])); + end; + s := Format('%d ($%.4x)', [r, r]); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'First row index'); + + Move(FBuffer[FBufferIndex], w, numBytes); + r2 := WordLEToN(w); + if FCurrRow = Row then begin + FDetails.Add('RowIndex information:'#13); + FDetails.Add(Format('RowIndex = %d (%s)', [r2, ABS_REL[c and $4000 <> 0]])); + end; + s := Format('%d ($%.4x)', [r2, r2]); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Last row index'); + + Move(FBuffer[FBufferIndex], w, numBytes); // column + c := WordLEToN(w); + Move(FBuffer[FBufferIndex+2], w, numBytes); + c2 := WordLEToN(w); + + if FCurrRow = Row then begin + FDetails.Add('ColIndex information:'#13); + FDetails.Add(Format('Bits 0-13: ColIndex = %d (%s)', [c and $3FFF, ABS_REL[c and $8000 <> 0]])); + if c and $4000 = 0 + then FDetails.Add('Bit 14=0: absolute column index') + else FDetails.Add('Bit 14=1: relative column index'); + if c and $8000 = 0 + then FDetails.Add('Bit 15=0: absolute row index') + else FDetails.Add('Bit 15=1: relative row index'); + end; + s := Format('%d ($%.4x)', [c, c]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'First column index'); + + if FCurrRow = Row then + begin + FDetails.Add('ColIndex information:'#13); + FDetails.Add(Format('Bits 0-13: ColIndex = %d (%s)', [c2 and $3FFF, ABS_REL[c2 and $8000 <> 0]])); + if c2 and $4000 = 0 + then FDetails.Add('Bit 14=0: absolute column index') + else FDetails.Add('Bit 14=1: relative column index'); + if c2 and $8000 = 0 + then FDetails.Add('Bit 15=0: absolute row index') + else FDetails.Add('Bit 15=1: relative row index'); + end; + s := Format('%d ($%.4x)', [c2, c2]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Last column index'); + end + else + begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + r := WordLEToN(w); + Move(FBuffer[FBufferIndex+2], w, numBytes); + r2 := WordLEToN(w); + + numbytes := 1; + c := FBuffer[FBufferIndex+4]; + c2 := FBuffer[FBufferIndex+5]; + + if FCurrRow = Row then + begin + FDetails.Add('RowIndex information:'#13); + FDetails.Add(Format('Bits 0-13: RowIndex = %d (%s)', [r and $3FFF, ABS_REL[r and $4000 <> 0]])); + if r and $4000 = 0 + then FDetails.Add('Bit 14=0: absolute column index') + else FDetails.Add('Bit 14=1: relative column index'); + if r and $8000 = 0 + then FDetails.Add('Bit 15=0: absolute row index') + else FDetails.Add('Bit 15=1: relative row index'); + end; + s := Format('%d ($%.4x)', [r, r]); + ShowInRow(FCurrRow, FBufferIndex, 2, s, 'First row index'); + + if FCurrRow = Row then + begin + FDetails.Add('RowIndex information:'#13); + FDetails.Add(Format('Bits 0-13: RowIndex = %d (%s)', [r2 and $3FFF, ABS_REL[r2 and $4000 <> 0]])); + if r2 and $4000 = 0 + then FDetails.Add('Bit 14=0: absolute column index') + else FDetails.Add('Bit 14=1: relative column index'); + if r2 and $8000 = 0 + then FDetails.Add('Bit 15=0: absolute row index') + else FDetails.Add('Bit 15=1: relative row index'); + end; + s := Format('%d ($%.4x)', [r2, r2]); + ShowInRow(FCurrRow, FBufferIndex, 2, s, 'Last row index'); + + if FCurrRow = Row then + begin + FDetails.Add('ColIndex information:'#13); + FDetails.Add(Format('ColIndex = %d (%s)', [c, ABS_REL[r and $8000 <> 0]])); + end; + s := Format('%d ($%.4x)', [c, c]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'First column index'); + + if FCurrRow = Row then + begin + FDetails.Add('ColIndex information:'#13); + FDetails.Add(Format('ColIndex = %d (%s)', [c2, ABS_REL[r2 and $8000 <> 0]])); + end; + s := Format('%d ($%.4x)', [c2, c2]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Last column index'); + end; +end; + + +procedure TBIFFGrid.ShowClrtClient; +var + w: Word; + dw: DWord; + numbytes: Word; +begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + + RowCount := FixedRows + w + 1; + + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Number of colors (must be 3)'); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]), + 'Foreground color (system window text color)'); + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]), + 'Background color (system window color)'); + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]), '???'); +end; + + +procedure TBIFFGrid.ShowCodePage; +var + numBytes: Word; + w: Word; + s: String; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + s := CodePageName(w); + if Row = FCurrRow then begin + FDetails.Add('Code page:'#13); + FDetails.Add(Format('$%.04x = %s', [w, s])); + end; + if s <> '' then s := 'Code page identifier (' + s + ')' else s := 'Code page identifier'; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), s); +end; + + +procedure TBIFFGrid.ShowColInfo; +var + numBytes: Integer; + w: Word; +begin + if FFormat = sfExcel2 then + exit; + + RowCount := FixedRows + 5; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index of first column in range'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index of last column in range'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d (%f characters)', [w, w/256]), + 'Width of the columns in 1/256 of the width of the zero character, using default font (first FONT record in the file)'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to XF record for default column formatting'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Column options:'#13); + if w and $0001 = 0 + then FDetails.Add('Bit $0001 = 0: Columns are NOT hidden') + else FDetails.Add('Bit $0001 = 1: Columns are hidden'); + FDetails.Add(Format('Bits $0700 = %d: Outline level of the columns (0 = no outline)', [(w and $0700) shr 8])); + if w and $1000 = 0 + then FDetails.Add('Bit $1000 = 0: Columns are NOT collapsed') + else FDetails.Add('Bit $1000 = 1: Columns are collapsed'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), 'Option flags'); +end; + + +procedure TBIFFGrid.ShowColWidth; +var + numBytes: Integer; + w: Word; + b: Byte; +begin + if FFormat <> sfExcel2 then + exit; + + RowCount := FixedRows + 3; + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'Index of first column'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'Index of last column'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Width of the columns in 1/256 of the width of the zero character, using default font (first FONT record in the file)'); +end; + + +procedure TBIFFGrid.ShowContinue; +var + numbytes: Integer; + s: String; + sa: ansistring; + sw: widestring; + ls: Integer; + i, j: Integer; + w: Word; + n: Integer; + run: Integer; + total2: Integer; + optn: Byte; + rtfRuns: TRichTextFormattingRuns; + rtfBufferIndex: LongWord; +begin + case FInfo of + BIFFNODE_TXO_CONTINUE1: + begin + RowCount := FixedRows + 1; + numbytes := Length(FBuffer); + if FBuffer[FBufferIndex] = 0 then begin + ls := Length(FBuffer)-1; + SetLength(sa, ls); + Move(FBuffer[FBufferIndex+1], sa[1], ls); + s := AnsiToUTF8(sa); + end else + if FBuffer[FBufferIndex] = 1 then begin + ls := (Length(FBuffer) - 1) div SizeOf(WideChar); + SetLength(sw, ls); + Move(FBuffer[FBufferIndex+1], sw[1], ls*SizeOf(WideChar)); + s := UTF8Encode(sw); + end else + s := 'ERROR!!!'; + s := UTF8StringReplace(s, #$0A, '[/n]', [rfReplaceAll]); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Comment text'); + end; + + BIFFNODE_TXO_CONTINUE2: + begin + RowCount := FixedRows + 1000; + n := 0; + numBytes := 2; + run := 1; + while FBufferIndex < Length(FBuffer) - 4*SizeOf(Word) do begin + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), Format( + 'Run %d: Index of first character using this font (0-based)', [run])); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToSTr(WordLEToN(w)), + Format('Run %d: Index to FONT record', [run])); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Not used'); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Not used'); + inc(n); + + inc(run); + end; + + // lastRun + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Number of characters'); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Not used'); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInrow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Not used'); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Not used'); + inc(n); + + RowCount := FixedRows + n; + end; + + BIFFNODE_SST_CONTINUE: + begin // Continues an SST record + if FPendingCharCount = -1 then + begin + RowCount := FixedRows + 1; + ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Please select preceding SST record first.'); + exit; + end; + + RowCount := FixedRows + FTotalSST; + n := 0; + + optn := FBuffer[FBufferIndex]; + if optn and $01 = $01 then // wide characters + ExtractString(FBufferIndex+1, true, FPendingCharCount, s, numBytes) + else + ExtractString(FBufferIndex+1, false, FPendingCharCount, s, numbytes); + FPendingCharCount := -1; + inc(numbytes, 1); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, Format('Shared String #%d (rest)', [FCounterSST])); + inc(n); + + FPendingCharCount := -1; + + for i:=FCounterSST+1 to FTotalSST do + begin + FCounterSST := i; + ExtractString(FBufferIndex, 2, true, s, numBytes, rtfRuns, rtfBufferIndex); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, Format('Shared string #%d', [i])); + inc(n); + if Length(rtfRuns) > 0 then begin + numBytes := 2; + for j:=0 to High(rtfRuns) do + begin + ShowInRow(FCurrRow, rtfBufferIndex, 2, IntToStr(rtfRuns[j].FirstIndex), + Format('Rich-Text formatting run #%d, index of first character', [j])); + ShowInRow(FCurrRow, rtfBufferIndex, 2, IntToStr(rtfRuns[j].FontIndex), + Format('Rich-Text formatting run #%d, font index', [j])); + inc(n, 2); + end; + end; + if FPendingCharCount > 0 then + begin + FInfo := BIFFNODE_SST_CONTINUE; + break; + end; + end; + RowCount := FixedRows + n; + if FPendingCharCount = 0 then + FPendingCharCount := -1; + end; + end; +end; + + +procedure TBIFFGrid.ShowCountry; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 2; + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Windows country identifier for UI language of Excel'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Windows country identifier of system regional settings'); +end; + + +procedure TBIFFGrid.ShowDateMode; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + '0 = Base date is 1899-Dec-31, 1 = Base date is 1904-Jan-01'); +end; + +procedure TBIFFGrid.ShowDBCell; +var + i, n: Integer; + dw: DWord; + w: Word; + numBytes: Integer; +begin + if FFormat < sfExcel5 then exit; + + n := (Length(FBuffer) - 4) div 2; + RowCount := FixedRows + 1 + n; + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), + 'Relative offset to first ROW record in the Row Block'); + + numBytes := 2; + for i:=1 to n do begin + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Relative offsets to calculate stream position of the first cell record in row'); + end; +end; + +procedure TBIFFGrid.ShowDefColWidth; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Column width in characters, using the width of the zero character from default '+ + 'font (first FONT record in the file) + some extra space.'); +end; + + +procedure TBIFFGrid.ShowDefinedName; +var + numBytes: Integer; + b: Byte; + w: Word; + isFuncMacro: Boolean; + lenName: Word; + ansistr: AnsiString; + widestr: WideString; + s: String; + macro: Boolean; + formulaSize: Word; + firstTokenBufIdx: Integer; + token: Byte; + r,c, r2,c2: Integer; + builtinName: Boolean; +begin + BeginUpdate; + RowCount := FixedRows + 1000; + // Brute force simplification because of unknown row count at this point + // Will be reduced at the end. + + builtinName := false; + if FFormat = sfExcel2 then + begin + numBytes := 1; + b := FBuffer[FBufferIndex]; + isFuncMacro := b and $02 <> 0; + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + if b and $02 = 0 then + FDetails.Add(' Bit $02 = 0: NO function macro or command macro') + else + FDetails.Add('* Bit $02 = 1: Function macro or command macro'); + if b and $04 = 0 then + FDetails.Add(' Bit $04 = 0: NO Complex function (array formula or user defined)') + else + FDetails.Add('* Bit $04 = 1: Complex function (array formula or user defined)'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), + 'Option flags'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + if isFuncMacro then + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + '$01 = Function macro, $02 = Command macro') + else + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'unknown'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Keyboard shortcut (only for command macro names)'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'Length of the name (character count)'); + lenName := b; + + numbytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(b), + 'Size of the formula data'); + formulaSize := b; + end + else + begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + macro := (w and $0008 <> 0); + builtinName := (w and $0020 <> 0); + FDetails.Add('Option flags:'#13); + if w and $0001 = 0 + then FDetails.Add(' Bit $0001 (flag name "hidden") = 0: Visible') + else FDetails.Add('* Bit $0001 (flag name "hidden")= 1: Hidden'); + if w and $0002 = 0 + then FDetails.Add(' Bit §0002 (flag name "func") = 0: Command macro') + else FDetails.Add('* Bit $0002 (flag name "func") = 1: Function macro'); + if w and $0004 = 0 + then FDetails.Add(' Bit $0004 (flag name "vbasic") = 0: Sheet macro') + else FDetails.Add('* Bit $0004 (flag name "vbasic") = 1: Visual basic macro'); + if w and $0008 = 0 + then FDetails.Add(' Bit $0008 (flag name "macro") = 0: Standard name') + else FDetails.Add('* Bit $0008 (flag name "macro") = 1: Macro name'); + if w and $0010 = 0 + then FDetails.Add(' Bit $0010 (flag name "complex") = 0: Simple formula') + else FDetails.Add('* Bit $0010 (flag name "complex") = 1: Complex formula (array formula or user defined)'); + if w and $0020 = 0 + then FDetails.Add(' Bit $0020 (flag name "builtin") = 0: User-defined name') + else FDetails.Add('* Bit $0020 (flag name "builtin") = 1: Built-in name'); + case (w and $0FC0) shr 6 of + 0: if macro then + FDetails.Add(' Bit $0FC0 = 0: --- forbidden value, must be > 0 ---') + else + FDetails.Add(' Bit $0FC0 (flag name "funcgroup") = 0: not used (requires "macro" = 1)'); + 1: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 1: financial'); + 2: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 2: date & time'); + 3: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 3: math & trig'); + 4: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 4: statistical'); + 5: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 5: lookup & reference'); + 6: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 6: database'); + 7: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 7: text'); + 8: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 8: logical'); + 9: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 9: information'); + 10: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 10: commands'); + 11: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 11: customizing'); + 12: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 12: macro control'); + 13: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 13: dde/external'); + 14: FDetails.Add('* Bit $0FC0 (flag name "funcgroup") = 14: user defined'); + end; + if w and $1000 = 0 + then FDetails.Add(' Bit $1000 (flag name "binary") = 0: formula definition') + else FDetails.add('* Bit $1000 (flag name "binary") = 1: binary data (BIFF5-BIFF8)'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Option flags'); + + numbytes := 1; + b := FBuffer[FBufferIndex]; + ShowInrow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]), + 'Keyboard shortcurt (only for command macro names)'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + 'Length of the name (character count)'); + lenName := b; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Size of the formula data'); + formulaSize := w; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if FFormat = sfExcel5 then + ShowInRow(FCurrRow, FBufferIndex, NumBytes, IntToStr(w), + '0 = Global name, otherwise index to EXTERNSHEET record (one-based)') + else + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'not used'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, NumBytes, IntToStr(w), + '0 = Global name, otherwise index to sheet (one-based)'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b), + 'Length of the menu text (character count)'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b), + 'Length of the description text (character count)'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b), + 'Length of the help topic text (character count)'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b), + 'Length of the status bar text (character count)'); + + if FFormat = sfExcel5 then begin + numBytes := lenName * sizeOf(ansiChar); + SetLength(ansiStr, lenName); + Move(FBuffer[FBufferIndex], ansiStr[1], numbytes); + s := AnsiToUTF8(ansistr); + end else + begin + if (FBuffer[FBufferIndex] and $01 = 0) //and (not IgnoreCompressedFlag) + then begin // compressed --> 1 byte per character + SetLength(ansiStr, lenName); + numbytes := lenName*SizeOf(ansiChar) + 1; + Move(FBuffer[FBufferIndex + 1], ansiStr[1], lenName*SizeOf(AnsiChar)); + s := AnsiToUTF8(ansiStr); + end else begin + SetLength(wideStr, lenName); + numBytes := lenName*SizeOf(WideChar) + 1; + Move(FBuffer[FBufferIndex + 1], wideStr[1], lenName*SizeOf(WideChar)); + s := UTF8Encode(WideStringLEToN(wideStr)); + end; + end; + if builtinName and (Length(s) = 1) then begin + s := Format('%s ($%x --> ', [s, ord(s[1])]); + case ord(s[1]) of + 0: s := s + 'Consolidate_Area)'; + 1: s := s + 'Auto_Open)'; + 2: s := s + 'Auto_Close)'; + 3: s := s + 'Extract)'; + 4: s := s + 'Database)'; + 5: s := s + 'Citeria)'; + 6: s := s + 'Print_Area)'; + 7: s := s + 'Print_Titles)'; + 8: s := s + 'Recorder)'; + 9: s := s + 'Data_Form)'; + 10: s := s + 'Auto_Activate)'; + 11: s := s + 'Auto_Deactivate)'; + 12: s := s + 'Sheet_Title)'; + 13: s := s + '_FilterDatabase)'; + else s := s + 'unknown meaning)'; + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Name (Unicode string without length field)'); + end; + + firstTokenBufIdx := FBufferIndex; + while FBufferIndex < firstTokenBufIdx + formulaSize do begin + token := FBuffer[FBufferIndex]; + numBytes := 1; + case token of + $10: + begin + numBytes := 1; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tList (List operator)'); + end; + + $29, $39, $49: + begin + case token of + $29: s := 'Token tMemFuncR'; + $39: s := 'Token tMemFuncV'; + $49: s := 'Token tMemFuncV'; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]), s); + if FFormat = sfExcel2 then + begin + numbytes := 1; + b := FBuffer[FBufferIndex]; + w := b; + end else + begin + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Size of following subexpression (ends after offset ' + IntToStr(FBufferIndex+w-1+numbytes) + ')'); + end; + + $3A, $3B, $5A, $5B, $7A, $7B: + begin + case token of + $3A: s := 'Token tRef3dR for "3D or external reference to a cell" (R = Reference)'; + $5A: s := 'Token tRef3dV for "3D or external reference to a cell" (V = Value)'; + $7A: s := 'Token tRef3dA for "3D or external reference to a cell" (A = Area)'; + $3B: s := 'Token tArea3dR for "3D or external reference to a cell range" (R = Reference)'; + $5B: s := 'Token tArea3dV for "3D or external reference to a cell range" (V = Value)'; + $7B: s := 'Token tArea3dA for "3D or external reference to a cell range" (A = Area)'; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]), s); + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if FFormat = sfExcel5 then begin + if w and $8000 <> 0 then begin // negative value --> 3D reference + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(SmallInt(w)), + 'negative --> 3D reference, 1-based index to EXTERNSHEET record = ' + IntToStr(-SmallInt(w))); + numBytes := 8; + ShowInRow(FCurrRow, FBufferIndex, numBytes, '', 'Not used'); + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Zero-based index to first referenced sheet ($FFFF = deleted sheet)'); + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Zero-based index to last referenced sheet ($FFFF = deleted sheet)'); + end else + begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'External reference, 1-based index to EXTERNSHEET record'); + numBytes := 12; + ShowInRow(FCurrRow, FBufferIndex, numBytes, '', 'Not used'); + end; + end else + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Index to REF entry in EXTERNSHEET record'); + + if token in [$3A, $5A, $7A] then + ShowCellAddress // Cell address + else + ShowCellAddressRange(FFormat); // Cell range + end; + + else + numBytes := 1; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + '(unknown token)'); + end; // case + end; // while + + RowCount := FCurrRow; + EndUpdate(true); +end; + +procedure TBIFFGrid.ShowDefRowHeight; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + IfThen(FFormat = sfExcel2, 1, 2); + + if FFormat = sfExcel2 then begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Default height for unused rows:'#13); + FDetails.Add(Format( + 'Bits $7FFF = %d: Default height for unused rows, in twips = 1/20 of a point', + [w and $7FFF])); + if w and $8000 = 0 then + FDetails.Add('Bit $8000 = 0: Row height changed manually') + else + FDetails.Add('Bit $8000 = 1: Row height not changed manually'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Default height for unused rows, in twips = 1/20 of a point'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + if w and $0001 = 0 + then FDetails.Add('Bit $0001 = 0: Row height and default font height do match') + else FDetails.Add('Bit $0001 = 1: Row height and default font height do not match'); + if w and $0002 = 0 + then FDetails.Add('Bit $0002 = 0: Row is visible') + else FDetails.Add('Bit $0002 = 1: Row is hidden'); + if w and $0004 = 0 + then FDetails.Add('Bit $0004 = 0: No additional space above the row') + else FDetails.Add('Bit $0004 = 1: Additional space above the row'); + if w and $0008 = 0 + then FDetails.Add('Bit $0008 = 0: No additional space below the row') + else FDetails.Add('Bit $0008 = 1: Additional space below the row'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [WordLEToN(w)]), + 'Option flags'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Default height for unused rows, in twips = 1/20 of a point'); + end; +end; + + +procedure TBIFFGrid.ShowDelta; +var + numBytes: Integer; + dbl: Double; +begin + RowCount := FixedRows + 1; + numBytes := 8; + Move(FBuffer[FBufferIndex], dbl, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), + 'Maximum change in iteration (IEEE 754 floating-point value, 64-bit double precision)'); +end; + + +procedure TBIFFGrid.ShowDimensions; +var + numBytes: Integer; + dw: DWord; + w: Word; +begin + RowCount := FixedRows + IfThen(FFormat = sfExcel2, 4, 5); + + if FFormat = sfExcel8 then begin + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(DWordLEToN(dw)), + 'Index to first used row'); + + Move(FBuffer[FBufferIndex], dw, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(DWordLEToN(dw)), + 'Index to last used row, increased by 1'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to first used row'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to last used row, increased by 1'); + end; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to first used column'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to last used column, increased by 1'); + + if FFormat <> sfExcel2 then begin + numBytes := 2; + ShowInRow(FCurrRow, FBufferIndex, numBytes, '', + '(not used)'); + end; +end; + + +procedure TBIFFGrid.ShowDSF; +var + w: Word; + numbytes: Integer; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), 'Reserved, MUST be ignored'); +end; + + +procedure TBIFFGrid.ShowEOF; +begin + RowCount := FixedRows + 1; + ShowInRow(FCurrRow, FBufferIndex, 0, '', '(no content)'); +end; + + +procedure TBIFFGrid.ShowExcel9File; +begin + RowCount := FixedRows + 1; + ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Optional and unused'); +end; + + +procedure TBIFFGrid.ShowExternBook; +var + numBytes: Integer; + w: Word; + wideStr: WideString; + ansiStr: AnsiString; + s: String; + i, n: Integer; + b: Byte; + rtfRuns: TRichTextFormattingRuns; +begin + BeginUpdate; + RowCount := FixedRows + 1000; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + n := WordLEToN(w); + + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(n), + 'Number of sheet names / number of sheets'); + + if Length(FBuffer) - FBufferIndex = 2 then begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + SetLength(ansiStr, 1); + ansiStr[1] := char((w and $FF00) shr 8); + s := Format('%s (string bytes <#%.2x> <#%.2x>)', [ansistr, w and $00FF, (w and $FF00) shr 8]); + + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, + '(relict of BIFF5)'); + end else begin + ExtractString(FBufferIndex, 2, true, s, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Encoded URL without sheet name:'#13); + case s[1] of + #0: FDetails.Add('First character = #00: Reference relative to current sheet'); + #1: FDetails.Add('First character = #01: Encoded URL follows'); + #2: if FFormat = sfExcel8 then + FDetails.Add('First character = #02: Reference to a sheet in own document; sheet name follows') + else + FDetails.Add('First character = #02: Reference to the corrent sheet (nothing will follow)'); + #3: if FFormat = sfExcel5 then + FDetails.Add('First character = #03: Reference to a sheet in own document; sheet name follows') + else + FDetails.Add('First character = #03: not used'); + #4: if FFormat = sfExcel5 then + FDetails.ADd('First character = #03: Reference to the own workbook, sheet is unspecified (nothing will follow)') + else + FDetails.Add('First character = #03: not used'); + end; + end; + if s[1] in [#0, #1, #2, #3, #4] then Delete(s, 1, 1); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, + 'Encoded URL without sheet name (Unicode string, 16-bit string length)'); + + for i:=0 to n-1 do begin + ExtractString(FBufferIndex, 2, true, s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, + 'Sheet name (Unicode string with 16-bit string length)'); + end; + end; + + RowCount := FCurrRow; + EndUpdate(true); +end; + + +procedure TBIFFGrid.ShowExternCount; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Number of following EXTERNSHEET records'); +end; + + +procedure TBIFFGrid.ShowExternSheet; +var + numBytes: Integer; + w: Word; + s: String; + nREF: Integer; + i: Integer; + len: Byte; + ansiStr: AnsiString; +begin + if FFormat <= sfExcel5 then begin + RowCount := FixedRows + 1; + len := FBuffer[0]; + if FBuffer[1] = $03 then inc(len); + numBytes := len*SizeOf(AnsiChar) + 1; + SetLength(ansiStr, len); + Move(FBuffer[1], ansiStr[1], len*SizeOf(AnsiChar)); + s := AnsiToUTF8(ansiStr); + if FCurrRow = Row then begin + FDetails.Add('Encoded document and sheet name:'#13); + if s[1] = #03 then begin + FDetails.Add('First character = $03: EXTERNSHEET stores a reference to one of the own sheets'); + FDetails.Add('Document name: ' + Copy(s, 2, Length(s))); + end else + if (Length(s) = 1) and (s[1] = ':') then begin + FDetails.Add('Special EXTERNSHEET record for an add-in function. EXTERNName record with the name of the function follows.'); + end else + FDetails.Add('Document name: ' + s); + end; + if s[1] = #03 then begin + Delete(s, 1, 1); + s := '<#03>' + s; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, + 'Encoded document and sheet name (Byte string, 8-bit string length)'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + nREF := WordLEToN(w); + + RowCount := FixedRows + 1 + nREF*3; + + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(nREF), + 'Number of following REF structures'); + + for i:=1 to nREF do begin + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + Format('REF #%d: Index to EXTERNBOOK record', [i])); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + Format('REF #%d: Index to first sheet in EXTERNBOOK sheet list', [i])); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + Format('REF #%d: Index to last sheet in EXTERNBOOK sheet list', [i])); + end; + end; +end; + + +procedure TBIFFGrid.ShowFileSharing; +var + numbytes: Integer; + w: Word; + s: String; +begin + RowCount := FixedRows + 3; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Recommend read-only state when loading the file:'#13); + if w = 0 then FDetails.Add('0 = no') else FDetails.Add('1 = yes'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Recommend read-only state when loading the file'); + + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), + 'Hash value calculated from the read-only password'); + + ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8, + s, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'User name of the file creator' + IfThen(FFormat = sfExcel8, + ' (Unicode string, 16-bit string length)', + ' (byte string, 8-bit string length)' + )); +end; + + +procedure TBIFFGrid.ShowFnGroupCount; +var + numbytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Number of built-in function categories:'#13); + case w of + $000E: + FDetails.Add( + 'There are 14 built-in function categories in the workbook.'#13+ + 'This implies that the file was last saved by a specific version of the application.'#13+ + 'The following 9 built-in function categories are visible to the end-user:'#13+ + ' Financial'#13+ + ' Date & Time'#13+ + ' Math & Trig'#13+ + ' Statistical'#13+ + ' Lookup & Reference'#13+ + ' Database'#13+ + ' Text'#13+ + ' Logical'#13+ + ' Information'#13+ + 'The following 5 built-in function categories are not visible to the end-user:'#13+ + ' UserDefined'#13+ + ' Commands'#13+ + ' Customize'#13+ + ' MacroControl'#13+ + ' DDEExternal' + ); + $0010: + FDetails.Add( + 'There are 16 built-in function categories in the workbook.'#13+ + 'This implies that the file was last saved by a specific version of the application'#13+ + 'The following 11 built-in function categories are visible to the end-user:'#13+ + ' Financial'#13+ + ' Date & time'#13+ + ' Math & Trig'#13+ + ' Statistical'#13+ + ' Lookup & Reference'+ + ' Database'#13+ + ' Text'#13+ + ' Logical'#13+ + ' Information'#13+ + ' Engineering'#13+ + ' Cube'#13+ + 'The following 5 built-in function categories are not visible to the end-user:'#13+ + ' UserDefined'#13+ + ' Commands'#13+ + ' Customize'#13+ + ' MacroControl'#13+ + ' DDEExternal' + ); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Number of built-in function categories'); +end; + + +procedure TBIFFGrid.ShowFont; +var + numbytes: Integer; + w: Word; + b: Byte; + s: String; +begin + RowCount := IfThen(FFormat = sfExcel2, 3, 10) + FixedRows; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d (= %.1gpt)', [w, w/20]), + 'Font height in twips (=1/20 point)'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + if w and $0001 = 0 + then FDetails.Add(' Bit $0001 = 0: not bold') + else FDetails.Add('x Bit $0001 = 1: bold (redundant in BIFF5-BIFF8)'); + if w and $0002 = 0 + then FDetails.Add(' Bit $0002 = 0: not italic') + else FDetails.Add('x Bit $0002 = 1: italic'); + if w and $0004 = 0 + then FDetails.Add(' Bit $0004 = 0: not underlined') + else FDetails.Add('x Bit $0004 = 1: underlined (redundant in BIFF5-BIFF8)'); + if w and $0008 = 0 + then FDetails.Add(' Bit $0008 = 0: not struck out') + else FDetails.Add('x Bit $0008 = 1: struck out'); + if w and $0010 = 0 + then FDetails.Add(' Bit $0010 = 0: not outlined') + else FDetails.Add('x Bit $0010 = 1: outlined'); + if w and $0020 = 0 + then FDetails.Add(' Bit $0020 = 0: not shadowed') + else FDetails.Add('x Bit $0020 = 1: shadowed'); + if w and $0040 = 0 + then FDetails.Add(' Bit $0040 = 0: not condensed') + else FDetails.Add('x Bit $0040 = 1: condensed'); + if w and $0080 = 0 + then FDetails.Add(' Bit $0080 = 0: not extended') + else FDetails.Add('x Bit $0080 = 1: extended'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Option flags'); + + if FFormat <> sfExcel2 then begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Color index'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w,w]), + 'Font weight (400=normal, 700=bold)'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Escapement:'#13); + case w of + 0: FDetails.Add('0 = none'); + 1: FDetails.Add('1 = superscript'); + 2: FDetails.Add('2 = subscript'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), + 'Escapement ($00=none, $01=superscript, $02=subscript)'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Underline type:'#13); + case b of + $00: FDetails.Add('$00 = no underline'); + $01: FDetails.Add('$01 = single underline'); + $02: FDetails.Add('$02 = double underline'); + $21: FDetails.Add('$21 = single accounting'); + $22: FDetails.Add('$22 = double accounting'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), + 'Underline type ($00=none, $01=single, $02=double, ...)'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Font family:'#13); + case b of + $00: FDetails.Add('$00 = None (unknown or don''t care)'); + $01: FDetails.Add('$01 = Roman (variable width, serifed)'); + $02: FDetails.Add('$02 = Swiss (variable width, sans-serifed)'); + $03: FDetails.Add('$03 = Modern (fixed width, serifed or sans-serifed)'); + $04: FDetails.Add('$04 = Script (cursive)'); + $05: FDetails.Add('$05 = Decorative (specialised, for example Old English, Fraktur)'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%.2x', [b]), + 'Font family'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + case b of + $00: s := 'ANSI Latin'; + $01: s := 'System default'; + $02: s := 'Symbol'; + $4D: s := 'Apple Roman'; + $80: s := 'ANSI Japanese Shift-JIS'; + $81: s := 'ANSI Korean (Hangul)'; + $82: s := 'ANSI Korean (Johab)'; + $86: s := 'ANSI Chinese Simplified GBK'; + $88: s := 'ANSI Chinese Traditional BIG5'; + $A1: s := 'ANSI Greek'; + $A2: s := 'ANSI Turkish'; + $A3: s := 'ANSI Vietnamese'; + $B1: s := 'ANSI Hebrew'; + $B2: s := 'ANSI Arabic'; + $BA: s := 'ANSI Baltic'; + $CC: s := 'ANSI Cyrillic'; + $DE: s := 'ANSI Thai'; + $EE: s := 'ANSI Latin II (Central European)'; // East Europe in MS docs! + $FF: s := 'OEM Latin I'; + else s := ''; + end; + if s <> '' then s := Format('$%.2x: %s', [b, s]) else s := Format('$%.2x', [b]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, + 'Character set'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Not used'); + end; + + ExtractString(FBufferIndex, 1, FFormat=sfExcel8, s, numbytes); + if FFormat = sfExcel8 then + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Font name (unicode string, 8-bit string length)') + else + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Font name (byte string, 8-bit string length)'); +end; + + +procedure TBIFFGrid.ShowFontColor; +var + numBytes: Integer; + w: Word; + s: String; +begin + RowCount := FixedRows + 1; + NumBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + case w of + $0000: s := 'EGA Black (rgb = $000000)'; + $0001: s := 'EGA White (rgb = $FFFFFF)'; + $0002: s := 'EGA Red (rgb = $0000FF)'; + $0003: s := 'EGA Green (rgb = $00FF00)'; + $0004: s := 'EGA Blue (rgb = $FF0000)'; + $0005: s := 'EGA Yellow (rgb = $00FFFF)'; + $0006: s := 'EGA Magenta (rgb = $FF00FF)'; + $0007: s := 'EGA Cyan (rgb = $FFFF00)'; + $7FFF: s := 'Automatic (system window text colour)'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.04x)', [w, w]), + Format('Font color index into preceding FONT record (%s)', [s])); +end; + +procedure TBIFFGrid.ShowFooter; +var + numbytes: Integer; + s: String; +begin + RowCount := FixedRows + 1; + if Length(FBuffer) = 0 then + ShowInRow(FCurrRow, FBufferIndex, 0, '', '(empty record)') + else + begin + ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8, + s, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Page footer string' + IfThen(FFormat = sfExcel8, + ' (Unicode string, 16-bit string length)', + ' (byte string, 8-bit string length)' + )); + end; +end; + + +procedure TBIFFGrid.ShowFormat; +var + numBytes: Integer; + w: word; + b: Byte; + s: String; +begin + RowCount := IfThen(FFormat = sfExcel2, FixedRows + 1, FixedRows + 2); + if FFormat <> sfExcel2 then begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'FormatIndex used in other records'); + end; + b := IfThen(FFormat=sfExcel8, 2, 1); + ExtractString(FBufferIndex, b, (FFormat=sfExcel8), s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, + Format('Number format string (%s string, %d-bit string length)', [GetStringType, b*8])); +end; + + +procedure TBIFFGrid.ShowFormatCount; +var + numBytes: Integer; + w: Word; +begin + if FFormat = sfExcel2 then begin + RowCount := 1 + FixedRows; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Number of FORMAT records'); + end; +end; + + +procedure TBIFFGrid.ShowFormula; +var + numBytes: Integer; + b: Byte; + w: Word; + q: QWord; + dbl: double absolute q; + bytearr: array[0..7] of byte absolute q; + wordarr: array[0..3] of word absolute q; +// s: String; + tokenBytes: Integer; +// firstTokenBufIdx: Integer; +// token: Byte; + r,c, r2,c2: Integer; +begin + BeginUpdate; + RowCount := FixedRows + 1000; + // Brute force simplification because of unknown row count at this point + // Will be reduced at the end. + + // Offset 0 = Row, Offset 2 = Column + ShowRowColData(FBufferIndex); + // Offset 4 = Cell attributes (BIFF2) or XF ecord index (> BIFF2) + if FFormat = sfExcel2 then begin + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Cell protection and XF index:'#13); + FDetails.Add(Format('x Bits 5-0 = %d: XF Index', [b and $3F])); + case b and $40 of + 0: FDetails.Add(' Bit 6 = 0: Cell is NOT locked.'); + 1: FDetails.Add('x Bit 6 = 1: Cell is locked.'); + end; + case b and $80 of + 0: FDetails.Add(' Bit 7 = 0: Formula is NOT hidden.'); + 1: FDetails.Add('x Bit 7 = 1: Formula is hidden.'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Cell protection and XF index'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Indexes to format and font records:'#13); + FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f])); + FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Indexes of format and font records'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Cell style:'#13); + case b and $07 of + 0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL'); + 1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT'); + 2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED'); + 3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT'); + 4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED'); + end; + if b and $08 = 0 + then FDetails.Add('Bit 3 = 0: Cell has NO left border') + else FDetails.Add('Bit 3 = 1: Cell has left black border'); + if b and $10 = 0 + then FDetails.Add('Bit 4 = 0: Cell has NO right border') + else FDetails.Add('Bit 4 = 1: Cell has right black border'); + if b and $20 = 0 + then FDetails.Add('Bit 5 = 0: Cell has NO top border') + else FDetails.Add('Bit 5 = 1: Cell has top black border'); + if b and $40 = 0 + then FDetails.Add('Bit 6 = 0: Cell has NO bottom border') + else FDetails.Add('Bit 6 = 1: Cell has bottom black border'); + if b and $80 = 0 + then FDetails.Add('Bit 7 = 0: Cell has NO shaded background') + else FDetails.Add('Bit 7 = 1: Cell has shaded background'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]), + 'Cell style'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Index of XF record'); + end; + + // Offset 6: Result of formula + numBytes := 8; + Move(FBuffer[FBufferIndex], q, numBytes); + if wordarr[3] <> $FFFF then begin + if FCurrRow = Row then begin + FDetails.Add('Formula result:'#13); + FDetails.Add(Format('Bytes 0-7: $%.15x --> IEEE 764 floating-point value, 64-bit double precision'#13+ + ' = %g', [q, dbl])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), + 'Result of formula (IEEE 764 floating-point value, 64-bit double precision)'); + end else begin + case bytearr[0] of + 0: begin // String result + if FCurrRow = Row then begin + FDetails.Add('Formula result:'#13); + FDetails.Add('Byte 0 = 0 --> Result is string, follows in STRING record'); + FDetails.Add('Byte 1-5: Not used'); + FDetails.Add('Byte 6&7: $FFFF --> no floating point number'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]), + 'Result is a string, follows in STRING record'); + end; + 1: begin // BOOL result + if FCurrRow = Row then begin + FDetails.Add('Formula result:'#13); + FDetails.Add('Byte 0 = 1 --> Result is BOOLEAN'); + FDetails.Add('Byte 1: Not used'); + if bytearr[2] = 0 + then FDetails.Add('Byte 2 = 0 --> FALSE') + else FDetails.Add('Byte 2 = 1 --> TRUE'); + FDetails.Add('Bytes 3-5: Not used'); + FDetails.Add('Bytes 6&7: $FFFF --> no floating point number'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]), + 'Result is BOOLEAN'); + end; + 2: begin // ERROR result + if FCurrRow = Row then begin + FDetails.Add('Formula result:'#13); + FDetails.Add('Byte 0 = 2 --> Result is an ERROR value'); + FDetails.Add('Byte 1: Not used'); + case bytearr[2] of + $00: FDetails.Add('Byte 2 = $00 --> #NULL! Intersection of two cell ranges is empty'); + $07: FDetails.Add('Byte 2 = $07 --> #DIV/0! Division by zero'); + $0F: FDetails.Add('Byte 2 = $0F --> #VALUE! Wrong type of operand'); + $17: FDetails.Add('Byte 2 = $17 --> #REF! Illegal or deleted cell reference'); + $1D: FDetails.Add('Byte 2 = $1D --> #NAME? Wrong function or range name'); + $24: FDetails.Add('Byte 2 = $24 --> #NUM! Value range overflow'); + $2A: FDetails.Add('Byte 2 = $2A --> #N/A Argument or function not available'); + end; + FDetails.Add('Bytes 6&7: $FFFF --> no floating point number'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]), + 'Result is an ERROR value'); + end; + 3: begin // EMPTY cell + if FCurrRow = Row then begin + FDetails.Add('Formula result:'#13); + FDetails.Add('Byte 0 = 3 --> Result is an empty cell, for example an empty string'); + FDetails.Add('Byte 1-5: Not used'); + FDetails.Add('Bytes 6&7: $FFFF --> no floating point number'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]), + 'Result is an EMPTY cell (empty string)'); + end; + end; + end; + + // Option flags + if FFormat = sfExcel2 then begin + numBytes := 1; + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + case b of + 0: FDetails.Add('0 = Do not recalculate'); + 1: FDetails.Add('1 = Recalculate always'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), 'Option flags'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + if w and $0001 = 0 + then FDetails.Add('Bit $0001 = 0: Do not recalculate') + else FDetails.Add('Bit $0001 = 1: Recalculate always'); + FDetails.Add('Bit $0002: Reserved - MUST be zero, MUST be ignored'); + if w and $0004 = 0 + then FDetails.Add('Bit $0004 = 0: Cell does NOT have a fill alignment or a center-across-selection alignment.') + else FDetails.Add('Bit $0004 = 1: Cell has either a fill alignment or a center-across-selection alignment.'); + if w and $0008 = 0 + then FDetails.Add('Bit $0008 = 0: Formula is NOT part of a shared formula') + else FDetails.Add('Bit $0008 = 1: Formula is part of a shared formula'); + FDetails.Add('Bit $0010: Reserved - MUST be zero, MUST be ignored'); + if w and $0020 = 0 + then FDetails.Add('Bit $0020 = 0: Formula is NOT excluded from formula error checking') + else FDetails.Add('Bit $0020 = 1: Formula is excluded from formula error checking'); + FDetails.Add('Bits $FC00: Reserved - MUST be zero, MUST be ignored'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), + 'Option flags'); + end; + + // Not used + if (FFormat >= sfExcel5) then begin + numBytes := 4; + ShowInRow(FCurrRow, FBufferIndex, numBytes, '', '(not used'); + end; + + // Size of Token array (in Bytes) + if FFormat = sfExcel2 then begin + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + tokenBytes := b; + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + tokenBytes := WordLEToN(w); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(tokenBytes), + 'Size of formula data (in Bytes)'); + + ShowFormulaTokens(tokenBytes); + + RowCount := FCurrRow; + EndUpdate(true); +end; + + +procedure TBIFFGrid.ShowFormulaTokens(ATokenBytes: Integer); +var + numBytes: Integer; + b: Byte; + w: Word; + s: String; + dbl: Double; + firstTokenBufIndex: Integer; + token: Byte; + r, c: Word; +begin + // Tokens and parameters + firstTokenBufIndex := FBufferIndex; + while FBufferIndex < firstTokenBufIndex + ATokenBytes do begin + token := FBuffer[FBufferIndex]; + numBytes := 1; + case token of + $01: begin + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]), + 'Token for "Cell is part of shared formula"'); + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to row of first FORMULA record in the formula range'); + if FFormat = sfExcel2 then begin + numbytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + 'Index to column of first FORMULA record in the formula range'); + end else begin + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to column of first FORMULA record in the formula range'); + end; + end; + $02: begin + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]), + 'Token for "Cell is part of a multiple operations table"'); + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to first row of the table range'); + if FFormat = sfExcel2 then begin + numbytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + 'Index to first column of the table range'); + end else begin + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to first column of the table range'); + end; + end; + $03: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "+" (add)'); + $04: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "-" (subtract)'); + $05: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "*" (multiply)'); + $06: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "/" (divide)'); + $07: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "^" (power)'); + $08: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "&" (concat)'); + $09: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "<" (less than)'); + $0A: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "<=" (less equal)'); + $0B: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "=" (equal)'); + $0C: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token ">=" (greater equal)'); + $0D: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token ">" (greater than)'); + $0E: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "<>" (not equal)'); + $0F: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token " " (intersect)'); + $10: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "list character"'); + $11: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token ":" (range)'); + $12: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "+" (unary plus)'); + $13: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "-" (unary minus)'); + $14: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "%" (percent)'); + $15: ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]), + 'Token "()" (operator in parenthesis)'); + $16: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token "missing argument"'); + $17: begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tSTR (Label)'); + ExtractString(FBufferIndex, 1, (FFormat = sfExcel8), s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'String value'); + end; + $1C: begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tERR (Error)'); + numBytes := 1; + b := FBuffer[FBufferIndex]; + if FCurrRow = Row then begin + FDetails.Add('Error code:'#13); + FDetails.Add(Format('Code $%.2x --> "%s"', [b, GetErrorValueStr(TsErrorValue(b))])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Error code'); + end; + $1D: begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tBOOL'); + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + '0=FALSE, 1=TRUE'); + end; + $1E: begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tINT (Integer)'); + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Integer value'); + end; + $1F: begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tNUM (Number)'); + numBytes := 8; + Move(FBuffer[FBufferIndex], dbl, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%g', [dbl]), //FloatToStr(dbl), + 'IEEE 754 floating-point value'); + end; + $20, $40, $60: + begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tARRAY'); + if FFormat = sfExcel2 then numBytes := 6 else numBytes := 7; + ShowInRow(FCurrRow, FBufferIndex, numbytes, '', '(not used)'); + end; + $21, $41, $61: + begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tFUNC (Function with fixed argument count)'); + if FFormat = sfExcel2 then begin + numBytes := 1; + b := FBuffer[FBufferIndex]; + s := Format('Index of function (%s)', [SheetFuncName(b)]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), s); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + s := Format('Index of function (%s)', [SheetFuncName(w)]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), s); + end; + end; + $22, $42, $62: + begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tFUNCVAR (Function with variable argument count)'); + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'Number of arguments'); + if FFormat = sfExcel2 then begin + numBytes := 1; + s := Format('Index of built-in function (%s)', [SheetFuncName(b)]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), s); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + s := Format('Index of built-in function (%s)', [SheetFuncName(w)]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), s); + end; + end; + $23, $43, $63: + begin + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + 'Token tNAME'); + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + case FFormat of + sfExcel2: s := 'DEFINEDNAME or EXTERNALNAME record'; + sfExcel5: s := 'DEFINEDNAME record in Global Link Table'; + sfExcel8: s := 'DEFINEDNAME record in Link Table'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + '1-based index to '+s); + case FFormat of + sfExcel2: numBytes := 5; + sfExcel5: numBytes := 12; + sfExcel8: numBytes := 2; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, '', '(not used)'); + end; + $24, $44, $64: + begin + case token of + $24: s := 'reference'; + $44: s := 'value'; + $64: s := 'array'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + Format('Token tREF (Cell %s)', [s])); + ShowCellAddress; + end; + $25, $45, $65: + begin + case token of + $25: s := 'reference'; + $45: s := 'value'; + $65: s := 'array'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + Format('Token tAREA (Cell range %s)', [s])); + ShowCellAddressRange(FFormat); + end; + $2C, $4C, $6C: + begin + case token of + $2C: s := 'reference'; + $4C: s := 'value'; + $6C: s := 'array'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + Format('Token tREFN (Relative reference to cell %s in same sheet)', [s])); + + // Encoded relative cell address + if FFormat = sfExcel8 then + begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); // row + r := WordLEToN(w); + Move(FBuffer[FBufferIndex+2], w, numBytes); // column with flags + c := WordLEToN(w); + + { Note: The bitmask assignment to relative column/row is reversed in relation + to OpenOffice documentation in order to match with Excel files. } + if Row = FCurrRow then begin + FDetails.Add('Encoded cell address (row):'#13); + if c and $8000 = 0 then + begin + FDetails.Add('Row index is ABSOLUTE (see Encoded column index)'); + FDetails.Add('Absolute row index: ' + IntToStr(r)); + end else + begin + FDetails.Add('Row index is RELATIVE (see Encoded column index)'); + FDetails.Add('Relative row index: ' + IntToStr(Smallint(r))); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [r, r]), + 'Encoded row index'); + + // Bit mask $4000 --> column + // Bit mask $8000 --> row + if Row = FCurrRow then + begin + FDetails.Add('Encoded cell address (column):'#13); + if c and $4000 = 0 + then FDetails.Add('Bit 14=0: Column index is ABSOLUTE') + else FDetails.Add('Bit 14=1: Column index is RELATIVE'); + if c and $8000 = 0 + then FDetails.Add('Bit 15=0: Row index is ABSOLUTE') + else FDetails.Add('Bit 15=1: Row index is RELATIVE'); + FDetails.Add(''); + if c and $4000 = 0 + then FDetails.Add('Absolute column index: ' + IntToStr(Lo(c))) + else FDetails.Add('Relative column index: ' + IntToStr(ShortInt(Lo(c)))); + end; + + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [c, c]), + 'Encoded column index'); + end + else + // Excel5 (Excel2 does not support shared formulas) + begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + r := w and $3FFF; + b := FBuffer[FBufferIndex+2]; + c := b; + + // Bit mask $4000 --> column + // Bit mask $8000 --> row + if Row = FCurrRow then begin + FDetails.Add('Encoded cell address (row):'#13); + if w and $4000 = 0 + then FDetails.Add('Bit 14=0: Column index is ABSOLUTE') + else FDetails.Add('Bit 14=0: Column index is RELATIVE'); + if w and $8000 = 0 + then FDetails.Add('Bit 15=0: Row index is ABSOLUTE') + else FDetails.Add('Bit 15=1: Row index is RELATIVE'); + FDetails.Add(''); + if w and $8000 = 0 + then FDetails.Add('Absolute row index: ' + IntToStr(r)) + else FDetails.Add('Relative row index: ' + IntToStr(Smallint(r))); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Encoded row index'); + + if Row = FCurrRow then + begin + FDetails.Add('Encoded cell address (column):'#13); + if w and $4000 = 0 then begin + FDetails.Add('Column index is ABSOLUTE (see Encoded row index)'); + FDetails.Add('Absolute column index: ' + IntToStr(c)); + end else begin + FDetails.Add('Column index is RELATIVE (see Encoded row index)'); + FDetails.Add('Relative column index: ' + IntToStr(ShortInt(c))); + end; + end; + numBytes := 1; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]), + 'Encoded column index'); + end; + end; + + else + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]), + '(unknown token)'); + + end; // case + end; // while + +end; + +procedure TBIFFGrid.ShowGCW; +var + numBytes: Integer; + b: Byte; + w: Word; + i,j: Integer; + bit: Byte; +begin + RowCount := FixedRows + 33; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Size of Global Column Width bit field (1 bit per column), must be 32'); + + numBytes := 1; + for i:= 0 to w-1 do begin + b := FBuffer[FBufferIndex]; + if FCurrRow = Row then begin + FDetails.Add(Format('GCW (Global column width) record, byte #%d:'#13, [i])); + bit := 1; + for j:=0 to 7 do begin + if b and bit = 0 + then FDetails.Add(Format('Bit $%.2x=0: Column %d uses width of COLWIDTH record.', [bit, j+i*8])) + else FDetails.Add(Format('Bit $%.2x=1: Column %d uses width of STANDARDWIDTH record '+ + '(or, if not available, DEFCOLWIDTH record)', [bit, j+i*8])); + bit := bit * 2; + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), + Format('Widths of columns %d-%d', [i*8, i*8+7])); + end; +end; + + +procedure TBIFFGrid.ShowHeader; +var + numbytes: Integer; + s: String; +begin + RowCount := FixedRows + 1; + if Length(FBuffer) = 0 then + ShowInRow(FCurrRow, FBufferIndex, 0, '', '(empty record)') + else + begin + ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8, + s, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Page header string' + IfThen(FFormat = sfExcel8, + ' (Unicode string, 16-bit string length)', + ' (byte string, 8-bit string length)' + )); + end; +end; + + +procedure TBIFFGrid.ShowHideObj; +var + numBytes: word; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Viewing mode for objects:'#13); + case w of + 0: FDetails.Add('0 = Show all objects'); + 1: FDetails.Add('1 = Show placeholders'); + 2: FDetails.Add('2 = Do not show objects'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Viewing mode for objects'); +end; + + +procedure TBIFFGrid.ShowHyperlink; +var + numbytes: Word; + w: Word; + dw: DWord; + n: Integer; + guid: TGUID; + flags: DWord; + nchar, size: DWord; + widestr: WideString; + ansistr: ansistring; + s: String; +begin + n := 0; + RowCount := FixedRows + 1000; + + ShowCellAddressRange(FFormat); + inc(n, 4); + + numbytes := 16; + Move(FBuffer[FBufferIndex], guid, numbytes); + s := GuidToString(guid); + ShowInRow(FCurrRow, FBufferIndex, numbytes, GuidToString(guid), 'GUID of standard link'); + inc(n); + + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordToLE(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x (%d)', [dw, dw]), 'Unknown'); + inc(n); + + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + flags := DWordToLE(dw); + if FCurrRow = row then begin + FDetails.Add('Option flags:'#13); + if flags and $0001 = 0 + then FDetails.Add(' Bit $0001=0: No link') + else FDetails.ADd('* Bit $0001=1: File link or URL'); + if flags and $0002 = 0 + then FDetails.Add(' Bit $0002=0: Relative path') + else FDetails.Add('* Bit $0002=1: Absolute path'); + if flags and $0014 = 0 + then FDetails.Add(' Bits $0014=0: No desriptions') + else FDetails.Add('* Bits $0014=1: Description (both bits)'); + if flags and $0008 = 0 + then FDetails.Add(' Bit $0008=0: No text mark') + else FDetails.Add('* Bit $0008=1: Text mark'); + if flags and $0080 = 0 + then FDetails.Add(' Bit $0080=0: No target frame') + else FDetails.Add('* Bit $0080=1: Target frame'); + if flags and $0100 = 0 + then FDetails.Add(' Bit $0100=0: File link or URL') + else FDetails.Add('* Bit $0100=1: UNC path (incl server name)'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x (%d)', [flags, flags]), + 'Option flags'); + inc(n); + + if flags and $0014 = $0014 then // hyperlink has description + begin + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + nchar := DWordToLE(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(nchar), + 'Character count of description text, incl trailing zero word'); + inc(n); + + numbytes := 2*nchar; + SetLength(widestr, nchar); + Move(FBuffer[FBufferIndex], widestr[1], 2*nchar); + s := UTF16ToUTF8(widestr); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Character array of description text (no unicode string header, always 16-bit characters, zero-terminated)'); + inc(n); + end; + + if flags and $0080 <> 0 then // Hyperlink has target frame + begin + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + nchar := DWordToLE(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(nchar), + 'Character count of target frame, incl trailing zero word'); + inc(n); + + numbytes := 2*nchar; + SetLength(widestr, nchar); + Move(FBuffer[FBufferIndex], widestr[1], 2*nchar); + s := UTF16ToUTF8(widestr); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Character array of target frame (no unicode string header, always 16-bit characters, zero-terminated)'); + inc(n); + end; + + if flags and $0011 <> 0 then // hyperlink contains URL ***OR*** a local file + begin + numbytes := 16; + Move(FBuffer[FBufferIndex], guid, numbytes); + s := GuidToString(guid); + if s = '{79EAC9E0-BAF9-11CE-8C82-00AA004BA90B}' then // case: URL + begin + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'GUID of URL Moniker'); + inc(n); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + size := DWordToLE(dw); + nchar := size div 2 - 1; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(size), + 'Size of URL character array, incl. trailing zero word'); + inc(n); + + numbytes := 2*nchar; + SetLength(widestr, nchar); + Move(FBuffer[FBufferIndex], widestr[1], 2*nchar); + s := UTF16ToUTF8(widestr); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Character array of the URL (no unicode string header, always 16-bit characters, zero-terminated'); + inc(n); + end else + if s = '{00000303-0000-0000-C000-000000000046}' then // case: local file + begin + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'GUID of File Moniker'); + inc(n); + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Directory up-level count. Each leading "..\" in the file link is deleted and increases this counter.'); + inc(n); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + nchar := DWordToLE(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(nchar), + 'Character count of the shortened file path and name, incl trailing zero byte.'); + inc(n); + + numbytes := nchar; + SetLength(ansistr, nchar); + Move(FBuffer[FBufferIndex], ansistr[1], nchar); + s := AnsiToUTF8(ansistr); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Character array of the shortened file path and name (no unicode string header, always 8-bit characters, zero-terminated)'); + inc(n); + + for w := 1 to 6 do begin + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [DWordToLE(dw)]), + 'Unknown'); + inc(n); + end; + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + size := DWordToLE(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(size), + 'Size of following file link field incl string length field and additional data field'); + inc(n); + + if size > 0 then begin + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + size := DWordToLE(dw); + nchar := size div 2; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(size), + 'Size of extended file path and name character array'); + inc(n); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordToLE(w)), + 'Unknown'); + inc(n); + + numBytes := size; + SetLength(widestr, nchar); + Move(FBuffer[FBufferIndex], widestr[1], numbytes); + s := UTF16ToUTF8(widestr); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Character array of extended file path and array (No unicode string header, always 16-bit characters, NOT zero-terminated)'); + inc(n); + end; + end; + end // if flags and $0011 <> 0 + else begin + // case: Hyperlink to current workbook + end; + + if flags and $0008 <> 0 then // hyperlink contains text mark field + begin + numBytes := 4; + + Move(FBuffer[FBufferIndex], dw, numbytes); + nchar := DWordToLE(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(nchar), + 'Character count of the text mark, incl trailing zero word'); + inc(n); + + numbytes := 2*nchar; + SetLength(widestr, nchar); + Move(FBuffer[FBufferIndex], widestr[1], 2*nchar); + s := UTF16ToUTF8(widestr); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Character array of the text mark, without "#" sign, no unicode string header, always 16-bit characters, zero-terminated)'); + inc(n); + end; + + RowCount := FixedRows + n; +end; + + +procedure TBIFFGrid.ShowHyperlinkTooltip; +var + numbytes: Word; + w: Word; + widestr: widestring; + s: string; +begin + RowCount := FixedRows + 6; + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordToLE(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x (%d)', [w, w]), + 'Repeated record ID'); + + ShowCellAddressRange(sfExcel8); + + numbytes := Length(FBuffer) - FBufferIndex; + SetLength(widestr, numbytes div 2); + Move(FBuffer[FBufferIndex], widestr[1], numbytes); + s := UTF16toUTF8(widestr); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + 'Character array of the tool tip, no Unicode string header, always 16-bit characters, zero-terminated'); +end; + + +procedure TBIFFGrid.ShowInRow(var ARow: Integer; var AOffs: LongWord; + ASize: Word; AValue,ADescr: String; ADescrOnly: Boolean = false); +begin + if ADescrOnly then + begin + Cells[0, ARow] := ''; + Cells[1, ARow] := ''; + Cells[2, ARow] := ''; + end else + begin + Cells[0, ARow] := IntToStr(AOffs); + Cells[1, ARow] := IntToStr(ASize); + Cells[2, ARow] := AValue; + end; + Cells[3, ARow] := ADescr; + inc(ARow); + inc(AOffs, ASize); +end; + + +procedure TBIFFGrid.ShowInteger; +var + numBytes: Integer; + w: Word; + b: Byte; +begin + // BIFF2 only + if (FFormat <> sfExcel2) then + exit; + + RowCount := FixedRows + 5; + ShowRowColData(FBufferIndex); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Cell protection and XF index:'#13); + FDetails.Add(Format('x Bits 5-0 = %d: XF Index', [b and $3F])); + case b and $40 of + 0: FDetails.Add(' Bit 6 = 0: Cell is NOT locked.'); + 1: FDetails.Add('x Bit 6 = 1: Cell is locked.'); + end; + case b and $80 of + 0: FDetails.Add(' Bit 7 = 0: Formula is NOT hidden.'); + 1: FDetails.Add('x Bit 7 = 1: Formula is hidden.'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Cell protection and XF index'); + + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Indexes to format and font records:'#13); + FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f])); + FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Indexes of format and font records'); + + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Cell style:'#13); + case b and $07 of + 0: FDetails.Add('x Bits 2-0 = 0: Horizontal alignment is GENERAL'); + 1: FDetails.Add('x Bits 2-0 = 1: Horizontal alignment is LEFT'); + 2: FDetails.Add('x Bits 2-0 = 2: Horizontal alignment is CENTERED'); + 3: FDetails.Add('x Bits 2-0 = 3: Horizontal alignment is RIGHT'); + 4: FDetails.Add('x Bits 2-0 = 4: Horizontal alignment is FILLED'); + end; + if b and $08 = 0 + then FDetails.Add(' Bit 3 = 0: Cell has NO left border') + else FDetails.Add('x Bit 3 = 1: Cell has left black border'); + if b and $10 = 0 + then FDetails.Add(' Bit 4 = 0: Cell has NO right border') + else FDetails.Add('x Bit 4 = 1: Cell has right black border'); + if b and $20 = 0 + then FDetails.Add(' Bit 5 = 0: Cell has NO top border') + else FDetails.Add('x Bit 5 = 1: Cell has top black border'); + if b and $40 = 0 + then FDetails.Add(' Bit 6 = 0: Cell has NO bottom border') + else FDetails.Add('x Bit 6 = 1: Cell has bottom black border'); + if b and $80 = 0 + then FDetails.Add(' Bit 7 = 0: Cell has NO shaded background') + else FDetails.Add('x Bit 7 = 1: Cell has shaded background'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]), + 'Cell style'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Unsigned 16-bit integer cell value'); +end; + + +procedure TBIFFGrid.ShowInterfaceEnd; +begin + RowCount := FixedRows + 1; + ShowInRow(FCurrRow, FBufferIndex, 0, '', 'End of Globals Substream'); +end; + + +procedure TBIFFGrid.ShowInterfaceHdr; +var + numbytes: Integer; + w: Word; +begin + if FFormat < sfExcel8 then begin + RowCount := FixedRows; + exit; + end; + + RowCount := FixedRows + 1; + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Code page of user interface:'#13); + FDetails.Add(Format('$%.4x = %s', [w, CodePageName(w)])); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]), + 'Begin of Globals Substream, code page of user interface'); +end; + + +procedure TBIFFGrid.ShowIteration; +var + numBytes: Integer; + w: word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Iterations:'#13); + case w of + 0: FDetails.Add('0 = Iterations off'); + 1: FDetails.Add('1 = Iterations on'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Iterations on/off'); +end; + + +procedure TBIFFGrid.ShowIXFE; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to XF record'); +end; + +// Called for LABEL +procedure TBIFFGrid.ShowLabelCell; +var + numBytes: Integer; + b: Byte; + w: Word; + s: String; +begin + RowCount := IfThen(FFormat = sfExcel2, FixedRows + 6, FixedRows + 4); + ShowRowColData(FBufferIndex); + if (FFormat = sfExcel2) then begin + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Cell protection and XF index:'#13); + FDetails.Add(Format('x Bits 5-0 = %d: XF Index', [b and $3F])); + case b and $40 of + 0: FDetails.Add(' Bit 6 = 0: Cell is NOT locked.'); + 1: FDetails.Add('x Bit 6 = 1: Cell is locked.'); + end; + case b and $80 of + 0: FDetails.Add(' Bit 7 = 0: Formula is NOT hidden.'); + 1: FDetails.Add('x Bit 7 = 1: Formula is hidden.'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Cell protection and XF index'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Indexes to format and font records:'#13); + FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f])); + FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Indexes of format and font records'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Cell style:'#13); + case b and $07 of + 0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL'); + 1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT'); + 2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED'); + 3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT'); + 4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED'); + end; + if b and $08 = 0 + then FDetails.Add('Bit 3 = 0: Cell has NO left border') + else FDetails.Add('Bit 3 = 1: Cell has left black border'); + if b and $10 = 0 + then FDetails.Add('Bit 4 = 0: Cell has NO right border') + else FDetails.Add('Bit 4 = 1: Cell has right black border'); + if b and $20 = 0 + then FDetails.Add('Bit 5 = 0: Cell has NO top border') + else FDetails.Add('Bit 5 = 1: Cell has top black border'); + if b and $40 = 0 + then FDetails.Add('Bit 6 = 0: Cell has NO bottom border') + else FDetails.Add('Bit 6 = 1: Cell has bottom black border'); + if b and $80 = 0 + then FDetails.Add('Bit 7 = 0: Cell has NO shaded background') + else FDetails.Add('Bit 7 = 1: Cell has shaded background'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]), + 'Cell style'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Index of XF record'); + end; + + b := IfThen(FFormat=sfExcel2, 1, 2); + ExtractString(FBufferIndex, b, (FFormat = sfExcel8), s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, + Format('%s string, %d-bit string length', [GetStringType, b*8])); +end; + + +procedure TBIFFGrid.ShowLabelSSTCell; +var + numBytes: Integer; + w: Word; + dw: DWord; +begin + RowCount := FixedRows + 4; + ShowRowColData(FBufferIndex); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Index of XF record'); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + dw := DWordLEToN(dw); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(dw), + 'Index into SST record (shared string table)'); +end; + + +procedure TBIFFGrid.ShowLeftMargin; +var + numBytes: Integer; + dbl: Double; +begin + RowCount := FixedRows + 1; + numBytes := 8; + Move(FBuffer[FBufferIndex], dbl, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), + 'Left page margin in inches (IEEE 754 floating-point value, 64-bit double precision)'); +end; + + +procedure TBIFFGrid.ShowMergedCells; +var + w: Word; + numBytes: Integer; + i, n: Integer; +begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + n := WordLEToN(w); // count of merged ranges in this record + + RowCount := FixedRows + 1 + n*4; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(n), + 'Count of merged ranges in this record'); + + for i:=1 to n do begin + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + Format('Merged range #%d: First row = %d', [i, w])); + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + Format('Merged range #%d: Last row = %d', [i, w])); + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + Format('Merged range #%d: First column = %d', [i, w])); + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + Format('Merged range #%d: Last column = %d', [i, w])); + end; +end; + + +procedure TBIFFGrid.ShowMMS; +var + w: Word; + numbytes: Integer; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), 'Reserved, MUST be ignored'); +end; + +{ Only those records needed for comments are deciphered. } +procedure TBIFFGrid.ShowMSODrawing; +var + n: Integer; + numBytes: Integer; + w: Word; + dw: DWord; + recType: Word; + recLen: DWord; + isContainer: Boolean; + indent: String; + level: Integer; + + function PrepareIndent(ALevel: Integer): String; + var + i: Integer; + begin + Result := ''; + for i := 1 to ALevel do Result := Result + ' '; + end; + + procedure DoShowHeader(out ARecType: Word; out ARecLen: DWord; + out IsContainer: Boolean); + var + s: String; + instance: Word; + version: Word; + begin + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + IsContainer := (w and $000F = $000F); + + if IsContainer and (FBufferIndex > 0) then + indent := PrepareIndent(level+1); + + s := IfThen(IsContainer, '***** CONTAINER *****', '--- ATOM ---'); + ShowInRow(FCurrRow, FBufferIndex, 0, '', indent + s, TRUE); + inc(n); + + version := w and $000F; + instance := (w and $FFF0) shr 4; + if Row = FCurrRow then begin + FDetails.Add('OfficeArtDrawing Header:'#13); + FDetails.Add(Format('Bits 3-0 = $%.1x: Version', [version])); + FDetails.Add(Format('Bits 15-4 = $%.3x: Instance', [instance])); + end; + s := Format('OfficeArtDrawing Header: Version %d, instance %d', [version, instance]); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.04x', [w]), indent + s); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ARecType := w; + case ARecType of + $F00A: //, $F00B: + case instance of + $01: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Rectangle)'; + $02: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Rounded rectangle)'; + $03: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Ellipse)'; + $04: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Diamond)'; + $05: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Triangle)'; + $06: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Right triangle)'; + $07: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Parallelogram)'; + $08: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Trapezoid)'; + $09: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Hexagon)'; + $0A: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Pentagon)'; + $0B: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Plus)'; + $0C: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Star)'; + $0D: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Arrow)'; + $0E: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Thick arrow)'; + $0F: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Irregular pentagon)'; + $10: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Cube)'; + $11: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Speech balloon)'; + $12: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Seal)'; + $13: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Curved arc)'; + $14: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Line)'; + $15: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Plaque)'; + $16: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Cylinder)'; + $17: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Donut)'; + $CA: Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + ' (Text box)'; + end; + $F00B: + Cells[3, FCurrRow-1] := Cells[3, FCurrRow-1] + Format(' (i.e., %d properties)', [instance]); + end; + + case ARecType of + $F000: s := 'OfficeArtDggContainer (all the OfficeArt file records containing document-wide data)'; + $F001: s := 'OfficeArtBStoreContainer (all the BLIPs used in all the drawings associated with parent OfficeArtDggContainer record)'; + $F002: s := 'OfficeArtDgContainer (all file records for the objects in a drawing)'; + $F003: s := 'OfficeArtSpgrContainer (groups of shapes)'; + $F004: s := 'OfficeArtSpContainer (shape)'; + $F005: s := 'OfficeArtSolverContainer (rules applicable to the shapes contained in an OfficeArtDgContainer record)'; + $F006: s := 'OfficeArtFDGGBlock record (document-wide information about all drawings saved in the file)'; + $F007: s := 'OfficeArtFBSE record (File BLIP Store Entry (FBSE) containing information about the BLIP)'; + $F008: s := 'OfficeArtFDG record (number of shapes, drawing identifier, and shape identifier of the last shape in a drawing)'; + $F009: s := 'OfficeArtFSPGR record (coordinate system of the group shape that the anchors of the child shape are expressed in)'; + $F00A: s := 'OfficeArtFSP record (instance of a shape)'; + $F00B: s := 'OfficeArtFOPT record (table of OfficeArtRGFOPTE records)'; + $F00D: s := 'OfficeArtClientTextBox (text related data for a shape)'; + $F00F: s := 'OfficeArtChildAnchor record (anchors for the shape containing this record)'; + $F010: s := 'OfficeArtClientAnchor record (location of a shape)'; + $F011: s := 'OfficeArtClientData record (information about a shape)'; + $F012: s := 'OfficeArtFConnectorRule record (connection between two shapes by a connector shape)'; + $F014: s := 'OfficeArtFArcRule record (Specifies an arc rule. Each arc shape MUST correspond to a unique arc rule)'; + $F017: s := 'OfficeArtFCalloutRule record (Callout rule: One callout rule MUST exist per callout shape)'; + $F01A: s := 'OfficeArtBlipEMF record (BLIP file data for the enhanced metafile format (EMF))'; + $F01B: s := 'OfficeArtBlipWMF record (BLIP file data for the Windows Metafile Format (WMF))'; + $F01C: s := 'OfficeArtBlipPICT record (BLIP file data for the Macintosh PICT format)'; + $F01D: s := 'OfficeArtBlipJPEG record (BLIP file data for the Joint Photographic Experts Group (JPEG) format)'; + $F01E: s := 'OfficeArtBlipPNG record (BLIP file data for the Portable Network Graphics (PNG) format)'; + $F01F: s := 'OfficeArtBlipDIB record (BLIP file data for the device-independent bitmap (DIB) format)'; + $F029: s := 'OfficeArtBlipTIFF record (BLIP file data for the TIFF format)'; + $F118: s := 'OfficeArtFRITContainer record (container for the table of group identifiers that are used for regrouping ungrouped shapes)'; + $F119: s := 'OfficeArtFDGSL record (specifies both the selected shapes and the shape that is in focus in the drawing)'; + $F11A: s := 'OfficeArtColorMRUContainer record (most recently used custom colors)'; + $F11D: s := 'OfficeArtFPSPL record (former hierarchical position of the containing object that is either a shape or a group of shapes)'; + $F11E: s := 'OfficeArtSplitMenuColorContainer record (container for the colors that were most recently used to format shapes)'; + $F121: s := 'OfficeArtSecondaryFOPT record (table of OfficeArtRGFOPTE records)'; + $F122: s := 'OfficeArtTertiaryFOPT record (table of OfficeArtRGFOPTE records)'; + else s := 'Not known to BIFFExplorer'; + end; + if Row = FCurrRow then begin + FDetails.Add('OfficeArtDrawing Record Type:'#13); + FDetails.Add(Format('$%.4x: %s', [ARecType, s])); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), + indent + 'Record type: ' + s); + inc(n); + + numbytes := 4; + Move(FBuffer[FbufferIndex], ARecLen, Numbytes); + ARecLen := DWordLEToN(ARecLen); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d', [ARecLen]), + indent + 'Record length'); + inc(n); + end; + + procedure DoShowOfficeArtFDG; // $F008 + // The OfficeArtFDG record specifies the number of shapes, the drawing + // identifier, and the shape identifier of the last shape in a drawing. + begin + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(dw), + indent + 'Number of shapes in this drawing'); + inc(n); + + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(dw), + indent + 'Shape identifier of the last shape in this drawing'); + inc(n); + end; + + procedure DoShowOfficeArtFSPGR; // $F009 + // The OfficeArtFSPGR record specifies the coordinate system of the group + // shape that the anchors of the child shape are expressed in. This record + // is present only for group shapes. + var + rt: Word; + begin + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(LongInt(DWordLEToN(dw))), + indent + 'xLeft'); + inc(n); + + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(LongInt(DWordLEToN(dw))), + indent + 'yTop'); + inc(n); + + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(LongInt(DWordLEToN(dw))), + indent + 'xRight'); + inc(n); + + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(LongInt(DWordLEToN(dw))), + indent + 'yBottom'); + inc(n); + end; + + procedure DoShowOfficeArtFSP; // §F00A + // The OfficeArtFSP record specifies an instance of a shape. + // The record header contains the shape type, and the record itself + // contains the shape identifier and a set of bits that further define the shape. + var + rt: word; + s: String; + begin + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), + indent + 'Shape identifier'); + inc(n); + + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + s := ''; + if Row = FCurrRow then begin + FDetails.Add('Shape options:'#13); + if dw and $0001 = 0 + then FDetails.Add(' Bit 1=0: Shape is NOT a group shape') + else FDetails.Add('x Bit 1=1: Shape is a group shape'); + if dw and $0002 = 0 + then FDetails.Add(' Bit 2=0: Shape is NOT a child shape') + else FDetails.Add('x Bit 2=1: Shape is a child shape'); + if dw and $0004 = 0 + then FDetails.Add(' Bit 3=0: Shape is NOT the top-most group shape (patriarch)') + else FDetails.Add('x Bit 3=1: Shaoe is the top-most group shape (patriarch)'); + if dw and $0008 = 0 + then FDetails.Add(' Bit 4=0: Shape has NOT been deleted') + else FDetails.Add('x Bit 4=1: Shape has been deleted'); + if dw and $0010 = 0 + then FDetails.Add(' Bit 5=0: Shape is NOT an OLE object') + else FDetails.Add('x Bit 5=1: Shape is an OLE object'); + if dw and $0020 = 0 + then FDetails.Add(' Bit 6=0: Shape does NOT have a valid master in the hspMaster property') + else FDetails.Add('x Bit 6=1: Shape has a valid master in the hspMaster property'); + if dw and $0040 = 0 + then FDetails.Add(' Bit 7=0: Shape is NOT horizontally flipped') + else FDetails.Add('x Bit 7=1: Shape is horizontally flipped'); + if dw and $0080 = 0 + then FDetails.Add(' Bit 8=0: Shape is NOT vertically flipped') + else FDetails.Add('x Bit 8=1: Shape is vertically flipped'); + if dw and $0100 = 0 + then FDetails.Add(' Bit 9=0: Shape is NOT a connector shape') + else FDetails.Add('x Bit 9=1: Shape is a connector shape'); + if dw and $0200 = 0 + then FDetails.Add(' Bit 10=0: Shape doe NOT have an anchor') + else FDetails.Add('x Bit 10=1: Shape has an anchor'); + if dw and $0400 = 0 + then FDetails.Add(' Bit 11=0: Shape is NOT a background shape') + else FDetails.Add('x Bit 11=1: Shape is a background shape'); + if dw and $0800 = 0 + then FDetails.Add(' Bit 12=0: Shape does NOT have a shape type property') + else FDetails.Add('x Bit 12=1: Shape has a shape type property'); + FDetails.Add(' Bits 13-32: unused'); + end; + s := ''; + if dw and $0001 <> 0 then s := s + 'group shape, '; + if dw and $0002 <> 0 then s := s + 'child shape, '; + if dw and $0004 <> 0 then s := s + 'patriarch, '; + if dw and $0008 <> 0 then s := s + 'deleted, '; + if dw and $0010 <> 0 then s := s + 'OLE object, '; + if dw and $0020 <> 0 then s := s + 'master, '; + if dw and $0040 <> 0 then s := s + 'flipped hor, '; + if dw and $0080 <> 0 then s := s + 'flipped vert, '; + if dw and $0100 <> 0 then s := s + 'connector shape, '; + if dw and $0200 <> 0 then s := s + 'anchor, '; + if dw and $0400 <> 0 then s := s + 'background, '; + if dw and $0800 <> 0 then s := s + 'shape type, '; + if s <> '' then begin + Delete(s, Length(s)-1, 2); + s := 'Shape options: ' + s; + end else + s := 'Shape options'; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x (%d)', [dw, dw]), indent + s); + inc(n); + end; + + procedure DoShowOfficeArtFOPT(ARecLen: DWord); // $F00B + // The OfficeArtFOPT record specifies a table of OfficeArtRGFOPTE records. + var + startIndex: Int64; + opid: Word; + op: DWord; + s: String; + begin + startIndex := FBufferIndex; + while FBufferIndex < startIndex + ARecLen do + begin + numbytes := 2; + Move(FBuffer[FBufferIndex], opid, numbytes); + opid := WordLEToN(opid); + case opid of + $007F: s := 'OfficeArtFOPTEOPID: Protection boolean properties'; + $0080: s := 'OfficeArtFOPTEOPID: TextID'; + $0081: s := 'OfficeArtFOPTEOPID: dxTextLeft'; + $0082: s := 'OfficeArtFOPTEOPID: dyTextTop'; + $0083: s := 'OfficeArtFOPTEOPID: dxTextRight'; + $0084: s := 'OfficeArtFOPTEOPID: dyTextBottom'; + $0085: s := 'OfficeArtFOPTEOPID: Wrap text'; + $0086: s := 'OfficeArtFOPTEOPID: Unused'; + $0087: s := 'OfficeArtFOPTEOPID: Text anchor'; + $0088: s := 'OfficeArtFOPTEOPID: Text flow'; + $0089: s := 'OfficeArtFOPTEOPID: Font rotation'; + $008A: s := 'OfficeArtFOPTEOPID: Next shape in sequence of linked shapes'; + $008B: s := 'OfficeArtFOPTEOPID: Text direction'; + $008C: s := 'OfficeArtFOPTEOPID: Unused'; + $008D: s := 'OfficeArtFOPTEOPID: Unused'; + $00BF: s := 'OfficeArtFOPTEOPID: Boolean properties for the text in a shape'; + $00C0: s := 'OfficeArtFOPTEOPID: Text for this shape’s geometry text'; + $00C2: s := 'OfficeArtFOPTEOPID: Alignment of text in the shape'; + $00C3: s := 'OfficeArtFOPTEOPID: Font size, in points, of the geometry text for this shape'; + $00C4: s := 'OfficeArtFOPTEOPID: Amount of spacing between characters in the text'; + $00C5: s := 'OfficeArtFOPTEOPID: Font to use for the text'; + $0158: s := 'OfficeArtFOPTEOPID: Type of connection point'; + $0181: s := 'OfficeArtFOPTEOPID: Fill color'; + $0183: s := 'OfficeArtFOPTEOPID: Background color of the fill'; + $0185: s := 'OfficeArtFOPTEOPID: Foreground color of the fill'; + $01BF: s := 'OfficeArtFOPTEOPID: Fill style boolean properties'; + $01C0: s := 'OfficeArtFOPTEOPID: Line color'; + $01C3: s := 'OfficeArtFOPTEOPID: Line foreground color for black & white display mode'; + $01FF: s := 'OfficeArtFOPTEOPID: Line style boolean properties'; + $0201: s := 'OfficeArtFOPTEOPID: Shadow color'; + $0203: s := 'OfficeArtFOPTEOPID: Shadow primary color modifier if in black & white mode'; + $023F: s := 'OfficeArtFOPTEOPID: Shadow style boolean properties'; + $03BF: s := 'OfficeArtFOPTEOPID: Group shape boolean properties'; + else s := 'OfficeArtFOPTEOPID that specifies the header information for this property'; + end; + s := indent + s; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x (%d)', [opid, opid]), s); + inc(n); + + numbytes := 4; + Move(FBuffer[FBufferIndex], op, numbytes); + op := DWordLEToN(op); + s := ''; + case opid of + $0087: case op of + 0: s := s + ': text at top'; + 1: s := s + ': text vertically centered'; + 2: s := s + ': text at bottom'; + 3: s := s + ': text at top and centered horizontally'; + 4: s := s + ': text at center of box'; + 5: s := s + ': text at bottom and centered horizontally'; + end; + $0089: case op of + 0: s := s + ': horizontal'; + 1: s := s + ': 90° clockwise, vertical down'; + 2: s := s + ': horizontal, 180° rotated'; + 3: s := s + ': 90° counter-clickwise, vertical up'; + end; + $008B: case op of + 0: s := s + ': left-to-right'; + 1: s := s + ': right-to-left'; + 2: s := s + ': determined from text string'; + end; + $00C2: case op of + 0: s := s + ': stretched'; + 1: s := s + ': centered'; + 2: s := s + ': left-aligned'; + 3: s := s + ': right-aligned'; + 4: s := s + ': justified'; + 5: s := s + ': word-justified'; + end; + $0158: case op of + 0: s := s + ': no connection point'; + 1: s := s + ': Edit points of shape are used as connection points'; + 2: s := s + ': Custom array of connection points'; + 3: s := s + ': standard 4 connection points at the top, bottom, left, and right side centers'; + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x (%d)', [op, op]), + indent + 'Value of this property' + s); + inc(n); + end; + end; + + procedure DoShowOfficeArtClientTextbox(ARecLen: Word); // $F00D + begin + numBytes := ARecLen; + ShowInRow(FCurrRow, FBufferIndex, numbytes, '...', indent + 'Text data follow in TXO record', true); + inc(n); + end; + + procedure DoShowOfficeArtClientAnchorData(StructureKind: Integer); // $F010 + begin + case StructureKind of + 0: begin // OfficeArtClientAnchorSheet + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Move/resize flags:'#13); + if w and $0001 = 0 + then FDetails.Add(' Bit 0=0: Shape will NOT be kept intact when the cells are moved') + else FDetails.Add('x Bit 0=1: Shape will be kept intact when the cells are moved'); + if w and $0002 = 0 + then FDetails.Add(' Bit 1=0: Shape will NOT be kept intact when the cells are resized') + else FDetails.Add('x Bit 1=1: Shape will be kept intact when the cells are resized'); + FDetails.Add( ' Bit 2=0: reserved (must be 0)'); + FDetails.Add( ' Bit 3=0: reserved (must be 0)'); + FDetails.Add( ' Bit 4=0: reserved (must be 0)'); + FDetails.Add( ' Bits 15-5: unused'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), indent + 'Move/resize flags'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'Column of the cell under the top left corner of the bounding rectangle of the shape'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'x coordinate of the top left corner of the bounding rectangle relative to the corner of the underlying cell (as 1/1024 of that cell’s width)'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'Row of the cell under the top left corner of the bounding rectangle of the shape'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'y coordinate of the top left corner of the bounding rectangle relative to the corner of the underlying cell (as 1/256 of that cell’s height'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'Column of the cell under the bottom right corner of the bounding rectangle of the shape'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'x coordinate of the bottom right corner of the bounding rectangle relative to the corner of the underlying cell (as 1/1024 of that cell’s width)'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'Row of the cell under the bottom right corner of the bounding rectangle of the shape'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'y coordinate of the bottom right corner of the bounding rectangle relative to the corner of the underlying cell (as 1/256 of that cell’s height'); + inc(n); + end; + 1: begin // "Small" rectangle structure + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'top'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'left'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'right'); + inc(n); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + indent + 'bottom'); + inc(n); + end; + 2: begin // standard rectangle structure + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), + indent + 'top'); + inc(n); + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), + indent + 'left'); + inc(n); + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), + indent + 'right'); + inc(n); + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), + indent + 'bottom'); + inc(n); + end; + end; + end; + + procedure DoShowOfficeArtRecord(out ARecType:Word; out ARecLen: DWord; + out AIsContainer: Boolean); + var + startIndex, endindex: Int64; + totalbytes: Int64; + begin + totalbytes := 0; + startIndex := FBufferIndex + 8; + repeat + DoShowHeader(ARecType, ARecLen, AIsContainer); + if totalbytes = 0 then endindex := startindex + ARecLen; + totalbytes := totalbytes + ARecLen; + if AIsContainer then begin + inc(level); + indent := PrepareIndent(level); + DoShowOfficeArtRecord(ARecType, ARecLen, AIsContainer); + end else + case ARecType of + $F008: DoShowOfficeArtFDG; + $F009: DoShowOfficeArtFSPGR; // shape group + $F00A: DoShowOfficeArtFSP; // instance of a shape + $F00B: DoShowOfficeArtFOPT(ARecLen); // table of OfficeArtRGFOPTE records + $F00D: DoShowOfficeArtClientTextbox(ARecLen); + $F010: DoShowOfficeArtClientAnchorData(0); // 0 - use OfficeArtClientAnchorSheet because contained in a sheet stream +// $F122: DoShowOfficeArtRGFOPTE(AIndent); // (tertiary) table of OfficeArtRGFOPTE records + else if ARecLen <> 0 then begin + ShowInRow(FCurrRow, FBufferIndex, ARecLen, '', indent + 'Skipping this unknown record...'); + inc(n); + end; + end; + until (FBufferIndex >= endindex) or (FBufferIndex >= Length(FBuffer)); + dec(level); + FBufferIndex := endindex; + end; + +begin + RowCount := FixedRows + 1000; + n := 0; + level := -1; + DoShowOfficeArtRecord(recType, recLen, isContainer); + RowCount := FixedRows + n; +end; + +procedure TBIFFGrid.ShowMulBlank; +var + w: Word; + numbytes: Integer; + i, nc: Integer; +begin + nc := (Length(FBuffer) - 6) div 2; + RowCount := FixedRows + 3 + nc; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to row'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to first column'); + + for i:=0 to nc-1 do begin + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + Format('Index to XF record #%d', [i])); + end; + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to last column'); +end; + + +procedure TBIFFGrid.ShowMulRK; +var + w: Word; + numBytes: Integer; + i, nc: Integer; + dw: DWord; + encint: DWord; + encdbl: QWord; + dbl: Double absolute encdbl; + s: String; +begin + nc := (Length(FBuffer) - 6) div 6; + RowCount := FixedRows + 3 + nc*2; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to row'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to first column'); + + for i:=0 to nc-1 do begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + Format('Index to XF record #%d', [i])); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + + if Row = FCurrRow then begin + FDetails.Add('RK Value:'#13); + if dw and $00000001 = 0 + then FDetails.Add('Bit 0 = 0: Value not changed') + else FDetails.Add('Bit 0 = 1: Encoded value is multiplied by 100.'); + if dw and $00000002 = 0 + then FDetails.Add('Bit 1 = 0: Floating point value') + else FDetails.Add('Bit 1 = 1: Signed integer value'); + if dw and $00000002 = 0 then begin + encdbl := (QWord(dw) and QWord($FFFFFFFFFFFFFFFC)) shl 32; + if dw and $00000001 = 1 then + s := Format('%.2f', [dbl*0.01]) + else + s := Format('%.0f', [dbl]); + end + else begin + s := Format('$%.16x', [-59000000]); + encint := ((dw and DWord($FFFFFFFC)) shr 2) or (dw and DWord($C0000000)); + // "arithmetic shift" = replace left-most bits by original bits + if dw and $00000001 = 1 then + s := FloatToStr(encint*0.01) + else + s := IntToStr(encint); + end; + FDetails.Add('Bits 31-2: Encoded value ' + s); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, + Format('$%.8x', [dw]), + Format('RK value #%d', [i]) + ); + end; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to last column'); +end; + +procedure TBIFFGrid.ShowNote; +var + numBytes: Integer; + w: Word = 0; + s: String; +begin + RowCount := IfThen(FFormat = sfExcel8, 6, 5); + + // Offset 0: Row and Col index + ShowRowColData(FBufferIndex); + + if FFormat = sfExcel8 then begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Comment flags:'#13); + if (w and $0002 <> 0) + then FDetails.Add('x Bit 1=1: Comment is shown at all times') + else FDetails.Add(' Bit 1=0: Comment is not shown at all tiems'); + if (w and $0080 <> 0) + then FDetails.Add('x Bit 7=1: Row with comment is hidden') + else FDetails.Add(' Bit 7=0: Row with comment is visible'); + if (w and $0100 <> 0) + then FDetails.Add('x Bit 8=1: Column with comment is hidden') + else FDetails.Add(' Bit 8=0: Column with comment is visible'); + FDetails.Add('All other bits are reserved and must be ignored.'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Flags'); + + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Object ID'); + + ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8, + s, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Author'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Total length of comment'); + + numBytes := Min(Length(FBuffer) - FBufferIndex, 2048); + SetLength(s, numBytes); + Move(FBuffer[FBufferIndex], s[1], numBytes); + SetLength(s, Length(s)); + s := UTF8StringReplace(s, #10, '[\n]', [rfReplaceAll]); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Comment text'); + end; +end; + + +procedure TBIFFGrid.ShowNumberCell; +var + numBytes: Integer; + b: Byte = 0; + w: Word = 0; + dbl: Double; +begin + RowCount := IfThen(FFormat = sfExcel2, FixedRows + 6, FixedRows + 4); + // Offset 0: Row & Offsset 2: Column + ShowRowColData(FBufferIndex); + // Offset 4: Cell attributes (BIFF2) or XF ecord index (> BIFF2) + if FFormat = sfExcel2 then begin + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Cell protection and XF index:'#13); + FDetails.Add(Format('x Bits 5-0 = %d: XF Index', [b and $3F])); + case b and $40 of + 0: FDetails.Add(' Bit 6 = 0: Cell is NOT locked.'); + 1: FDetails.Add('x Bit 6 = 1: Cell is locked.'); + end; + case b and $80 of + 0: FDetails.Add(' Bit 7 = 0: Formula is NOT hidden.'); + 1: FDetails.Add('x Bit 7 = 1: Formula is hidden.'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Cell protection and XF index'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Indexes to format and font records:'#13); + FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f])); + FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]), + 'Indexes of format and font records'); + + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + if Row = FCurrRow then begin + FDetails.Add('Cell style:'#13); + case b and $07 of + 0: FDetails.Add('x Bits 2-0 = 0: Horizontal alignment is GENERAL'); + 1: FDetails.Add('x Bits 2-0 = 1: Horizontal alignment is LEFT'); + 2: FDetails.Add('x Bits 2-0 = 2: Horizontal alignment is CENTERED'); + 3: FDetails.Add('x Bits 2-0 = 3: Horizontal alignment is RIGHT'); + 4: FDetails.Add('x Bits 2-0 = 4: Horizontal alignment is FILLED'); + end; + if b and $08 = 0 + then FDetails.Add(' Bit 3 = 0: Cell has NO left border') + else FDetails.Add('x Bit 3 = 1: Cell has left black border'); + if b and $10 = 0 + then FDetails.Add(' Bit 4 = 0: Cell has NO right border') + else FDetails.Add('x Bit 4 = 1: Cell has right black border'); + if b and $20 = 0 + then FDetails.Add(' Bit 5 = 0: Cell has NO top border') + else FDetails.Add('x Bit 5 = 1: Cell has top black border'); + if b and $40 = 0 + then FDetails.Add(' Bit 6 = 0: Cell has NO bottom border') + else FDetails.Add('x Bit 6 = 1: Cell has bottom black border'); + if b and $80 = 0 + then FDetails.Add(' Bit 7 = 0: Cell has NO shaded background') + else FDetails.Add('x Bit 7 = 1: Cell has shaded background'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]), + 'Cell style'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Index of XF record'); + end; + // Offset 6: Double value + numBytes := 8; + Move(FBuffer[FBufferIndex], dbl, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%g', [dbl]), //FloatToStr(dbl), + 'IEEE 764 floating-point value'); +end; + + +procedure TBIFFGrid.ShowObj; +var + numBytes: Integer; + w: Word; + dw: DWord; + savedBufferIndex: Integer; + fieldType: Word; + fieldSize: Word; + s: String; + i, n: Integer; + guid: TGUID; +begin + if FFormat = sfExcel8 then begin + n := 0; + RowCount := FixedRows + 1000; + while FBufferIndex < Length(FBuffer) do begin + savedBufferIndex := FBufferIndex; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + fieldType := WordLEToN(w); + case fieldType of + $00: s := 'ftEnd (End of OBJ record)'; + $01: s := '(Reserved)'; + $02: s := '(Reserved)'; + $03: s := '(Reserved)'; + $04: s := 'ftMacro (fmla-style macro)'; + $05: s := 'ftButton (Command button)'; + $06: s := 'ftGmo (Group marker)'; + $07: s := 'ftCf (Clipboard format)'; + $08: s := 'ftPioGrbit (Picture option flags)'; + $09: s := 'ftPictFmla (Picture fmla-style macro)'; + $0A: s := 'ftCbls (Checkbox link)'; + $0B: s := 'ftRbo (Radio button)'; + $0C: s := 'ftSbs (Scrollbar)'; + $0D: s := 'ftNts (Note structure)'; + $0E: s := 'ftSbsFmla (Scroll bar fmla-style macro)'; + $0F: s := 'ftGboData (Group box data)'; + $10: s := 'ftEdoData (Edit control data)'; + $11: s := 'ftRboData (Radio button data)'; + $12: s := 'ftCblsData (Check box data)'; + $13: s := 'ftLbsData (List box data)'; + $14: s := 'ftCblsFmla (Check box link fmla-style macro)'; + $15: s := 'ftCmo (Common object data)'; + else s := '(unknown)'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [fieldType]), + Format('Subrecord type: %s', [s])); + inc(n); + + Move(FBuffer[FBufferIndex], w, numBytes); + fieldSize := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(fieldSize), 'Size of subrecord'); + inc(n); + + case fieldType of + $000D: + begin // ftNts, from https://msdn.microsoft.com/en-us/library/office/dd951373%28v=office.12%29.aspx + numBytes := 16; + Move(FBuffer[FBufferIndex], guid, numBytes); + s := GuidToString(guid); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'GUID of comment'); + inc(n); + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Shared Note (0 = false, 1 = true)'); + inc(n); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), + 'Unused (undefined, must be ignored)'); + inc(n); + end; + + $0015: + begin // common object data + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + case w of + $00: s := 'Group'; + $01: s := 'Line'; + $02: s := 'Rectangle'; + $03: s := 'Oval'; + $04: s := 'Arc'; + $05: s := 'Chart'; + $06: s := 'Text'; + $07: s := 'Button'; + $08: s := 'Picture'; + $09: s := 'Polybon'; + $0A: s := '(Reserved)'; + $0B: s := 'Checkbox'; + $0C: s := 'Option button'; + $0D: s := 'Edit box'; + $0E: s := 'Label'; + $0F: s := 'Dialog box'; + $10: s := 'Spinner'; + $11: s := 'Scrollbar'; + $12: s := 'List box'; + $13: s := 'Group box'; + $14: s := 'Combobox'; + $15: s := '(Reserved)'; + $16: s := '(Reserved)'; + $17: s := '(Reserved)'; + $18: s := '(Reserved)'; + $19: s := 'Comment'; + $1A: s := '(Reserved)'; + $1B: s := '(Reserved)'; + $1C: s := '(Reserved)'; + $1D: s := '(Reserved)'; + $1E: s := 'Microsoft Office drawing'; + else s := '(Unknown)'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]), + 'Object type: '+s); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.04x', [w]), + 'Object ID number'); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + if w and $0001 <> 0 + then FDetails.Add('x Bit $0001 = 1: Object is locked when sheet is protected.') + else FDetails.Add(' Bit $0001 = 0: Object is NOT locked when sheet is protected.'); + if w and $000E <> 0 + then FDetails.Add('! Bit $0002 <> 0: Reserved - must be zero - THIS SEEMS TO BE AN ERROR!') + else FDetails.Add(' Bit $0002 = 0: Reserved - must be zero'); + if w and $0010 <> 0 + then FDetails.Add('x Bit $0010 = 1: Image of this object is intended to be included when printing') + else FDetails.Add(' Bit $0010 = 0: Image of this object is NOT intended to be included when printing'); + if w and $1FE0 <> 0 + then FDetails.Add('! Bits 12-5 <> 0: Reserved - must be zero - THIS SEEMS TO BE AN ERROR!') + else FDetails.Add(' Bits 12-5 = 0: Reserved - must be zero'); + if w and $2000 <> 0 + then FDetails.Add('x Bit $2000 = 1: Object uses automatic fill style.') + else FDetails.Add(' Bit $2000 = 0: Object does NOT use automatic fill style.'); + if w and $4000 <> 0 + then FDetails.Add('x Bit $4000 = 1: Object uses automatic line style.') + else FDetails.Add(' Bit $4000 = 0: Object does NOT use automatic line style.'); + if w and $8000 <> 0 + then FDetails.Add('! Bit $8000 = 1: Reserved - must be zero - THIS SEEMS TO BE AN ERROR!') + else FDetails.Add(' Bit $8000 = 0: Reserved - must be zero.'); + end; + s := ''; + if w and $0001 <> 0 then s := s + 'locked, '; + if w and $0010 <> 0 then s := s + 'print, '; + if w and $2000 <> 0 then s := s + 'automatic fill style, '; + if w and $4000 <> 0 then s := s + 'automatic line style, '; + if s <> '' then begin + Delete(s, Length(s)-1, 2); + s := 'Option flags:' + s; + end else + s := 'Option flags'; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]), s); + inc(n); + end; + end; + FBufferIndex := savedBufferIndex + 4 + fieldSize; + end; + RowCount := FixedRows + n; + end else + if FFormat = sfExcel5 then begin + // to do + end; +end; + + +procedure TBIFFGrid.ShowPageSetup; +var + numBytes: Integer; + w: Word; + s: String; + dbl: Double; +begin + if FFormat in [sfExcel5, sfExcel8] then RowCount := FixedRows + 11 + else RowCount := FixedRows + 6; + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Paper size:'#13); + FDetails.Add(Format('%d - %s', [w, PaperSizeName(w)])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Paper size'); + + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Scaling factor in percent'); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLETON(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Start page number'); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLETON(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Fit worksheet width to this number of pages (0 = use as many as needed)'); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLETON(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Fit worksheet height to this number of pages (0 = use as many as needed)'); + + { Excel4 not supported in fpspreadsheet } + { + if FFormat = sfExcel4 then begin + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLETON(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + if w and $0001 = 0 + then FDetails.Add('Bit $0001 = 0: Print pages in columns') + else FDetails.Add('Bit $0001 = 1: Print pages in rows'); + if w and $0002 = 0 + then FDetails.add('Bit $0002 = 0: Landscape') + else FDetails.Add('Bit $0002 = 1: Portrait'); + if w and $0004 = 0 + then FDetails.Add('Bit $0004 = 0: Paper size, scaling factor, and paper orientation ' + + '(portrait/landscape) ARE initialised') + else FDetails.Add('Bit $0004 = 1: Paper size, scaling factor, and paper orientation ' + + '(portrait/landscape) are NOT initialised'); + if w and $0008 = 0 + then FDetails.Add('Bit $0008 = 0: Print colored') + else FDetails.add('Bit $0008 = 1: Print black and white'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x (%d)', [w, w]), + 'Option flags'); + end else } + if (FFormat in [sfExcel5, sfExcel8]) then begin + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + if w and $0001 = 0 + then FDetails.Add('Bit $0001 = 0: Print pages in columns') + else FDetails.Add('Bit $0001 = 1: Print pages in rows'); + if w and $0002 = 0 + then FDetails.add('Bit $0002 = 0: Landscape') + else FDetails.Add('Bit $0002 = 1: Portrait'); + if w and $0004 = 0 + then FDetails.Add('Bit $0004 = 0: Paper size, scaling factor, and paper orientation ' + + '(portrait/landscape) ARE initialised') + else FDetails.Add('Bit $0004 = 1: Paper size, scaling factor, and paper orientation ' + + '(portrait/landscape) are NOT initialised'); + if w and $0008 = 0 + then FDetails.Add('Bit $0008 = 0: Print colored') + else FDetails.add('Bit $0008 = 1: Print black and white'); + if w and $0010 = 0 + then FDetails.Add('Bit $0010 = 0: Default print quality') + else FDetails.Add('Bit $0010 = 1: Draft quality'); + if w and $0020 = 0 + then FDetails.Add('Bit $0020 = 0: Do NOT print cell notes') + else FDetails.Add('Bit $0020 = 0: Print cell notes'); + if w and $0040 = 0 + then FDetails.Add('Bit $0040 = 0: Use paper orientation (portrait/landscape) flag abov') + else FDetails.Add('Bit $0040 = 1: Use default paper orientation (landscape for chart sheets, portrait otherwise)'); + if w and $0080 = 0 + then FDetails.Add('Bit $0080 = 0: Automatic page numbers') + else FDetails.Add('Bit $0080 = 1: Use start page number above'); + if w and $0200 = 0 + then FDetails.Add('Bit $0200 = 0: Print notes as displayed') + else FDetails.Add('Bit $0200 = 1: Print notes at end of sheet'); + case (w and $0C00) shr 10 of + 0: FDetails.Add('Bit $0C00 = 0: Print errors as displayed'); + 1: FDetails.add('Bit $0C00 = 1: Do not print errors'); + 2: FDetails.Add('Bit $0C00 = 2: Print errors as "--"'); + 3: FDetails.Add('Bit $0C00 = 4: Print errors as "#N/A"'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x (%d)', [w, w]), + 'Option flags'); + + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Print resolution in dpi'); + + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Vertical print resolution in dpi'); + + numBytes := 8; + Move(FBuffer[FBufferIndex], dbl, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), 'Header margin'); + + Move(FBuffer[FBufferIndex], dbl, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), 'Footer margin'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Number of copies to print'); + end; + +end; + +procedure TBIFFGrid.ShowPalette; +var + numBytes: Integer; + w: Word; + dw: DWord; + npal: Integer; + i: Integer; + s: String; +begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + npal := WordLEToN(w); + + RowCount := FixedRows + 1 + npal; + + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(npal), + 'Number of palette colors'); + + for i := 0 to npal-1 do begin + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + dw := DWordLEToN(dw); + s := Format('Palette color, index #%d ($%.2x)',[i, i]); + case i of + $00..$07: ; + $08..$3F: s := s + ', user-defined palette'; + $40 : s := s + ', system window text color for border lines'; + $41 : s := s + ', system window background color for pattern background'; + $43 : s := s + ', system face color (dialogue background color)'; + $4D : s := s + ', system window text colour for chart border lines'; + $4E : s := s + ', system window background color for chart areas'; + $4F : s := s + ', automatic color for chart border lines (seems to be always Black)'; + $50 : s := s + ', system tool tip background color (used in note objects)'; + $51 : s := s + ', system tool tip text color (used in note objects)'; + $7FFF : s := s + ', system window text color for fonts'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.8x', [dw]), s); + end; +end; + + +procedure TBIFFGrid.ShowPane; +var + numBytes: Integer; + w: Word; + b: Byte; +begin + RowCount := FixedRows + IfThen(FFormat < sfExcel5, 5, 6); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Position of vertical split (twips or columns (if frozen))'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Position of horizontal split (twips or rows (if frozen))'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to first visible row in bottom pane(s)'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to first visible column in right pane(s)'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(b), + 'Identifier of pane with active cell cursor'); + + if FFormat >= sfExcel5 then begin + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(b), 'not used'); + end; +end; + +procedure TBIFFGrid.ShowPassword; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Password verifier for sheet or workbook:'#13); + if w = 0 + then FDetails.Add('0 = No password') + else FDetails.Add(Format('$%.4x = Password verifier', [w])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Password verifier for sheet or workbook'); +end; + + +procedure TBIFFGrid.ShowPrecision; +var + numbytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Precision-as-displayed mode:'#13); + if w = 0 + then FDetails.Add('0 = Precision-as-displayed mode selected') + else FDetails.Add('1 = Precision-as-displayed mode NOT selected'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Precision-as-displayed mode'); +end; + + +procedure TBIFFGrid.ShowPrintGridLines; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Print sheet grid lines:'#13); + if w = 0 + then FDetails.Add('0 = Do not print sheet grid lines') + else FDetails.Add('1 = Print sheet grid lines'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Print sheet grid lines'); +end; + + +procedure TBIFFGrid.ShowPrintHeaders; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Print row/column headers'); + FDetails.Add('(the area with row numbers and column letters):'#13); + if w = 0 + then FDetails.Add('0 = Do not print row/column headers') + else FDetails.Add('1 = Print row/column headers'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Print row/column headers'); +end; + + +procedure TBIFFGrid.ShowProt4Rev; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Removal of the shared workbook''s revision logs:'#13); + if w = 0 + then FDetails.Add('0 = Removal of the shared workbook''s revision logs is allowed.') + else FDetails.Add('1 = Removal of the shared workbook''s revision logs is NOT allowed.'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Removal of the shared workbook''s revision logs'); +end; + + +procedure TBIFFGrid.ShowProt4RevPass; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Password verifier needed to change the PROT4REV record:'#13); + if w = 0 + then FDetails.Add('0 = No password.') + else FDetails.Add(Format('$%.04x = Password verifier.', [w])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w,w]), + 'Password verifier needed to change the PROT4REV record'); +end; + + +procedure TBIFFGrid.ShowProtect; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Protection state of the workbook:'#13); + if w = 0 + then FDetails.Add(' 0 = Workbook is NOT protected.') + else FDetails.Add('x 1 = Workbook is protected.'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), + 'Protection state of the workbook'); +end; + + +procedure TBIFFGrid.ShowRecalc; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('"Recalculate before save" option:'#13); + if w = 0 + then FDetails.Add(' 0 = Do not recalculate') + else FDetails.Add('x 1 = Recalculate before saving the document'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Recalculate before saving'); +end; + + +procedure TBIFFGrid.ShowRefMode; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FbufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Cell reference mode:'#13); + if w = 0 + then FDetails.Add('0 = RC mode (i.e. cell address shown as "R(1)C(-1)"') + else FDetails.Add('1 = A1 mode (i.e. cell address shown as "B1")'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Cell reference mode'); +end; + + +procedure TBIFFGrid.ShowRefreshAll; +var + numbytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('RefreshAll record:'#13); + if w = 0 + then FDetails.Add(' 0 = Do not force refresh of external data ranges, PivotTables and XML maps on workbook load.') + else FDetails.Add('x 1 = Force refresh of external data ranges, PivotTables and XML maps on workbook load.'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]), + 'Force refresh of external data ranges, Pivot tables and XML maps on workbook load'); +end; + + +procedure TBIFFGrid.ShowRightMargin; +var + numBytes: Integer; + dbl: Double; +begin + RowCount := FixedRows + 1; + numBytes := 8; + Move(FBuffer[FBufferIndex], dbl, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), + 'Right page margin in inches (IEEE 754 floating-point value, 64-bit double precision)'); +end; + + +procedure TBIFFGrid.ShowRK; +var + numBytes: Integer; + w: Word; + dw: DWord; + encint: DWord; + encdbl: QWord; + dbl: Double absolute encdbl; + s: String; +begin + RowCount := FixedRows + 4; + + ShowRowColData(FBufferIndex); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to XF record'); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + dw := DWordLEToN(dw); + + if dw and $00000002 = 0 then begin + encdbl := (QWord(dw) and QWord($FFFFFFFFFFFFFFFC)) shl 32; + if dw and $00000001 = 1 then + s := Format('%.2f', [dbl*0.01]) + else + s := Format('%.0f', [dbl]); + end + else begin + s := Format('$%.16x', [-59000000]); + encint := ((dw and DWord($FFFFFFFC)) shr 2) or (dw and DWord($C0000000)); + // "arithmetic shift" = replace left-most bits by original bits + if dw and $00000001 = 1 then + s := FloatToStr(encint*0.01) + else + s := IntToStr(encint); + end; + + if Row = FCurrRow then begin + FDetails.Add('RK Value:'#13); + if dw and $00000001 = 0 + then FDetails.Add('Bit 0 = 0: Value not changed') + else FDetails.Add('Bit 0 = 1: Encoded value is multiplied by 100.'); + if dw and $00000002 = 0 + then FDetails.Add('Bit 1 = 0: Floating point value') + else FDetails.Add('Bit 1 = 1: Signed integer value'); + FDetails.Add('Bits 31-2: Encoded value ' + s); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, + Format('$%.8x', [QWord(dw)]), 'RK value ['+s+']'); +end; + + +procedure TBIFFGrid.ShowRow; +var + numBytes: Integer; + dw: DWord; + w: Word; + b: Byte; +begin + RowCount := FixedRows + IfThen(FFormat = sfExcel2, 10, 7); + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index of this row'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to column of the first cell which is described by a cell record'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to column of the last cell which is described by a cell record, increased by 1'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Row height:'#13); + FDetails.Add(Format('Bits 14-0 = %d: Row height in twips (1/20 pt) --> %.1f-pt', + [w and $7FFF, (w and $7FFF)/20.0]) + ); + if w and $8000 = 0 + then FDetails.Add('Bit 15 = 0: Row has custom height') + else FDetails.Add('Bit 15 = 1: Row has default height'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), + 'Bits 14-0: Height of row in twips (1/20 pt), Bit 15: Row has default height'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, '', '(not used)'); + + if FFormat = sfExcel2 then begin + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + '0=No defaults written, 1=Default row attribute field and XF index occur below'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Relative offset to calculate stream position of the first cell record for this row'); + + if b = 1 then begin + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'Cell protection and XF index'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'Indexes to FORMAT and FONT records'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'Cell style'); + end; + end + else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, '', + 'In BIFF5-BIFF8 this field is not used anymore, but the DBCELL record instead.'); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + if Row = FCurrRow then begin + FDetails.Add('Option flags and default row formatting:'#13); + FDetails.Add(Format('Bits 0-2 = %d: Outline level of the row', [dw and $00000007])); + if dw and $00000010 = 0 + then FDetails.Add('Bit 4 = 0: Outline group does not start or end here and is not collapsed') + else FDetails.Add('Bit 4 = 1: Outline group starts or ends here and is collapsed'); + if dw and $00000020 = 0 + then FDetails.Add('Bit 5 = 0: Row is NOT hidden') + else FDetails.Add('Bit 5 = 1: Row is hidden'); + if dw and $00000040 = 0 + then FDetails.Add('Bit 6 = 0: Row height and default font height do match.') + else FDetails.Add('Bit 6 = 1: Row height and default font height do NOT match.'); + if dw and $00000080 = 0 + then FDetails.Add('Bit 7 = 0: Row does NOT have explicit default format.') + else FDetails.Add('Bit 7 = 1: Row has explicit default format.'); + FDetails.Add('Bit 8 = 1: Is always 1'); + FDetails.Add(Format('Bits 16-27 = %d: Index to default XF record', [(dw and $0FFF0000) shr 16])); + if dw and $10000000 = 0 + then FDetails.Add('Bit 28 = 0: No additional space above the row.') + else FDetails.Add('Bit 28 = 1: Additional space above the row.'); + if dw and $20000000 = 0 + then FDetails.Add('Bit 29 = 0: No additional space below the row.') + else FDetails.Add('Bit 29 = 1: Additional space below the row.'); + if dw and $40000000 = 0 + then FDetails.Add('Bit 30 = 0: D0 NOT show phonetic text for all cells in the row.') + else FDetails.Add('Bit 30 = 1: Show phonetic text for all cells in the row.'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.8x', [dw]), + 'Option flags and default row formatting'); + end; +end; + +procedure TBIFFGrid.ShowRowColData(var ABufIndex: LongWord); +var + w: Word; + numBytes: Integer; +begin + // Row + numBytes := 2; + Move(FBuffer[ABufIndex], w, numBytes); + ShowInRow(FCurrRow, ABufIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to row'); + // Column + numBytes := 2; + Move(FBuffer[ABufIndex], w, numBytes); + ShowInRow(FCurrRow, ABufIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to column'); +end; + + +procedure TBIFFGrid.ShowRString; +var + numBytes: Integer; + b: Byte; + w: Word; + s: String; + len: Integer; + j: Integer; + wideStr: wideString; + ansiStr: ansiString; +begin + if FFormat < sfExcel5 then + exit; + + RowCount := FixedRows + 5; + + ShowRowColData(FBufferIndex); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), + 'Index of XF record'); + + // String length + Move(FBuffer[FBufferIndex], w, 2); + len := WordLEToN(w); + + if FFormat = sfExcel8 then + begin + SetLength(widestr, len); + Move(FBuffer[FBufferIndex+3], widestr[1], len*2); + s := UTF8Encode(WideStringLEToN(widestr)); + numbytes := 3 + len*2; + end else + begin + SetLength(ansistr, len); + Move(FBuffer[FBufferIndex+2], ansistr[1], len); + s := AnsiToUTF8(ansistr); + numbytes := 2 + len; + end; + + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, + Format('%s string, 16-bit string length', [GetStringType])); + + // Number of rich-text formatting runs + numbytes := IfThen(FFormat = sfExcel8, 2, 1); + Move(FBuffer[FBufferIndex], w, numbytes); + len := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(len), + 'Count of rich-text formatting runs'); + + // Formatting run data + RowCount := RowCount + 2*len; + for j:=0 to len-1 do + begin + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + Format('Rich-Text formatting run #%d, index of first character', [j])); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + Format('Rich-Text formatting run #%d, font index', [j])); + end; +end; + + +procedure TBIFFGrid.ShowSelection; +var + numBytes: Integer; + w: word; + b: Byte; + i, n: Integer; +begin + Move(FBuffer[FBufferIndex+7], w, 2); + n := WordLEToN(w); + + RowCount := FixedRows + 5 + n*4; + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Pane identifier (see PANE record)'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to row of the active cell'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to column of the active cell'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index into the following cell range list to the entry that contains the active cell'); + + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(n), + 'Number of following cell range addresses'); + + numbytes := 2; + for i:=1 to n do begin + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to first row'); + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to last row'); + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to first column'); + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to last column'); + end; +end; + + +procedure TBIFFGrid.ShowSharedFormula; +var + numBytes: Integer; + b: Byte; + w: Word; + tokenBytes: Integer; +begin + BeginUpdate; + RowCount := FixedRows + 1000; + // Brute force simplification because of unknown row count at this point + // Will be reduced at the end. + + // Index to first row + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to first row'); + + // Index to last row + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to last row'); + + // Index to first column + numBytes := 1; // 8-bit also for BIFF8! + Move(FBuffer[FBufferIndex], b, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to first column'); + + // Index to last column + numBytes := 1; // 8-bit also for BIFF8! + Move(FBuffer[FBufferIndex], b, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to last column'); + + // Not used + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Not used'); + + // Number of existing FORMULA records for this shared formula + numBytes := 1; + Move(FBuffer[FBufferIndex], b, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Number of FORMULA records in shared formula'); + + // Size of Token array (in Bytes) + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + tokenBytes := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(tokenBytes), + 'Size of formula data (in Bytes)'); + + // Formula tokens + ShowFormulaTokens(tokenBytes); + + RowCount := FCurrRow; + EndUpdate(true); +end; + + +procedure TBIFFGrid.ShowSheet; +var + numBytes: Integer; + dw: DWord; + b: Byte; + s: String; +begin + RowCount := FixedRows + 4; + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + dw := DWordLEToN(dw); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.8x)', [dw, dw]), + 'Absolute stream position of BOF record of sheet represented by this record.'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + 'Sheet state (0=visible, 1=hidden, 2="very" hidden)'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), + 'Sheet type ($00=worksheet, $02=Chart, $06=VB module)'); + + ExtractString(FBufferIndex, 1, (FFormat = sfExcel8), s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, IfThen(FFormat=sfExcel8, + 'Sheet name (unicode string, 8-bit string length)', + 'Sheet name (byte string, 8-bit string length)') + ); +end; + + +procedure TBIFFGrid.ShowSheetPR; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + if w and $0001 = 0 + then FDetails.Add(' Bit $0001 = 0: Do not show automatic page breaks') + else FDetails.Add('x Bit $0001 = 1: Show automatic page breaks'); + if w and $0010 = 0 + then FDetails.Add(' Bit $0010 = 0: Standard sheet') + else FDetails.Add('x Bit $0010 = 1: Dialog sheet (BIFF5-BIFF8)'); + if w and $0020 = 0 + then FDetails.Add(' Bit $0020 = 0: No automatic styles in outlines') + else FDetails.Add('x Bit $0020 = 1: Apply automatic styles to outlines'); + if w and $0040 = 0 + then FDetails.Add(' Bit $0040 = 0: Outline buttons above outline group') + else FDetails.Add('x Bit $0040 = 1: Outline buttons below outline group'); + if w and $0080 = 0 + then FDetails.Add(' Bit $0080 = 0: Outline buttons left of outline group') + else FDetails.Add('x Bit $0080 = 1: Outline buttons right of outline group'); + if w and $0100 = 0 + then FDetails.Add(' Bit $0100 = 0: Scale printout in percent') + else FDetails.Add('x Bit $0100 = 1: Fit printout to number of pages'); + if w and $0200 = 0 + then FDetails.Add(' Bit $0200 = 0: Save external linked values (BIFF3-BIFF4 only)') + else FDetails.Add('x Bit $0200 = 1: Do NOT save external linked values (BIFF3-BIFF4 only)'); + if w and $0400 = 0 + then FDetails.Add(' Bit $0400 = 0: Do not show row outline symbols') + else FDetails.Add('x Bit $0400 = 1: Show row outline symbols'); + if w and $0800 = 0 + then FDetails.Add(' Bit $0800 = 0: Do not show column outline symbols') + else FDetails.Add('x Bit $0800 = 1: Show column outline symbols'); + case (w and $3000) shr 12 of + 0: FDetails.Add('x Bits $3000 = $0000: Arrange windows tiled'); + 1: FDetails.Add('x Bits $3000 = $1000: Arrange windows horizontal'); + 2: FDetails.Add('x Bits $3000 = $2000: Arrange windows vertical'); + 3: FDetails.Add('x Bits $3000 = $3000: Arrange windows cascaded'); + end; + if w and $4000 = 0 + then FDetails.Add('x Bits $4000 = 0: Excel like expression evaluation (BIFF4-BIFF8 only)') + else FDetails.Add('x Bits $4000 = 1: Lotus like expression evaluation (BIFF4-BIFF8 only)'); + if w and $8000 = 0 + then FDetails.Add('x Bits $8000 = 0: Excel like formula editing (BIFF4-BIFF8 only)') + else FDetails.Add('x Bits $8000 = 1: Lotus like formula editing (BIFF4-BIFF8 only)'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x (%d)', [w, w]), + 'Option flags'); +end; + +procedure TBIFFGrid.ShowSST; +var + numBytes: Integer; + s: String; + total1, total2: DWord; + i, j, n: Integer; + rtfRuns: TRichTextFormattingRuns; + rtfIndex: LongWord; + w: Word; +begin + numBytes := 4; + Move(FBuffer[FBufferIndex], total1, numBytes); + Move(FBuffer[FBufferIndex+4], total2, numBytes); + total1 := DWordLEToN(total1); + total2 := DWordLEToN(total2); + FTotalSST := total2; + + RowCount := FixedRows + 1000; + + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(total1), + 'Total number of shared strings in the workbook'); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(total2), + 'Number of following strings'); + + FPendingCharCount := -1; + n := 0; + for i:=1 to FTotalSST do begin + FCounterSST := i; + ExtractString(FBufferIndex, 2, true, s, numBytes, rtfRuns, rtfIndex); // BIFF8 only --> 2 length bytes + inc(n); + if FPendingCharCount = 0 then begin + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, IfThen(Length(rtfRuns) > 0, + Format('Shared string #%d (Count of Rich-Text formatting runs: %d)', [i, Length(rtfRuns)]), + Format('Shared string #%d', [i]))); +// ShowInRow(FCurrRow, FBufferIndex, numBytes, s, Format('Shared string #%d', [i])); + for j:=0 to High(rtfRuns) do + begin + ShowInRow(FCurrRow, rtfIndex, 2, IntToStr(rtfRuns[j].FirstIndex), + Format(' Rich-Text formatting run #%d, index of first character', [j])); + ShowInRow(FCurrRow, rtfIndex, 2, IntToStr(rtfRuns[j].FontIndex), + Format(' Rich-Text formatting run #%d, font index', [j])); + inc(n, 2); + end; + end + else + begin + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, Format('Shared string #%d - partial (--> CONTINUE)', [i])); + FInfo := BIFFNODE_SST_CONTINUE; + break; + end; + end; + RowCount := FixedRows + 2 + n; +end; + + +procedure TBIFFGrid.ShowStandardWidth; +var + w: Word; + numBytes: Integer; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d (%f characters)', [w, w/256]), + 'Default column width (overrides DFCOLWIDTH, in 1/256 of "0" width)'); +end; + + +procedure TBIFFGrid.ShowString; +var + numBytes: Integer; + s: String; +begin + RowCount := FixedRows + 1; + case FFormat of + sfExcel2: + begin + ExtractString(FBufferIndex, 1, false, s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Byte string, 8-bit string length'); + end; + sfExcel5: + begin + ExtractString(FBufferIndex, 2, false, s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Byte string, 16-bit string length'); + end; + sfExcel8: + begin + ExtractString(FBufferIndex, 2, true, s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Unicode string, 16-bit string length'); + end; + end; +end; + + +procedure TBIFFGrid.ShowStyle; +var + numBytes: Integer; + b: Byte; + w: Word; + s: String; + isRowLevel: Boolean; + isColLevel: Boolean; +begin + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if w and $8000 = 0 then + RowCount := FixedRows + 2 + else + RowCount := FixedRows + 3; + if Row = FCurrRow then begin + FDetails.Add('Style:'#13); + FDetails.Add(Format('Bits 0-11 = %d: Index to style XF record', [w and $0FFFF])); + if w and $8000 = 0 + then FDetails.Add('Bit 15 = 0: user-defined style') + else FDetails.Add('Bit 15 = 1: built-in style'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Style index and type'); + + if w and $8000 = 0 then begin + if FFormat = sfExcel8 then begin + ExtractString(FBufferIndex, 2, true, s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Style name (Unicode string, 16-bit string length)'); + end else begin + ExtractString(FBufferIndex, 1, false, s, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Style name (Byte string, 8-bit string length)'); + end; + end else begin + numbytes := 1; + b := FBuffer[FBufferIndex]; + isRowLevel := (b = 1); + isColLevel := (b = 2); + if FCurrRow = Row then begin + FDetails.Add('Identifier for built-in cell style:'#13); + case b of + 0: FDetails.Add('0 = normal'); + 1: FDetails.Add('1 = RowLevel (see next field)'); + 2: FDetails.Add('2 = ColLevel (see next field)'); + 3: FDetails.Add('3 = Comma'); + 4: FDetails.Add('4 = Currency'); + 5: FDetails.Add('5 = Percent'); + 6: FDetails.Add('6 = Comma [0]'); + 7: FDetails.Add('7 = Currency [0]'); + 8: FDetails.Add('8 = Hyperlink'); + 9: FDetails.Add('9 = Followed hyperlink'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'Identifier for built-in cell style'); + + b := FBuffer[FBufferIndex]; + if FCurrRow = Row then begin + FDetails.Add('Level for RowLevel or ColLevel style (zero-based):'#13); + if b = $FF then + FDetails.Add('$FF = no RowLevel or ColLevel style') + else + if isRowLevel then + FDetails.Add('RowLevel = ' + IntToStr(b)) + else if isColLevel then + FDetails.Add('ColLevel = ' + IntToStr(b)); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + 'Level for RowLevel or ColLevel style (if available)'); + end; +end; + + +procedure TBIFFGrid.ShowStyleExt; +var + numBytes: Integer; + w: Word; + b: Byte; + bs: Byte; + s: String; +begin + RowCount := FixedRows + 11; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [wordLEToN(w)]), + 'Future record type'); + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Attributes:'#13); + if w and $0001 = 0 + then FDetails.Add(' Bit 0 = 0: The containing record does not specify a range of cells.') + else FDetails.Add('x Bit 0 = 1: The containing record specifies a range of cells.'); + FDetails.Add('Bit 1: specifies wether to alert the user of possible problems '+ + 'when saving the file whithout having reckognized this record.'); + FDetails.Add('Bits 2-15: reserved (MUST be zero, MUST be ignored)'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), + 'Attributes'); + numbytes := 8; + ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'Reserved'); + + numbytes := 1; + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Flags:'#13); + if b and $01 = 0 + then FDetails.Add(' Bit 0 = 0: no built-in style') + else FDetails.Add('x Bit 0 = 1: built-in style'); + if b and $02 = 0 + then FDetails.Add(' Bit 1 = 0: NOT hidden') + else FDetails.Add('x Bit 1 = 1: hidden (i.e. is displayed in user interface)'); + FDetails.Add('Bit 2: specifies whether the built-in cell style was modified '+ + 'by the user and thus has a custom definition.'); + FDetails.Add('Bit 3-7: Reserved'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Flags'); + + numbytes := 1; + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Category:'#13); + case b of + 0: FDetails.Add('Bits 0-7 = 0: Custom style'); + 1: FDetails.Add('Bits 0-7 = 1: Good, bad, neutral style'); + 2: FDetails.Add('Bits 0-7 = 2: Data model style'); + 3: FDetails.Add('Bits 0-7 = 3: Title and heading style'); + 4: FDetails.Add('Bits 0-7 = 4: Themed cell style'); + 5: FDetails.Add('Bits 0-7 = 5: Number format style'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Category'); + + numbytes := 1; + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Built-in style:'#13); + FDetails.Add('An unsigned integer that specifies the type of the built-in cell style.'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Built-in style'); + bs := b; + + numbytes := 1; + b := FBuffer[FBufferindex]; + if Row = FCurrRow then begin + FDetails.Add('Outline depth level:'#13); + FDetails.Add('An unsigned integer that specifies the depth level of row/column automatic outlining.'); + if (bs in [1, 2]) then + FDetails.Add(Format('Bits 0-7 = %d: Outline level is %d', [b, b+1])) + else + FDetails.Add(Format('Bits 0-7 = $%.2x: MUST be $FF, MUST be ignoried', [b])); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [b]), 'Outline depth level'); + + ExtractString(FBufferIndex, 1, true, s, numBytes, true); + ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Name of the style name to extend (Unicode string, 8-bit string length)'); + + numbytes := 2; + ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'XFProps (reserved)'); + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Count of XFProp structures to follow in array'); + + +end; + + +procedure TBiffGrid.ShowTabID; +var + numbytes: Integer; + w: word; + i, n: Integer; +begin + numbytes := 2; + n := Length(FBuffer) div numbytes; + RowCount := FixedRows + n; + for i := 1 to n do begin + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]), + 'Unique sheet identifier'); + end; +end; + + +procedure TBIFFGrid.ShowTopMargin; +var + numBytes: Integer; + dbl: Double; +begin + RowCount := FixedRows + 1; + numBytes := 8; + Move(FBuffer[FBufferIndex], dbl, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), + 'Top page margin in inches (IEEE 754 floating-point value, 64-bit double precision)'); +end; + + +procedure TBIFFGrid.ShowTXO; +var + numbytes: Word; + w: Word; + s, sh, sv, sl: String; +begin + RowCount := FixedRows + 9; + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + sh := ''; + case (w and $000E) shr 1 of + 1: sh := sh + 'left-aligned, '; + 2: sh := sh + 'centered, '; + 3: sh := sh + 'right-aligned, '; + 4: sh := sh + 'justified, '; + end; + sv := ''; + case (w and $0070) shr 4 of + 1: sv := sv + 'top, '; + 2: sv := sv + 'middle, '; + 3: sv := sv + 'bottom, '; + 4: sv := sv + 'justify, '; + end; + s := sh + sv; + if (w and $0200) shr 9 <> 0 then s := s + 'lock text, '; + if Row = FCurrRow then begin + FDetails.Add( 'Option flags:'#13); + FDetails.Add( 'Bit 0: Reserved'); + if sh = '' then sh := 'none'; + FDetails.Add( 'Bits 1-3: 0 = Horizontal text alignment: ' + sh); + if sv = '' then sv := 'none'; + FDetails.Add( 'Bits 4-6: 0 = Vertical text alignment: ' + sv); + FDetails.Add( 'Bits 7-8: Reserved'); + case (w and $0200) shr 9 of + 0: FDetails.Add('Bit 9: Lock Text Option is off.'); + 1: FDetails.Add('Bit 9: Lock Text Option is on'); + end; + FDetails.Add( 'Bits 10-15: Reserved'); + end; + if s <> '' then begin + Delete(s, Length(s)-1, 2); + s := 'Option flags: ' + s; + end else + s := 'Option flags'; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), s); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + case w of + 0: s := 'No rotation (text appears left to right'; + 1: s := 'Text appears top to bottom; letters are upright'; + 2: s := 'Text is rotated 90 degrees counterclockwise'; + 3: s := 'Text is rotated 90 degrees clockwise'; + end; + if Row = FCurrRow then begin + FDetails.Add('Orientation of text with the object boundary:'#13); + FDetails.Add(Format('%d = %s', [w, s])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), + 'Orientation of text within the object boundary: ' + s); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Reserved (must be 0)'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Reserved (must be 0)'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Reserved (must be 0)'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Length (in characters) of text (in first following CONTINUE record)'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Length of formatting runs (in second following CONTINUE record)'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Reserved (must be 0)'); + + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Reserved (must be 0)'); +end; + +procedure TBIFFGrid.ShowWindow1; +var + numBytes: Word; + b: Byte; + w: word; +begin + RowCount := FixedRows + IfThen(FFormat < sfExcel5, 5, 9); + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Horizontal position of the document window (in twips = 1/20 pt)'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Vertical position of the document window (in twips = 1/20 pt)'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Width of the document window (in twips = 1/20 pt)'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Height of the document window (in twips = 1/20 pt)'); + + if FFormat < sfExcel5 then begin + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(b)), + '0 = Window is visible; 1 = window is hidden'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'); + if w and $0001 = 0 + then FDetails.Add(' Bit $0001 = 0: Window is visible') + else FDetails.Add('x Bit $0001 = 1: Window is hidden'); + if w and $0002 = 0 + then FDetails.Add(' Bit $0002 = 0: Window is open') + else FDetails.Add('x Bit $0002 = 1: Window is minimized'); + if w and $0008 = 0 + then FDetails.Add(' Bit $0008 = 0: Horizontal scrollbar hidden') + else FDetails.Add('x Bit $0008 = 1: Horizontal scrollbar visible'); + if w and $0010 = 0 + then FDetails.Add(' Bit $0010 = 0: Vertical scrollbar hidden') + else FDetails.Add('x Bit $0010 = 1: Vertical scrollbar visible'); + if w and $0020 = 0 + then FDetails.Add(' Bit $0020 = 0: Worksheet tab bar hidden') + else FDetails.Add('x Bit $0020 = 1: Worksheet tab bar visible'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]), + 'Option flags'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to active (displayed) worksheet'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index of first visible tab in the worksheet tab bar'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Number of selected worksheets (highlighted in the worksheet tab bar)'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Width of worksheet tab bar (in 1/1000 of window width). '+ + 'The remaining space is used by the horizontal scrollbar.'); + end; +end; + + +procedure TBIFFGrid.ShowWindow2; +var + numBytes: Word; + b: Byte; + w: word; + dw : DWord; +begin + if FFormat = sfExcel2 then begin + RowCount := FixedRows + 9; + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + '0 = Show formula results; 1 = Show formulas'); + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + '0 = Do not show grid lines; 1 = Show grid lines'); + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + '0 = Do not show sheet headers; 1 = Show sheet headers'); + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + '0 = Panes are not frozen; 1 = Panes are frozen'); + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + '0 = Show zero values as empty cells; 1 = Show zero values'); + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to first visible row'); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to first visible column'); + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), + '0 = Use manual grid line colour (below); 1 = Use automatic grid line colour'); + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [DWordLEToN(dw)]), + 'Grid line RGB color'); + end else begin + RowCount := FixedRows + IfThen(FFormat = sfExcel5, 4, 8); + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'); + if w and $0001 = 0 + then FDetails.Add(' Bit $0001 = 0: Show formula results') + else FDetails.Add('x Bit $0001 = 1: Show formulas'); + if w and $0002 = 0 + then FDetails.Add(' Bit $0002 = 0: Do not show grid lines') + else FDetails.Add('x Bit $0002 = 1: Show grid lines'); + if w and $0004 = 0 + then FDetails.Add(' Bit $0004 = 0: Do not show sheet headers') + else FDetails.Add('x Bit $0004 = 1: Show sheet headers'); + if w and $0008 = 0 + then FDetails.Add(' Bit $0008 = 0: Panes are not frozen') + else FDetails.Add('x Bit $0008 = 1: Panes are frozen'); + if w and $0010 = 0 + then FDetails.Add(' Bit $0010 = 0: Show zero values as empty cells') + else FDetails.Add('x Bit $0010 = 1: Show zero values'); + if w and $0020 = 0 + then FDetails.Add(' Bit $0020 = 0: Manual grid line color') + else FDetails.Add('x Bit $0020 = 1: Automatic grid line color'); + if w and $0040 = 0 + then FDetails.Add(' Bit $0040 = 0: Columns from left to right') + else FDetails.Add('x Bit $0040 = 1: Columns from right to left'); + if w and $0080 = 0 + then FDetails.Add(' Bit $0080 = 0: Do not show outline symbols') + else FDetails.Add('x Bit $0080 = 1: Show outline symbols'); + if w and $0100 = 0 + then FDetails.Add(' Bit $0100 = 0: Keep splits if pane freeze is removed') + else FDetails.Add('x Bit $0100 = 1: Remove splits if pane freeze is removed'); + if w and $0200 = 0 + then FDetails.Add(' Bit $0200 = 0: Sheet not selected') + else FDetails.Add('x Bit $0200 = 1: Sheet selected'); + if w and $0400 = 0 + then FDetails.Add(' Bit $0400 = 0: Sheet not active') + else FDetails.Add('x Bit $0400 = 1: Sheet active'); + if w and $0800 = 0 + then FDetails.Add(' Bit $0800 = 0: Show in normal view') + else FDetails.Add('x Bit $0800 = 1: Show in page break preview'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]), + 'Option flags'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to first visible row'); + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Index to first visible column'); + + if FFormat =sfExcel5 then begin + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [DWordLEToN(dw)]), + 'Grid line RGB color'); + end else begin + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Color index of grid line color'); + + ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'Not used'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Cached magnification factor in page break preview (in percent); 0 = Default (60%)'); + + Move(FBuffer[FBufferIndex], w, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Cached magnification factor in normal view (in percent); 0 = Default (100%)'); + + numBytes := 4; + ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'Not used'); + end; + end; +end; + + +procedure TBIFFGrid.ShowWindowProtect; +var + numBytes: Integer; + w: Word; +begin + RowCount := FixedRows + 1; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Protection state of workbook windows:'#13); + if w = 0 + then FDetails.Add('0 = The workbook windows can be resized or moved and the window state can be changed.') + else FDetails.Add('1 = The workbook windows cannot be resized or moved and the window state cannot be changed.'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), + 'Protection state of the workbook windows'); +end; + + +procedure TBIFFGrid.ShowWriteAccess; +var + numbytes: Integer; + s: String; +begin + RowCount := FixedRows + 1; + ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8, s, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'User name (i.e., the name that you type when you install Microsoft Excel'); +end; + + +procedure TBIFFGrid.ShowWriteProt; +begin + RowCount := FixedRows + 2; + ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Write protect: if present file is write-protected'); + ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Write protection password is in FILESHARING record'); +end; + + +procedure TBIFFGrid.ShowXF; +var + numBytes: Word; + b: Byte; + w: word; + dw : DWord; +begin + if FFormat = sfExcel2 then begin + RowCount := FixedRows + 4; + numBytes := 1; + b := FBuffer[FBufferIndex]; + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), + 'Index to font record'); + ShowInRow(FCurrRow, FBufferIndex, numBytes, '', + '(not used)'); + + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Number format and cell flags:'#13); + FDetails.Add(Format('Bits 0-5 = %d: Index to FORMAT record', [b and $3F])); + if b and $40 = 0 + then FDetails.Add('Bit 6 = 0: Cell is not locked') + else FDetails.Add('Bit 6 = 1: Cell is locked'); + if b and $80 = 0 + then FDetails.Add('Bit 7 = 0: Formula is not hidden') + else FDetails.Add('Bit 7 = 1: Formula is hidden'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]), + 'Number format and cell flags'); + + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Horizontal alignment, border style and background:'#13); + case b and $07 of + 0: FDetails.Add('Bits $07 = 0: Horizontal alignment "General"'); + 1: FDetails.Add('Bits $07 = 1: Horizontal alignment "Left"'); + 2: FDetails.Add('Bits $07 = 2: Horizontal alignemnt "Centered"'); + 3: FDetails.Add('Bits $07 = 3: Horizontal alignment "Right"'); + 4: FDetails.Add('Bits $07 = 4: Horizontal alignment "Filled"'); + 5: FDetails.Add('Bits $07 = 5: Horizontal alignment "Justified"'); + 6: FDetails.Add('Bits $07 = 6: Horizontal alignment "Centred across selection"'); + 7: FDetails.Add('Bits $07 = 7: Horizontal alignment "Distributed"'); + end; + if b and $08 = 0 + then FDetails.Add('Bit $08 = 0: Cell has no left border') + else FDetails.Add('Bit $08 = 1: Cell has left black border'); + if b and $10 = 0 + then FDetails.Add('Bit $10 = 0: Cell has no right border') + else FDetails.Add('Bit $10 = 1: Cell has right black border'); + if b and $20 = 0 + then FDetails.Add('Bit $20 = 0: Cell has no top border') + else FDetails.Add('Bit $20 = 1: Cell has top black border'); + if b and $40 = 0 + then FDetails.Add('Bit $40 = 0: Cell has no bottom border') + else FDetails.Add('Bit $40 = 1: Cell has bottom black border'); + if b and $80 = 0 + then FDetails.Add('Bit $80 = 0: Cell has no shaded background') + else FDetails.Add('Bit $80 = 1: Cell has shaded background'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), + 'Horizontal alignment, border style, and background'); + end + else + begin // XF (BIFF5 and BIFF8) + RowCount := FixedRows + IfThen(FFormat=sfExcel5, 7, 10); + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to font record'); + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), + 'Index to format record'); + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('XFType, cell protection, parent style XF:'#13); + if w and $0001 = 0 + then FDetails.Add('Bit $0001 = 0: Cell is not locked') + else FDetails.Add('Bit $0001 = 1: Cell is locked'); + if w and $0002 = 0 + then FDetails.Add('Bit $0002 = 0: Formula is not hidden') + else FDetails.Add('Bit $0002 = 1: Formula is hidden'); + if w and $0004 = 0 + then FDetails.Add('Bit $0004 = 0: Cell XF') + else FDetails.Add('Bit $0004 = 1: Style XF'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]), + 'XFType, cell protection, parent style XF'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Alignment and text break:'#13); + case b and $03 of + 0: FDetails.Add('Bits 0-2 = 0: Horizontal alignment "General"'); + 1: FDetails.Add('Bits 0-2 = 1: Horizontal alignment "Left"'); + 2: FDetails.Add('Bits 0-2 = 2: Horizontal alignemnt "Centered"'); + 3: FDetails.Add('Bits 0-2 = 3: Horizontal alignment "Right"'); + 4: FDetails.Add('Bits 0-2 = 4: Horizontal alignment "Filled"'); + 5: FDetails.Add('Bits 0-2 = 5: Horizontal alignment "Justified"'); + 6: FDetails.Add('Bits 0-2 = 6: Horizontal alignment "Centred across selection"'); + 7: if FFormat = sfExcel8 then + FDetails.Add('x Bits 0-2 = 7: Horizontal alignment "Distributed"'); + end; + if b and $08 = 0 + then FDetails.Add('Bit 3 = 0: Text is not wrapped.') + else FDetails.Add('Bit 3 = 1: Text is wrapped at right border.'); + case (b and $70) shr 4 of + 0: FDetails.Add('Bits 4-6 = 0: Vertical alignment "Top"'); + 1: FDetails.Add('Bits 4-6 = 1: Vertical alignment "Centered"'); + 2: FDetails.Add('Bits 4-6 = 2: Vertical alignment "Bottom"'); + 3: FDetails.Add('Bits 4-6 = 3: Vertical alignment "Justified"'); + 4: if FFormat = sfExcel8 then + FDetails.Add('Bits 4-6 = 4: Vertical alignment "Distributed"'); + end; + if FFormat = sfExcel8 then begin + if b and $80 = 0 + then FDetails.Add('Bit 3 = 0: Do NOT justify last line in justified or distibuted text') + else FDetails.Add('Bit 3 = 1: Justify last line in justified or distibuted text'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), + 'Alignment and text break'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + if FFormat = sfExcel5 then begin + if Row = FCurrRow then begin + FDetails.Add('Text orientation and flags for used attribute groups:'#13); + case (b and $03) of + 0: FDetails.Add('Bits $03 = 0: not rotated'); + 1: FDetails.Add('Bits $03 = 1: not rotated, letters stacked top-to-bottom'); + 2: FDetails.Add('Bits $03 = 2: text rotated 90° counter-clockwise'); + 3: FDetails.Add('Bits $03 = 3: text rotated 90° clockwise'); + end; + if b and $04 = 0 + then FDetails.Add('Bit $04 = 0: No flag for number format') + else FDetails.Add('Bit $04 = 1: Flag for number format'); + if b and $08 = 0 + then FDetails.Add('Bit $08 = 0: No flag for font') + else FDetails.Add('Bit $08 = 2: Flag for font'); + if b and $10 = 0 + then FDetails.Add('Bit $10 = 0: No flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction') + else FDetails.Add('Bit $10 = 1: Flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction'); + if b and $20 = 0 + then FDetails.Add('Bit $20 = 0: No flag for border lines') + else FDetails.Add('Bit $20 = 1: Flag for border lines'); + if b and $40 = 0 + then FDetails.Add('Bit $40 = 0: No flag for background area style') + else FDetails.Add('Bit $40 = 1: Flag for background area style'); + if b and $80 = 0 + then FDetails.Add('Bit $80 = 0: No flag for cell protection (cell locked and formula hidden)') + else FDetails.Add('Bit $80 = 1: Flag for cell protection (cell locked and formula hidden)'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [b]), + 'Text orientation and flags for used attribute groups'); + end else + begin // sfExcel8 + if Row = FCurrRow then begin + FDetails.Add('Text rotation angle:'#13); + if b = 0 then + FDetails.Add('not rotated') + else if b <= 90 then + FDetails.Add(Format('%d degrees counter-clockwise', [b])) + else if b <= 180 then + FDetails.Add(Format('%d degrees clockwize', [b-90])) + else if b = 255 then + FDetails.Add('not rotated, letters stacked top-to-bottom'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), 'Text rotation angle'); + end; + + if FFormat = sfExcel8 then begin + numBytes := 1; + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Indentation, shrink to cell size, and text direction:'#13); + FDetails.Add(Format('Bits 0-3: Indent level = %d', [b and $0F])); + if b and $10 = 0 + then FDetails.Add('Bit $10 = 0: Don''t shrink content to fit into cell') + else FDetails.Add('Bit $10 = 1: Shrink content to fit into cell'); + if b and $20 = 0 + then FDetails.Add('Bit $20 = 0: Merge Cell option is OFF') + else FDetails.Add('Bit $20 = 1: Merge Cell option is ON'); + case (b and $C0) shr 6 of + 0: FDetails.Add('Bits 6-7 = 0: Text direction according to context'); + 1: FDetails.Add('Bits 6-7 = 1: Text direction left-to-right'); + 2: FDetails.Add('Bits 6-7 = 2: Text direction right-to-left'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), + 'Indentation, shrink to cell size, and text direction'); + + numBytes := 1; + b := FBuffer[FBufferIndex]; + if Row = FCurrRow then begin + FDetails.Add('Flags for used attribute groups:'#13); + if b and $04 = 0 + then FDetails.Add('Bit $04 = 0: No flag for number format') + else FDetails.Add('Bit $04 = 1: Flag for number format'); + if b and $08 = 0 + then FDetails.Add('Bit $08 = 0: No flag for font') + else FDetails.Add('Bit $08 = 2: Flag for font'); + if b and $10 = 0 + then FDetails.Add('Bit $10 = 0: No flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction') + else FDetails.Add('Bit $10 = 1: Flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction'); + if b and $20 = 0 + then FDetails.Add('Bit $20 = 0: No flag for border lines') + else FDetails.Add('Bit $20 = 1: Flag for border lines'); + if b and $40 = 0 + then FDetails.Add('Bit $40 = 0: No flag for background area style') + else FDetails.Add('Bit $40 = 1: Flag for background area style'); + if b and $80 = 0 + then FDetails.Add('Bit $80 = 0: No flag for cell protection (cell locked and formula hidden)') + else FDetails.Add('Bit $80 = 1: Flag for cell protection (cell locked and formula hidden)'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [b]), + 'Flags for used attribute groups'); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + if Row = FCurrRow then begin + FDetails.Add('Cell border lines and background area:'#13); + case dw and $0000000F of + $0000: FDetails.Add('Bits 0-3 = 0: Left border = No line'); + $0001: FDetails.Add('Bits 0-3 = 1: Left border = thin solid line'); + $0002: FDetails.Add('Bits 0-3 = 2: Left border = medium solid line'); + $0003: FDetails.Add('Bits 0-3 = 3: Left border = dashed line'); + $0004: FDetails.Add('Bits 0-3 = 4: Left border = dotted line'); + $0005: FDetails.Add('Bits 0-3 = 5: Left border = thick solid line'); + $0006: FDetails.Add('Bits 0-3 = 6: Left border = double solid line'); + $0007: FDetails.Add('Bits 0-3 = 7: Left border = hair line'); + $0008: FDetails.Add('Bits 0-3 = 8: Left border = medium dashed'); + $0009: FDetails.Add('Bits 0-3 = 9: Left border = thin dash-dotted'); + $000A: FDetails.Add('Bits 0-3 = 10: Left border = medium dash-dotted'); + $000B: FDetails.Add('Bits 0-3 = 11: Left border = thin dash-dot-dotted'); + $000C: FDetails.Add('Bits 0-3 = 12: Left border = medium dash-dot-dotted'); + $000D: FDetails.Add('Bits 0-3 = 13: Left border = slanted medium dash-dotted'); + end; + case dw and $000000F0 of + $0000: FDetails.Add('Bits 4-7 = 0: Right border = No line'); + $0010: FDetails.Add('Bits 4-7 = 1: Right border = thin solid line'); + $0020: FDetails.Add('Bits 4-7 = 2: Right border = medium solid line'); + $0030: FDetails.Add('Bits 4-7 = 3: Right border = dashed line'); + $0040: FDetails.Add('Bits 4-7 = 4: Right border = dotted line'); + $0050: FDetails.Add('Bits 4-7 = 5: Right border = thick solid line'); + $0060: FDetails.Add('Bits 4-7 = 6: Right border = double solid line'); + $0070: FDetails.Add('Bits 4-7 = 7: Right border = hair line'); + $0080: FDetails.Add('Bits 4-7 = 8: Right border = medium dashed'); + $0090: FDetails.Add('Bits 4-7 = 9: Right border = thin dash-dotted'); + $00A0: FDetails.Add('Bits 4-7 = 10: Right border = medium dash-dotted'); + $00B0: FDetails.Add('Bits 4-7 = 11: Right border = thin dash-dot-dotted'); + $00C0: FDetails.Add('Bits 4-7 = 12: Right border = medium dash-dot-dotted'); + $00D0: FDetails.Add('Bits 4-7 = 13: Right border = slanted medium dash-dotted'); + end; + case dw and $00000F00 of + $0000: FDetails.Add('Bits 8-11 = 0: Top border = No line'); + $0100: FDetails.Add('Bits 8-11 = 1: Top border = thin solid line'); + $0200: FDetails.Add('Bits 8-11 = 2: Top border = medium solid line'); + $0300: FDetails.Add('Bits 8-11 = 3: Top border = dashed line'); + $0400: FDetails.Add('Bits 8-11 = 4: Top border = dotted line'); + $0500: FDetails.Add('Bits 8-11 = 5: Top border = thick solid line'); + $0600: FDetails.Add('Bits 8-11 = 6: Top border = double solid line'); + $0700: FDetails.Add('Bits 8-11 = 7: Top border = hair line'); + $0800: FDetails.Add('Bits 8-11 = 8: Top border = medium dashed'); + $0900: FDetails.Add('Bits 8-11 = 9: Top border = thin dash-dotted'); + $0A00: FDetails.Add('Bits 8-11 = 10: Top border = medium dash-dotted'); + $0B00: FDetails.Add('Bits 8-11 = 11: Top border = thin dash-dot-dotted'); + $0C00: FDetails.Add('Bits 8-11 = 12: Top border = medium dash-dot-dotted'); + $0D00: FDetails.Add('Bits 8-11 = 13: Top border = slanted medium dash-dotted'); + end; + case dw and $0000F000 of + $0000: FDetails.Add('Bits 12-15 = 0: Bottom border = No line'); + $1000: FDetails.Add('Bits 12-15 = 1: Bottom border = thin solid line'); + $2000: FDetails.Add('Bits 12-15 = 2: Bottom border = medium solid line'); + $3000: FDetails.Add('Bits 12-15 = 3: Bottom border = dashed line'); + $4000: FDetails.Add('Bits 12-15 = 4: Bottom border = dotted line'); + $5000: FDetails.Add('Bits 12-15 = 5: Bottom border = thick solid line'); + $6000: FDetails.Add('Bits 12-15 = 6: Bottom border = double solid line'); + $7000: FDetails.Add('Bits 12-15 = 7: Bottom border = hair line'); + $8000: FDetails.Add('Bits 12-15 = 8: Bottom border = medium dashed'); + $9000: FDetails.Add('Bits 12-15 = 9: Bottom border = thin dash-dotted'); + $A000: FDetails.Add('Bits 12-15 = 10: Bottom border = medium dash-dotted'); + $B000: FDetails.Add('Bits 12-15 = 11: Bottom border = thin dash-dot-dotted'); + $C000: FDetails.Add('Bits 12-15 = 12: Bottom border = medium dash-dot-dotted'); + $D000: FDetails.Add('Bits 12-15 = 13: Bottom border = slanted medium dash-dotted'); + end; + FDetails.Add(Format('Bits 16-22 = %d: Color index for left line color', [(dw and $007F0000) shr 16])); + FDetails.Add(Format('Bits 23-29 = %d: Color index for right line color', [(dw and $3F800000) shr 23])); + if dw and $40000000 = 0 + then FDetails.Add('Bit 30 = 0: No diagonal line from top left to right bottom') + else FDetails.Add('Bit 30 = 1: Diagonal line from top left to right bottom'); + if dw and $80000000 = 0 + then FDetails.Add('Bit 31 = 0: No diagonal line from bottom left to right top') + else FDetails.Add('Bit 31 = 1: Diagonal line from bottom left to right top'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]), + 'Cell border lines and background area'); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + if Row = FCurrRow then begin + FDetails.Add('Cell border lines and background area (cont''d):'#13); + FDetails.Add(Format('Bits 0-6 = %d: Color index for top line color', [(dw and $0000007F)])); + FDetails.Add(Format('Bits 7-13 = %d: Color index for bottom line color', [(dw and $00003F80) shr 7])); + FDetails.Add(Format('Bits 14-20 = %d: Color index for diagonal line color', [(dw and $001FC000) shr 14])); + case dw and $01E00000 shr 17 of + $0: FDetails.Add('Bits 21-24 = 0: Diagonal line style = No line'); + $1: FDetails.Add('Bits 21-24 = 1: Diagonal line style = thin solid line'); + $2: FDetails.Add('Bits 21-24 = 2: Diagonal line style = medium solid line'); + $3: FDetails.Add('Bits 21-24 = 3: Diagonal line style = dashed line'); + $4: FDetails.Add('Bits 21-24 = 4: Diagonal line style = dotted line'); + $5: FDetails.Add('Bits 21-24 = 5: Diagonal line style = thick solid line'); + $6: FDetails.Add('Bits 21-24 = 6: Diagonal line style = double solid line'); + $7: FDetails.Add('Bits 21-24 = 7: Diagonal line style = hair line'); + $8: FDetails.Add('Bits 21-24 = 8: Diagonal line style = medium dashed'); + $9: FDetails.Add('Bits 21-24 = 9: Diagonal line style = thin dash-dotted'); + $A: FDetails.Add('Bits 21-24 = 10: Diagonal line style = medium dash-dotted'); + $B: FDetails.Add('Bits 21-24 = 11: Diagonal line style = thin dash-dot-dotted'); + $C: FDetails.Add('Bits 21-24 = 12: Diagonal line style = medium dash-dot-dotted'); + $D: FDetails.Add('Bits 21-24 = 13: Diagonal line style = slanted medium dash-dotted'); + end; + case (dw and $FC000000) shr 26 of + $00: FDetails.Add('Bits 26-31 = 0: Fill pattern = No fill'); + $01: FDetails.Add('Bits 26-31 = 1: Fill pattern = solid fill'); + $02: FDetails.Add('Bits 26-31 = 2: Fill pattern = medium fill'); + $03: FDetails.Add('Bits 26-31 = 3: Fill pattern = dense fill'); + $04: FDetails.Add('Bits 26-31 = 4: Fill pattern = sparse fill'); + $05: FDetails.Add('Bits 26-31 = 5: Fill pattern = horizontal fill'); + $06: FDetails.Add('Bits 26-31 = 6: Fill pattern = vertical fill'); + $07: FDetails.Add('Bits 26-31 = 7: Fill pattern = backslash fill'); + $08: FDetails.Add('Bits 26-31 = 8: Fill pattern = slash fill'); + $09: FDetails.Add('Bits 26-31 = 9: Fill pattern = coarse medium fill'); + $0A: FDetails.Add('Bits 26-31 = 10: Fill pattern = coarse medium horiz fill'); + $0B: FDetails.Add('Bits 26-31 = 11: Fill pattern = sparse horizontal fill'); + $0C: FDetails.Add('Bits 26-31 = 12: Fill pattern = sparse vertical fill'); + $0D: FDetails.Add('Bits 26-31 = 13: Fill pattern = sparse backslash fill'); + $0E: FDetails.Add('Bits 26-31 = 14: Fill pattern = sparse slash fill'); + $0F: FDetails.Add('Bits 26-31 = 15: Fill pattern = cross fill'); + $10: FDetails.Add('Bits 26-31 = 16: Fill pattern = dense backslash fill'); + $11: FDetails.Add('Bits 26-31 = 17: Fill pattern = very sparse fill'); + $12: FDetails.Add('Bits 26-31 = 18: Fill pattern = extremely sparse fill'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]), + 'Cell border lines and background area (cont''d)'); + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Cell border lines and background area (cont''d):'#13); + FDetails.Add(Format('Bits 0-6 = %d: Color index for pattern color', [(w and $007F)])); + FDetails.Add(Format('Bits 7-13 = %d: Color index for pattern background color', [(w and $3F80) shr 7])); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), + 'Cell border lines and background area (cont''d)'); + end; + + if FFormat = sfExcel5 then begin + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + dw := DWordLEToN(dw); + if Row = FCurrRow then begin + FDetails.Add('Cell border lines and background area:'#13); + FDetails.Add(Format('Bits 0-6 = %d: Color index for pattern color', [(dw and $007F)])); + FDetails.Add(Format('Bits 7-13 = %d: Color index for pattern background color', [(dw and $3F80) shr 7])); + case (dw and $003F0000) shr 16 of + $00: FDetails.Add('Bits 16-21 = 0: Fill pattern = No fill'); + $01: FDetails.Add('Bits 16-21 = 1: Fill pattern = solid fill'); + $02: FDetails.Add('Bits 16-21 = 2: Fill pattern = medium fill'); + $03: FDetails.Add('Bits 16-21 = 3: Fill pattern = dense fill'); + $04: FDetails.Add('Bits 16-21 = 4: Fill pattern = sparse fill'); + $05: FDetails.Add('Bits 16-21 = 5: Fill pattern = horizontal fill'); + $06: FDetails.Add('Bits 16-21 = 6: Fill pattern = vertical fill'); + $07: FDetails.Add('Bits 16-21 = 7: Fill pattern = backslash fill'); + $08: FDetails.Add('Bits 16-21 = 8: Fill pattern = slash fill'); + $09: FDetails.Add('Bits 16-21 = 9: Fill pattern = coarse medium fill'); + $0A: FDetails.Add('Bits 16-21 = 10: Fill pattern = coarse medium horiz fill'); + $0B: FDetails.Add('Bits 16-21 = 11: Fill pattern = sparse horizontal fill'); + $0C: FDetails.Add('Bits 16-21 = 12: Fill pattern = sparse vertical fill'); + $0D: FDetails.Add('Bits 16-21 = 13: Fill pattern = sparse backslash fill'); + $0E: FDetails.Add('Bits 16-21 = 14: Fill pattern = sparse slash fill'); + $0F: FDetails.Add('Bits 16-21 = 15: Fill pattern = cross fill'); + $10: FDetails.Add('Bits 16-21 = 16: Fill pattern = dense backslash fill'); + $11: FDetails.Add('Bits 16-21 = 17: Fill pattern = very sparse fill'); + $12: FDetails.Add('Bits 16-21 = 18: Fill pattern = extremely sparse fill'); + end; + case dw and $01C00000 shr 22 of + $0: FDetails.Add('Bits 22-24 = 0: Bottom line style = No line'); + $1: FDetails.Add('Bits 22-24 = 1: Bottom line style = thin solid line'); + $2: FDetails.Add('Bits 22-24 = 2: Bottom line style = medium solid line'); + $3: FDetails.Add('Bits 22-24 = 3: Bottom line style = dashed line'); + $4: FDetails.Add('Bits 22-24 = 4: Bottom line style = dotted line'); + $5: FDetails.Add('Bits 22-24 = 5: Bottom line style = thick solid line'); + $6: FDetails.Add('Bits 22-24 = 6: Bottom line style = double solid line'); + $7: FDetails.Add('Bits 22-24 = 7: Bottom line style = hair line'); + end; + FDetails.Add(Format('Bits 25-31 = %d: Color index for bottom line color', [(dw and $FE000000) shr 25])); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]), + 'Cell border lines & background area'); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + dw := DWOrdLEToN(dw); + if Row = FCurrRow then begin + FDetails.Add('Cell border lines (cont''d):'#13); + case dw and $00000007 of + $00: FDetails.Add('Bits 0-2 = 0: Top border = No line'); + $01: FDetails.Add('Bits 0-2 = 1: Top border = thin solid line'); + $02: FDetails.Add('Bits 0-2 = 2: Top border = medium solid line'); + $03: FDetails.Add('Bits 0-2 = 3: Top border = dashed line'); + $04: FDetails.Add('Bits 0-2 = 4: Top border = dotted line'); + $05: FDetails.Add('Bits 0-2 = 5: Top border = thick solid line'); + $06: FDetails.Add('Bits 0-2 = 6: Top border = double solid line'); + $07: FDetails.Add('Bits 0-2 = 7: Top border = hair line'); + end; + case (dw and $00000038) shr 3 of + $0000: FDetails.Add('Bits 3-5 = 0: Left border = No line'); + $0001: FDetails.Add('Bits 3-5 = 1: Left border = thin solid line'); + $0002: FDetails.Add('Bits 3-5 = 2: Left border = medium solid line'); + $0003: FDetails.Add('Bits 3-5 = 3: Left border = dashed line'); + $0004: FDetails.Add('Bits 3-5 = 4: Left border = dotted line'); + $0005: FDetails.Add('Bits 3-5 = 5: Left border = thick solid line'); + $0006: FDetails.Add('Bits 3-5 = 6: Left border = double solid line'); + $0007: FDetails.Add('Bits 3-5 = 7: Left border = hair line'); + end; + case (dw and $000001C0) shr 6 of + $0000: FDetails.Add('Bits 6-8 = 0: Right border = No line'); + $0010: FDetails.Add('Bits 6-8 = 1: Right border = thin solid line'); + $0020: FDetails.Add('Bits 6-8 = 2: Right border = medium solid line'); + $0030: FDetails.Add('Bits 6-8 = 3: Right border = dashed line'); + $0040: FDetails.Add('Bits 6-8 = 4: Right border = dotted line'); + $0050: FDetails.Add('Bits 6-8 = 5: Right border = thick solid line'); + $0060: FDetails.Add('Bits 6-8 = 6: Right border = double solid line'); + $0070: FDetails.Add('Bits 6-8 = 7: Right border = hair line'); + end; + FDetails.Add(Format('Bits 9-15 = %d: Color index for top line color', [(dw and $0000FE00) shr 7])); + FDetails.Add(Format('Bits 16-22 = %d: Color index for left line color', [(dw and $007F0000) shr 16])); + FDetails.Add(Format('Bits 23-29 = %d: Color index for right line color', [(dw and $3F800000) shr 23])); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.8x', [dw]), + 'Cell border lines (cont''d)'); + end; + end; +end; + + +procedure TBIFFGrid.ShowXFCRC; +var + numBytes: Integer; + w: Word; + dw: DWord; +begin + RowCount := FixedRows + 7; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [wordLEToN(w)]), + 'Future record type'); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Attributes:'#13); + if w and $0001 = 0 + then FDetails.Add(' Bit 0 = 0: The containing record does not specify a range of cells.') + else FDetails.Add('x Bit 0 = 1: The containing record specifies a range of cells.'); + FDetails.Add('Bit 1: specifies wether to alert the user of possible problems '+ + 'when saving the file whithout having reckognized this record.'); + FDetails.Add('Bits 2-15: reserved (MUST be zero, MUST be ignored)'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), + 'Attributes'); + + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved'); + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved'); + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Reserved'); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Count of XF records'); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w,w]), + 'Checksum of XF records'); +end; + + +procedure TBIFFGrid.ShowXFEXT; +var + numBytes: Integer; + w: Word; + dw: DWord; + i, n: Integer; + et: Word; + es: Word; + ct: Word; + buffidx: Cardinal; + s: String; +begin + BeginUpdate; + + RowCount := FixedRows + 100; + + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [wordLEToN(w)]), + 'Future record type'); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Attributes:'#13); + if w and $0001 = 0 + then FDetails.Add(' Bit 0 = 0: The containing record does not specify a range of cells.') + else FDetails.Add('x Bit 0 = 1: The containing record specifies a range of cells.'); + FDetails.Add('Bit 1: specifies wether to alert the user of possible problems '+ + 'when saving the file whithout having reckognized this record.'); + FDetails.Add('Bits 2-15: reserved (MUST be zero, MUST be ignored)'); + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), + 'Attributes'); + + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved'); + Move(FBuffer[FBufferIndex], dw, numbytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved'); + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Reserved'); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w,w]), + 'XF index'); + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Reserved'); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + 'Number of extension properties'); + n := w; + + for i:=1 to n do begin + buffidx := FBufferIndex; + numbytes := 2; + Move(FBuffer[FBufferIndex], et, numbytes); + et := WordLEToN(et); + if Row = FCurrRow then begin + FDetails.Add('Type:'#13); + case et of + $04: FDetails.Add('Full color extension that specifies the cell interior foreground color.'); + $05: FDetails.Add('Full color extension that specifies the cell interior background color.'); + $06: FDetails.Add('Gradient extension that specifies a cell interior gradient fill.'); + $07: FDetails.Add('Full color extension that specifies the top cell border color.'); + $08: FDetails.Add('Full color extension that specifies the bottom cell border color.'); + $09: FDetails.Add('Full color extension that specifies the left cell border color.'); + $0A: FDetails.Add('Full color extension that specifies the right cell border color.'); + $0B: FDetails.Add('Full color extension that specifies the diagonal cell border color.'); + $0D: FDetails.Add('Full color extension that specifies the cell text color.'); + $0E: FDetails.Add('2-byte unsigned integer that specifies a font scheme.'); + $0F: FDetails.Add('2-byte unsigned integer that specifies the text indentation level (MUST be <= 250).'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [et, et]), + Format('Extension property #%d: Type', [i])); + Move(FBuffer[FBufferIndex], es, numbytes); + es := WordLEToN(es); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(es), + Format('Extension property #%d: Data size', [i])); + + case et of + $04, $05, $07..$0D: // FullColorExt + begin + numbytes := 2; + Move(FBuffer[FBufferIndex], ct, numbytes); + ct := WordLEToN(ct); + if Row = FCurrRow then begin + FDetails.Add('Full color extension - Color type:'#13); + case ct of + 0: FDetails.Add('0 - Automatic color'); + 1: FDetails.Add('1 - Indexed color'); + 2: FDetails.Add('2 - RGB color'); + 3: FDetails.Add('3 - Theme color'); + 4: FDetails.Add('4 - Color not set'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(ct), + Format('Extension property #%d (Full color extension): Color type', [i])); + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(integer(w)), + Format('Extension property #%d (Full color extension): Color tint', [i])); + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + case ct of + 0: s := '(dummy - MUST be 0)'; + 1: s := '(index)'; + 2: s := '(RGB value)'; + 3: s := '(theme)'; + else s := ''; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.8x)', [QWord(dw), QWord(dw)]), + Format('Extension property #%d (Full color extension): value %s', [i, s])); + numbytes := 4; + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.8x)', [QWord(dw), QWord(dw)]), + Format('Extension property #%d (Full color extension): Reserved', [i])); + Move(FBuffer[FBufferIndex], dw, numbytes); + dw := DWordLEToN(dw); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.8x)', [QWord(dw), QWord(dw)]), + Format('Extension property #%d (Full color extension): Reserved', [i])); + end; + + $06: // Gradient + begin + ShowInRow(FCurrRow, FBufferIndex, es, '(var)', + Format('Extension property #%d (Gradient): - not interpreted here -', [i])); + end; + + $0E: // Font scheme + begin + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Font scheme:'#13); + case w of + 0: FDetails.Add('0 - No font scheme'); + 1: FDetails.Add('1 - Major scheme'); + 2: FDetails.Add('2 - Minor scheme'); + 3: FDetails.Add('3 - Ninched scheme'); + end; + end; + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w,w]), + Format('Extension property #%d Font scheme', [i])); + end; + + $0F: // Text indentation level + begin + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), + Format('Extension property #%d Text indentation level', [i])); + end; + end; + FBufferIndex := buffidx + es; + end; + RowCount := FCurrRow; + + EndUpdate(true); +end; + + +end. + diff --git a/applications/biffexplorer/bebiffutils.pas b/applications/biffexplorer/bebiffutils.pas new file mode 100644 index 000000000..cc41ff108 --- /dev/null +++ b/applications/biffexplorer/bebiffutils.pas @@ -0,0 +1,801 @@ +unit beBIFFUtils; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils; + +function BOFName(ACode: Word): String; +function CodePageName(AID: Word): String; +function PaperSizeName(ACode: Word): String; +function RecTypeName(ARecType: Word): String; +function SheetFuncName(AIndex: Word): String; +function ErrorCodeName(ACode: Byte): String; + + +implementation + +function BOFName(ACode: Word): String; +begin + case ACode of + $0005: Result := 'Workbook globals'; + $0006: Result := 'Visual Basic module'; + $0010: Result := 'Sheet or dialog'; + $0020: Result := 'Chart'; + $0040: Result := 'Macro sheet'; + $0100: Result := 'Workspace'; + else Result := ''; + end; +end; + +function CodePageName(AID: Word): String; +begin + case AID of + $0016: Result := 'ASCII'; + $01B5: Result := 'IBM PC CP-437 (US)'; + $02D0: Result := 'IBM PC CP-720 (OEM Arabic)'; + $02E1: Result := 'IBM PC CP-737 (Greek)'; + $0307: Result := 'IBM PC CP-775 (Baltic)'; + $0352: Result := 'IBM PC CP-850 (Latin I)'; + $0354: Result := 'IBM PC CP-852 (Latin II (Central European))'; + $0357: Result := 'IBM PC CP-855 (Cyrillic)'; + $0359: Result := 'IBM PC CP-857 (Turkish)'; + $035A: Result := 'IBM PC CP-858 (Multilingual Latin I with Euro)'; + $035C: Result := 'IBM PC CP-860 (Portuguese)'; + $035D: Result := 'IBM PC CP-861 (Icelandic)'; + $035E: Result := 'IBM PC CP-862 (Hebrew)'; + $035F: Result := 'IBM PC CP-863 (Canadian (French))'; + $0360: Result := 'IBM PC CP-864 (Arabic)'; + $0361: Result := 'IBM PC CP-865 (Nordic)'; + $0362: Result := 'IBM PC CP-866 (Cyrillic (Russian))'; + $0365: Result := 'IBM PC CP-869 (Greek (Modern))'; + $036A: Result := 'Windows CP-874 (Thai)'; + $03A4: Result := 'Windows CP-932 (Japanese Shift-JIS)'; + $03A8: Result := 'Windows CP-936 (Chinese Simplified GBK)'; + $03B5: Result := 'Windows CP-949 (Korean (Wansung))'; + $03B6: Result := 'Windows CP-950 (Chinese Traditional BIG5)'; + $04B0: Result := 'UTF-16 (BIFF8)'; + $04E2: Result := 'Windows CP-1250 (Latin II) (Central European)'; + $04E3: Result := 'Windows CP-1251 (Cyrillic)'; + $04E4: Result := 'Windows CP-1252 (Latin I) (BIFF4-BIFF5)'; + $04E5: Result := 'Windows CP-1253 (Greek)'; + $04E6: Result := 'Windows CP-1254 (Turkish)'; + $04E7: Result := 'Windows CP-1255 (Hebrew)'; + $04E8: Result := 'Windows CP-1256 (Arabic)'; + $04E9: Result := 'Windows CP-1257 (Baltic)'; + $04EA: Result := 'Windows CP-1258 (Vietnamese)'; + $0551: Result := 'Windows CP-1361 (Korean (Johab))'; + $2710: Result := 'Apple Roman'; + $8000: Result := 'Apple Roman'; + $8001: Result := 'Windows CP-1252 (Latin I) (BIFF2-BIFF3)'; + else Result := ''; + end; +end; + +function ErrorCodeName(ACode: Byte): String; +begin + case ACode of + $00: Result := '#NULL! - Intersection of two cell ranges is empty'; + $07: Result := '#DIV/0! - Division by zero'; + $0F: Result := '#VALUE! - Wrong type of operand'; + $17: Result := '#REF! - Illegal or deleted cell reference'; + $1D: Result := '#NAME? - Wrong function or range name'; + $24: Result := '#NUM! - Value range overflow'; + $2A: Result := '#N/A - Argument or function not available'; + else Result := '(unknown)'; + end; +end; + + +function PaperSizeName(ACode: Word): String; +begin + case ACode of + 0: Result := 'Undefined'; + 1: Result := 'Letter (8.5" x 11")'; + 2: Result := 'Letter small (8.5" x 11")'; + 3: Result := 'Tabloid (11" x 17")'; + 4: Result := 'Ledger (17" x 11")'; + 5: Result := 'Legal (8.5" x 14")'; + 6: Result := 'Statement (5.5" x 8.5")'; + 7: Result := 'Executive (7.25" x 10.5")'; + 8: Result := 'A3 (297mm x 420mm)'; + 9: Result := 'A4 (210mm x 297mm)'; + 10: Result := 'A4 small (210mm x 297mm)'; + 11: Result := 'A5 (148mm x 210mm)'; + 12: Result := 'B4 (JIS) (257mm x 364mm)'; + 13: Result := 'B5 (JIS) (182mm x 257mm)'; + 14: Result := 'Folio (8.5" x 13")'; + 15: Result := 'Quarto (215mm x 275mm)'; + 16: Result := '10x14 (10" x 14")'; + 17: Result := '11x17 (11" x 17")'; + 18: Result := 'Note (8.5" x 11")'; + 19: Result := 'Envelope #9 (3 7/8" x 8 7/8")'; + 20: Result := 'Envelope #10 (4 1/8" x 9 1/2")'; + 21: Result := 'Envelope #11 (4 1/2" x 10 3/8")'; + 22: Result := 'Envelope #12 (4 3/4" x 11")'; + 23: Result := 'Envelope #14 (5" x 11.5")'; + 24: Result := 'C (17" x 22")'; + 25: Result := 'D (22" x 34")'; + 26: Result := 'E (34" x 44")'; + 27: Result := 'Envelope DL (110mm x 220mm)'; + 28: Result := 'Envelope C5 (162mm x 229mm)'; + 29: Result := 'Envelope C3 (324mm x 458mm)'; + 30: Result := 'Envelope C4 (229mm x 324mm)'; + 31: Result := 'Envelope C6 (114mm x 162mm)'; + 32: Result := 'Envelope C6/C5 (114mm x 229mm)'; + 33: Result := 'B4 (ISO) (250mm x 353mm)'; + 34: Result := 'B5 (ISO) (176mm x 250mm)'; + 35: Result := 'B6 (ISO) (125mm x 176mm)'; + 36: Result := 'Envelope Italy (110mm x 230mm)'; + 37: Result := 'Envelope Monarch (3 7/8" x 7 1/2")'; + 38: Result := '6 3/4 Envelope (3 5/8" x 6 1/2")'; + 39: Result := 'US Standard Fanfold (14 7/8" x 11")'; + 40: Result := 'German Std. Fanfold (8.5" x 12")'; + 41: Result := 'German Legal Fanfold (8.5" x 13")'; + 42: Result := 'B4 (ISO) (250mm x 353mm)'; + 43: Result := 'Japanese Postcard (100mm x 148mm)'; + 44: Result := '9x11 (9" x 11")'; + 45: Result := '10x11 (10" x 11")'; + 46: Result := '15x11 (15" x 11")'; + 47: Result := 'Envelope Invite (220mm x 220mm)'; + 48: Result := 'Undefined'; + 49: Result := 'Undefined'; + 50: Result := 'Letter Extra (9.5" x 12")'; + 51: Result := 'Legal Extra (9.5" x 15")'; + 52: Result := 'Tabloid Extra (11 11/16" x 18")'; + 53: Result := 'A4 Extra (235mm x 322mm)'; + 54: Result := 'Letter Transverse (8.5" x 11")'; + 55: Result := 'A4 Transverse (210mm x 297mm)'; + 56: Result := 'Letter Extra Transv. (9.5" x 12")'; + 57: Result := 'Super A/A4 (227mm x 356mm)'; + 58: Result := 'Super B/A3 (305mm x 487mm)'; + 59: Result := 'Letter Plus (8.5" x 12 11/16")'; + 60: Result := 'A4 Plus (210mm x 330mm)'; + 61: Result := 'A5 Transverse (148mm x 210mm)'; + 62: Result := 'B5 (JIS) Transverse (182mm x 257mm)'; + 63: Result := 'A3 Extra (322mm x 445mm)'; + 64: Result := 'A5 Extra (174mm x 235mm)'; + 65: Result := 'B5 (ISO) Extra (201mm x 276mm)'; + 66: Result := 'A2 (420mm s 594mm)'; + 67: Result := 'A3 Transverse (297mm x 420mm)'; + 68: Result := 'A3 Extra Transverse (322mm x 445mm)'; + 69: Result := 'Dbl. Japanese Postcard (200mm x 148mm)'; + 70: Result := 'A6 (105mm x 148mm)'; + 75: Result := 'Letter Rotated (11" x 8.5")'; + 76: Result := 'A3 Rotated (420mm x 297mm)'; + 77: Result := 'A4 Rotated (297mm x 210mm)'; + 78: Result := 'A5 Rotated (210mm x 148mm)'; + 79: Result := 'B4 (JIS) Rotated (364mm x 257mm)'; + 80: Result := 'B5 (JIS) Rotated (257mm x 182mm)'; + 81: Result := 'Japanese Postcard Rot. (148mm x 100mm)'; + 82: Result := 'Dbl. Jap. Postcard Rot. (148mm x 200mm)'; + 83: Result := 'A6 Rotated (148mm x 105mm)'; + 88: Result := 'B6 (JIS) (128mm x 182mm)'; + 89: Result := 'B6 (JIS) Rotated (182mm x 128mm)'; + 90: Result := '2x11 (12" x 11")'; + else Result := '(unknown)'; + end; +end; + +function RecTypeName(ARecType: Word): String; +begin + case ARecType of + $0000: Result := 'DIMENSION'; + $0001: Result := 'BLANK'; + $0002: Result := 'INTEGER'; + $0003: Result := 'NUMBER'; + $0004: Result := 'LABEL'; + $0005: Result := 'BoolErr'; + $0006: Result := 'FORMULA'; + $0007: Result := 'STRING'; + $0008: Result := 'ROW'; + $0009: Result := 'BOF: Begin of file'; + $000A: Result := 'EOF: End of file'; + $000B: Result := 'INDEX'; + $000C: Result := 'CALCCOUNT: Iteration count'; + $000D: Result := 'CALCMODE: Calculation mode'; + $000E: Result := 'PRECISION: Precision'; + $000F: Result := 'REFMODE: Reference mode'; + $0010: Result := 'DELTA: Iteration increment'; + $0011: Result := 'ITERATION: Iteration mode'; + $0012: Result := 'PROTECT: Protection flag'; + $0013: Result := 'PASSWORD: Protection password'; + $0014: Result := 'HEADER: Print header on each page'; + $0015: Result := 'FOOTER: Print footer on each page'; + $0016: Result := 'EXTERNCOUNT: Number of external references'; + $0017: Result := 'EXTERNSHEET: External reference'; + $0018: Result := 'DEFINEDNAME: Name and token array of an internal defined name'; + $0019: Result := 'WINDOWPROTECT: Windows are protected'; + $001A: Result := 'VERTICALPAGEBREAKS: Explicit column page breaks'; + $001B: Result := 'HORIZONALPAGEBREAKS: Explicit row page breaks'; + $001C: Result := 'NOTE: Comment associated with a cell'; + $001D: Result := 'SELECTION: Current selection'; + $001E: Result := 'FORMAT: Number format record'; + $001F: Result := 'FORMATCOUNT: Count of number formats'; + $0020: Result := 'ColumnDefault'; + $0021: Result := 'Array'; + $0022: Result := '1904: 1904 date system'; + $0023: Result := 'ExternalName'; + $0024: Result := 'COLWIDTH'; + $0025: Result := 'DefaultRowHeight'; + $0026: Result := 'LEFTMARGIN: Left margin measurement'; + $0027: Result := 'RIGHTMARGIN: Right margin measurement'; + $0028: Result := 'TOPMARGIN: Top margin measurement'; + $0029: Result := 'BOTTOMMARGIN: Bottom margin measurement'; + $002A: Result := 'PRINTHEADERS: Print row/column labels'; + $002B: Result := 'PRINTGRIDLINES: Print gridlines flag'; + $002F: Result := 'FILEPASS: File is password-protected'; + $0031: Result := 'FONT: Font and font formatting information'; + $0032: Result := 'FONT2'; + $0033: Result := 'PRINTSIZE: Printed size of chart'; + $0036: Result := 'DataTable'; + $0037: Result := 'DateTable2'; + $003C: Result := 'CONTINUE: Continues long records'; + $003D: Result := 'WINDOW1: Window information'; + $003E: Result := 'WINDOW2: Window information'; + $0040: Result := 'BACKUP: Save backup version of the file'; + $0041: Result := 'PANE: Number of panes and their position'; + $0042: Result := 'CODEPAGE: Default code page'; // also: CODENAME: VBE object name ??? + $0043: Result := 'XF: Extended format'; + $0044: Result := 'IXFE'; + $0045: Result := 'FONTCOLOR'; + $004D: Result := 'PLS: Environment-specific print record'; + $0050: Result := 'DCON: Data consolidation information'; + $0051: Result := 'DCONREF: Data consolidation references'; + $0055: Result := 'DEFCOLWIDTH: Default width for columns'; + $0056: Result := 'BuiltInFmtCount'; + $0059: Result := 'XCT: CRN record count'; + $005A: Result := 'CRN: Non-resident operands'; + $005B: Result := 'FILESHARING: File-sharing information'; + $005C: Result := 'WRITEACCESS: Write access user name'; + $005D: Result := 'OBJ: Properties of an object in a sheet'; + $005E: Result := 'UNCALCED: Recalculation status'; + $005F: Result := 'SAVERECALC: Recalculate before saving'; + $0060: Result := 'TEMPLATE: Workbook is a template'; + $0063: Result := 'OBJPROTECT: Objects are protected'; + $007D: Result := 'COLINFO: Column formatting information'; + $007E: Result := 'RK: Cell value, RK number'; + $007F: Result := 'IMDATA: Image data'; + $0080: Result := 'GUTS: Size of row and column gutters'; + $0081: Result := 'SHEETPR: Additional workspace information'; + $0082: Result := 'GRIDSET: State change of Gridlines option'; + $0083: Result := 'HCENTER: Center between horizontal margins'; + $0084: Result := 'VCENTER: Center between vertical margins'; + $0085: Result := 'BOUNDSSHEET: Sheet information'; + $0086: Result := 'WRITEPROT: Workbook is write-protected'; + $0087: Result := 'ADDIN: Workbook is an add-in macro'; + $0088: Result := 'EDG: Edition globals'; + $0089: Result := 'PUB: Publisher'; + $008C: Result := 'COUNTRY: Default country and WIN.INI country'; + $008D: Result := 'HIDEOBJ: Object display options'; + $0090: Result := 'SORT: Sorting options'; + $0091: Result := 'SUB: Subscriber'; + $0092: Result := 'PALETTE: Color palette definition'; + $0094: Result := 'LHRECORD: .WK? file conversion information'; + $0095: Result := 'LHNGRAPH: Named graph information'; + $0096: Result := 'SOUND: Sound note'; + $0098: Result := 'LPR: Sheet was printed using LINE.PRINT()'; + $0099: Result := 'STANDARDWIDTH: Standard column width'; + $009A: Result := 'FNGROUPNAME: Function group name'; + $009B: Result := 'FILTERMODE: Sheet contains filtered list'; + $009C: Result := 'FNGROUPCOUNT: Built-in function group count'; + $009D: Result := 'AUTOFILTERINFO: Drop-down arrow count'; + $009E: Result := 'AUTOFILTER: AutoFilter data'; + $00A0: Result := 'SCL: Window zoom magnification'; + $00A1: Result := 'PAGESETUP: PageSetup'; + $00A9: Result := 'COORDLIST: Polygon object vertex coordinates'; + $00AB: Result := 'GCW: Global column width flags'; + $00AE: Result := 'SCENMAN: Scenario output data'; + $00AF: Result := 'SCENARIO: Scenario data'; + $00B0: Result := 'SXVIEW: View definition'; + $00B1: Result := 'SXVD: View fields'; + $00B2: Result := 'SXVI: View item'; + $00B4: Result := 'SXIVD: Row/column field IDs'; + $00B5: Result := 'SXLI: Line item array'; + $00B6: Result := 'SXPI: Page item'; + $00B8: Result := 'DOCROUTE: Routing slip information'; + $00B9: Result := 'RECIPNAME: Recipient name'; + $00BC: Result := 'SHRFMLA: Shared formula'; + $00BD: Result := 'MULRK: Multiple RK cells'; + $00BE: Result := 'MULBLANK: Multiple blank cells'; + $00C1: Result := 'MMS: ADDMENU/DELMENU record group count'; + $00C2: Result := 'ADDMENU: Menu addition'; + $00C3: Result := 'DELMENU: Menu deletion'; + $00C5: Result := 'SXDI: Data item'; + $00C6: Result := 'SXDB: Pivot table cache data'; + $00CD: Result := 'SXSTRING: String'; + $00D0: Result := 'SXTBL: Multiple consolidation source info'; + $00D1: Result := 'SXBRGIITM: Page item name count'; + $00D2: Result := 'SXTBPG: Page item indexex'; + $00D3: Result := 'OBPROJ: Visual Basic project'; + $00D5: Result := 'SXIDSTM: Stream ID'; + $00D6: Result := 'RSTRING: Cell with character formatting'; + $00D7: Result := 'DBCELL: Stream offsets'; + $00DA: Result := 'BOOKBOOL: Workbook option flag'; + $00DC: Result := 'PARAMQRY: Query parameters'; // also: SXEXT: External source information + $00DD: Result := 'SCENPROTECT: Scenario Protection'; + $00DE: Result := 'OLESIZE: Size of OLE object'; + $00DF: Result := 'UDDESC: Description format for chart autoformat'; + $00E0: Result := 'XF: Extended format'; + $00E1: Result := 'INTERFACEHDR: Beginning of user interface records'; + $00E2: Result := 'INTERFACEEND: End of user interface records'; + $00E3: Result := 'SXVS: View source'; + $00E5: Result := 'MERGEDCELLS: List of merged cells'; + $00E9: Result := 'Bitmap'; + $00EA: Result := 'TABIDCONF: Sheet Tab ID of conflict history'; + $00EB: Result := 'MSODRAWINGGROUP: Microsoft Office drawing group'; + $00EC: Result := 'MSODRAWING: Microsoft Office drawing'; + $00ED: Result := 'MSODRAWINGSELECTION: Microsoft Office drawing selection'; + $00EF: Result := 'PhoneticPR'; + $00F0: Result := 'SXRULE: PivotTable rule data'; + $00F1: Result := 'SXEX: PivotTable view extended information'; + $00F2: Result := 'SXFILT: PivotTable rule filter'; + $00F4: Result := 'SXDXF: PivotTable formatting'; + $00F5: Result := 'SXITM: PivotTable item indexes'; + $00F6: Result := 'SXNAME: PivotTable name'; + $00F7: Result := 'SXSELECT: PivotTable selection information'; + $00F8: Result := 'SXPAIR: PivotTable name pair'; + $00F9: Result := 'SXFMLA: PivotTable parsed expression'; + $00FB: Result := 'SXFORMAT: PivotTable format record'; + $00FC: Result := 'SST: Shared string table'; + $00FD: Result := 'LABELSST: Cell value, string constant/SST'; + $00FF: Result := 'EXTSST: extended shared string table'; + $013D: Result := 'TABID: Sheet tab index array'; + $015F: Result := 'LabelRanges'; + $0160: Result := 'USESELFS: Natural language formulas flag'; + $0161: Result := 'DSF: Double stream file'; + $0162: Result := 'XL5MODIFY: Flag for DSF'; + $01AE: Result := 'EXTERNBOOK (SUPBOOK): Supporting workbook'; + $01AF: Result := 'PROT4REV: Shared workbook protection flag'; + $01B0: Result := 'CONDFMT: Conditional formatting range information'; + $01B1: Result := 'CF: Conditional formatting conditions'; + $01B2: Result := 'DVAL: Data validation information'; + $01B5: Result := 'DCONBIN: Data consolidation information'; + $01B6: Result := 'TXO: Text object'; + $01B7: Result := 'REFRESHALL: Refresh flag'; + $01B8: Result := 'HLINK: Hyperlink'; + $01BA: Result := 'CODENAME: Name of the workbook object'; + $01BB: Result := 'SXFDBTYPE: SQL datatype identifier'; + $01BC: Result := 'PROT4REVPASS: Shared workbook protection password'; + $01BE: Result := 'DV: Data validation criteria'; + $01C0: Result := 'EXCEL9FILE: Excel 9 file'; + $01C1: Result := 'RECALCID: Recalc information'; + $0200: Result := 'DIMENSIONS: Cell table size'; + $0201: Result := 'BLANK: Cell Value, blank cell'; + $0203: Result := 'NUMBER: Cell value, floating-point cell'; + $0204: Result := 'LABEL: Cell value, string constant'; + $0205: Result := 'BOOLERR: Cell Value, boolean or error'; + $0206: Result := 'FORMULA: Formula'; + $0207: Result := 'STRING: String value of a formula'; + $0208: Result := 'ROW: Describes a row'; + $0209: Result := 'BOF: Begin of file'; + $020B: Result := 'INDEX: Index record'; + $0218: Result := 'NAME: Defined name'; + $0221: Result := 'ARRAY: Array-entered formula'; + $0223: Result := 'EXTERNNAME: Externally referenced name'; + $0225: Result := 'DEFAULTROWHEIGHT: Default row height'; + $0231: Result := 'FONT: Font description'; + $0236: Result := 'TABLE: Data table'; + $023E: Result := 'WINDOW2: Sheet window information'; + $0243: Result := 'XF: Extended format'; + $027E: Result := 'RK: Cell value, RK number'; + $0293: Result := 'STYLE: Style information'; + $0406: Result := 'FORMULA: Cell formula'; + $0409: Result := 'BOF: Begin of file'; + $041E: Result := 'FORMAT: Number format'; + $0443: Result := 'XF: Extended format'; + $04BC: Result := 'SHAREDFMLA: Shared formula'; + $0800: Result := 'HLINKTOOLTIP: Hyperlink tooltip'; + $0801: Result := 'WEBPUB: Web publish item'; + $0802: Result := 'QSISXTAG: PivotTable and query table extensions'; + $0803: Result := 'DBQUERYEXT: Database query extensions'; + $0804: Result := 'EXTSTRING: FRT string'; + $0805: Result := 'TXTQUERY: Text query information'; + $0806: Result := 'QSIR: Query table formatting'; + $0807: Result := 'QSIF: Query table field formatting'; + $0809: Result := 'BOF: Beginning of file'; + $080A: Result := 'OLEDBCONN: OLE database connection'; + $080B: Result := 'WOPT: Web options'; + $080C: Result := 'SXVIEWEX: Pivot table OLAP extensions'; + $080D: Result := 'SXTH: Pivot table OLAP hierarchy'; + $080E: Result := 'SXPIEX: OLAP page item extensions'; + $080F: Result := 'SXVDTEX: View dimension OLAP extensions'; + $0810: Result := 'SXVIEWX9: Pivot table extensions'; + $0812: Result := 'CONTINUEFRT: Continued FRT'; + $0813: Result := 'REALTIMEDATA: Real-time data (RTD)'; + $0850: Result := 'CHARTFRTINFO: Future record identifiers'; + $0852: Result := 'STARTBLOCK: Beginning of a collection of records'; + $0853: Result := 'ENDBLOCK: End of a collection of records'; + $0862: Result := 'SHEETEXT: Extra sheet info'; + $0863: Result := 'BOOKEXT: Extra book info'; + $0864: Result := 'SXADDL: Pivot table additional info'; + $0865: Result := 'CRASHRECERR: Crash recovery error'; + $0866: Result := 'HFPICTURE: Header/footer picture'; + $0867: Result := 'FEATHEADR: Shared feature header'; + $0868: Result := 'FEAT: Shared feature record'; + $086A: Result := 'DATALABEXT: Chart data label extension'; + $086B: Result := 'DATALABEXTCONTENTS: Chart data label extension contents'; + $086C: Result := 'CELLWATCH: Cell watch'; + $086D: Result := 'FEATINFO: Shared feature info record'; + $0871: Result := 'FEATHEADR11: Shared feature header 11'; + $0872: Result := 'FEAT11: Shared feature 11 record'; + $0873: Result := 'FEATINFO11: Shared feature info 11 record'; + $0874: Result := 'DROPDOWNOBJIDS: Drop down opbject'; + $0875: Result := 'CONTINUEFRT11: Continue FRT 11'; + $0876: Result := 'DCONN: Data connection'; + $0877: Result := 'LIST12: Extra table data introduced in Excel 2007'; + $0878: Result := 'FEAT12: Shared feature 12 record'; + $0879: Result := 'CONDFMT12: Conditional formatting range information 12'; + $087A: Result := 'CF12: Conditional formatting condition 12'; + $087B: Result := 'CFEX: Conditional formatting extension'; + $087C: Result := 'XFCRC: XF extension checksum'; + $087D: Result := 'XFEXT: XF extension'; + $087E: Result := 'EZFILTER12: Autofilter data introduced in Excel 2007'; + $087F: Result := 'CONTINUEFRT12: Continue FRT 12'; + $0881: Result := 'SXADDL12: Additional workbook connections information'; + $0884: Result := 'MDTINFO: Information about a metadata type'; + $0885: Result := 'MDXSTR: MDX metadata string'; + $0886: Result := 'MDXTUPLE: Tuple MDX metadata'; + $0887: Result := 'MDXSET: Set MDX metadata'; + $0888: Result := 'MDXPROP: Member property MDX metadata'; + $0889: Result := 'MDXKPI: Key performance indicator MDX metadata'; + $088A: Result := 'MDTB: Block of metadata records'; + $088B: Result := 'PLV: Page layout view settings in Excel 2007'; + $088C: Result := 'COMPAR12: Compatibility checker 12'; + $088D: Result := 'DXF: Differential XF'; + $088E: Result := 'TABLESTYLES: Table styles'; + $088F: Result := 'TABLESTYLE: Table style'; + $0890: Result := 'TABLESTYLEELEMENT: Table style element'; + $0892: Result := 'STYLEEXT: Named cell style extension'; + $0893: Result := 'NAMEPUBLISH: Publish to Excel server data for name'; + $0894: Result := 'NAMECMT: Name comment'; + $0895: Result := 'SORTDATA12: Sort data 12'; + $0896: Result := 'THEME: Theme'; + $0897: Result := 'GUIDTYPELIB: VB project typelib GUID'; + $0898: Result := 'FNGRP12: Function group'; + $0899: Result := 'NAMEFNGRP12: Extra function group'; + $089A: Result := 'MTRSETTINGS: Multi-threaded calculation settings'; + $089B: Result := 'COMPRESSPICTURES: Automatic picture compression mode'; + $089C: Result := 'HEADERFOOTER: Header footer'; + $089E: Result := 'CRTMLFRT: Additional properties for chart elements'; + $08A3: Result := 'FORCEFULLCALCULATION: Force full calculation settings'; + $08A4: Result := 'SHAPEPROPSSTREAM: Shape formatting properties for chart elements'; + $08A5: Result := 'TEXTPROPSSTREAM: Additional text properties for text in entire chart'; + $08A7: Result := 'CRTLAYOUT12A: Layout information for a plot area'; + $08C1: Result := 'LISTOBJ: List object'; + $08C2: Result := 'LISTFIELD: List field'; + $08C3: Result := 'LISTDV: List data validation'; + $08C4: Result := 'LISTCONDFMT: List conditional formatting'; + $08C5: Result := 'LIST CF: List cell formatting'; + $08C6: Result := 'FMQRY: Filemaker queries'; + $08C7: Result := 'FMSQRY: Filemaker queries'; + $08C8: Result := 'PLV: Page layout view in Mac Excel 11'; + $08C9: Result := 'LNEXT: Extenstion information for borders in Mac Office 11'; + $08CA: Result := 'MKREXT: Extension information for markers in Mac Office 11'; + $08CB: Result := 'CRTCOOPT: Color options for chart series in Mac Office 11'; + $1001: Result := 'UNITS: ignored'; + $1002: Result := 'CHART: Position and size of chart area'; + $1003: Result := 'SERIES: Properties of the data for a series, a trendline, or error bars'; + $1004: Result := 'CHSOURCELINK: Source of data series'; + $1006: Result := 'DATAFORMAT: Formatting properties for data point or series'; + $1007: Result := 'CHLINEFORMAT: Formatting attributes of line or border'; + $1009: Result := 'MARKERFORMAT: Color, size, and shape of data markers'; + $100A: Result := 'AREAFORMAT: Patterns and colors in filled chart region'; + $100B: Result := 'PIEFORMAT: Distance of a data point(s) from pie'; + $100C: Result := 'ATTACHEDLABEL: Properties of series data label'; + $100D: Result := 'CHSTRING: Category name of series, or text for text box in chart'; + $1014: Result := 'CHARTFORMAT: Properties of a chart group'; + $1015: Result := 'LEGEND: Properties of a legend'; + $1016: Result := 'SERIESLIST: Specifies the series for the chart'; + $1017: Result := 'BAR: identifies a bar/column chart group'; + $1018: Result := 'LINE: identifies a line chart group'; + $1019: Result := 'PIE: identifies a pie/doughnut chart group'; + $101A: Result := 'AREA: identifies an area chart group'; + $101B: Result := 'SCATTER: identifies scatter or bubble chart group'; + $101D: Result := 'AXIS: Properties of an axis'; + $101E: Result := 'TICK: Attributes of axis labels and ticks'; + $101F: Result := 'VALUERANGE: Properties of value axis'; + $1021: Result := 'CHAXISLINE: Part of the axis specified by the LINEFORMAT record'; + $1022: Result := 'CRTLINK: not used'; + $1024: Result := 'DEFAULTTEXT: Text elements formatted by TEXT record'; + $1025: Result := 'TEXT: Properties of an attached label'; + $1026: Result := 'FONTX: Font for a given text element'; + $1027: Result := 'OBJECTLINK: specifies object on chart, or entire chart, to which TEXT record is linked.'; + $1032: Result := 'CHFRAME: Border and area formatting of chart'; + $1033: Result := 'CHBEGIN: Indicates begin of a chart record block'; + $1034: Result := 'CHEND: Indicates end of a chart record block'; + $1035: Result := 'PLOTAREA: empty --> see FRAME record specifying plot area properties'; + $103A: Result := 'CHART3D: plot area of the chart group is rendered in a 3-D'; + $103C: Result := 'PICF: Layout of a picture attached to a picture-filled chart elemen'; + $103D: Result := 'DROPBAR: attributes of the up/down bars between multiple series of line chart group'; + $103E: Result := 'RADAR: identifies a radar chart group'; + $103F: Result := 'SURF: identifies a surface chart group'; + $1040: Result := 'RADARAREA: identifies a filled radar chart group'; + $1041: Result := 'AXISPARENT: Properties of an axis group'; + $1043: Result := 'LEGENDEXCEPTION: Information on legend item changed from default'; + $1044: Result := 'SHTPROPS: Chart properties defined by the Chart Sheet Substream ABNF'; + $1045: Result := 'SERTOCRT: Specifies chart group for the current series'; + $1046: Result := 'AXESUSED: Number of axis groups on the chart'; + $1048: Result := 'SBASEREF: Location of a PivotTable view referenced by a chart'; + $104A: Result := 'SERPARENT: Series to which the current trendline or error bar corresponds'; + $104B: Result := 'SERAUXTREND: Specifies a trendline'; + $104E: Result := 'IFMTRECORD: Number format to use for the text on an axis'; + $104F: Result := 'POS: Size/position for legend, attached label, or plot area'; + $1050: Result := 'ALRUNS: Rich Text Formatting within chart titles, trendline, and data labels'; + $1051: Result := 'BRAI: Reference to data used in chart'; + $105B: Result := 'SERAUXERRBAR: Error bar properties'; + $105C: Result := 'CLRTCLIENT: Custom color palette for chart'; + $105D: Result := 'SERFMT: Properties of series data points, markers, or lines'; + $105F: Result := 'CHART3DBARSHAPE: Shape of the data points in bar or column chart group'; + $1060: Result := 'FBI: Scalable font information (chart)'; + $1061: Result := 'BOPPOP: Chart group is a bar or a pie of pie chart'; + $1062: Result := 'AXCEXT: Additional extension properties of a date axis'; + $1063: Result := 'DAT: Chart Sheet Substream ABNF for data table within chart area'; + $1064: Result := 'PLOTGROWTH: Scale factors for font scaling'; + $1065: Result := 'SIINDEX: Specifies data of a chart'; + $1066: Result := 'GELFRAME: Properties of a fill pattern for parts of a chart'; + $1067: Result := 'BOPPOPCUSTOM: Series data points contained in the secondary bar/pie'; + $1068: Result := 'FBI2: Scalable font information (chart)'; + else + Result := '<unknown>'; + end; +end; + +function SheetFuncName(AIndex: Word): String; +begin + case AIndex of + 0 : result := 'COUNT'; + 1 : Result := 'IF'; + 2 : Result := 'ISNA'; + 3 : Result := 'ISERROR'; + 4 : Result := 'SUM'; + 5 : Result := 'AVERAGE'; + 6 : Result := 'MIN'; + 7 : Result := 'MAX'; + 8 : Result := 'ROW'; + 9 : Result := 'COLUMN'; + 10 : Result := 'NA'; + 11 : Result := 'NPV'; + 12 : Result := 'STDEV'; + 13 : Result := 'DOLLAR'; + 14 : Result := 'FIXED'; + 15 : Result := 'SIN'; + 16 : Result := 'COS'; + 17 : Result := 'TAN'; + 18 : Result := 'ATAN'; + 19 : Result := 'PI'; + 20 : Result := 'SQRT'; + 21 : Result := 'EXP'; + 22 : Result := 'LN'; + 23 : Result := 'LOG10'; + 24 : Result := 'ABS'; + 25 : Result := 'INT'; + 26 : Result := 'SIGN'; + 27 : Result := 'ROUND'; + 28 : Result := 'LOOKUP'; + 29 : Result := 'INDEX'; + 30 : Result := 'REPT'; + 31 : Result := 'MID'; + 32 : Result := 'LEN'; + 33 : Result := 'VALUE'; + 34 : Result := 'TRUE'; + 35 : Result := 'FALSE'; + 36 : Result := 'AND'; + 37 : Result := 'OR'; + 38 : Result := 'NOT'; + 39 : Result := 'MOD'; + 40 : Result := 'DCOUNT'; + 41 : Result := 'DSUM'; + 42 : Result := 'DAVERAGE'; + 43 : Result := 'DMIN'; + 44 : Result := 'DMAX'; + 45 : Result := 'DSTDEV'; + 46 : Result := 'VAR'; + 47 : Result := 'DVAR'; + 48 : Result := 'TEXT'; + 49 : Result := 'LINEST'; + 50 : Result := 'TREND'; + 51 : Result := 'LOGEST'; + 52 : Result := 'GROWTH'; + 56 : Result := 'PV'; + 57 : Result := 'FV'; + 58 : Result := 'NPER'; + 59 : Result := 'PMT'; + 60 : Result := 'RATE'; + 61 : Result := 'MIRR'; + 62 : Result := 'IRR'; + 63 : Result := 'RAND'; + 64 : Result := 'MATCH'; + 65 : Result := 'DATE'; + 66 : Result := 'TIME'; + 67 : Result := 'DAY'; + 68 : Result := 'MONTH'; + 69 : Result := 'YEAR'; + 70 : Result := 'WEEKDAY'; + 71 : Result := 'HOUR'; + 72 : Result := 'MINUTE'; + 73 : Result := 'SECOND'; + 74 : Result := 'NOW'; + 75 : Result := 'AREAS'; + 76 : Result := 'ROWS'; + 77 : Result := 'COLUMNS'; + 78 : Result := 'OFFSET'; + 82 : Result := 'SEARCH'; + 83 : Result := 'TRANSPOSE'; + 86 : Result := 'TYPE'; + 97 : Result := 'ATAN2'; + 98 : Result := 'ASIN'; + 99 : Result := 'ACOS'; + 100 : Result := 'CHOOSE'; + 101 : Result := 'HLOOKUP'; + 102 : Result := 'VLOOKUP'; + 105 : Result := 'ISREF'; + 109 : Result := 'LOG'; + 111 : Result := 'CHAR'; + 112 : Result := 'LOWER'; + 113 : Result := 'UPPER'; + 114 : Result := 'PROPER'; + 115 : Result := 'LEFT'; + 116 : Result := 'RIGHT'; + 117 : Result := 'EXACT'; + 118 : Result := 'TRIM'; + 119 : Result := 'REPLACE'; + 120 : Result := 'SUBSTITUTE'; + 121 : Result := 'CODE'; + 124 : Result := 'FIND'; + 125 : Result := 'CELL'; + 126 : Result := 'ISERR'; + 127 : Result := 'ISTEXT'; + 128 : Result := 'ISNUMBER'; + 129 : Result := 'ISBLANK'; + 130 : Result := 'T'; + 131 : Result := 'N'; + 140 : Result := 'DATEVALUE'; + 141 : Result := 'TIMEVALUE'; + 142 : Result := 'SLN'; + 143 : Result := 'SYD'; + 144 : Result := 'DDB'; + 148 : Result := 'INDIRECT'; + 162 : Result := 'CLEAN'; + 163 : Result := 'MDETERM'; + 164 : Result := 'MINVERSE'; + 165 : Result := 'MMULT'; + 167 : Result := 'IPMT'; + 168 : Result := 'PPMT'; + 169 : Result := 'COUNTA'; + 183 : Result := 'PRODUCT'; + 184 : Result := 'FACT'; + 189 : Result := 'DPRODUCT'; + 190 : Result := 'ISNONTEXT'; + 193 : Result := 'STDEVP'; + 194 : Result := 'VARP'; + 195 : Result := 'DSTDEVP'; + 196 : Result := 'DVARP'; + 197 : Result := 'TRUNC'; + 198 : Result := 'ISLOGICAL'; + 199 : Result := 'DCOUNTA'; + 204 : Result := 'YEN/USDOLLAR'; + 205 : Result := 'FINDB'; + 206 : Result := 'SEARCHB'; + 207 : Result := 'REPLACEB'; + 208 : Result := 'LEFTB'; + 209 : Result := 'RIGHTB'; + 210 : Result := 'MIDB'; + 211 : Result := 'LENB'; + 212 : Result := 'ROUNDUP'; + 213 : Result := 'ROUNDDOWN'; + 214 : Result := 'ASC'; + 215 : Result := 'JIS / DBCS'; + 216 : Result := 'RANK'; + 219 : Result := 'ADDRESS'; + 220 : Result := 'DAYS360'; + 221 : Result := 'TODAY'; + 222 : Result := 'VDB'; + 227 : Result := 'MEDIAN'; + 228 : Result := 'SUMPRODUCT'; + 229 : Result := 'SINH'; + 230 : Result := 'COSH'; + 231 : Result := 'TANH'; + 232 : Result := 'ASINH'; + 233 : Result := 'ACOSH'; + 234 : Result := 'ATANH'; + 235 : Result := 'DGET'; + 244 : Result := 'INFO'; + 247 : Result := 'DB'; + 252 : Result := 'FREQUENCY'; + 261 : Result := 'ERROR.TYPE'; + 269 : Result := 'AVEDEV'; + 270 : Result := 'BETADIST'; + 271 : Result := 'GAMMALN'; + 272 : Result := 'BETAINV'; + 273 : Result := 'BINOMDIST'; + 274 : Result := 'CHIDIST'; + 275 : Result := 'CHIINV'; + 276 : Result := 'COMBIN'; + 277 : Result := 'CONFIDENCE'; + 278 : Result := 'CRITBINOM'; + 279 : Result := 'EVEN'; + 280 : Result := 'EXPONDIST'; + 281 : Result := 'FDIST'; + 282 : Result := 'FINV'; + 283 : Result := 'FISHER'; + 284 : Result := 'FISHERINV'; + 285 : Result := 'FLOOR'; + 286 : Result := 'GAMMADIST'; + 287 : Result := 'GAMMAINV'; + 288 : Result := 'CEILING'; + 289 : Result := 'HYPGEOMDIST'; + 290 : Result := 'LOGNORMDIST'; + 291 : Result := 'LOGINV'; + 292 : Result := 'NEGBINOMDIST'; + 293 : Result := 'NORMDIST'; + 294 : Result := 'NORMSDIST'; + 295 : Result := 'NORMINV'; + 296 : Result := 'NORMSINV'; + 297 : Result := 'STANDARDIZE'; + 298 : Result := 'ODD'; + 299 : Result := 'PERMUT'; + 300 : Result := 'POISSON'; + 301 : Result := 'TDIST'; + 302 : Result := 'WEIBULL'; + 303 : Result := 'SUMXMY2'; + 304 : Result := 'SUMX2MY2'; + 305 : Result := 'SUMX2PY2'; + 306 : Result := 'CHITEST'; + 307 : Result := 'CORREL'; + 308 : Result := 'COVAR'; + 309 : Result := 'FORECAST'; + 310 : Result := 'FTEST'; + 311 : Result := 'INTERCEPT'; + 312 : Result := 'PEARSON'; + 313 : Result := 'RSQ'; + 314 : Result := 'STEYX'; + 315 : Result := 'SLOPE'; + 316 : Result := 'TTEST'; + 317 : Result := 'PROB'; + 318 : Result := 'DEVSQ'; + 319 : Result := 'GEOMEAN'; + 320 : Result := 'HARMEAN'; + 321 : Result := 'SUMSQ'; + 322 : Result := 'KURT'; + 323 : Result := 'SKEW'; + 324 : Result := 'ZTEST'; + 325 : Result := 'LARGE'; + 326 : Result := 'SMALL'; + 327 : Result := 'QUARTILE'; + 328 : Result := 'PERCENTILE'; + 329 : Result := 'PERCENTRANK'; + 330 : Result := 'MODE'; + 331 : Result := 'TRIMMEAN'; + 332 : Result := 'TINV'; + 336 : Result := 'CONCATENATE'; + 337 : Result := 'POWER'; + 342 : Result := 'RADIANS'; + 343 : Result := 'DEGREES'; + 344 : Result := 'SUBTOTAL'; + 345 : Result := 'SUMIF'; + 346 : Result := 'COUNTIF'; + 347 : Result := 'COUNTBLANK'; + 350 : Result := 'ISPMT'; + 351 : Result := 'DATEDIF'; + 352 : Result := 'DATESTRING'; + 353 : Result := 'NUMBERSTRING'; + 354 : Result := 'ROMAN'; + 358 : Result := 'GETPIVOTDATA'; + 359 : Result := 'HYPERLINK'; + 360 : Result := 'PHONETIC'; + 361 : Result := 'AVERAGEA'; + 362 : Result := 'MAXA'; + 363 : Result := 'MINA'; + 364 : Result := 'STDEVPA'; + 365 : Result := 'VARPA'; + 366 : Result := 'STDEVA'; + 367 : Result := 'VARA'; + else Result := 'unknown'; + end; +end; + +end. + diff --git a/applications/biffexplorer/behtml.pas b/applications/biffexplorer/behtml.pas new file mode 100644 index 000000000..71f255619 --- /dev/null +++ b/applications/biffexplorer/behtml.pas @@ -0,0 +1,245 @@ +unit beHTML; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Graphics; + +type + THTMLHeader = (h1, h2, h3, h4, h5); + THeaderColors = Array[THTMLHeader] of TColor; + + THTMLDocument = class + private + FLines: TStrings; + FRawMode: Boolean; + FIndent: Integer; + function Indent: String; + function Raw(const AText: String): String; + function ColorToHTML(AColor: TColor): String; + public + constructor Create; + destructor Destroy; override; + procedure AddEmptyLine; + procedure AddListItem(const AText: String); + procedure AddHeader(AHeader: THTMLHeader; const AText: String); + procedure AddParagraph(const AText: String); + procedure BeginDocument(const ATitle: String; const AHeaderColors: THeaderColors; + ARawMode: Boolean=false); + procedure BeginBulletList; + procedure BeginNumberedList; + function Bold(const AText: String): String; + procedure EndDocument; + procedure EndBulletList; + procedure EndNumberedList; + function Hyperlink(const AText, ALink: String): String; + function Italic(const AText: String): String; + property Lines: TStrings read FLines; + end; + +implementation + +uses + StrUtils, LCLIntf; + +constructor THTMLDocument.Create; +begin + inherited; + FLines := TStringList.Create; +end; + +destructor THTMLDocument.Destroy; +begin + FLines.Free; + inherited; +end; + +procedure THTMLDocument.AddHeader(AHeader: THTMLHeader; const AText: String); +begin + if FRawMode then + FLines.Add(Raw(AText)) + else + FLines.Add(Format('%s<h%d>%s</h%d>', [Indent, ord(AHeader)+1, AText, ord(AHeader)+1])); +end; + + (* + +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<!-- HTML Codes by Quackit.com --> +<title> +Title appears in the browser's title bar... + + + + + +

Heading goes here...

+

Enter your paragraph text here...

+ + + *) + +procedure THTMLDocument.BeginDocument(const ATitle: String; + const AHeaderColors: THeaderColors; ARawMode: Boolean = false); +begin + FRawMode := ARawMode; + FLines.Clear; + if not FRawMode then begin + FLines.Add(''); + FLines.Add(''); + FLines.Add(' '); + FLines.Add(' '); + FLines.Add(' ' + ATitle + ''); + FLines.Add(' '); + FLines.Add(' '); + FLines.Add(' '); + FIndent := 4; + end; +end; + +procedure THTMLDocument.BeginBulletList; +begin + if not FRawMode then begin + FLines.Add(Indent + '
    '); + inc(FIndent, 2); + end; +end; + +procedure THTMLDocument.BeginNumberedList; +begin + if not FRawMode then begin + FLines.Add(Indent + '
      '); + inc(FIndent, 2); + end; +end; + +procedure THTMLDocument.AddEmptyLine; +begin + if FRawMode then + FLines.Add('') + else + FLines.Add('
      '); +end; + +procedure THTMLDocument.AddListItem(const AText: String); +begin + if FRawMode then + FLines.Add('- ' + Raw(AText)) + else + FLines.Add(Indent + '
    1. ' + AText + '
    2. '); +end; + +procedure THTMLDocument.AddParagraph(const AText: String); +begin + if FRawMode then + FLines.Add(Raw(AText)) + else + FLines.Add(Indent + '

      ' + AText + '

      '); +end; + +function THTMLDocument.Bold(const AText: String): String; +begin + if FRawMode then + Result := AText + else + Result := '' + AText + ''; +end; + +function THTMLDocument.ColorToHTML(AColor: TColor): String; +var + tmpRGB: LongInt; +begin + tmpRGB := ColorToRGB(AColor) ; + Result := Format('#%.2x%.2x%.2x', [ + GetRValue(tmpRGB), + GetGValue(tmpRGB), + GetBValue(tmpRGB) + ]) ; +end; + +procedure THTMLDocument.EndDocument; +begin + if not FRawMode then begin + FLines.Add(' '); + FLines.Add(''); + end; +end; + +procedure THTMLDocument.EndBulletList; +begin + if not FRawMode then begin + dec(FIndent, 2); + FLines.Add(Indent + '
'); + end; +end; + +procedure THTMLDocument.EndNumberedList; +begin + if not FRawMode then begin + dec(FIndent, 2); + FLines.Add(Indent + ''); + end; +end; + +function THTMLDocument.Hyperlink(const AText, ALink: String): String; +begin + if FRawMode then + Result := Format('%s (%s)', [AText, ALink]) + else + Result := Format('%s', [ALink, AText]); +end; + +function THTMLDocument.Indent: String; +begin + Result := DupeString(' ', FIndent); +end; + +function THTMLDocument.Italic(const AText: String): String; +begin + if FRawMode then + Result := AText + else + Result := '' + AText + ''; +end; + +function THTMLDocument.Raw(const AText: String): String; +var + i, n: Integer; +begin + Result := ''; + if AText = '' then + exit; + n := Length(AText); + i := 1; + while (i <= n) do begin + if AText[i] = '<' then + repeat + inc(i); + until (i = n) or (AText[i] = '>') + else + Result := Result + AText[i]; + inc(i); + end; +end; + +end. + diff --git a/applications/biffexplorer/bemain.lfm b/applications/biffexplorer/bemain.lfm new file mode 100644 index 000000000..0b4b9dd21 --- /dev/null +++ b/applications/biffexplorer/bemain.lfm @@ -0,0 +1,1061 @@ +object MainForm: TMainForm + Left = 352 + Height = 576 + Top = 177 + Width = 1089 + AllowDropFiles = True + Caption = 'BIFF Explorer' + ClientHeight = 556 + ClientWidth = 1089 + Menu = MainMenu + OnCloseQuery = FormCloseQuery + OnCreate = FormCreate + OnDestroy = FormDestroy + OnDropFiles = FormDropFiles + OnShow = FormShow + ShowHint = True + LCLVersion = '1.7' + object Splitter1: TSplitter + Left = 419 + Height = 508 + Top = 25 + Width = 5 + end + object ToolBar: TToolBar + Left = 4 + Height = 25 + Top = 0 + Width = 1085 + AutoSize = True + BorderSpacing.Left = 4 + ButtonHeight = 25 + Caption = 'ToolBar' + EdgeBorders = [] + Images = ImageList + TabOrder = 1 + object ToolButton1: TToolButton + Left = 1 + Top = 0 + Action = AcFileOpen + DropdownMenu = RecentFilesPopupMenu + Style = tbsDropDown + end + object ToolButton2: TToolButton + Left = 64 + Top = 0 + Action = AcFileQuit + end + object ToolButton3: TToolButton + Left = 36 + Top = 0 + Action = AcFind + end + object ToolButton4: TToolButton + Left = 59 + Height = 25 + Top = 0 + Width = 5 + Caption = 'ToolButton4' + Style = tbsDivider + end + end + object DetailPanel: TPanel + Left = 424 + Height = 508 + Top = 25 + Width = 665 + Align = alClient + BevelOuter = bvNone + ClientHeight = 508 + ClientWidth = 665 + TabOrder = 3 + object PageControl: TPageControl + Left = 0 + Height = 508 + Top = 0 + Width = 665 + ActivePage = PgValues + Align = alClient + TabIndex = 1 + TabOrder = 0 + OnChange = PageControlChange + object PgAnalysis: TTabSheet + Caption = 'Analysis' + ClientHeight = 480 + ClientWidth = 657 + object AnalysisDetails: TMemo + Left = 0 + Height = 191 + Top = 289 + Width = 657 + Align = alBottom + Font.CharSet = ANSI_CHARSET + Font.Height = -12 + Font.Name = 'Courier New' + Font.Pitch = fpFixed + Font.Quality = fqDraft + ParentFont = False + ScrollBars = ssAutoBoth + TabOrder = 0 + WordWrap = False + end + object DetailsSplitter: TSplitter + Cursor = crVSplit + Left = 0 + Height = 5 + Top = 284 + Width = 657 + Align = alBottom + ResizeAnchor = akBottom + end + end + object PgValues: TTabSheet + Caption = 'Values' + ClientHeight = 480 + ClientWidth = 657 + object ValueGrid: TStringGrid + Left = 0 + Height = 158 + Top = 322 + Width = 657 + Align = alBottom + ColCount = 3 + DefaultColWidth = 100 + FixedCols = 0 + MouseWheelOption = mwGrid + Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goDrawFocusSelected, goColSizing, goThumbTracking, goSmoothScroll] + RowCount = 9 + TabOrder = 3 + TitleStyle = tsNative + OnClick = ValueGridClick + OnPrepareCanvas = ValueGridPrepareCanvas + ColWidths = ( + 112 + 142 + 141 + ) + Cells = ( + 3 + 0 + 0 + 'Data type' + 1 + 0 + 'Value' + 2 + 0 + 'Offset range' + ) + end + object HexSplitter: TSplitter + Cursor = crVSplit + Left = 0 + Height = 5 + Top = 317 + Width = 657 + Align = alBottom + ResizeAnchor = akBottom + end + object HexEditor: TKHexEditor + Left = 0 + Height = 286 + Top = 31 + Width = 657 + AddressPrefix = '$' + Align = alClient + Colors.HorzLines = clBlack + Colors.InactiveCaretBkGnd = clGrayText + Colors.InactiveCaretSelBkGnd = clGrayText + Colors.InactiveCaretSelText = clHighlightText + Colors.InactiveCaretText = clHighlightText + Colors.Separators = clBtnFace + Colors.VertLines = clScrollBar + DigitGrouping = 1 + DisabledDrawStyle = eddNormal + Font.Height = -13 + Font.Name = 'Courier New' + Font.Pitch = fpFixed + Font.Style = [fsBold] + LineHeightPercent = 120 + ReadOnly = True + TabOrder = 1 + OnClick = HexEditorClick + OnKeyDown = HexEditorKeyDown + end + object HexEditorParamsPanel: TPanel + Left = 0 + Height = 31 + Top = 0 + Width = 657 + Align = alTop + BevelOuter = bvNone + ClientHeight = 31 + ClientWidth = 657 + TabOrder = 0 + object CbHexAddress: TCheckBox + Left = 3 + Height = 19 + Top = 5 + Width = 131 + Caption = 'Hexadecimal address' + Checked = True + OnChange = CbHexAddressChange + State = cbChecked + TabOrder = 0 + end + object CbHexEditorLineSize: TComboBox + Left = 152 + Height = 23 + Top = 3 + Width = 121 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'normal lines' + 'long lines' + ) + OnChange = CbHexEditorLineSizeChange + Style = csDropDownList + TabOrder = 1 + Text = 'normal lines' + end + object CbHexSingleBytes: TCheckBox + Left = 296 + Height = 19 + Top = 5 + Width = 83 + Caption = 'Single bytes' + Checked = True + OnChange = CbHexSingleBytesChange + State = cbChecked + TabOrder = 2 + end + end + end + end + end + object TreePanel: TPanel + Left = 0 + Height = 508 + Top = 25 + Width = 419 + Align = alLeft + BevelOuter = bvNone + ClientHeight = 508 + ClientWidth = 419 + Constraints.MinWidth = 275 + TabOrder = 2 + object FindPanel: TPanel + Left = 0 + Height = 30 + Top = 478 + Width = 419 + Align = alBottom + BevelOuter = bvNone + ClientHeight = 30 + ClientWidth = 419 + TabOrder = 1 + Visible = False + object BtnFindNext: TSpeedButton + Left = 240 + Height = 22 + Top = 3 + Width = 23 + Action = AcFindNext + Flat = True + end + object BtnFindPrev: TSpeedButton + Left = 216 + Height = 22 + Top = 3 + Width = 23 + Action = AcFindPrev + Flat = True + end + object SpeedButton3: TSpeedButton + Left = 3 + Height = 22 + Top = 3 + Width = 23 + Action = AcFindClose + Flat = True + end + object CbFind: TComboBox + Left = 28 + Height = 23 + Top = 3 + Width = 183 + ItemHeight = 15 + OnChange = CbFindChange + OnKeyPress = CbFindKeyPress + TabOrder = 0 + end + end + object BIFFTree: TVirtualStringTree + Left = 0 + Height = 478 + Top = 0 + Width = 419 + Align = alClient + ButtonStyle = bsTriangle + DefaultText = 'Node' + Header.AutoSizeIndex = 4 + Header.Columns = < + item + Alignment = taRightJustify + CaptionAlignment = taRightJustify + MinWidth = 80 + Options = [coAllowClick, coDraggable, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coVisible, coAllowFocus, coUseCaptionAlignment] + Position = 0 + Text = 'Offset' + Width = 80 + end + item + Alignment = taCenter + MinWidth = 60 + Position = 1 + Text = 'Record ID' + Width = 80 + end + item + MinWidth = 80 + Position = 2 + Text = 'Record name' + Width = 130 + end + item + Alignment = taCenter + MinWidth = 50 + Position = 3 + Text = 'Index' + end + item + MinWidth = 80 + Position = 4 + Text = 'Record description' + Width = 80 + end> + Header.DefaultHeight = 24 + Header.Font.Style = [fsBold] + Header.Height = 24 + Header.Options = [hoAutoResize, hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible] + HintMode = hmTooltip + PopupMenu = TreePopupMenu + TabOrder = 0 + TreeOptions.AutoOptions = [toAutoDropExpand, toAutoScrollOnExpand, toAutoSpanColumns, toAutoTristateTracking, toAutoDeleteMovedNodes] + TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowRoot, toThemeAware, toUseBlendedImages] + TreeOptions.SelectionOptions = [toFullRowSelect] + OnBeforeCellPaint = BIFFTreeBeforeCellPaint + OnFocusChanged = BIFFTreeFocusChanged + OnFreeNode = BIFFTreeFreeNode + OnGetText = BIFFTreeGetText + OnPaintText = BIFFTreePaintText + OnGetNodeDataSize = BIFFTreeGetNodeDataSize + end + end + object StatusBar: TStatusBar + Left = 0 + Height = 23 + Top = 533 + Width = 1089 + Panels = < + item + Width = 120 + end + item + Width = 180 + end + item + Width = 150 + end + item + Width = 180 + end> + SimplePanel = False + end + object ImageList: TImageList + left = 80 + top = 64 + Bitmap = { + 4C69090000001000000010000000FFFFFF00FFFFFF000088CC610088CC810088 + CC810088CC810088CC810088CC810088CC810088CC810088CC810088CC810088 + CC810088CC610087CB00FFFFFF00FFFFFF00FFFFFF000087CA8388DCF4FF60C0 + E9FF5FBFEAFF80D3F4FF9CE3FDFFA2E6FFFFA2E6FFFFA2E6FFFFA2E6FFFFA6EA + FFFF0087CA830087CA00FFFFFF00FFFFFF00FFFFFF000085C885ACF1FFFFABEF + FEFF95E2F8FF6EC9EDFF48A8D9FF98DCFEFF98DCFEFF98DCFEFF98DCFEFFA1E5 + FFFF0085C8850085C800FFFFFF00FFFFFF00FFFFFF000084C587A9EEFDFFA4E9 + FCFFA4E9FCFFAAEEFDFF42A1D1FF97DBFDFF97DBFDFF97DBFDFF97DBFDFF9FE3 + FEFF0084C5870084C500FFFFFF00FFFFFF00FFFFFF000082C28AA6EBFCFFA1E6 + FBFFA1E6FBFFA6EBFCFF3C9DCFFF96DAFCFF96DAFCFF96DAFCFF96DAFCFF9EE2 + FDFF0082C28A0082C200FFFFFF00FFFFFF00FFFFFF00007FBF8DA3E8FBFF9DE3 + F9FF9DE3F9FFA3E8FBFF3594C5FF94D8FAFF94D8FAFF94D8FAFF94D8FAFF9BDF + FCFF007FBF8D007FBE00FFFFFF00FFFFFF00FFFFFF00007DBB909EE5F9FF98DF + F6FF98DFF6FF9EE5F9FF3290C0FF92D6F8FF92D6F8FF92D6F8FF92D6F8FF99DD + FAFF007DBB90007DBB33FFFFFF00FFFFFF00FFFFFF00007BB8949BE1F7FF94DB + F4FF94DBF4FF9BE1F7FF308DBCFF90D4F6FF90D4F6FF90D4F6FF90D4F6FF97DB + F9FFFEFEFDFF007BB894FFFFFF00FFFFFF00FFFFFF000078B49797DEF6FF90D8 + F2FF90D8F2FF97DEF6FF2D89B7FF8FD3F5FF8FD3F5FF8FD3F5FF8FD3F5FF95D9 + F8FFF5F5EEFF0078B497FFFFFF00FFFFFF00FFFFFF000076B09B92DAF4FF8BD4 + F0FF8BD4F0FF92DAF4FF2B85B3FF8DD1F3FF8DD1F3FF8DD1F3FF8DD1F3FF93D7 + F6FFEBEBDDFF0076B09BFFFFFF00FFFFFF00FFFFFF000073AC9E8ED6F2FF87D0 + EDFF87D0EDFF8ED6F2FF2882AFFF8BCFF1FF8BCFF1FF8BCFF1FF8BCFF1FF91D5 + F5FFFEC941FF0073AC9EFFFFFF00FFFFFF00FFFFFF00006FA7A48AD3F0FF82CD + EBFF82CDEBFF8AD3F0FF267EABFF8ACEF0FF8ACEF0FF8ACEF0FF8ACEF0FF8FD3 + F4FFF4B62EFF006FA7A4FFFFFF00FFFFFF00FFFFFF00006699B287D1EFFF7FCA + E9FF7FCAE9FF87D0EFFF267DA9FF8DD1F3FF8DD1F3FF8DD1F3FF8DD1F3FF90D4 + F5FF006699B200679B3EFFFFFF00FFFFFF00FFFFFF00005E8D8E3591BDF169B8 + DDFA81CBECFF84CEEEFF005C8BEF005D8CBE005D8CBE005D8CBE005D8CBE005D + 8CBE005E8D8E00669900FFFFFF00FFFFFF00FFFFFF00005D8C00006599500064 + 97991C7AA9C052A5CDE0005B89C1005B8900005B8900005B8900005B8900005B + 8900005D8C0000669900FFFFFF00FFFFFF00FFFFFF00005D8C00006497000062 + 9300005E8E30005C8C7C0059879200598700005B8900005B8900005B8900005B + 8900005D8C0000669900FFFFFF00003F9300003F9300003F9300003F9424003F + 948A003E93CC004095CC004095CC004095CC004095CC004095CC004095CC0040 + 95CC004095CC00409599FFFFFF00003F9300003F9324003F938A0E4B9CD33F76 + C0EC5D90D4FF3365A9FFA0A0A0FFA9A9A9FFA9A9A9FFAAAAAAFFACACACFFAEAE + AEFFB0B0B0FF003E93CCFFFFFF00003F938A0E4A9CD33E75BFEC5487CBFF3669 + ADFF23569AFF3363A6FFA9A9A9FFBCBCBCFFBDBDBDFFBFBFBFFFC1C1C1FFC4C4 + C4FFC7C7C7FF003C90CCFFFFFF00003D91CC5D90D4FF3568ACFF285B9FFF1A4D + 91FF4477BBFF3361A4FFA9A9A9FFBDBDBDFFBFBFBFFFC1C1C1FFC4C4C4FFC7C7 + C7FFC9C9C9FF003A8DCCFFFFFF00003B8ECC588BCFFF1A4D91FF4376BAFF3265 + A9FF4376BAFF335FA1FFAAAAAAFFBFBFBFFFC1C1C1FFC4C4C4FFC7C7C7FFC9C9 + C9FFCCCCCCFF003789CCFFFFFF0000398BCC5588CCFF275A9EFF4174B8FF3164 + A8FF4174B8FF335D9EFFACACACFFC1C1C1FFC4C4C4FFC7C7C7FFC9C9C9FFCCCC + CCFFD0D0D0FF003485CCFFFFFF00003688CC5386CAFF295CA0FF3F72B6FF3063 + A7FF3F72B6FF335A9AFFAEAEAEFFC4C4C4FFC7C7C7FFC9C9C9FFCCCCCCFFD0D0 + D0FFD3D3D3FF003181CCFFFFFF00003485CC5083C7FF1D5094FF3265A9FF2D60 + A4FF3D70B4FF335897FFB0B0B0FFC7C7C7FFC9C9C9FFCCCCCCFFD0D0D0FFD3D3 + D3FFD5D5D5FF002E7DCCFFFFFF00003181CC4C7FC3FFBBBBBBFF22518CFF2C5F + A3FF3B6EB2FF335593FFB3B3B3FFC9C9C9FFCCCCCCFFD0D0D0FFD3D3D3FFD5D5 + D5FFD8D8D8FF002B78CCFFFFFF00002E7DCC497CC0FF09336FFF215090FF2B5E + A2FF396CB0FF335290FFB5B5B5FFCCCCCCFFD0D0D0FFD3D3D3FFD5D5D5FFD8D8 + D8FFDBDBDBFF002774CCFFFFFF00002B79CC477ABEFF2C5FA3FF376AAEFF2B5E + A2FF376AAEFF33508CFFB8B8B8FFD0D0D0FFD3D3D3FFD5D5D5FFD8D8D8FFDBDB + DBFFDCDCDCFF00246FCCFFFFFF00002875CC4477BBFF2C5FA3FF3568ACFF2B5E + A2FF2E61A5FF334B87FFBBBBBBFFD3D3D3FFD5D5D5FFD8D8D8FFDBDBDBFFDCDC + DCFFDDDDDDFF001F68CCFFFFFF00002571CC4174B8FF2C5FA3FF2D60A4FF2356 + 9AFF3D70B4FF334179FFBEBEBEFFD5D5D5FFD8D8D8FFDBDBDBFFDCDCDCFFDDDD + DDFFDDDDDDFF001258CCFFFFFF0000226DCC3F72B6FF225599FF3B6EB2FF2C51 + 96EC0A1C60D3000749A4000648CC000648CC000648CC000648CC000648CC0006 + 48CC000648CC00074A99FFFFFF00001B64CC4174B8FF2A5094EC0A1A5ED30008 + 4A8A000648240006470000044500000445000004450000044500000445000004 + 45000004450000064800FFFFFF0000105500000B4FCC0007498A000546240005 + 4700000648000006470000044500000445000004450000044500000445000004 + 45000004450000064800FFFFFF004E4E4E005151510054545411555555405555 + 555A555555655555555A5555554054545411515151004E4E4E004E4E4E002957 + 27000569000007710000FFFFFF004E4E4E005151512163605E62B1A3948BE6D5 + C5AFF4EADFC7EDE3D9B5B6ACA28E63605E62515151214E4E4E004E4E4E004E4E + 4E004E4E4E004E4E4E00FFFFFF004D4D4D115F5D5A64D5C1AEA1EBD8C4C2F6EC + E2CDF6EDE4CEF6EDE4CEF6ECE2CDD9C8B8A4605D5B644D4D4D114C4C4C004C4C + 4C004C4C4C004C4C4C00FFFFFF0049494943AD9F918EE8D2BBC0F1E3D5C8F6ED + E4CEF6EDE4CEF6EDE4CEF6EDE4CEF2E4D6C8B0A4979149494943494949004949 + 49004949490049494900FFFFFF0043434360E0CBB6ACE8D2BBC0E8D2BBC0E8D2 + BBC0E8D2BBC0E8D2BBC0E8D2BBC0E8D2BBC0E4D3C1B243434360434343004343 + 43004343430043434300FFFFFF003E3E3E6FE9D4BEBEE8D2BBC0E8D2BBC0F6ED + E4CEF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEEDDBC8C53E3E3E6F3E3E3E003E3E + 3E003E3E3E003E3E3E00FFFFFF0038383865E1CFBCB1E8D2BBC0F6EDE4CEF6ED + E4CEF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEE7D8CAB838383865383838003838 + 38003838380038383800FFFFFF0031313149A89D9397EAD5BFC3F5E9DFCBF6ED + E4CEF6EDE4CEF6EDE4CEF6EDE4CEF6ECE2CEADA69E9D31313149313131003131 + 31003131310031313100FFFFFF00262626143B3A3874D7C9BCAFF3E6D9CBF6ED + E4CEF6EDE4CEF6EDE4CEF7EFE6D1E4DED9C84544437D34343424383838003939 + 39003A3A3A003C3C3C00FFFFFF001E1E1E001111112A2826257D9D9791A5EEE8 + E3C4F9F3EDD6EFEAE5C5A3A09EA92827278AACACACFF373737753737371F3939 + 39003A3A3A003C3C3C00FFFFFF001E1E1E000E0E0E0004040416040404560404 + 04790404048704040479040404560404042A0F0F0F86B8B5B5FF4241417E3838 + 382A3A3A3A003C3C3C00FFFFFF001E1E1E000E0E0E0004040400040404000404 + 040004040400040404000404040004040400040404251A1A1A8FC8C1C1FF4F4D + 4D88393939383C3C3C00FFFFFF001E1E1E000E0E0E0004040400040404000404 + 040004040400040404000404040004040400040404000404043328272799D9CE + CEFF605C5C913A3A3A4DFFFFFF001E1E1E000E0E0E0004040400040404000404 + 0400040404000404040004040400040404000404040004040400040404443B38 + 38A2E8D8D8FF32323276FFFFFF001E1E1E000E0E0E0004040400040404000404 + 0400040404000404040004040400040404000404040004040400040404000404 + 045C090909892525252CFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000000A7000000AA480000 + AACC0000AA480000A7000000A3000000A3000000A7000000AA480000AACC0000 + AA480000A700FFFFFF00FFFFFF00FFFFFF00FFFFFF000000A7480000A7CC7777 + FFFF0000A7CC0000A7480000A3000000A3000000A7480000A7CC7070F8FF0000 + A7CC0000A748FFFFFF00FFFFFF00FFFFFF00FFFFFF000000A3CC7676FEFF4C4C + D4FF7272FAFF0000A3CC0000A3480000A3480000A3CC6262EAFF4C4CD4FF5C5C + E4FF0000A3CCFFFFFF00FFFFFF00FFFFFF00FFFFFF000000A1480000A0CC6767 + EFFF3636BEFF5E5EE6FF0000A0CC0000A0CC4F4FD7FF3636BEFF4545CDFF0000 + A0CC0000A148FFFFFF00FFFFFF00FFFFFF00FFFFFF000000A00000009C480000 + 9BCC5353DBFF2E2EB7FF3D3DC6FF3131BAFF15159FFF1E1EA8FF00009BCC0000 + 9C480000A000FFFFFF00FFFFFF00FFFFFF00FFFFFF000000A00000009B000000 + 9848000097CC2525B4FF1111A2FF1111A2FF1414A5FF000097CC000098480000 + 9B000000A000FFFFFF00FFFFFF00FFFFFF00FFFFFF000000870000008D000000 + 9148000092CC1515AFFF1111ACFF1111ACFF1111ACFF000092CC000091480000 + 8D0000008700FFFFFF00FFFFFF00FFFFFF00FFFFFF000000870000008C480000 + 8DCC1111B8FF1111B8FF1111B8FF1111B8FF1111B8FF1111B8FF00008DCC0000 + 8C4800008700FFFFFF00FFFFFF00FFFFFF00FFFFFF0000008748000087CC1111 + C4FF1111C4FF1111C4FF000087CC000087CC1111C4FF1111C4FF1111C4FF0000 + 87CC00008748FFFFFF00FFFFFF00FFFFFF00FFFFFF00000082CC1111D0FF1111 + D0FF1111D0FF000082CC0000834800008348000082CC1111D0FF1111D0FF1111 + D0FF000082CCFFFFFF00FFFFFF00FFFFFF00FFFFFF0000007548000072CC1111 + D8FF000072CC00007548000082000000820000007548000072CC1111D8FF0000 + 72CC00007548FFFFFF00FFFFFF00FFFFFF00FFFFFF0000007200000061480000 + 5FCC00006148000072000000820000008200000072000000614800005FCC0000 + 614800007200FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00E69B3D33E89D3FCCE89D3FCCE89D + 3FCCE89D3FCCE89D3FCCE89D3FCCE89D3FCCE89D3FCCE89D3FCCE89D3FCCE89D + 3FCCE89D3FCCE69B3D33FFFFFF00FFFFFF00E2973A15DF9438AEF0BD69E6FEDE + 92FFFCD68AFFFCD68AFFFCD68AFFFCD68AFFFCD589FFFCD589FFFDDA8EFFEFBA + 66E6DF9438AEE2973A15FFFFFF00FFFFFF00E2973A00D98E3315D2872EAEE8B5 + 63E6F7CB7FFFEAAF63FFE0A559FFD69B4FFFD19549FFDDAC60FFDFA553E6D287 + 2EAED98E3315E2973A00FFFFFF00FFFFFF00E2973A00D98E3300CB802915C379 + 23AEDCA553E6E3B25DFFD3953BFFD3953BFFDFA952FFD69A45E6C37923AECB80 + 2915D98E3300E2973A00FFFFFF00FFFFFF00E2973A00D98E3300CB802900BC71 + 1D15B56A18AED69737E6F1B436FFF0B130FFD4922DE6B56A18AEBC711D15CB80 + 2900D98E3300E2973A00FFFFFF00FFFFFF00E2973A00D98E3300CB802900BC71 + 1D00AE631215A75C0DAED38D19E6D38C18E6A75C0DAEAE631215BC711D00CB80 + 2900D98E3300E2973A00FFFFFF00FFFFFF00E2973A00D98E3300CB802900BC71 + 1D00AE631200A05509159B5005AE9B5005AEA0550915AE631200BC711D00CB80 + 2900D98E3300E2973A00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00A75C0D00B56A1800C3792300D287 + 2E00DF943800E79C3E5CE89D3FCCE79C3E5CDF943800D2872E00C3792300B56A + 1800A75C0D00FFFFFF00FFFFFF00FFFFFF00A75C0D00B56A1800C3792300D287 + 2E00DE93375CDF9438CCFFE599FFDF9438CCDE93375CD2872E00C3792300B56A + 1800A75C0D00FFFFFF00FFFFFF00FFFFFF00A75C0D00B56A1800C3792300D085 + 2D5CD2872ECCFEE195FFF8C77BFFFDDE92FFD2872ECCD0852D5CC3792300B56A + 1800A75C0D00FFFFFF00FFFFFF00FFFFFF00A75C0D00B56A1800C177225CC379 + 23CCFBD98DFFECB367FFE1A75BFFD79C50FFE7BC70FFC37923CCC177225CB56A + 1800A75C0D00FFFFFF00FFFFFF00FFFFFF00A75C0D00B368175CB56A18CCF8CF + 80FFE6A53EFFDE9D2FFFDD9C2EFFDD9C2EFFDD9C2EFFE6AE47FFB56A18CCB368 + 175CA75C0D00FFFFFF00FFFFFF00FFFFFF00A55A0C5CA75C0DCCF9C966FFFAB8 + 27FFFBB71CFFFBB518FFFBB414FFFBB311FFFBB20FFFFBB20EFFFBB516FFA75C + 0DCCA55A0C5CFFFFFF00FFFFFF00FFFFFF009C5106999B5005CC9B5005CC9B50 + 05CC9B5005CC9B5005CC9B5005CC9B5005CC9B5005CC9B5005CC9B5005CC9B50 + 05CC9C510699FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF004F4F4F005050500052525200535353005454 + 54155555553E555555555555556355555563555555555555553E545454155353 + 530052525200505050004F4F4F004F4F4F0050505000525252075353533D7373 + 7378C8C8C8D2EAEAEAFFE6E6E6FFE6E6E6FFEAEAEAFFC7C7C7D2727272785353 + 533D52525207505050004F4F4F004F4F4F00505050075151514D9F9F9FA1E6E6 + E6FFD7B0A3FFD08C75FFCD6C4BFFCD6C4BFFD08C75FFD6AFA2FFE3E4E4FF9D9D + 9DA15151514D505050074F4F4F004E4E4E004E4E4E3F9D9D9DA2DFDFDFFFCB79 + 5EFFDB7A58FFEE906EFFF49674FFF49573FFED8E6CFFDA7856FFCB795EFFDBDC + DCFF999999A24E4E4E3F4E4E4E004B4B4B176C6C6C7CDFE0E0FFC8755AFFE283 + 61FFDC7A58FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE2987FFFDF7E5CFFC875 + 5AFFD8D8D8FF6969697C4B4B4B1748484842BEBFBFD4C9A295FFD57452FFE281 + 5FFFD26C4AFFD5A291FFD5A291FFE6C2B6FFFFFFFFFFE0AD9CFFDF7C5AFFD16E + 4CFFC7A194FFB2B3B3D4484848424545455CDBDCDCFFC07C65FFDF805EFFCF69 + 47FFCF6947FFCA6442FFC05937FFC28775FFCCCCCCFFBC8877FFAD4624FFC360 + 3EFFC07C65FFCBCCCCFF4545455C4242426CCFCFCFFFBC5B3BFFDD7D5BFFC962 + 40FFBD5330FFB44A26FFBC7259FFD1CFCFFFC7AEA5FFB24824FFB34925FFBD55 + 32FFBC5B3BFFC3C3C3FF4242426C3E3E3E6ECCCCCCFFB95737FFD5714DFFBF50 + 2AFFBD4E27FFB94B26FFDBDBDBFFDBDBDBFFB94C26FFBD4E27FFBD4E27FFC154 + 2EFFB95737FFC0C0C0FF3E3E3E6E3A3A3A61D0CFCFFFB9745DFFCF643EFFC954 + 2AFFC9542AFFC9542AFFBB4D27FFBB4D27FFC9542AFFC9542AFFC9542AFFC554 + 2CFFB9745DFFC2C0C0FF3A3A3A6137373747ACAAAAD7C09789FFBF5631FFD85F + 33FFD55A2DFFCB542AFFEEEEEEFFEEEEEEFFCB542AFFD55A2DFFD55A2DFFBC4F + 29FFBF9588FFA29F9FD7373737473434341953515186D4CECEFFB35F44FFC957 + 2FFFE16132FFD3592DFFEEEEEEFFEEEEEEFFD3592DFFE15F30FFC8532AFFB35F + 44FFD0C9C9FF5150508634343419303030002F2F2F48827E7EACDBD1D1FFAC57 + 3CFFB6461FFFD6592AFFE56230FFE56230FFD6592AFFB6461FFFAC573CFFDACF + CFFF817C7CAC2F2F2F48303030002E2E2E002C2C2C092626265C807979AFE4D6 + D6FFC69689FFAD644EFF943312FF943312FFAD644EFFC69689FFE3D6D6FF7F79 + 79AF2626265C2C2C2C092E2E2E002E2E2E002C2C2C001E1E1E091717174F3D3A + 3A92B2A6A6DCEBDADAFFEBDADAFFEBDADAFFEBDADAFFB2A6A6DC3D3A3A921717 + 174F1E1E1E092C2C2C002E2E2E002E2E2E002C2C2C001E1E1E00131313000D0D + 0D1D0B0B0B540B0B0B740B0B0B870B0B0B870B0B0B740B0B0B540D0D0D1D1313 + 13001E1E1E002C2C2C002E2E2E00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF007A7A7A117C7C7C547C7C7C677C7C7C547A7A + 7A1177777700555555000000A2000000A200000099000000A6000000A7210000 + A8A60000A8CC0000A8A60000A72165656557CACACAB8FDFDFDFFCACACAB86565 + 65575B5B5B005555550016168E0000009FCC0000993300009E0000009FA73F3F + D5E87272FEFF3F3FD5E800009FA74545456FF2F2F2FFF2F2F2FFF2F2F2FF4545 + 456F55555566555555662B2B7400000092B3000092CC00009200000092CC5656 + F5FF5656F5FF5656F5FF000092CC2525255E9F9F9FBDE4E4E4FF9F9F9FBD2525 + 255E474747005555550016167700000084CC00008B3300008600000084A71F1F + BEE83434EBFF1F1FBEE8000084A70E0E0E140B0B0B610B0B0B770B0B0B610E0E + 0E141010100055555500000080000000800000008B0000006E0000006B210000 + 67A6000067CC000067A600006B21FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF007A7A7A117C7C7C547C7C7C677C7C7C547A7A + 7A1177777700555555005555550055555500EA846200EB856329EC8664D0EC86 + 64FFEC8664D0EB856329FFFFFF0065656557CACACAB8FDFDFDFFCACACAB86565 + 65575B5B5B0055555500555555005555550079605700E27C5AD0EEA891FFF7CE + C1FFEEA891FFE27C5AD0FFFFFF004545456FF2F2F2FFF2F2F2FFF2F2F2FF4545 + 456F5454546654545466545454665454546654545466D46F4DFFF0C7BAFFF0C7 + BAFFF0C7BAFFD46F4DFFFFFFFF002525255E9F9F9FBDE4E4E4FF9F9F9FBD2525 + 255E45454500505050004C4C4C66505050006E554D00C4613FD0D7927BFFE8BF + B2FFD7927BFFC4613FD0FFFFFF000E0E0E140B0B0B610B0B0B770B0B0B610E0E + 0E1410101000414141004141416641414100B9563400B8553329B65331D0B653 + 31FFB65331D0B8553329FFFFFF000B0B0B000606060006060600060606000B0B + 0B002323230035353500353535663535350077463500B6533100B3502E00B350 + 2E00B3502E00B6533100FFFFFF000B0B0B000606060006060600060606000B0B + 0B00282828002828280028282866282828006E372600B1452300AE442200AE44 + 2200AE442200B1452300FFFFFF000B0B0B000606060006060600060606001111 + 11001A1A1A001A1A1A001A1A1A661A1A1A00B3462400B2462429B14523D0B145 + 23FFB14523D0B2462429FFFFFF000B0B0B000606060006060600080808000F0F + 0F000F0F0F000F0F0F000F0F0F660A0A0A0034180F00BD4B29D0DB9078FFF7CE + C1FFDB9078FFBD4B29D0FFFFFF000B0B0B000606060006060600070707000707 + 070007070700070707000606064D0505056605050566CC5331FFF0C7BAFFF0C7 + BAFFF0C7BAFFCC5331FFFFFFFF000B0B0B000606060006060600070707000707 + 0700070707000707070005050500030303003A1A1100DB5B39D0E4917AFFE8BF + B2FFE4917AFFDB5B39D0FFFFFF000B0B0B000606060006060600070707000707 + 070007070700070707000505050003030300E5613F00E7624029E86341D0E863 + 41FFE86341D0E7624029FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00 + } + end + object MainMenu: TMainMenu + Images = ImageList + left = 168 + top = 136 + object MnuFile: TMenuItem + Caption = 'File' + object MnuFileOpen: TMenuItem + Action = AcFileOpen + Bitmap.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000064000000640000000000000000000000FFFFFF00FFFF + FF00005D8C000064970000629300005E8E30005C8C7C0059879200598700005B + 8900005B8900005B8900005B8900005D8C0000669900FFFFFF00FFFFFF00FFFF + FF00005D8C0000659950006497991C7AA9C052A5CDE0005B89C1005B8900005B + 8900005B8900005B8900005B8900005D8C0000669900FFFFFF00FFFFFF00FFFF + FF00005E8D8E3591BDF169B8DDFA81CBECFF84CEEEFF005C8BEF005D8CBE005D + 8CBE005D8CBE005D8CBE005D8CBE005E8D8E00669900FFFFFF00FFFFFF00FFFF + FF00006699B287D1EFFF7FCAE9FF7FCAE9FF87D0EFFF267DA9FF8DD1F3FF8DD1 + F3FF8DD1F3FF8DD1F3FF90D4F5FF006699B200679B3EFFFFFF00FFFFFF00FFFF + FF00006FA7A48AD3F0FF82CDEBFF82CDEBFF8AD3F0FF267EABFF8ACEF0FF8ACE + F0FF8ACEF0FF8ACEF0FF8FD3F4FFF4B62EFF006FA7A4FFFFFF00FFFFFF00FFFF + FF000073AC9E8ED6F2FF87D0EDFF87D0EDFF8ED6F2FF2882AFFF8BCFF1FF8BCF + F1FF8BCFF1FF8BCFF1FF91D5F5FFFEC941FF0073AC9EFFFFFF00FFFFFF00FFFF + FF000076B09B92DAF4FF8BD4F0FF8BD4F0FF92DAF4FF2B85B3FF8DD1F3FF8DD1 + F3FF8DD1F3FF8DD1F3FF93D7F6FFEBEBDDFF0076B09BFFFFFF00FFFFFF00FFFF + FF000078B49797DEF6FF90D8F2FF90D8F2FF97DEF6FF2D89B7FF8FD3F5FF8FD3 + F5FF8FD3F5FF8FD3F5FF95D9F8FFF5F5EEFF0078B497FFFFFF00FFFFFF00FFFF + FF00007BB8949BE1F7FF94DBF4FF94DBF4FF9BE1F7FF308DBCFF90D4F6FF90D4 + F6FF90D4F6FF90D4F6FF97DBF9FFFEFEFDFF007BB894FFFFFF00FFFFFF00FFFF + FF00007DBB909EE5F9FF98DFF6FF98DFF6FF9EE5F9FF3290C0FF92D6F8FF92D6 + F8FF92D6F8FF92D6F8FF99DDFAFF007DBB90007DBB33FFFFFF00FFFFFF00FFFF + FF00007FBF8DA3E8FBFF9DE3F9FF9DE3F9FFA3E8FBFF3594C5FF94D8FAFF94D8 + FAFF94D8FAFF94D8FAFF9BDFFCFF007FBF8D007FBE00FFFFFF00FFFFFF00FFFF + FF000082C28AA6EBFCFFA1E6FBFFA1E6FBFFA6EBFCFF3C9DCFFF96DAFCFF96DA + FCFF96DAFCFF96DAFCFF9EE2FDFF0082C28A0082C200FFFFFF00FFFFFF00FFFF + FF000084C587A9EEFDFFA4E9FCFFA4E9FCFFAAEEFDFF42A1D1FF97DBFDFF97DB + FDFF97DBFDFF97DBFDFF9FE3FEFF0084C5870084C500FFFFFF00FFFFFF00FFFF + FF000085C885ACF1FFFFABEFFEFF95E2F8FF6EC9EDFF48A8D9FF98DCFEFF98DC + FEFF98DCFEFF98DCFEFFA1E5FFFF0085C8850085C800FFFFFF00FFFFFF00FFFF + FF000087CA8388DCF4FF60C0E9FF5FBFEAFF80D3F4FF9CE3FDFFA2E6FFFFA2E6 + FFFFA2E6FFFFA2E6FFFFA6EAFFFF0087CA830087CA00FFFFFF00FFFFFF00FFFF + FF000088CC610088CC810088CC810088CC810088CC810088CC810088CC810088 + CC810088CC810088CC810088CC810088CC610087CB00FFFFFF00 + } + end + object MnuFileReopen: TMenuItem + Caption = 'Recently opened files' + end + object MenuItem9: TMenuItem + Caption = '-' + end + object MnuDumpToFile: TMenuItem + Action = AcDumpToFile + end + object MenuItem4: TMenuItem + Caption = '-' + end + object MnuFileQuit: TMenuItem + Action = AcFileQuit + Bitmap.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 200000000000000400006400000064000000000000000000000000105500000B + 4FCC0007498A0005462400054700000648000006470000044500000445000004 + 45000004450000044500000445000004450000064800FFFFFF00001B64CC4174 + B8FF2A5094EC0A1A5ED300084A8A000648240006470000044500000445000004 + 45000004450000044500000445000004450000064800FFFFFF0000226DCC3F72 + B6FF225599FF3B6EB2FF2C5196EC0A1C60D3000749A4000648CC000648CC0006 + 48CC000648CC000648CC000648CC000648CC00074A99FFFFFF00002571CC4174 + B8FF2C5FA3FF2D60A4FF23569AFF3D70B4FF334179FFBEBEBEFFD5D5D5FFD8D8 + D8FFDBDBDBFFDCDCDCFFDDDDDDFFDDDDDDFF001258CCFFFFFF00002875CC4477 + BBFF2C5FA3FF3568ACFF2B5EA2FF2E61A5FF334B87FFBBBBBBFFD3D3D3FFD5D5 + D5FFD8D8D8FFDBDBDBFFDCDCDCFFDDDDDDFF001F68CCFFFFFF00002B79CC477A + BEFF2C5FA3FF376AAEFF2B5EA2FF376AAEFF33508CFFB8B8B8FFD0D0D0FFD3D3 + D3FFD5D5D5FFD8D8D8FFDBDBDBFFDCDCDCFF00246FCCFFFFFF00002E7DCC497C + C0FF09336FFF215090FF2B5EA2FF396CB0FF335290FFB5B5B5FFCCCCCCFFD0D0 + D0FFD3D3D3FFD5D5D5FFD8D8D8FFDBDBDBFF002774CCFFFFFF00003181CC4C7F + C3FFBBBBBBFF22518CFF2C5FA3FF3B6EB2FF335593FFB3B3B3FFC9C9C9FFCCCC + CCFFD0D0D0FFD3D3D3FFD5D5D5FFD8D8D8FF002B78CCFFFFFF00003485CC5083 + C7FF1D5094FF3265A9FF2D60A4FF3D70B4FF335897FFB0B0B0FFC7C7C7FFC9C9 + C9FFCCCCCCFFD0D0D0FFD3D3D3FFD5D5D5FF002E7DCCFFFFFF00003688CC5386 + CAFF295CA0FF3F72B6FF3063A7FF3F72B6FF335A9AFFAEAEAEFFC4C4C4FFC7C7 + C7FFC9C9C9FFCCCCCCFFD0D0D0FFD3D3D3FF003181CCFFFFFF0000398BCC5588 + CCFF275A9EFF4174B8FF3164A8FF4174B8FF335D9EFFACACACFFC1C1C1FFC4C4 + C4FFC7C7C7FFC9C9C9FFCCCCCCFFD0D0D0FF003485CCFFFFFF00003B8ECC588B + CFFF1A4D91FF4376BAFF3265A9FF4376BAFF335FA1FFAAAAAAFFBFBFBFFFC1C1 + C1FFC4C4C4FFC7C7C7FFC9C9C9FFCCCCCCFF003789CCFFFFFF00003D91CC5D90 + D4FF3568ACFF285B9FFF1A4D91FF4477BBFF3361A4FFA9A9A9FFBDBDBDFFBFBF + BFFFC1C1C1FFC4C4C4FFC7C7C7FFC9C9C9FF003A8DCCFFFFFF00003F938A0E4A + 9CD33E75BFEC5487CBFF3669ADFF23569AFF3363A6FFA9A9A9FFBCBCBCFFBDBD + BDFFBFBFBFFFC1C1C1FFC4C4C4FFC7C7C7FF003C90CCFFFFFF00003F9300003F + 9324003F938A0E4B9CD33F76C0EC5D90D4FF3365A9FFA0A0A0FFA9A9A9FFA9A9 + A9FFAAAAAAFFACACACFFAEAEAEFFB0B0B0FF003E93CCFFFFFF00003F9300003F + 9300003F9300003F9424003F948A003E93CC004095CC004095CC004095CC0040 + 95CC004095CC004095CC004095CC004095CC00409599FFFFFF00 + } + end + end + object MnuRecord: TMenuItem + Caption = 'Record' + object MnuFind: TMenuItem + Action = AcFind + AutoCheck = True + Bitmap.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000064000000640000000000000000000000FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF001E1E1E000E0E + 0E00040404000404040004040400040404000404040004040400040404000404 + 040004040400040404000404045C090909892525252CFFFFFF001E1E1E000E0E + 0E00040404000404040004040400040404000404040004040400040404000404 + 040004040400040404443B3838A2E8D8D8FF32323276FFFFFF001E1E1E000E0E + 0E00040404000404040004040400040404000404040004040400040404000404 + 04000404043328272799D9CECEFF605C5C913A3A3A4DFFFFFF001E1E1E000E0E + 0E00040404000404040004040400040404000404040004040400040404000404 + 04251A1A1A8FC8C1C1FF4F4D4D88393939383C3C3C00FFFFFF001E1E1E000E0E + 0E000404041604040456040404790404048704040479040404560404042A0F0F + 0F86B8B5B5FF4241417E3838382A3A3A3A003C3C3C00FFFFFF001E1E1E001111 + 112A2826257D9D9791A5EEE8E3C4F9F3EDD6EFEAE5C5A3A09EA92827278AACAC + ACFF373737753737371F393939003A3A3A003C3C3C00FFFFFF00262626143B3A + 3874D7C9BCAFF3E6D9CBF6EDE4CEF6EDE4CEF6EDE4CEF7EFE6D1E4DED9C84544 + 437D3434342438383800393939003A3A3A003C3C3C00FFFFFF0031313149A89D + 9397EAD5BFC3F5E9DFCBF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF6ECE2CEADA6 + 9E9D3131314931313100313131003131310031313100FFFFFF0038383865E1CF + BCB1E8D2BBC0F6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEE7D8 + CAB83838386538383800383838003838380038383800FFFFFF003E3E3E6FE9D4 + BEBEE8D2BBC0E8D2BBC0F6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEEDDB + C8C53E3E3E6F3E3E3E003E3E3E003E3E3E003E3E3E00FFFFFF0043434360E0CB + B6ACE8D2BBC0E8D2BBC0E8D2BBC0E8D2BBC0E8D2BBC0E8D2BBC0E8D2BBC0E4D3 + C1B24343436043434300434343004343430043434300FFFFFF0049494943AD9F + 918EE8D2BBC0F1E3D5C8F6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF2E4D6C8B0A4 + 97914949494349494900494949004949490049494900FFFFFF004D4D4D115F5D + 5A64D5C1AEA1EBD8C4C2F6ECE2CDF6EDE4CEF6EDE4CEF6ECE2CDD9C8B8A4605D + 5B644D4D4D114C4C4C004C4C4C004C4C4C004C4C4C00FFFFFF004E4E4E005151 + 512163605E62B1A3948BE6D5C5AFF4EADFC7EDE3D9B5B6ACA28E63605E625151 + 51214E4E4E004E4E4E004E4E4E004E4E4E004E4E4E00FFFFFF004E4E4E005151 + 510054545411555555405555555A555555655555555A55555540545454115151 + 51004E4E4E004E4E4E00295727000569000007710000FFFFFF00 + } + end + end + object MnuHelp: TMenuItem + Caption = 'Help' + object MenuItem2: TMenuItem + Action = AcAbout + Bitmap.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 20000000000000040000640000006400000000000000000000002E2E2E002C2C + 2C001E1E1E00131313000D0D0D1D0B0B0B540B0B0B740B0B0B870B0B0B870B0B + 0B740B0B0B540D0D0D1D131313001E1E1E002C2C2C002E2E2E002E2E2E002C2C + 2C001E1E1E091717174F3D3A3A92B2A6A6DCEBDADAFFEBDADAFFEBDADAFFEBDA + DAFFB2A6A6DC3D3A3A921717174F1E1E1E092C2C2C002E2E2E002E2E2E002C2C + 2C092626265C807979AFE4D6D6FFC69689FFAD644EFF943312FF943312FFAD64 + 4EFFC69689FFE3D6D6FF7F7979AF2626265C2C2C2C092E2E2E00303030002F2F + 2F48827E7EACDBD1D1FFAC573CFFB6461FFFD6592AFFE56230FFE56230FFD659 + 2AFFB6461FFFAC573CFFDACFCFFF817C7CAC2F2F2F4830303000343434195351 + 5186D4CECEFFB35F44FFC9572FFFE16132FFD3592DFFEEEEEEFFEEEEEEFFD359 + 2DFFE15F30FFC8532AFFB35F44FFD0C9C9FF515050863434341937373747ACAA + AAD7C09789FFBF5631FFD85F33FFD55A2DFFCB542AFFEEEEEEFFEEEEEEFFCB54 + 2AFFD55A2DFFD55A2DFFBC4F29FFBF9588FFA29F9FD7373737473A3A3A61D0CF + CFFFB9745DFFCF643EFFC9542AFFC9542AFFC9542AFFBB4D27FFBB4D27FFC954 + 2AFFC9542AFFC9542AFFC5542CFFB9745DFFC2C0C0FF3A3A3A613E3E3E6ECCCC + CCFFB95737FFD5714DFFBF502AFFBD4E27FFB94B26FFDBDBDBFFDBDBDBFFB94C + 26FFBD4E27FFBD4E27FFC1542EFFB95737FFC0C0C0FF3E3E3E6E4242426CCFCF + CFFFBC5B3BFFDD7D5BFFC96240FFBD5330FFB44A26FFBC7259FFD1CFCFFFC7AE + A5FFB24824FFB34925FFBD5532FFBC5B3BFFC3C3C3FF4242426C4545455CDBDC + DCFFC07C65FFDF805EFFCF6947FFCF6947FFCA6442FFC05937FFC28775FFCCCC + CCFFBC8877FFAD4624FFC3603EFFC07C65FFCBCCCCFF4545455C48484842BEBF + BFD4C9A295FFD57452FFE2815FFFD26C4AFFD5A291FFD5A291FFE6C2B6FFFFFF + FFFFE0AD9CFFDF7C5AFFD16E4CFFC7A194FFB2B3B3D4484848424B4B4B176C6C + 6C7CDFE0E0FFC8755AFFE28361FFDC7A58FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + FFFFE2987FFFDF7E5CFFC8755AFFD8D8D8FF6969697C4B4B4B174E4E4E004E4E + 4E3F9D9D9DA2DFDFDFFFCB795EFFDB7A58FFEE906EFFF49674FFF49573FFED8E + 6CFFDA7856FFCB795EFFDBDCDCFF999999A24E4E4E3F4E4E4E004F4F4F005050 + 50075151514D9F9F9FA1E6E6E6FFD7B0A3FFD08C75FFCD6C4BFFCD6C4BFFD08C + 75FFD6AFA2FFE3E4E4FF9D9D9DA15151514D505050074F4F4F004F4F4F005050 + 5000525252075353533D73737378C8C8C8D2EAEAEAFFE6E6E6FFE6E6E6FFEAEA + EAFFC7C7C7D2727272785353533D52525207505050004F4F4F004F4F4F005050 + 50005252520053535300545454155555553E5555555555555563555555635555 + 55555555553E545454155353530052525200505050004F4F4F00 + } + end + end + end + object ActionList: TActionList + Images = ImageList + left = 168 + top = 64 + object AcFileOpen: TAction + Caption = 'Open' + Hint = 'Open xls file' + ImageIndex = 0 + OnExecute = AcFileOpenExecute + ShortCut = 16463 + end + object AcFileQuit: TAction + Caption = 'Quit' + Hint = 'Quit application' + ImageIndex = 1 + OnExecute = AcFileQuitExecute + ShortCut = 32856 + end + object AcFind: TAction + AutoCheck = True + Caption = 'Find' + Hint = 'Find record' + ImageIndex = 2 + OnExecute = AcFindExecute + ShortCut = 16454 + end + object AcFindClose: TAction + ImageIndex = 3 + OnExecute = AcFindCloseExecute + end + object AcFindNext: TAction + ImageIndex = 4 + OnExecute = AcFindNextExecute + end + object AcFindPrev: TAction + ImageIndex = 5 + OnExecute = AcFindPrevExecute + end + object AcAbout: TAction + Caption = 'About...' + ImageIndex = 6 + OnExecute = AcAboutExecute + end + object AcNodeExpand: TAction + Caption = 'Expand node' + Hint = 'Expand current node' + ImageIndex = 8 + OnExecute = AcNodeExpandExecute + OnUpdate = AcNodeExpandUpdate + end + object AcNodeCollapse: TAction + Caption = 'Collapse node' + Hint = 'Collapse current node' + ImageIndex = 7 + OnExecute = AcNodeCollapseExecute + OnUpdate = AcNodeCollapseUpdate + end + object AcDumpToFile: TAction + Caption = 'Dump to text file' + OnExecute = AcDumpToFileExecute + end + end + object OpenDialog: TOpenDialog + DefaultExt = '.xls' + Filter = 'Excel files (*.xls)|*.xls' + left = 160 + top = 296 + end + object RecentFilesPopupMenu: TPopupMenu + left = 160 + top = 208 + object MenuItem1: TMenuItem + Caption = 'New Item1' + end + end + object TreePopupMenu: TPopupMenu + Images = ImageList + left = 160 + top = 392 + object MenuItem3: TMenuItem + Action = AcNodeExpand + Bitmap.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000064000000640000000000000000000000FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000B0B0B000606 + 060006060600070707000707070007070700070707000505050003030300E561 + 3F00E7624029E86341D0E86341FFE86341D0E7624029FFFFFF000B0B0B000606 + 0600060606000707070007070700070707000707070005050500030303003A1A + 1100DB5B39D0E4917AFFE8BFB2FFE4917AFFDB5B39D0FFFFFF000B0B0B000606 + 060006060600070707000707070007070700070707000606064D050505660505 + 0566CC5331FFF0C7BAFFF0C7BAFFF0C7BAFFCC5331FFFFFFFF000B0B0B000606 + 060006060600080808000F0F0F000F0F0F000F0F0F000F0F0F660A0A0A003418 + 0F00BD4B29D0DB9078FFF7CEC1FFDB9078FFBD4B29D0FFFFFF000B0B0B000606 + 06000606060006060600111111001A1A1A001A1A1A001A1A1A661A1A1A00B346 + 2400B2462429B14523D0B14523FFB14523D0B2462429FFFFFF000B0B0B000606 + 060006060600060606000B0B0B00282828002828280028282866282828006E37 + 2600B1452300AE442200AE442200AE442200B1452300FFFFFF000B0B0B000606 + 060006060600060606000B0B0B00232323003535350035353566353535007746 + 3500B6533100B3502E00B3502E00B3502E00B6533100FFFFFF000E0E0E140B0B + 0B610B0B0B770B0B0B610E0E0E1410101000414141004141416641414100B956 + 3400B8553329B65331D0B65331FFB65331D0B8553329FFFFFF002525255E9F9F + 9FBDE4E4E4FF9F9F9FBD2525255E45454500505050004C4C4C66505050006E55 + 4D00C4613FD0D7927BFFE8BFB2FFD7927BFFC4613FD0FFFFFF004545456FF2F2 + F2FFF2F2F2FFF2F2F2FF4545456F545454665454546654545466545454665454 + 5466D46F4DFFF0C7BAFFF0C7BAFFF0C7BAFFD46F4DFFFFFFFF0065656557CACA + CAB8FDFDFDFFCACACAB8656565575B5B5B005555550055555500555555007960 + 5700E27C5AD0EEA891FFF7CEC1FFEEA891FFE27C5AD0FFFFFF007A7A7A117C7C + 7C547C7C7C677C7C7C547A7A7A1177777700555555005555550055555500EA84 + 6200EB856329EC8664D0EC8664FFEC8664D0EB856329FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 + } + end + object MenuItem5: TMenuItem + Action = AcNodeCollapse + Bitmap.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000064000000640000000000000000000000FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF000E0E0E140B0B + 0B610B0B0B770B0B0B610E0E0E14101010005555550000008000000080000000 + 8B0000006E0000006B21000067A6000067CC000067A600006B212525255E9F9F + 9FBDE4E4E4FF9F9F9FBD2525255E474747005555550016167700000084CC0000 + 8B3300008600000084A71F1FBEE83434EBFF1F1FBEE8000084A74545456FF2F2 + F2FFF2F2F2FFF2F2F2FF4545456F55555566555555662B2B7400000092B30000 + 92CC00009200000092CC5656F5FF5656F5FF5656F5FF000092CC65656557CACA + CAB8FDFDFDFFCACACAB8656565575B5B5B005555550016168E0000009FCC0000 + 993300009E0000009FA73F3FD5E87272FEFF3F3FD5E800009FA77A7A7A117C7C + 7C547C7C7C677C7C7C547A7A7A1177777700555555000000A2000000A2000000 + 99000000A6000000A7210000A8A60000A8CC0000A8A60000A721FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00 + } + end + object MenuItem6: TMenuItem + Caption = '-' + end + object MenuItem7: TMenuItem + Action = AcFind + AutoCheck = True + Bitmap.Data = { + 36040000424D3604000000000000360000002800000010000000100000000100 + 2000000000000004000064000000640000000000000000000000FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF + FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF001E1E1E000E0E + 0E00040404000404040004040400040404000404040004040400040404000404 + 040004040400040404000404045C090909892525252CFFFFFF001E1E1E000E0E + 0E00040404000404040004040400040404000404040004040400040404000404 + 040004040400040404443B3838A2E8D8D8FF32323276FFFFFF001E1E1E000E0E + 0E00040404000404040004040400040404000404040004040400040404000404 + 04000404043328272799D9CECEFF605C5C913A3A3A4DFFFFFF001E1E1E000E0E + 0E00040404000404040004040400040404000404040004040400040404000404 + 04251A1A1A8FC8C1C1FF4F4D4D88393939383C3C3C00FFFFFF001E1E1E000E0E + 0E000404041604040456040404790404048704040479040404560404042A0F0F + 0F86B8B5B5FF4241417E3838382A3A3A3A003C3C3C00FFFFFF001E1E1E001111 + 112A2826257D9D9791A5EEE8E3C4F9F3EDD6EFEAE5C5A3A09EA92827278AACAC + ACFF373737753737371F393939003A3A3A003C3C3C00FFFFFF00262626143B3A + 3874D7C9BCAFF3E6D9CBF6EDE4CEF6EDE4CEF6EDE4CEF7EFE6D1E4DED9C84544 + 437D3434342438383800393939003A3A3A003C3C3C00FFFFFF0031313149A89D + 9397EAD5BFC3F5E9DFCBF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF6ECE2CEADA6 + 9E9D3131314931313100313131003131310031313100FFFFFF0038383865E1CF + BCB1E8D2BBC0F6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEE7D8 + CAB83838386538383800383838003838380038383800FFFFFF003E3E3E6FE9D4 + BEBEE8D2BBC0E8D2BBC0F6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEEDDB + C8C53E3E3E6F3E3E3E003E3E3E003E3E3E003E3E3E00FFFFFF0043434360E0CB + B6ACE8D2BBC0E8D2BBC0E8D2BBC0E8D2BBC0E8D2BBC0E8D2BBC0E8D2BBC0E4D3 + C1B24343436043434300434343004343430043434300FFFFFF0049494943AD9F + 918EE8D2BBC0F1E3D5C8F6EDE4CEF6EDE4CEF6EDE4CEF6EDE4CEF2E4D6C8B0A4 + 97914949494349494900494949004949490049494900FFFFFF004D4D4D115F5D + 5A64D5C1AEA1EBD8C4C2F6ECE2CDF6EDE4CEF6EDE4CEF6ECE2CDD9C8B8A4605D + 5B644D4D4D114C4C4C004C4C4C004C4C4C004C4C4C00FFFFFF004E4E4E005151 + 512163605E62B1A3948BE6D5C5AFF4EADFC7EDE3D9B5B6ACA28E63605E625151 + 51214E4E4E004E4E4E004E4E4E004E4E4E004E4E4E00FFFFFF004E4E4E005151 + 510054545411555555405555555A555555655555555A55555540545454115151 + 51004E4E4E004E4E4E00295727000569000007710000FFFFFF00 + } + end + end + object SaveDialog: TSaveDialog + DefaultExt = '.txt' + Filter = 'Text files (*.txt)|*.txt' + left = 289 + top = 296 + end +end diff --git a/applications/biffexplorer/bemain.pas b/applications/biffexplorer/bemain.pas new file mode 100644 index 000000000..f7c2a90ea --- /dev/null +++ b/applications/biffexplorer/bemain.pas @@ -0,0 +1,1472 @@ +unit beMain; + +{$mode objfpc}{$H+} + +interface + +uses + ActnList, Classes, ComCtrls, ExtCtrls, Grids, Menus, StdCtrls, SysUtils, + FileUtil, Forms, Controls, Graphics, Dialogs, Buttons, Types, VirtualTrees, + {$ifdef USE_NEW_OLE} + fpolebasic, + {$else} + fpolestorage, + {$endif} + fpstypes, KHexEditor, + mrumanager, beTypes, beBIFFGrid; + +type + + { TMainForm } + TMainForm = class(TForm) + AcFileOpen: TAction; + AcFileQuit: TAction; + AcFind: TAction; + AcFindNext: TAction; + AcFindPrev: TAction; + AcAbout: TAction; + AcFindClose: TAction; + AcNodeExpand: TAction; + AcNodeCollapse: TAction; + AcDumpToFile: TAction; + ActionList: TActionList; + BIFFTree: TVirtualStringTree; + CbFind: TComboBox; + CbHexAddress: TCheckBox; + CbHexEditorLineSize: TComboBox; + CbHexSingleBytes: TCheckBox; + ImageList: TImageList; + HexEditor: TKHexEditor; + MainMenu: TMainMenu; + AnalysisDetails: TMemo; + MenuItem1: TMenuItem; + MenuItem3: TMenuItem; + MenuItem5: TMenuItem; + MenuItem6: TMenuItem; + MenuItem7: TMenuItem; + MnuDumpToFile: TMenuItem; + MenuItem9: TMenuItem; + MnuFind: TMenuItem; + MnuRecord: TMenuItem; + MnuFileReopen: TMenuItem; + MenuItem4: TMenuItem; + MnuHelp: TMenuItem; + MenuItem2: TMenuItem; + MnuFileQuit: TMenuItem; + MnuFileOpen: TMenuItem; + MnuFile: TMenuItem; + OpenDialog: TOpenDialog; + PageControl: TPageControl; + DetailPanel: TPanel; + FindPanel: TPanel; + HexEditorParamsPanel: TPanel; + SaveDialog: TSaveDialog; + SpeedButton3: TSpeedButton; + TreePopupMenu: TPopupMenu; + TreePanel: TPanel; + BtnFindNext: TSpeedButton; + BtnFindPrev: TSpeedButton; + RecentFilesPopupMenu: TPopupMenu; + BtnCloseFind: TSpeedButton; + Splitter1: TSplitter; + HexSplitter: TSplitter; + PgAnalysis: TTabSheet; + PgValues: TTabSheet; + DetailsSplitter: TSplitter; + StatusBar: TStatusBar; + ToolButton3: TToolButton; + ToolButton4: TToolButton; + ValueGrid: TStringGrid; + ToolBar: TToolBar; + ToolButton1: TToolButton; + ToolButton2: TToolButton; + procedure AcAboutExecute(Sender: TObject); + procedure AcDumpToFileExecute(Sender: TObject); + procedure AcFileOpenExecute(Sender: TObject); + procedure AcFileQuitExecute(Sender: TObject); + procedure AcFindCloseExecute(Sender: TObject); + procedure AcFindExecute(Sender: TObject); + procedure AcFindNextExecute(Sender: TObject); + procedure AcFindPrevExecute(Sender: TObject); + procedure AcNodeCollapseExecute(Sender: TObject); + procedure AcNodeCollapseUpdate(Sender: TObject); + procedure AcNodeExpandExecute(Sender: TObject); + procedure AcNodeExpandUpdate(Sender: TObject); + procedure BIFFTreeBeforeCellPaint(Sender: TBaseVirtualTree; + TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; + CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect); + procedure BIFFTreeFocusChanged(Sender: TBaseVirtualTree; + Node: PVirtualNode; Column: TColumnIndex); + procedure BIFFTreeFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode); + procedure BIFFTreeGetNodeDataSize(Sender: TBaseVirtualTree; + var NodeDataSize: Integer); + procedure BIFFTreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; + Column: TColumnIndex; TextType: TVSTTextType; var CellText: String); +// procedure BIFFTreeInitNode(Sender: TBaseVirtualTree; ParentNode, +// Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); + procedure BIFFTreePaintText(Sender: TBaseVirtualTree; + const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; + TextType: TVSTTextType); + procedure CbFindChange(Sender: TObject); + procedure CbFindKeyPress(Sender: TObject; var Key: char); + procedure CbHexAddressChange(Sender: TObject); + procedure CbHexEditorLineSizeChange(Sender: TObject); + procedure CbHexSingleBytesChange(Sender: TObject); + procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure FormDropFiles(Sender: TObject; const FileNames: array of String); + procedure FormShow(Sender: TObject); + procedure HexEditorClick(Sender: TObject); + procedure HexEditorKeyDown(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure ListViewSelectItem(Sender: TObject; Item: TListItem; + Selected: Boolean); + procedure PageControlChange(Sender: TObject); + procedure ValueGridClick(Sender: TObject); + procedure ValueGridPrepareCanvas(Sender: TObject; ACol, ARow: Integer; + AState: TGridDrawState); + + private + MemStream: TMemoryStream; + OLEStorage: TOLEStorage; + FFileName: String; + FFormat: TsSpreadsheetFormat; + FBuffer: TBIFFBuffer; + FCurrOffset: Integer; + FXFIndex: Integer; + FFontIndex: Integer; + FFormatIndex: Integer; + FRowIndex: Integer; + FExternSheetIndex: Integer; + FAnalysisGrid: TBIFFGrid; + FMRUMenuManager : TMRUMenuManager; + procedure AddToHistory(const AText: String); + procedure AnalysisGridDetails(Sender: TObject; ADetails: TStrings); + procedure AnalysisGridPrepareCanvas(Sender: TObject; ACol, ARow: Integer; + AState: TGridDrawState); + procedure AnalysisGridSelection(Sender: TObject; ACol, ARow: Integer); + procedure DumpToFile(const AFileName: String); + procedure ExecFind(ANext, AKeep: Boolean); + function GetBIFFNodeData: PBiffNodeData; + function GetRecType: Word; + function GetValueGridDataSize: Integer; + procedure LoadFile(const AFileName: String); overload; + procedure LoadFile(const AFileName: String; AFormat: TsSpreadsheetFormat); overload; + procedure MRUMenuManagerRecentFile(Sender:TObject; const AFileName:string); + procedure PopulateAnalysisGrid; + procedure PopulateHexDump; + procedure PopulateValueGrid; + procedure ReadCmdLine; + procedure ReadFromIni; + procedure ReadFromStream(AStream: TStream); + procedure UpdateCaption; + procedure UpdateCmds; + procedure UpdateStatusbar; + procedure WriteToIni; + + public + procedure BeforeRun; + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.lfm} + +uses + IniFiles, LazUTF8, LazFileUtils, Math, StrUtils, LCLType, + KFunctions, + fpsUtils, + beUtils, beBIFFUtils, beAbout; + +const + VALUE_ROW_INDEX = 1; + VALUE_ROW_BITS = 2; + VALUE_ROW_BYTE = 3; + VALUE_ROW_SHORTINT = 4; + VALUE_ROW_WORD = 5; + VALUE_ROW_SMALLINT = 6; + VALUE_ROW_DWORD = 7; + VALUE_ROW_LONGINT = 8; + VALUE_ROW_QWORD = 9; + VALUE_ROW_INT64 = 10; + VALUE_ROW_SINGLE = 11; + VALUE_ROW_DOUBLE = 12; + VALUE_ROW_ANSISTRING = 13; + VALUE_ROW_PANSICHAR = 14; + VALUE_ROW_WIDESTRING = 15; + VALUE_ROW_PWIDECHAR = 16; + + MAX_HISTORY = 16; + + +{ TMyHexEditor } + +type + TMyHexEditor = class(TKHexEditor); + + (* +{ Virtual tree nodes } + +type + TObjectNodeData = record + Data: TObject; + end; + PObjectNodeData = ^TObjectNodeData; + *) + +{ TMainForm } + +procedure TMainForm.AcAboutExecute(Sender: TObject); +var + F: TAboutForm; +begin + F := TAboutForm.Create(nil); + try + F.ShowModal; + finally + F.Free; + end; +end; + + +procedure TMainForm.AcDumpToFileExecute(Sender: TObject); +begin + if FFileName = '' then + exit; + + with SaveDialog do begin + FileName := ChangeFileExt(ExtractFileName(FFileName), '') + '_dumped.txt'; + if Execute then + DumpToFile(FileName); + end; +end; + + +procedure TMainForm.AcFileOpenExecute(Sender: TObject); +begin + with OpenDialog do begin + if Execute then LoadFile(FileName); + end; +end; + + +procedure TMainForm.AcFileQuitExecute(Sender: TObject); +begin + Close; +end; + + +procedure TMainForm.AcFindCloseExecute(Sender: TObject); +begin + AcFind.Checked := false; + FindPanel.Hide; +end; + + +procedure TMainForm.AcFindExecute(Sender: TObject); +begin + if AcFind.Checked then begin + FindPanel.Show; + CbFind.SetFocus; + end else begin + FindPanel.Hide; + end; +end; + + +procedure TMainForm.AcFindNextExecute(Sender: TObject); +begin + ExecFind(true, false); +end; + + +procedure TMainForm.AcFindPrevExecute(Sender: TObject); +begin + ExecFind(false, false); +end; + + +procedure TMainForm.AcNodeCollapseExecute(Sender: TObject); +var + node: PVirtualNode; +begin + node := BiffTree.FocusedNode; + if node <> nil then begin + if BiffTree.GetNodeLevel(node) > 0 then + node := node^.Parent; + BiffTree.Expanded[node] := false; + end; +end; + +procedure TMainForm.AcNodeCollapseUpdate(Sender: TObject); +var + node: PVirtualNode; +begin + node := BiffTree.FocusedNode; + if node <> nil then begin + if BiffTree.GetNodeLevel(node) > 0 then + node := node^.Parent; + end; + AcNodeCollapse.Enabled := (node <> nil) and BiffTree.Expanded[node]; +end; + + +procedure TMainForm.AcNodeExpandExecute(Sender: TObject); +var + node: PVirtualNode; +begin + node := BiffTree.FocusedNode; + if node <> nil then begin + if BiffTree.GetNodeLevel(node) > 0 then + node := node^.Parent; + BiffTree.Expanded[node] := true; + end; +end; + + +procedure TMainForm.AcNodeExpandUpdate(Sender: TObject); +var + node: PVirtualNode; +begin + node := BiffTree.FocusedNode; + if node <> nil then begin + if BiffTree.GetNodeLevel(node) > 0 then + node := node^.Parent; + end; + AcNodeExpand.Enabled := (node <> nil) and not BiffTree.Expanded[node]; +end; + +procedure TMainForm.AddToHistory(const AText: String); +begin + if (AText <> '') and (CbFind.Items.IndexOf(AText) = -1) then begin + CbFind.Items.Insert(0, AText); + while CbFind.Items.Count > MAX_HISTORY do + CbFind.Items.Delete(CbFind.Items.Count-1); + end; +end; + + +procedure TMainForm.AnalysisGridDetails(Sender: TObject; ADetails: TStrings); +begin + AnalysisDetails.Lines.Assign(ADetails); +end; + + +procedure TMainForm.AnalysisGridPrepareCanvas(Sender: TObject; ACol, + ARow: Integer; AState: TGridDrawState); +begin + if ARow = 0 then FAnalysisGrid.Canvas.Font.Style := [fsBold]; +end; + + +procedure TMainForm.AnalysisGridSelection(Sender: TObject; ACol, ARow: Integer); +var + s: String; +begin + if ARow < FAnalysisGrid.RowCount then + begin + s := FAnalysisGrid.Cells[0, ARow]; + if s <> '' then + begin + FCurrOffset := StrToInt(s); + PopulateValueGrid; + UpdateStatusbar; + end; + end; +end; + + +procedure TMainForm.BeforeRun; +begin + ReadFromIni; + ReadCmdLine; +end; + + +procedure TMainForm.BIFFTreeBeforeCellPaint(Sender: TBaseVirtualTree; + TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; + CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect); +var + s: String; +begin + if (Sender.GetNodeLevel(Node) = 0) and (Column = 0) then begin + // Left-align parent nodes (column 0 is right-aligned) + BiffTreeGetText(Sender, Node, 0, ttNormal, s); + TargetCanvas.Font.Style := [fsBold]; + ContentRect.Right := CellRect.Left + TargetCanvas.TextWidth(s) + 30; + end; +end; + + +procedure TMainForm.BIFFTreeFocusChanged(Sender: TBaseVirtualTree; + Node: PVirtualNode; Column: TColumnIndex); +var + data: PBiffNodeData; + n: Word; +begin + if Node^.Parent = Sender.RootNode then + begin + HexEditor.Clear; + for n:=1 to ValueGrid.RowCount-1 do + begin + ValueGrid.Cells[1, n] := ''; + ValueGrid.Cells[2, n] := ''; + end; + FAnalysisGrid.RowCount := 2; + FAnalysisGrid.Rows[1].Clear; + AnalysisDetails.Lines.Clear; + exit; + end; + + data := Sender.GetNodeData(Node); + + // Move to start of record + 2 bytes to skip record type ID. + MemStream.Position := PtrInt(data^.Offset) + 2; + + // Read size of record + n := WordLEToN(MemStream.ReadWord); + + // Read record data + SetLength(FBuffer, n); + if n > 0 then + MemStream.ReadBuffer(FBuffer[0], n); + + // Update user interface + if (BiffTree.FocusedNode <> nil) and (BiffTree.GetNodeLevel(BiffTree.FocusedNode) > 0) + then begin + Statusbar.Panels[0].Text := Format('Record ID: $%.4x', [data^.RecordID]); + Statusbar.Panels[1].Text := data^.RecordName; + Statusbar.Panels[2].Text := Format('Record size: %d bytes', [n]); + Statusbar.Panels[3].Text := ''; + end else begin + Statusbar.Panels[0].Text := ''; + Statusbar.Panels[1].Text := data^.RecordName; + Statusbar.Panels[2].Text := ''; + Statusbar.Panels[3].Text := ''; + end; + PopulateHexDump; + PageControlChange(nil); +end; + + +procedure TMainForm.BIFFTreeFreeNode(Sender: TBaseVirtualTree; + Node: PVirtualNode); +var + data: PBiffNodeData; +begin + data := Sender.GetNodeData(Node); + if data <> nil then + begin + data^.RecordName := ''; + data^.RecordDescription := ''; + end; +end; + + +procedure TMainForm.BIFFTreeGetNodeDataSize(Sender: TBaseVirtualTree; + var NodeDataSize: Integer); +begin + NodeDataSize := SizeOf(TBiffNodeData); +end; + + +procedure TMainForm.BIFFTreeGetText(Sender: TBaseVirtualTree; + Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; + var CellText: String); +var + data: PBiffNodeData; +begin + CellText := ''; + data := Sender.GetNodeData(Node); + if data <> nil then + case Sender.GetNodeLevel(Node) of + 0: if Column = 0 then CellText := data^.RecordName; + 1: case Column of + 0: CellText := IntToStr(data^.Offset); + 1: CellText := Format('$%.4x', [data^.RecordID]); + 2: CellText := data^.RecordName; + 3: if data^.Index > -1 then CellText := IntToStr(data^.Index); + 4: cellText := data^.RecordDescription; + end; + end; +end; + + +procedure TMainForm.BIFFTreePaintText(Sender: TBaseVirtualTree; + const TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; + TextType: TVSTTextType); +begin + // Paint parent node in bold font. + if (Sender.GetNodeLevel(Node) = 0) and (Column = 0) then + TargetCanvas.Font.Style := [fsBold]; +end; + + +procedure TMainForm.CbFindChange(Sender: TObject); +begin + ExecFind(true, true); +end; + + +procedure TMainForm.CbFindKeyPress(Sender: TObject; var Key: char); +begin + if Key = #13 then + ExecFind(true, false); +end; + +procedure TMainForm.CbHexAddressChange(Sender: TObject); +begin + if CbHexAddress.Checked then + begin + HexEditor.AddressMode := eamHex; + HexEditor.AddressPrefix := '$'; + end else + begin + HexEditor.AddressMode := eamDec; + HexEditor.AddressPrefix := ''; + end; + CbHexEditorLineSizeChange(nil); +end; + +procedure TMainForm.CbHexEditorLineSizeChange(Sender: TObject); +begin + case CbHexEditorLineSize.ItemIndex of + 0: HexEditor.LineSize := IfThen(HexEditor.AddressMode = eamHex, 16, 10); + 1: HexEditor.LineSize := IfThen(HexEditor.AddressMode = eamHex, 32, 20); + end; +end; + +procedure TMainForm.CbHexSingleBytesChange(Sender: TObject); +begin + HexEditor.DigitGrouping := IfThen(CbHexSingleBytes.Checked, 1, 2); +end; + +procedure TMainForm.DumpToFile(const AFileName: String); +var + list: TStringList; + parentnode, node: PVirtualNode; + parentdata, data: PBiffNodeData; +begin + list := TStringList.Create; + try + parentnode := BiffTree.GetFirst; + while parentnode <> nil do begin + parentdata := BiffTree.GetNodeData(parentnode); + list.Add(parentdata^.RecordName); + node := BIffTree.GetFirstChild(parentnode); + while node <> nil do begin + data := BiffTree.GetNodeData(node); + List.Add(Format(' %.04x %s (%s)', [data^.RecordID, data^.RecordName, data^.RecordDescription])); + node := BiffTree.GetNextSibling(node); + end; + List.Add(''); + parentnode := BiffTree.GetNextSibling(parentnode); + end; + + list.SaveToFile(AFileName); + finally + list.Free; + end; +end; + + +procedure TMainForm.ExecFind(ANext, AKeep: Boolean); +var + s: String; + node, node0: PVirtualNode; + + function GetRecordname(ANode: PVirtualNode; UseLowercase: Boolean = true): String; + var + data: PBIffNodeData; + begin + data := BiffTree.GetNodeData(ANode); + if Assigned(data) then begin + if UseLowercase then + Result := lowercase(data^.RecordName) + else + Result := data^.RecordName; + end else + Result := ''; + end; + + function GetNextNode(ANode: PVirtualNode): PVirtualNode; + var + nextparent: PVirtualNode; + begin + Result := BiffTree.GetNextSibling(ANode); + if (Result = nil) and (ANode <> nil) then begin + nextparent := BiffTree.GetNextSibling(ANode^.Parent); + if nextparent = nil then + nextparent := BiffTree.GetFirst; + Result := BiffTree.GetFirstChild(nextparent); + end; + end; + + function GetPrevNode(ANode: PVirtualNode): PVirtualNode; + var + prevparent: PVirtualNode; + begin + Result := BiffTree.GetPreviousSibling(ANode); + if (Result = nil) and (ANode <> nil) then begin + prevparent := BiffTree.GetPreviousSibling(ANode^.Parent); + if prevparent = nil then + prevparent := BiffTree.GetLast; + Result := BiffTree.GetLastChild(prevparent); + end; + end; + +begin + if CbFind.Text = '' then + exit; + + s := Lowercase(CbFind.Text); + node0 := BiffTree.FocusedNode; + if node0 = nil then + node0 := BiffTree.GetFirst; + if BiffTree.GetNodeLevel(node0) = 0 then + node0 := BiffTree.GetFirstChild(node0); + + if ANext then begin + if AKeep + then node := node0 + else node := GetNextNode(node0); + repeat + if pos(s, GetRecordname(node)) > 0 then begin + BiffTree.FocusedNode := node; + BiffTree.Selected[node] := true; + BiffTree.ScrollIntoView(node, true); + AddToHistory(GetRecordname(node, false)); + exit; + end; + node := GetNextNode(node); + until (node = node0) or (node = nil); + end else begin + if AKeep + then node := node0 + else node := GetPrevNode(node0); + repeat + if pos(s, GetRecordName(node)) > 0 then begin + BiffTree.FocusedNode := node; + BiffTree.Selected[node] := true; + BiffTree.ScrollIntoView(node, true); + AddToHistory(GetRecordName(node, false)); + exit; + end; + node := GetPrevNode(node); + until (node = node0) or (node = nil); + end; +end; + + +procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: boolean); +begin + if CanClose then + try + WriteToIni; + except + MessageDlg('Could not write setting to configuration file.', mtError, [mbOK], 0); + end; +end; + + +procedure TMainForm.FormCreate(Sender: TObject); +begin + FMRUMenuManager := TMRUMenuManager.Create(self); + with FMRUMenuManager do begin + Name := 'MRUMenuManager'; + IniFileName := GetAppConfigFile(false); + IniSection := 'RecentFiles'; + MaxRecent := 16; + MenuCaptionMask := '&%x - %s'; // & --> create hotkey + MenuItem := MnuFileReopen; + PopupMenu := RecentFilesPopupMenu; + OnRecentFile := @MRUMenuManagerRecentFile; + end; + + HexEditor.Font.Style := []; + + FAnalysisGrid := TBIFFGrid.Create(self); + with FAnalysisGrid do begin + Parent := PgAnalysis; + Align := alClient; + DefaultRowHeight := ValueGrid.DefaultRowHeight; + Options := Options + [goDrawFocusSelected]; + TitleStyle := tsNative; + OnDetails := @AnalysisGridDetails; + OnPrepareCanvas := @AnalysisGridPrepareCanvas; + OnSelection := @AnalysisGridSelection; + TabOrder := 0; + end; + + with ValueGrid do begin + ColCount := 3; + RowCount := VALUE_ROW_PWIDECHAR + 1; + Cells[0, 0] := 'Data type'; + Cells[1, 0] := 'Value'; + Cells[2, 0] := 'Offset range'; + Cells[0, VALUE_ROW_INDEX] := 'Offset'; + Cells[0, VALUE_ROW_BITS] := 'Bits'; + Cells[0, VALUE_ROW_BYTE] := 'Byte'; + Cells[0, VALUE_ROW_SHORTINT] := 'ShortInt'; + Cells[0, VALUE_ROW_WORD] := 'Word'; + Cells[0, VALUE_ROW_SMALLINT] := 'SmallInt'; + Cells[0, VALUE_ROW_DWORD] := 'DWord'; + Cells[0, VALUE_ROW_LONGINT] := 'LongInt'; + Cells[0, VALUE_ROW_QWORD] := 'QWord'; + Cells[0, VALUE_ROW_INT64] := 'Int64'; + Cells[0, VALUE_ROW_SINGLE] := 'Single'; + Cells[0, VALUE_ROW_DOUBLE] := 'Double'; + Cells[0, VALUE_ROW_ANSISTRING] := 'AnsiString'; + Cells[0, VALUE_ROW_PANSICHAR] := 'PAnsiChar'; + Cells[0, VALUE_ROW_WIDESTRING] := 'WideString'; + Cells[0, VALUE_ROW_PWIDECHAR] := 'PWideChar'; + end; + + BiffTree.DefaultNodeHeight := BiffTree.Canvas.TextHeight('Tg') + 4; + BiffTree.Header.DefaultHeight := ValueGrid.DefaultRowHeight; + + UpdateCmds; +end; + + +procedure TMainForm.FormDestroy(Sender: TObject); +begin + if MemStream <> nil then + FreeAndNil(MemStream); + if OLEStorage <> nil then + FreeAndNil(OLEStorage); +end; + + +procedure TMainForm.FormDropFiles(Sender: TObject; + const FileNames: array of String); +begin + LoadFile(FileNames[0]); +end; + + +procedure TMainForm.FormShow(Sender: TObject); +begin + Width := Width + 1; // remove black rectangle next to ValueGrid + Width := Width - 1; +end; + + +function TMainForm.GetBIFFNodeData: PBiffNodeData; +begin + Result := nil; + if BiffTree.FocusedNode <> nil then + begin + Result := BiffTree.GetNodeData(BiffTree.FocusedNode); + if Result <> nil then + MemStream.Position := Result^.Offset; + end; +end; + + +function TMainForm.GetRecType: Word; +var + data: PBiffNodeData; +begin + Result := Word(-1); + if BiffTree.FocusedNode <> nil then + begin + data := BiffTree.GetNodedata(BiffTree.FocusedNode); + if data <> nil then + begin + MemStream.Position := data^.Offset; + Result := WordLEToN(MemStream.ReadWord); + end; + end; +end; + + +function TMainForm.GetValueGridDataSize: Integer; +begin + Result := -1; + case ValueGrid.Row of + VALUE_ROW_BITS : Result := SizeOf(Byte); + VALUE_ROW_BYTE : Result := SizeOf(Byte); + VALUE_ROW_SHORTINT : Result := SizeOf(ShortInt); + VALUE_ROW_WORD : Result := SizeOf(Word); + VALUE_ROW_SMALLINT : Result := SizeOf(SmallInt); + VALUE_ROW_DWORD : Result := SizeOf(DWord); + VALUE_ROW_LONGINT : Result := SizeOf(LongInt); + VALUE_ROW_QWORD : Result := SizeOf(QWord); + VALUE_ROW_INT64 : Result := SizeOf(Int64); + VALUE_ROW_SINGLE : Result := SizeOf(Single); + VALUE_ROW_DOUBLE : Result := SizeOf(Double); + end; +end; + +procedure TMainForm.HexEditorClick(Sender: TObject); +begin + FCurrOffset := HexEditor.SelStart.Index; + PopulateValueGrid; + ValueGridClick(nil); + UpdateStatusbar; +end; + +procedure TMainForm.HexEditorKeyDown(Sender: TObject; var Key: Word; + Shift: TShiftState); +var + sel: TKHexEditorSelection; +begin + case Key of + VK_LEFT : dec(FCurrOffset); + VK_RIGHT : inc(FCurrOffset); + VK_UP : dec(FCurrOffset, HexEditor.LineSize); + VK_DOWN : inc(FCurrOffset, HexEditor.LineSize); + VK_HOME : if (Shift = [ssCtrl]) then + FCurrOffset := 0 else + FCurrOffset := (FCurrOffset div HexEditor.LineSize) * HexEditor.LineSize; + VK_END : if (Shift = [ssCtrl]) then + FCurrOffset := High(FBuffer) else + FCurrOffset := succ(FCurrOffset div HexEditor.LineSize) * HexEditor.lineSize - 1; + VK_NEXT : begin + if (Shift = [ssCtrl]) then + inc(FCurrOffset, HexEditor.LineSize * HexEditor.LineCount) + else + inc(FCurrOffset, HexEditor.LineSize * HexEditor.GetClientHeightChars); + while (FCurrOffset > High(FBuffer)) do + dec(FCurrOffset, HexEditor.LineSize); + end; + VK_PRIOR : if (Shift = [ssCtrl]) then + FCurrOffset := FCurrOffset mod HexEditor.LineSize + else + begin + dec(FCurrOffset, HexEditor.LineSize * HexEditor.GetClientHeightChars); + while (FCurrOffset < 0) do + inc(FCurrOffset, HexEditor.LineSize); + end; + else exit; + end; + if FCurrOffset < 0 then FCurrOffset := 0; + if FCurrOffset > High(FBuffer) then FCurrOffset := High(FBuffer); + sel.Index := FCurrOffset; + sel.Digit := 0; + HexEditor.SelStart := sel; + HexEditorClick(nil); + if not HexEditor.CaretInView then + TMyHexEditor(HexEditor).ScrollTo(HexEditor.SelToPoint(HexEditor.SelStart, HexEditor.EditArea), false, true); + + // Don't process these keys any more! + Key := 0; +end; + +procedure TMainForm.LoadFile(const AFileName: String); +var + valid: Boolean; + excptn: Exception = nil; + ext: String; +begin + if not FileExistsUTF8(AFileName) then begin + MessageDlg(Format('File "%s" not found.', [AFileName]), mtError, [mbOK], 0); + exit; + end; + + ext := Lowercase(ExtractFileExt(AFilename)); + if ext <> '.xls' then begin + MessageDlg('BIFFExplorer can only process binary Excel files (extension ".xls")', + mtError, [mbOK], 0); + exit; + end; + + // .xls files can contain several formats. We look into the header first. + if ext = STR_EXCEL_EXTENSION then + begin + valid := GetFormatFromFileHeader(UTF8ToAnsi(AFileName), FFormat); + // It is possible that valid xls files are not detected correctly. Therefore, + // we open them explicitly by trial and error - see below. + if not valid then + FFormat := sfExcel8; + valid := true; + end else + FFormat := sfExcel8; + + while True do begin + try + LoadFile(AFileName, FFormat); + valid := True; + except + on E: Exception do begin + if FFormat = sfExcel8 then excptn := E; + valid := False + end; + end; + if valid or (FFormat = sfExcel2) then Break; + FFormat := Pred(FFormat); + end; + + // A failed attempt to read a file should bring an exception, so re-raise + // the exception if necessary. We re-raise the exception brought by Excel 8, + // since this is the most common format + if (not valid) and (excptn <> nil) then + raise excptn; +end; + + +procedure TMainForm.LoadFile(const AFileName: String; AFormat: TsSpreadsheetFormat); +var + OLEDocument: TOLEDocument; + streamname: UTF8String; + filestream: TFileStream; +begin + if MemStream <> nil then + FreeAndNil(MemStream); + + if OLEStorage <> nil then + FreeAndNil(OLEStorage); + + MemStream := TMemoryStream.Create; + + if AFormat = sfExcel2 then begin + fileStream := TFileStream.Create(UTF8ToSys(AFileName), fmOpenRead + fmShareDenyNone); + try + MemStream.CopyFrom(fileStream, fileStream.Size); + MemStream.Position := 0; +// MemStream.LoadFromFile(UTF8ToSys(AFileName)); + finally + filestream.Free; + end; + end else begin + OLEStorage := TOLEStorage.Create; + + // Only one stream is necessary for any number of worksheets + OLEDocument.Stream := MemStream; + if AFormat = sfExcel8 then streamname := 'Workbook' else streamname := 'Book'; + OLEStorage.ReadOLEFile(UTF8ToSys(AFileName), OLEDocument, streamname); + + // Check if the operation succeded + if MemStream.Size = 0 then + raise Exception.Create('BIFF Explorer: Reading the OLE document failed'); + end; + + // Rewind the stream and read from it + MemStream.Position := 0; + FFileName := ExpandFileName(AFileName); + ReadFromStream(MemStream); + + FFormat := AFormat; + UpdateCaption; + UpdateStatusbar; + + FMRUMenuManager.AddToRecent(AFileName); +end; + + +procedure TMainForm.ListViewSelectItem(Sender: TObject; Item: TListItem; + Selected: Boolean); +var + n: Word; +begin + if Selected then begin + // Move to start of record + 2 bytes to skip record type ID. + MemStream.Position := PtrInt(Item.Data) + 2; + + // Read size of record + n := WordLEToN(MemStream.ReadWord); + + // Read record data + SetLength(FBuffer, n); + MemStream.ReadBuffer(FBuffer[0], n); + + // Update user interface + Statusbar.Panels[0].Text := Format('Record ID: %s', [Item.SubItems[0]]); + Statusbar.Panels[1].Text := Item.SubItems[1]; + Statusbar.Panels[2].Text := Format('Record size: %s bytes', [Item.SubItems[3]]); + PopulateHexDump; + PageControlChange(nil); + end; +end; + + +procedure TMainForm.MRUMenuManagerRecentFile(Sender: TObject; + const AFileName: string); +begin + LoadFile(AFileName); +end; + + +procedure TMainForm.PopulateAnalysisGrid; +begin + FAnalysisGrid.SetBIFFNodeData(GetBiffNodeData, FBuffer, FFormat); +end; + + +procedure TMainForm.PopulateHexDump; +var + data: TDataSize; +begin + data.Size := Length(FBuffer); + data.Data := @FBuffer[0]; + HexEditor.Clear; + HexEditor.Append(0, data); +end; + + +procedure TMainForm.PopulateValueGrid; +var + buf: array[0..1023] of Byte; + w: word absolute buf; + dw: DWord absolute buf; + qw: QWord absolute buf; + dbl: double absolute buf; + sng: single absolute buf; + idx: Integer; + i, j: Integer; + s: String; + sw: WideString; + ls: Integer; +begin + idx := FCurrOffset; +// idx := HexEditor.SelStart.Index; + + i := ValueGrid.RowCount; + j := ValueGrid.ColCount; + + ValueGrid.Cells[1, VALUE_ROW_INDEX] := IntToStr(idx); + + if idx <= Length(FBuffer)-SizeOf(byte) then begin + ValueGrid.Cells[1, VALUE_ROW_BITS] := IntToBin(FBuffer[idx], 8); + ValueGrid.Cells[2, VALUE_ROW_BITS] := Format('%d ... %d', [idx, idx]); + ValueGrid.Cells[1, VALUE_ROW_BYTE] := IntToStr(FBuffer[idx]); + ValueGrid.Cells[2, VALUE_ROW_BYTE] := ValueGrid.Cells[2, VALUE_ROW_BITS]; + ValueGrid.Cells[1, VALUE_ROW_SHORTINT] := IntToStr(ShortInt(FBuffer[idx])); + ValueGrid.Cells[2, VALUE_ROW_SHORTINT] := ValueGrid.Cells[2, VALUE_ROW_BITS]; + end + else begin + ValueGrid.Cells[1, VALUE_ROW_BYTE] := ''; + ValueGrid.Cells[2, VALUE_ROW_BYTE] := ''; + ValueGrid.Cells[1, VALUE_ROW_SHORTINT] := ''; + ValueGrid.Cells[2, VALUE_ROW_SHORTINT] := ''; + end; + + if idx <= Length(FBuffer)-SizeOf(word) then begin + buf[0] := FBuffer[idx]; + buf[1] := FBuffer[idx+1]; + ValueGrid.Cells[1, VALUE_ROW_WORD] := IntToStr(WordLEToN(w)); + ValueGrid.Cells[2, VALUE_ROW_WORD] := Format('%d ... %d', [idx, idx+SizeOf(Word)-1]); + ValueGrid.Cells[1, VALUE_ROW_SMALLINT] := IntToStr(SmallInt(WordLEToN(w))); + ValueGrid.Cells[2, VALUE_ROW_SMALLINT] := ValueGrid.Cells[2, VALUE_ROW_WORD]; + end else begin + ValueGrid.Cells[1, VALUE_ROW_WORD] := ''; + ValueGrid.Cells[2, VALUE_ROW_WORD] := ''; + ValueGrid.Cells[1, VALUE_ROW_SMALLINT] := ''; + ValueGrid.Cells[2, VALUE_ROW_SMALLINT] := ''; + end; + + if idx <= Length(FBuffer) - SizeOf(DWord) then begin + for i:=0 to SizeOf(DWord)-1 do buf[i] := FBuffer[idx+i]; + ValueGrid.Cells[1, VALUE_ROW_DWORD] := IntToStr(DWordLEToN(dw)); + ValueGrid.Cells[2, VALUE_ROW_DWORD] := Format('%d ... %d', [idx, idx+SizeOf(DWord)-1]); + ValueGrid.Cells[1, VALUE_ROW_LONGINT] := IntToStr(LongInt(DWordLEToN(dw))); + ValueGrid.Cells[2, VALUE_ROW_LONGINT] := ValueGrid.Cells[2, VALUE_ROW_DWORD]; + end else begin + ValueGrid.Cells[1, VALUE_ROW_DWORD] := ''; + ValueGrid.Cells[2, VALUE_ROW_DWORD] := ''; + ValueGrid.Cells[1, VALUE_ROW_LONGINT] := ''; + ValueGrid.Cells[2, VALUE_ROW_LONGINT] := ''; + end; + + if idx <= Length(FBuffer) - SizeOf(QWord) then begin + for i:=0 to SizeOf(QWord)-1 do buf[i] := FBuffer[idx+i]; + ValueGrid.Cells[1, VALUE_ROW_QWORD] := Format('%d', [qw]); + ValueGrid.Cells[2, VALUE_ROW_QWORD] := Format('%d ... %d', [idx, idx+SizeOf(QWord)-1]); + ValueGrid.Cells[1, VALUE_ROW_INT64] := Format('%d', [Int64(qw)]); + ValueGrid.Cells[2, VALUE_ROW_INT64] := ValueGrid.Cells[2, VALUE_ROW_QWORD]; + end else begin + ValueGrid.Cells[1, VALUE_ROW_QWORD] := ''; + ValueGrid.Cells[2, VALUE_ROW_QWORD] := ''; + ValueGrid.Cells[1, VALUE_ROW_INT64] := ''; + ValueGrid.Cells[2, VALUE_ROW_INT64] := ''; + end; + + if idx <= Length(FBuffer) - SizeOf(single) then begin + for i:=0 to SizeOf(single)-1 do buf[i] := FBuffer[idx+i]; + ValueGrid.Cells[1, VALUE_ROW_SINGLE] := Format('%f', [sng]); + ValueGrid.Cells[2, VALUE_ROW_SINGLE] := Format('%d ... %d', [idx, idx+SizeOf(Single)-1]); + end else begin + ValueGrid.Cells[1, VALUE_ROW_SINGLE] := ''; + ValueGrid.Cells[2, VALUE_ROW_SINGLE] := ''; + end; + + if idx <= Length(FBuffer) - SizeOf(double) then begin + for i:=0 to SizeOf(double)-1 do buf[i] := FBuffer[idx+i]; + ValueGrid.Cells[1, VALUE_ROW_DOUBLE] := Format('%f', [dbl]); + ValueGrid.Cells[2, VALUE_ROW_DOUBLE] := Format('%d ... %d', [idx, idx+SizeOf(Double)-1]); + end else begin + ValueGrid.Cells[1, VALUE_ROW_DOUBLE] := ''; + ValueGrid.Cells[2, VALUE_ROW_DOUBLE] := ''; + end; + + if idx < Length(FBuffer) then begin + ls := FBuffer[idx]; + SetLength(s, ls); + i := idx + 1; + j := 0; + while (i < Length(FBuffer)) and (j < Length(s)) do begin + inc(j); + s[j] := char(FBuffer[i]); + inc(i); + end; + SetLength(s, j); + ValueGrid.Cells[1, VALUE_ROW_ANSISTRING] := s; + ValueGrid.Cells[2, VALUE_ROW_ANSISTRING] := Format('%d ... %d', [idx, ls * SizeOf(char) + 1]); + end else begin + ValueGrid.Cells[1, VALUE_ROW_ANSISTRING] := ''; + ValueGrid.Cells[2, VALUE_ROW_ANSISTRING] := ''; + end; + + s := StrPas(PChar(@FBuffer[idx])); + ValueGrid.Cells[1, VALUE_ROW_PANSICHAR] := s; + ValueGrid.Cells[2, VALUE_ROW_PANSICHAR] := Format('%d ... %d', [idx, idx + Length(s)]); + + if idx < Length(FBuffer) then begin + ls := FBuffer[idx]; + SetLength(sw, ls); + j := 0; + i := idx + 2; + while (i < Length(FBuffer)-1) and (j < Length(sw)) do begin + buf[0] := FBuffer[i]; + buf[1] := FBuffer[i+1]; + inc(i, SizeOf(WideChar)); + inc(j); + sw[j] := WideChar(w); + end; + SetLength(sw, j); + ValueGrid.Cells[1, VALUE_ROW_WIDESTRING] := UTF8Encode(sw); + ValueGrid.Cells[2, VALUE_ROW_WIDESTRING] := Format('%d ... %d', [idx, idx + ls*SizeOf(wideChar)+1]); + end else begin + ValueGrid.Cells[1, VALUE_ROW_WIDESTRING] := ''; + ValueGrid.Cells[2, VALUE_ROW_WIDESTRING] := ''; + end; + + s := UTF8Encode(StrPas(PWideChar(@FBuffer[idx]))); + ValueGrid.Cells[1, VALUE_ROW_PWIDECHAR] := s; + ValueGrid.Cells[2, VALUE_ROW_PWIDECHAR] := Format('%d ... %d', [idx, idx + Length(s)]); +end; + + +procedure TMainForm.ReadCmdLine; +begin + if ParamCount > 0 then + LoadFile(ParamStr(1)); +end; + + +procedure TMainForm.ReadFromIni; +var + ini: TCustomIniFile; + i: Integer; +begin + ini := CreateIni; + try + ReadFormFromIni(ini, 'MainForm', self); + + TreePanel.Width := ini.ReadInteger('MainForm', 'RecordList_Width', TreePanel.Width); + for i:=0 to BiffTree.Header.Columns.Count-1 do + BiffTree.Header.Columns[i].Width := ini.ReadInteger('MainForm', + Format('RecordList_ColWidth_%d', [i+1]), BiffTree.Header.Columns[i].Width); + + ValueGrid.Height := ini.ReadInteger('MainForm', 'ValueGrid_Height', ValueGrid.Height); + for i:=0 to ValueGrid.ColCount-1 do + ValueGrid.ColWidths[i] := ini.ReadInteger('MainForm', + Format('ValueGrid_ColWidth_%d', [i+1]), ValueGrid.ColWidths[i]); + + for i:=0 to FAnalysisGrid.ColCount-1 do + FAnalysisGrid.ColWidths[i] := ini.ReadInteger('MainForm', + Format('AnalysisGrid_ColWidth_%d', [i+1]), FAnalysisGrid.ColWidths[i]); + + AnalysisDetails.Height := ini.ReadInteger('MainForm', 'AnalysisDetails_Height', AnalysisDetails.Height); + + HexEditor.AddressMode := TKHexEditorAddressMode(ini.ReadInteger('HexEditor', + 'AddressMode', ord(HexEditor.AddressMode))); + CbHexAddress.Checked := HexEditor.AddressMode = eamHex; + CbHexAddressChange(nil); + + HexEditor.DigitGrouping := ini.ReadInteger('HexEditor', + 'DigitGrouping', HexEditor.DigitGrouping); + CbHexSingleBytes.Checked := HexEditor.DigitGrouping = 1; + CbHexSingleBytesChange(nil); + + HexEditor.LineSize := ini.ReadInteger('HexEditor', + 'LineSize', HexEditor.LineSize); + if HexEditor.LineSize in [10, 16] then + CbHexEditorLineSize.ItemIndex := 0 else CbHexEditorLineSize.ItemIndex := 1; + CbHexEditorLineSizeChange(nil); + + PageControl.ActivePageIndex := ini.ReadInteger('MainForm', 'PageIndex', PageControl.ActivePageIndex); + finally + ini.Free; + end; +end; + + +procedure TMainForm.ReadFromStream(AStream: TStream); +var + recType: Word; + recSize: Word; + p: Cardinal; + p0: Cardinal; + s: String; + i: Integer; + node, prevnode: PVirtualNode; + parentnode: PVirtualNode; + parentdata, data, prevdata: PBiffNodeData; + w: word; + crs: TCursor; +begin + crs := Screen.Cursor; + try + Screen.Cursor := crHourGlass; + BiffTree.Clear; + parentnode := nil; + FXFIndex := -1; + FFontIndex := -1; + FFormatIndex := -1; + FRowIndex := -1; + FExternSheetIndex := 0; // 1-based! + AStream.Position := 0; + while AStream.Position < AStream.Size do begin + p := AStream.Position; + recType := WordLEToN(AStream.ReadWord); + recSize := WordLEToN(AStream.ReadWord); + if (recType = 0) and (recSize = 0) then + break; + s := RecTypeName(recType); + i := pos(':', s); + // in case of BOF record: create new parent node for this substream + if (recType = $0009) or (recType = $0209) or (recType = $0409) or (recType = $0809) + then begin + // Read info on substream beginning here + p0 := AStream.Position; + AStream.Position := AStream.Position + 2; + w := WordLEToN(AStream.ReadWord); + AStream.Position := p0; + // add parent node for this substream + parentnode := BiffTree.AddChild(nil); + // add data to parent node + parentdata := BiffTree.GetNodeData(parentnode); + BiffTree.ValidateNode(parentnode, False); + parentdata^.Offset := p; + parentdata^.RecordName := BOFName(w); + FRowIndex := -1; + end; + // add node to parent node + node := BIFFTree.AddChild(parentnode); + data := BiffTree.GetNodeData(node); + BiffTree.ValidateNode(node, False); + data^.Offset := p; + data^.RecordID := recType; + if i > 0 then begin + data^.RecordName := copy(s, 1, i-1); + data^.RecordDescription := copy(s, i+2, Length(s)); + end else begin + data^.RecordName := s; + data^.RecordDescription := ''; + end; + case recType of + $0008, $0208: // Row + begin + inc(FRowIndex); + data^.Index := FRowIndex; + end; + $0031, $0231: // Font record + begin + inc(FFontIndex); + if FFontIndex > 3 then data^.Index := FFontIndex + 1 + else data^.Index := FFontIndex; + end; + $0043, $00E0: // XF record + begin + inc(FXFIndex); + data^.Index := FXFIndex; + end; + $0017: // EXTERNSHEET record + if FFormat < sfExcel8 then begin + inc(FExternSheetIndex); + data^.Index := FExternSheetIndex; + end; + $001E, $041E: // Format record + begin + inc(FFormatIndex); + data^.Index := FFormatIndex; + end; + $003C: // CONTINUE reocrd + begin + prevnode := BIFFTree.GetPrevious(node); + prevdata := BiffTree.GetNodeData(prevnode); + case prevdata^.RecordID of + $00FC: data^.Tag := BIFFNODE_SST_CONTINUE; // SST record + $01B6: data^.Tag := BIFFNODE_TXO_CONTINUE1; // TX0 record + $003C: begin // CONTINUE record + prevnode := BiffTree.GetPrevious(prevnode); + prevdata := BiffTree.GetNodeData(prevnode); + if prevdata^.RecordID = $01B6 then // TX0 record + data^.Tag := BIFFNODE_TXO_CONTINUE2; + end; + end; + end; + else + data^.Index := -1; + end; + + // advance stream pointer + AStream.Position := AStream.Position + recSize; + end; + + // expand all parent nodes + node := BiffTree.GetFirst; + while node <> nil do begin + BiffTree.Expanded[node] := true; + node := BiffTree.GetNextSibling(node); + end; + // Select first node + BiffTree.FocusedNode := BiffTree.GetFirst; + BiffTree.Selected[BiffTree.FocusedNode] := true; + + UpdateCmds; + + finally + Screen.Cursor := crs; + end; +end; + + +procedure TMainForm.PageControlChange(Sender: TObject); +var + sel: TKHexEditorSelection; + i, n: Integer; + s: String; +begin + if (BiffTree.FocusedNode = nil) or + (BiffTree.FocusedNode^.Parent = BiffTree.RootNode) + then + exit; + + PopulateAnalysisGrid; + for i:=1 to FAnalysisGrid.RowCount-1 do begin + s := FAnalysisGrid.Cells[0, i]; + if s = '' then break; + n := StrToInt(s); + if (n >= FCurrOffset) then + begin + FAnalysisGrid.Row := IfThen(n = FCurrOffset, i, i-1); + break; + end; + end; + + sel.Index := FCurrOffset; + sel.Digit := 0; + HexEditor.SelStart := sel; + PopulateValueGrid; + ValueGridClick(nil); +{ + if PageControl.ActivePage = PgAnalysis then + PopulateAnalysisGrid + else + if PageControl.ActivePage = PgValues then + begin + sel.Index := FCurrOffset; + sel.Digit := 0; + HexEditor.SelStart := sel; + PopulateValueGrid; + ValueGridClick(nil); + end;} +end; + + +procedure TMainForm.UpdateCaption; +begin + if FFileName = '' then + Caption := 'BIFF Explorer - (no file loaded)' + else + Caption := Format('BIFF Explorer - "%s [%s]', [ + FFileName, + GetFileFormatName(FFormat) + ]); +end; + + +procedure TMainForm.UpdateCmds; +begin + AcDumpToFile.Enabled := FFileName <> ''; + AcFind.Enabled := FFileName <> ''; +end; + + +procedure TMainForm.UpdateStatusbar; +begin + if FCurrOffset > -1 then + Statusbar.Panels[3].Text := Format('HexViewer offset: %d', [FCurrOffset]) + else + Statusbar.Panels[3].Text := ''; +end; + + +procedure TMainForm.ValueGridClick(Sender: TObject); +var + sel: TKHexEditorSelection; + n: Integer; +begin + sel := HexEditor.SelStart; + + n := GetValueGridDataSize; + if n > 0 then begin + sel.Digit := 0; + HexEditor.SelStart := sel; + inc(sel.Index, n-1); + sel.Digit := 1; + HexEditor.SelEnd := sel; + end else + HexEditor.SelEnd := sel; +end; + + +procedure TMainForm.ValueGridPrepareCanvas(sender: TObject; aCol, + aRow: Integer; aState: TGridDrawState); +begin + if ARow = 0 then ValueGrid.Canvas.Font.Style := [fsBold]; +end; + + +procedure TMainForm.WriteToIni; +var + ini: TCustomIniFile; + i: Integer; +begin + ini := CreateIni; + try + WriteFormToIni(ini, 'MainForm', self); + + ini.WriteInteger('MainForm', 'RecordList_Width', TreePanel.Width); + for i:=0 to BiffTree.Header.Columns.Count-1 do + ini.WriteInteger('MainForm', Format('RecordList_ColWidth_%d', [i+1]), BiffTree.Header.Columns[i].Width); + + ini.WriteInteger('MainForm', 'ValueGrid_Height', ValueGrid.Height); + for i:=0 to ValueGrid.ColCount-1 do + ini.WriteInteger('MainForm', Format('ValueGrid_ColWidth_%d', [i+1]), ValueGrid.ColWidths[i]); + + for i:=0 to FAnalysisGrid.ColCount-1 do + ini.WriteInteger('MainForm', Format('AnalysisGrid_ColWidth_%d', [i+1]), FAnalysisGrid.ColWidths[i]); + + ini.WriteInteger('MainForm', 'AnalysisDetails_Height', AnalysisDetails.Height); + + ini.WriteInteger('MainForm', 'PageIndex', PageControl.ActivePageIndex); + + ini.WriteInteger('HexEditor', 'AddressMode', ord(HexEditor.AddressMode)); + ini.WriteInteger('HexEditor', 'DigitGrouping', HexEditor.DigitGrouping); + ini.WriteInteger('HexEditor', 'LineSize', HexEditor.LineSize); + + finally + ini.Free; + end; +end; + +end. + diff --git a/applications/biffexplorer/betypes.pas b/applications/biffexplorer/betypes.pas new file mode 100644 index 000000000..f7c42631e --- /dev/null +++ b/applications/biffexplorer/betypes.pas @@ -0,0 +1,30 @@ +unit beTypes; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils; + +const + BIFFNODE_TXO_CONTINUE1 = 1; + BIFFNODE_TXO_CONTINUE2 = 2; + BIFFNODE_SST_CONTINUE = 3; + +type + { Virtual tree node data } + TBiffNodeData = record //class + Offset: Integer; + RecordID: Integer; + RecordName: String; + RecordDescription: String; + Index: Integer; + Tag: Integer; + end; + PBiffNodeData = ^TBiffNodeData; + +implementation + +end. + diff --git a/applications/biffexplorer/beutils.pas b/applications/biffexplorer/beutils.pas new file mode 100644 index 000000000..79362d95b --- /dev/null +++ b/applications/biffexplorer/beutils.pas @@ -0,0 +1,165 @@ +unit beUtils; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, IniFiles, Forms, + fpstypes, fpspreadsheet; + +function CreateIni : TCustomIniFile; +procedure ReadFormFromIni(ini: TCustomIniFile; ASection: String; AForm: TCustomForm); +procedure WriteFormToIni(ini: TCustomIniFile; ASection: String; AForm: TCustomForm); + +function GetFileFormatName(AFormat: TsSpreadsheetFormat): String; +function GetFormatFromFileHeader(const AFileName: TFileName; + out SheetType: TsSpreadsheetFormat): Boolean; + + +implementation + +uses + uvirtuallayer_ole; + +function CreateIni : TCustomIniFile; +var + cfg : string; +begin + cfg := GetAppConfigDir(false); + if not DirectoryExists(cfg) then + CreateDir(cfg); + result := TMemIniFile.Create(GetAppConfigFile(false)); +end; + +procedure ReadFormFromIni(ini: TCustomIniFile; ASection: String; AForm: TCustomForm); +var + L,T,W,H: Integer; + isMax: Boolean; +begin + L := ini.ReadInteger(ASection, 'Left', AForm.Left); + T := Ini.ReadInteger(ASection, 'Top', AForm.Top); + W := ini.ReadInteger(ASection, 'Width', AForm.Width); + H := ini.ReadInteger(ASection, 'Height', AForm.Height); + isMax := ini.ReadBool(ASection, 'Maximized', AForm.WindowState = wsMaximized); + if W > Screen.Width then W := Screen.Width; + if H > Screen.Height then H := Screen.Height; + if L < 0 then L := 0; + if T < 0 then T := 0; + if L + W > Screen.Width then L := Screen.Width - W; + if T + H > Screen.Height then T := Screen.Height - H; + AForm.Left := L; + AForm.Top := T; + AForm.Width := W; + AForm.Height := H; + if IsMax then + AForm.WindowState := wsMaximized + else + AForm.WindowState := wsNormal; +end; + + +procedure WriteFormToIni(ini: TCustomIniFile; ASection: String; AForm: TCustomForm); +begin + ini.WriteBool(ASection, 'Maximized', (AForm.WindowState = wsMaximized)); + if AForm.WindowState = wsNormal then begin + ini.WriteInteger(ASection, 'Left', AForm.Left); + ini.WriteInteger(ASection, 'Top', AForm.Top); + ini.WriteInteger(ASection, 'Width', AForm.Width); + ini.WriteInteger(ASection, 'Height', AForm.Height); + end; +end; + +function GetFileFormatName(AFormat: TsSpreadsheetFormat): string; +begin + case AFormat of + sfExcel2 : Result := 'BIFF2'; + { Excel3/4 not supported fpspreadsheet + sfExcel3 : Result := 'BIFF3'; + sfExcel4 : Result := 'BIFF4'; + } + sfExcel5 : Result := 'BIFF5'; + sfExcel8 : Result := 'BIFF8'; + sfooxml : Result := 'OOXML'; + sfOpenDocument : Result := 'Open Document'; + sfCSV : Result := 'CSV'; + sfWikiTable_Pipes : Result := 'WikiTable Pipes'; + sfWikiTable_WikiMedia : Result := 'WikiTable WikiMedia'; + else Result := '-unknown format-'; + end; +end; + +function GetFormatFromFileHeader(const AFileName: TFileName; + out SheetType: TsSpreadsheetFormat): Boolean; +const + BIFF2_HEADER: array[0..3] of byte = ( + $09,$00, $04,$00); // they are common to all BIFF2 files that I've seen + BIFF58_HEADER: array[0..7] of byte = ( + $D0,$CF, $11,$E0, $A1,$B1, $1A,$E1); + + function ValidOLEStream(AStream: TStream; AName: String): Boolean; + var + fsOLE: TVirtualLayer_OLE; + begin + AStream.Position := 0; + fsOLE := TVirtualLayer_OLE.Create(AStream); + try + fsOLE.Initialize; + Result := fsOLE.FileExists('/'+AName); + finally + fsOLE.Free; + end; + end; + +var + buf: packed array[0..7] of byte = (0,0,0,0,0,0,0,0); + stream: TStream; + i: Integer; + ok: Boolean; +begin + Result := false; + stream := TFileStream.Create(AFileName, fmOpenRead + fmShareDenyNone); + try + // Read first 8 bytes + stream.ReadBuffer(buf, 8); + + // Check for Excel 2 + ok := true; + for i:=0 to High(BIFF2_HEADER) do + if buf[i] <> BIFF2_HEADER[i] then + begin + ok := false; + break; + end; + if ok then + begin + SheetType := sfExcel2; + Exit(True); + end; + + // Check for Excel 5 or 8 + for i:=0 to High(BIFF58_HEADER) do + if buf[i] <> BIFF58_HEADER[i] then + exit; + + // Now we know that the file is a Microsoft compound document. + + // We check for Excel 5 in which the stream is named "Book" + if ValidOLEStream(stream, 'Book') then begin + SheetType := sfExcel5; + exit(True); + end; + + // Now we check for Excel 8 which names the stream "Workbook" + if ValidOLEStream(stream, 'Workbook') then begin + SheetType := sfExcel8; + exit(True); + end; + + finally + stream.Free; + end; +end; + +end. + diff --git a/applications/biffexplorer/mrumanager.pp b/applications/biffexplorer/mrumanager.pp new file mode 100644 index 000000000..847f886e9 --- /dev/null +++ b/applications/biffexplorer/mrumanager.pp @@ -0,0 +1,485 @@ +{ MRU (Most Recent Used) menu item manager + + Copyright (C) 2011 Michael Van Canneyt (michael@freepascal.org) + Modifications by Werner Pamler + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version with the following modification: + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules,and + to copy and distribute the resulting executable under terms of your choice, + provided that you also meet, for each linked independent module, the terms + and conditions of the license of that module. An independent module is a + module which is not derived from or based on this library. If you modify + this library, you may extend this exception to your version of the library, + but you are not obligated to do so. If you do not wish to do so, delete this + exception statement from your version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License + for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + modified by wp +} +unit mrumanager; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, inifiles, menus; + +Type + { TRecentMenuItem } + + TRecentMenuItem = Class(TMenuItem) + Private + FFileName : string; + Public + Property FileName : String Read FFileName; + end; + TRecentMenuItemClass = Class of TRecentMenuItem; + + { TMRUMenuManager } + + TOnRecentFileEvent = Procedure(Sender : TObject; Const AFileName : String) of object; + + TMRUMenuManager = Class(TComponent) + Private + FIniFileName: String; + FIniSection: String; + FOnRecent: TOnRecentFileEvent; + FRecent : TStrings; + FMaxRecent : Integer; + FMenuCaptionMask : string; + FMIRecent : TMenuItem; + FPMRecent : TPopupMenu; + FMaxItemLength : integer; + procedure SetIniFileName(const AValue:string); + procedure SetIniSection(const AValue:string); + procedure SetMaxItemLength(const AValue:integer); + procedure SetMenuCaptionMask(const AValue:string); + procedure SetMIRecent(const AValue: TMenuItem); + procedure SetPMRecent(const AValue: TPopupMenu); + procedure SetRecent(const AValue: TStrings); + protected + // Overrides. + procedure Loaded; override; + procedure Notification(AComponent: TComponent; Operation: TOperation); override; + // Return default name and section if either is empty. + procedure GetFileNameAndSection(Var AFileName,ASection : String); virtual; + // Override if you want to load additional values. + procedure LoadFromIni(Ini: TCustomIniFile; ASection: String); virtual; + // Override if you want to write additional values. + procedure SaveToIni(Ini: TCustomIniFile; ASection: String); virtual; + // Called when menu item is clicked. + procedure DoOnRecentClick(Sender: TObject); virtual; + // Override this if you want to create a custom class of menu itel. + function CreateMenuItem(AOwner: TComponent): TRecentMenuItem; virtual; + // Create a menu caption. Default is index followed by filename. + // Override if you want to customize. + Function CreateMenuCaption(AIndex : Integer; Const AFileName : String) : String; virtual; + Public + Constructor Create(AOwner : TComponent);override; + Destructor Destroy; override; + // Load files from ini file AFileName in section ASection. Calls ShowRecentFiles + // Need for explicit call only when IniFileName='' and IniSection='' and class created at run-time + procedure LoadRecentFilesFromIni(const AFileName: string=''; const ASection: String=''); + // Saves files to ini file AFileName in section ASection. + procedure SaveRecentFilesToIni(const AFileName: string=''; const ASection: String=''); + // Add a filename to the list of files. + // If an existing file is added, it is moved first in the list. + // If MaxRecent is attained, the last one is removed. + // Calls ShowRecentFiles. + procedure AddToRecent(AFileName: String); + // Re-populate the menu. + procedure ShowRecentFiles; + Published + // Max. items to be kept in the list. + Property MaxRecent : Integer Read FMaxRecent write FMaxRecent; + // Menu item to create a submenu under. Existing items will be removed. + Property MenuItem : TMenuItem Read FMIRecent Write SetMIRecent; + // Popupmenu attached to a toolbar button. Existing items will be removed. + Property PopupMenu : TPopupMenu Read FPMRecent Write SetPMRecent; + // Default ini filename. + Property IniFileName : String Read FIniFileName Write SetIniFileName; + // Default ini section. + Property IniSection : String Read FIniSection Write SetIniSection; + // Maximum length of recent menu item + Property MaxItemLength : integer Read FMaxItemLength Write SetMaxItemLength default 80; + // Format mask for MenuCaption: first placeholder must be %d, second %s, e.g. '%d - %s' + Property MenuCaptionMask : string read FMenuCaptionMask Write SetMenuCaptionMask; + // Recent items. If adding manually to the list, ShowRecentFiles must be called manually. + Property Recent : TStrings Read FRecent Write SetRecent; + // Called when the user clicks an recent meu item. + Property OnRecentFile : TOnRecentFileEvent Read FOnRecent Write FOnRecent; + end; + EMRUManager = Class(Exception); + +Const + DefaultIniFile = 'recent.ini'; + DefaultSection = 'Global'; + KeyMaxRecent = 'MaxRecent'; + KeyCount = 'Count'; + KeyFile = 'File%d'; + +implementation + +Resourcestring + SErrFailedToCreateDir = 'Failed to create directory "%s"'; + +const + DEFAULT_MASK = '%d. %s'; + +function MinimizeFileName(const AFileName:string; AMaxLen:integer) : string; + + procedure SplitPath(const APath:String; Parts: TStrings); + { Splits the provided path into constituent folder names } + var + i, j : Integer; + begin + if APath = '' then exit; + if not Assigned(Parts) then exit; + + i := Length(APath); + j := i; + while (i >= 1) do begin + if APath[i] = DirectorySeparator then begin + Parts.Insert(0, copy(APath, i+1, j-i)); + j := i; + end; + dec(i); + end; + Parts.Insert(0, copy(APath, 1, j)); + end; + + function AddStringsFromTo(AList:TStrings; FromIndex,ToIndex:integer) : string; + var + i : integer; + begin + result := ''; + for i:=FromIndex to ToIndex do + result := result + AList[i]; + end; + +var + Parts : TStringList; + i : integer; + tmp : string; +begin + result := AFileName; + if Length(AFileName) > AMaxLen then begin + Parts := TStringList.Create; + try + SplitPath(AFileName, Parts); + i := Parts.Count div 2; + while (i < Parts.Count) do begin + tmp := Format('%s...%s%s', [ + AddStringsFromTo(Parts, 0, i-1), + DirectorySeparator, + AddStringsFromTo(Parts, i+1, Parts.Count-1) + ]); + if Length(tmp) < AMaxLen then begin + result := tmp; + exit; + end else + Parts.Delete(i); + i := Parts.Count div 2; + end; + result := ExtractFileName(AFileName); + finally + Parts.Free; + end; + end; +end; + +procedure TMRUMenuManager.AddToRecent(AFileName : String); + +Var + I,J : Integer; + B : Boolean; + +begin + AFileName:=ExpandFileName(AFileName); + With FRecent do + begin + J:=IndexOf(AFileName); + If (J<>-1) then + begin + if (J>0) then + Exchange(0,J) + end + else + begin + While (Count>=FMaxRecent) do + Delete(Count-1); + Insert(0,AFileName) + end; + end; + ShowRecentFiles; +end; + +function TMRUMenuManager.CreateMenuItem(AOwner :TComponent) : TRecentMenuItem; + +begin + Result:=TRecentMenuItem.Create(AOwner); +end; + +function TMRUMenuManager.CreateMenuCaption(AIndex: Integer; + const AFileName: String): String; +var + fn : string; + mask : string; +begin + if FMaxItemLength > 0 then + fn := MinimizeFileName(AFileName, FMaxItemLength) + else + fn := AFileName; + if FMenuCaptionMask = '' then + mask := DEFAULT_MASK + else + mask := FMenuCaptionMask; + Result:=Format(mask, [AIndex+1,fn]); +end; + +procedure TMRUMenuManager.ShowRecentFiles; + +Var + I : Integer; + M : TRecentMenuItem; + +begin + if Assigned(FMIRecent) then begin + FMIRecent.clear; + For I:=0 to FRecent.Count-1 do + begin + M:=CreateMenuItem(Self.Owner); + M.Caption:=CreateMenuCaption(I,FRecent[i]); + M.FFileName:=FRecent[i]; + M.OnClick:=@DoOnRecentClick; + FMIRecent.Add(M); + end; + end; + if Assigned(FPMRecent) then begin + FPMRecent.Items.Clear; + for i:=0 to FRecent.Count-1 do + begin + M := CreateMenuItem(Self.Owner); + M.Caption := CreateMenuCaption(I, Recent[i]); + M.FFileName := FRecent[i]; + M.OnClick := @DoOnRecentClick; + FPMRecent.Items.Add(M); + end; + end; +end; + +procedure TMRUMenuManager.LoadFromIni(Ini : TCustomIniFile; ASection : String); + +Var + I,Count : Integer; + FN : String; + +begin + FRecent.Clear; + FMaxRecent:=Ini.ReadInteger(ASection,KeyMaxRecent,10); + Count:=Ini.ReadInteger(ASection,KeyCount,0); + For I:=1 to Count do + begin + FN:=Ini.ReadString(ASection,Format(KeyFile,[i]),''); + If (FN<>'') then + FRecent.Add(FN); + end; +end; + +procedure TMRUMenuManager.GetFileNameAndSection(var AFileName, ASection: String); + +begin + if (AFileName='') then + begin + AFileName:=GetAppConfigDir(False); + AFileName:=IncludeTrailingPathDelimiter(AFileName)+DefaultIniFile; + end; + if (ASection='') then + ASection:=DefaultSection; +end; + +procedure TMRUMenuManager.LoadRecentFilesFromIni(Const AFileName : string = ''; Const ASection : String = ''); + +Var + DN,FN,Sec : String; + Ini : TIniFile; + +begin + FN:=AFileName; + Sec:=ASection; + GetFileNameAndSection(FN,Sec); + DN:=ExtractFilePath(FN); + If ForceDirectories(DN) then + begin + If FileExists(FN) then + begin + Ini:=TIniFile.Create(FN); + try + LoadFromIni(Ini,Sec); + finally + Ini.Free; + end; + end; + end; + ShowRecentFiles; +end; + +procedure TMRUMenuManager.SaveToIni(Ini : TCustomIniFile; ASection : String); + +Var + I : Integer; +begin + Ini.EraseSection(ASection); + Ini.WriteInteger(ASection,KeyMaxRecent,FMaxRecent); + Ini.WriteInteger(ASection,KeyCount,FRecent.Count); + For I:=0 to FRecent.Count-1 do + Ini.WriteString(ASection,Format(KeyFile,[i+1]),FRecent[i]); + Ini.UpdateFile; +end; + +procedure TMRUMenuManager.SaveRecentFilesToIni(Const AFileName : string = ''; Const ASection : String = ''); + +Var + DN,FN,Sec : String; + Ini : TMemIniFile; + +begin + FN:=AFileName; + Sec:=ASection; + GetFileNameAndSection(FN,Sec); + DN:=ExtractFilePath(FN); + If not ForceDirectories(DN) then + Raise EMRUManager.CreateFmt(SErrFailedToCreateDir,[DN]); + Ini:=TMemIniFile.Create(FN); + try + SaveToIni(Ini,Sec); + finally + Ini.Free; + end; +end; + +procedure TMRUMenuManager.SetIniFileName(const AValue:string); +begin + if AValue <> FIniFileName then begin + FIniFileName := AValue; + LoadRecentFilesFromIni(FIniFileName, FIniSection); + end; +end; + +procedure TMRUMenuManager.SetIniSection(const AValue:string); +begin + if AValue <> FIniSection then begin + FIniSection := AValue; + LoadRecentFilesFromini(FIniFileName, FIniSection); + end; +end; + +procedure TMRUMenuManager.SetMaxItemLength(const AValue:integer); +begin + if FMaxItemLength <> AValue then begin + FMaxItemLength := AValue; + ShowRecentFiles; + end; +end; + +procedure TMRUMenuManager.SetMenuCaptionMask(const AValue:string); +begin + if FMenuCaptionMask <> AValue then begin + FMenuCaptionMask := AValue; + ShowRecentFiles; + end; +end; + +procedure TMRUMenuManager.SetMIRecent(const AValue: TMenuItem); +begin + if FMIRecent=AValue then exit; + If Assigned(FMIRecent) then + FMIRecent.RemoveFreeNotification(Self); + FMIRecent:=AValue; + If Assigned(FMIRecent) then + FMIRecent.FreeNotification(Self); + ShowRecentFiles; +end; + +procedure TMRUMenuManager.SetPMRecent(const AValue: TPopupMenu); +begin + if FPMRecent=AValue then exit; + if Assigned(FPMRecent) then + FPMRecent.RemoveFreeNotification(self); + FPMRecent := AValue; + if Assigned(FPMRecent) then + FPMRecent.FreeNotification(self); + ShowRecentFiles; +end; + +procedure TMRUMenuManager.SetRecent(const AValue: TStrings); +begin + if FRecent=AValue then exit; + FRecent.Assign(AValue); + ShowRecentFiles; +end; + +procedure TMRUMenuManager.loaded; +begin + inherited loaded; + if (FRecent.Count>0) and (assigned(FMIRecent) or assigned(FPMRecent))then + LoadRecentFilesFromIni(FIniFileName, FIniSection); +end; + +constructor TMRUMenuManager.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + FRecent:=TStringList.Create; + FMaxItemLength := 80; + FMenuCaptionMask := DEFAULT_MASK; +end; + +destructor TMRUMenuManager.Destroy; +begin + SaveRecentFilesToIni(FIniFileName, FIniSection); + FreeAndNil(FRecent); + inherited Destroy; +end; + +procedure TMRUMenuManager.Notification(AComponent: TComponent; + Operation: TOperation); +begin + inherited Notification(AComponent, Operation); + if (Operation = opRemove) then begin + if AComponent = FMIRecent then FMIRecent := nil; + if AComponent = FPMRecent then FPMRecent := nil; + end; + { original code - I think this is not correct: + inherited Notification(AComponent, Operation); + if (Operation=opRemove) and ((AComponent=FMIRecent) or (AComponent=FPMRecent)) then + exit; + } +end; + +procedure TMRUMenuManager.DoOnRecentClick(Sender: TObject); +Var + FN : String; +begin + With (Sender as TRecentMenuItem) do + FN:=FileName; + if (FN<>'') and (OnRecentFile<>Nil) then + OnRecentFile(Self,FN); +end; + +end. + diff --git a/applications/biffexplorer/readme.txt b/applications/biffexplorer/readme.txt new file mode 100644 index 000000000..375ddcdfc --- /dev/null +++ b/applications/biffexplorer/readme.txt @@ -0,0 +1,20 @@ +BIFF Explorer +-------------------------------------------------------------------------------- + +"BIFF Explorer" is a tool to peek into the internal structure of a binary +Excel file in biff format. + +It displays a list of the BIFF records contained in the xls file along with +name and explanation as described in various documentation files (see "About"). +Selecting one of the records loads its bytes into a simple hex viewer; for the +most important records I tried to decipher the contents of the hex values and +display their meaning in a grid and a memo (page "Analysis"). For the other +records select a byte in the hex viewer, and the program will display the +contents of that byte and the following ones as integer, double, string +(page "Values"). + +For compiling, note that the program requires the following packages +from ccr +- "KControls" +- "lclextensions" +- "VirtualTreeview-new"