unit beBIFFGrid; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Controls, Grids, fpspreadsheet; type TBIFFBuffer = array of byte; TBIFFDetailsEvent = procedure(Sender: TObject; ADetails: TStrings) of object; TBIFFGrid = class(TStringGrid) private FRecType: Word; FBuffer: TBIFFBuffer; FBufferIndex: LongWord; FFormat: TsSpreadsheetFormat; FCurrRow: Integer; FDetails: TStrings; FOnDetails: TBIFFDetailsEvent; function GetStringType: String; procedure ShowBackup; procedure ShowBlankCell; procedure ShowBOF; procedure ShowBookBool; procedure ShowBottomMargin; procedure ShowCalcCount; procedure ShowCalcMode; procedure ShowCellAddress; procedure ShowCellAddressRange; procedure ShowClrtClient; procedure ShowCodePage; procedure ShowColInfo; procedure ShowColWidth; procedure ShowCountry; procedure ShowDateMode; procedure ShowDBCell; procedure ShowDefColWidth; procedure ShowDefinedName; procedure ShowDefRowHeight; procedure ShowDelta; procedure ShowDimensions; procedure ShowDSF; procedure ShowEOF; procedure ShowExcel9File; procedure ShowExternalBook; 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 ShowInteger; procedure ShowInterfaceEnd; procedure ShowInterfaceHdr; procedure ShowIteration; procedure ShowIXFE; procedure ShowLabelCell; procedure ShowLabelSSTCell; procedure ShowLeftMargin; procedure ShowMergedCells; procedure ShowMMS; 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 ShowRow; procedure ShowSelection; procedure ShowSharedFormula; procedure ShowSheet; procedure ShowSheetPR; procedure ShowSST; procedure ShowStandardWidth; procedure ShowString; procedure ShowStyle; procedure ShowStyleExt; procedure ShowTabID; procedure ShowTopMargin; 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; ALenBytes: Byte; AUnicode: Boolean; out AString: String; out ANumBytes: Integer; IgnoreCompressedFlag: Boolean = false); procedure PopulateGrid; procedure ShowInRow(var ARow: Integer; var AOffs: LongWord; ASize: Word; AValue,ADescr: String); procedure ShowRowColData(var ABufIndex: LongWord); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure SetRecordType(ARecType: Word; ABuffer: TBIFFBuffer; AFormat: TsSpreadsheetFormat); published property OnDetails: TBIFFDetailsEvent read FOnDetails write FOnDetails; end; implementation uses StrUtils, Math, 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]; FDetails := TStringList.Create; 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; procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean; out AString: String; out ANumBytes: Integer; IgnoreCompressedFlag: Boolean = false); var ls: Integer; sa: ansiString; sw: WideString; w: Word; optn: Byte; begin 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 optn := FBuffer[ABufIndex + ALenBytes]; if (optn and $01 = 0) and (not IgnoreCompressedFlag) then begin // compressed --> 1 byte per character SetLength(sa, ls); ANumbytes := ls*SizeOf(AnsiChar) + ALenBytes + 1; Move(FBuffer[ABufIndex + ALenBytes + 1], sa[1], ls*SizeOf(AnsiChar)); AString := sa; end else begin SetLength(sw, ls); ANumBytes := ls*SizeOf(WideChar) + ALenBytes + 1; Move(FBuffer[ABufIndex + ALenBytes + 1], sw[1], ls*SizeOf(WideChar)); AString := UTF8Encode(WideStringLEToN(sw)); end; end else begin SetLength(sa, ls); ANumBytes := ls*SizeOf(AnsiChar) + ALenBytes; Move(FBuffer[ABufIndex + ALenBytes], sa[1], ls*SizeOf(AnsiChar)); AString := AnsiToUTF8(sa); 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; $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; $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; $00D7: ShowDBCell; $00DA: ShowBookBool; $00E0: ShowXF; $00E1: ShowInterfaceHdr; $00E2: ShowInterfaceEnd; $00E5: ShowMergedCells; $00FC: ShowSST; $00FD: ShowLabelSSTCell; $013D: ShowTabID; $0161: ShowDSF; $01AE: ShowExternalBook; $01AF: ShowProt4Rev; $01B7: ShowRefreshAll; $01BC: ShowProt4RevPass; $01C0: ShowExcel9File; $027E: ShowRK; $0293: ShowStyle; $04BC: ShowSharedFormula; $087C: ShowXFCRC; $087D: ShowXFEXT; $0892: ShowStyleExt; $105C: ShowClrtClient; else RowCount := 2; Rows[1].Clear; end; end; procedure TBIFFGrid.SetRecordType(ARecType: Word; ABuffer: TBIFFBuffer; AFormat: TsSpreadsheetFormat); begin FFormat := AFormat; FRecType := ARecType; 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.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; { 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, r2, c2: Integer; s: String; begin if FFormat = 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.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; begin BeginUpdate; RowCount := FixedRows + 1000; // Brute force simplification because of unknown row count at this point // Will be reduced at the end. 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); 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); ShowInRow(FCurrRow, FBufferIndex, numBytes, ansiStr, 'Character array of the name'); 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; ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Name (Unicode string without length field)'); end; end; firstTokenBufIdx := FBufferIndex; while FBufferIndex < firstTokenBufIdx + formulaSize do begin token := FBuffer[FBufferIndex]; numBytes := 1; case token of $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)), '3D reference, 1-based index to EXTERNSHEET record'); numBytes := 8; ShowInRow(FCurrRow, FBufferIndex, numBytes, '', 'Not used'); numBytes := 2; Move(FBuffer[FBufferIndex], w, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to first referenced sheet ($FFFF = deleted sheet)'); Move(FBuffer[FBufferIndex], w, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), '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; // 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.ShowExternalBook; var numBytes: Integer; w: Word; wideStr: WideString; ansiStr: AnsiString; s: String; i, n: Integer; 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 SetLength(ansiStr, 1); Move(FBuffer[FBufferIndex], ansiStr[1], 1); 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 (s[1] = ':') and (Length(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 Delete(s, 1, 1); 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 EXTERNALBOOK record', [i])); Move(FBuffer[FBufferIndex], w, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), Format('REF #%d: Index to first sheet in EXTERNALBOOK sheet list', [i])); Move(FBuffer[FBufferIndex], w, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), Format('REF #%d: Index to last sheet in EXTERNALBOOK 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, IntToStr(w), '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('Bit $0001 = 1: bold (redundant in BIFF5-BIFF8)'); if w and $0002 = 0 then FDetails.Add('Bit $0002 = 0: not italic') else FDetails.Add('Bit $0002 = 1: italic'); if w and $0004 = 0 then FDetails.Add('Bit $0004 = 0: not underlined') else FDetails.Add('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('Bit $0008 = 1: struck out'); if w and $0010 = 0 then FDetails.Add('Bit $0010 = 0: not outlined') else FDetails.Add('Bit $0010 = 1: outlined'); if w and $0020 = 0 then FDetails.Add('Bit $0020 = 0: not shadowed') else FDetails.Add('Bit $0020 = 1: shadowed'); if w and $0040 = 0 then FDetails.Add('Bit $0040 = 0: not condensed') else FDetails.Add('Bit $0040 = 1: condensed'); if w and $0080 = 0 then FDetails.Add('Bit $0080 = 0: not extended') else FDetails.Add('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(2), '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, '', '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; 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; 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('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; // 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; 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; 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; 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.ShowInRow(var ARow: Integer; var AOffs: LongWord; ASize: Word; AValue,ADescr: String); begin Cells[0, ARow] := IntToStr(AOffs); Cells[1, ARow] := IntToStr(ASize); Cells[2, ARow] := AValue; 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('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'); 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; 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('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; 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; 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, 4); // 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('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('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('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)); 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('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; // 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; begin RowCount := FixedRows + 5; numBytes := 2; Move(FBuffer[FBufferIndex], w, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [WordLEToN(w)]), 'ft (must be $15)'); numBytes := 2; Move(FBuffer[FBufferIndex], w, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [WordLEToN(w)]), 'cb (must be $12)'); numBytes := 2; w := WordLEToN(w); Move(FBuffer[FBufferIndex], w, numBytes); if Row = FCurrRow then begin FDetails.Add('Object type:'#13); case w of $00: FDetails.Add('$00 = Group'); $01: FDetails.Add('$01 = Line'); $02: FDetails.Add('$02 = Rectangle'); $03: FDetails.Add('$03 = Oval'); $04: FDetails.Add('$04 = Arc'); $05: FDetails.Add('$05 = Chart'); $06: FDetails.Add('$06 = Text'); $07: FDetails.Add('$07 = Button'); $08: FDetails.Add('$08 = Picture'); $09: FDetails.Add('$09 = Polygon'); $0B: FDetails.Add('$0B = Checkbox'); $0C: FDetails.Add('$0C = Radio button'); $0D: FDetails.Add('$0D = Edit box'); $0E: FDetails.Add('$0E = Label'); $0F: FDetails.Add('$0F = Dialog box'); $10: FDetails.Add('$10 = Spin control'); $11: FDetails.Add('$11 = Scrollbar'); $12: FDetails.Add('$12 = List'); $13: FDetails.Add('$13 = Group box'); $14: FDetails.ADd('$14 = Dropdown list'); $19: FDetails.Add('$19 = Note'); $1E: FDetails.Add('$1E = OfficeArt object'); else FDetails.Add(IntToStr(w) + ' = (unknown object)'); end; end; ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]), 'Object type (ot)'); numBytes := 2; Move(FBuffer[FBufferIndex], w, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [WordLEToN(w)]), 'Object ID'); numBytes := 2; Move(FBuffer[FBufferIndex], w, numBytes); w := WordLEToN(w); if Row = FCurrRow then begin FDetails.Add('Object flags:'#13); if w and $0001 <> 0 then FDetails.Add('Bit $0001 = 1: Object is locked') else FDetails.Add('Bit $0001 = 0: Object is NOT locked'); if w and $0002 <> 0 then FDetails.Add('Bit $0002 = 1: Reserved - must be zero!!!d') else FDetails.Add('Bit $0002 = 0: Reserved - must be zero'); if w and $0004 <> 0 then FDetails.Add('Bit $0004 = 1: Application is expected to choose object size') else FDetails.Add('Bit $0004 = 0: Application is NOT expected to choose object size'); if w and $0008 <> 0 then FDetails.Add('Bit $0008 = 1: Is a chart that is expected to be published when sheet is published') else FDetails.Add('Bit $0008 = 0: Is NOT a chart that is expected to be published when sheet is published'); if w and $0010 <> 0 then FDetails.Add('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'); FDetails.Add('Bit $0020 : unused'); FDetails.Add('Bit $0040 : unused'); if w and $0080 <> 0 then FDetails.Add('Bit $0080 = 1: Object is disabled') else FDetails.ADd('Bit $0080 = 0: Object is NOT disabled'); if w and $0100 <> 0 then FDetails.Add('Bit $0100 = 1: is an auxiliary object that can only be automatically inserted by the application') else FDetails.Add('Bit $0100 = 0: is NOT an auxiliary object that can only be automatically inserted by the application'); if w and $0200 <> 0 then FDetails.Add('Bit $0200 = 1: is expected to be updated on load to reflect the values in the range associated with the object') else FDetails.Add('Bit $0200 = 0: is NOT expected to be updated on load to reflect the values in the range associated with the object'); FDetails.Add('Bit $0400 : unused'); FDetails.Add('Bit $0800 : unused'); if w and $1000 <> 0 then FDetails.Add('Bit $1000 = 1: is expected to be updated whenever the value of a cell in the range associated with the object changes') else FDetails.Add('Bit $1000 = 0: is NOT expected to be updated whenever the value of a cell in the range associated with the object changes'); end; ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]), 'Flags'); 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('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('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('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.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('Bit $0001 = 1: Show automatic page breaks'); if w and $0010 = 0 then FDetails.Add('Bit $0010 = 0: Standard sheet') else FDetails.Add('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('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('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('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('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('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('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('Bit $0800 = 1: Show column outline symbols'); case (w and $3000) shr 12 of 0: FDetails.Add('Bits $3000 = $0000: Arrange windows tiled'); 1: FDetails.Add('Bits $3000 = $1000: Arrange windows horizontal'); 2: FDetails.Add('Bits $3000 = $2000: Arrange windows vertical'); 3: FDetails.Add('Bits $3000 = $3000: Arrange windows cascaded'); end; if w and $4000 = 0 then FDetails.Add('Bits $4000 = 0: Excel like expression evaluation (BIFF4-BIFF8 only)') else FDetails.Add('Bits $4000 = 1: Lotus like expression evaluation (BIFF4-BIFF8 only)'); if w and $8000 = 0 then FDetails.Add('Bits $8000 = 0: Excel like formula editing (BIFF4-BIFF8 only)') else FDetails.Add('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: Integer; begin numBytes := 4; Move(FBuffer[FBufferIndex], total1, numBytes); Move(FBuffer[FBufferIndex+4], total2, numBytes); total1 := DWordLEToN(total1); total2 := DWordLEToN(total2); RowCount := FixedRows + 2 + total2; ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(total1), 'Total number of shared strings in the workbook'); ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(total2), 'Number of following strings'); for i:=1 to total2 do begin ExtractString(FBufferIndex, 2, true, s, numBytes); ShowInRow(FCurrRow, FBufferIndex, numBytes, s, Format('Shared string #%d', [i])); end; 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('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('Bit 0 = 1: built-in style'); if b and $02 = 0 then FDetails.Add('Bit 1 = 0: NOT hidden') else FDetails.Add('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.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('Bit $0001 = 1: Window is hidden'); if w and $0002 = 0 then FDetails.Add('Bit $0002 = 0: Window is open') else FDetails.Add('Bit $0002 = 1: Window is minimized'); if w and $0008 = 0 then FDetails.Add('Bit $0008 = 0: Horizontal scrollbar hidden') else FDetails.Add('Bit $0008 = 1: Horizontal scrollbar visible'); if w and $0010 = 0 then FDetails.Add('Bit $0010 = 0: Vertical scrollbar hidden') else FDetails.Add('Bit $0010 = 1: Vertical scrollbar visible'); if w and $0020 = 0 then FDetails.Add('Bit $0020 = 0: Worksheet tab bar hidden') else FDetails.Add('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('Bit $0001 = 1: Show formulas'); if w and $0002 = 0 then FDetails.Add('Bit $0002 = 0: Do not show grid lines') else FDetails.Add('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('Bit $0004 = 1: Show sheet headers'); if w and $0008 = 0 then FDetails.Add('Bit $0008 = 0: Panes are not frozen') else FDetails.Add('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('Bit $0010 = 1: Show zero values'); if w and $0020 = 0 then FDetails.Add('Bit $0020 = 0: Manual grid line color') else FDetails.Add('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('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('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('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('Bit $0200 = 1: Sheet selected'); if w and $0400 = 0 then FDetails.Add('Bit $0400 = 0: Sheet not active') else FDetails.Add('Bit $0400 = 1: Sheet active'); if w and $0800 = 0 then FDetails.Add('Bit $0800 = 0: Show in normal view') else FDetails.Add('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('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: Don''t 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('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('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.