From e50643cc13069488614099185f8d6eea5e156b8a Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Wed, 4 Feb 2015 23:33:57 +0000 Subject: [PATCH] fpspreadsheet: More details on comment-related BIFF8 records in BIFFExplorer git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3927 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../reference/BIFFExplorer/BIFFExplorer.lpi | 8 +- .../reference/BIFFExplorer/BIFFExplorer.lpr | 2 +- .../reference/BIFFExplorer/bebiffgrid.pas | 226 +++++++++++++++++- .../reference/BIFFExplorer/bemain.pas | 57 +++-- .../reference/BIFFExplorer/betypes.pas | 38 +++ 5 files changed, 296 insertions(+), 35 deletions(-) create mode 100644 components/fpspreadsheet/reference/BIFFExplorer/betypes.pas diff --git a/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpi b/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpi index 4056bbc5b..153eb8ebe 100644 --- a/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpi +++ b/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpi @@ -111,7 +111,7 @@ - + @@ -131,6 +131,7 @@ + @@ -161,6 +162,11 @@ + + + + + diff --git a/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpr b/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpr index 0405991f8..7c4ed43a0 100644 --- a/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpr +++ b/components/fpspreadsheet/reference/BIFFExplorer/BIFFExplorer.lpr @@ -8,7 +8,7 @@ uses {$ENDIF}{$ENDIF} Interfaces, // this includes the LCL widgetset Forms, virtualtreeview_package, laz_fpspreadsheet, beabout, bebiffgrid, - bebiffutils, behtml, beutils, mrumanager, beMain; + bebiffutils, behtml, beutils, mrumanager, beMain, beTypes; {$R *.res} diff --git a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas index cd17afadd..1c5c72607 100644 --- a/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas +++ b/components/fpspreadsheet/reference/BIFFExplorer/bebiffgrid.pas @@ -5,7 +5,7 @@ unit beBIFFGrid; interface uses - Classes, SysUtils, Controls, Grids, fpstypes, fpspreadsheet; + Classes, SysUtils, Controls, Grids, fpstypes, fpspreadsheet, beTypes; type TBIFFBuffer = array of byte; @@ -18,6 +18,7 @@ type FBuffer: TBIFFBuffer; FBufferIndex: LongWord; FFormat: TsSpreadsheetFormat; + FInfo: Integer; FCurrRow: Integer; FDetails: TStrings; FOnDetails: TBIFFDetailsEvent; @@ -37,6 +38,7 @@ type procedure ShowCodePage; procedure ShowColInfo; procedure ShowColWidth; + procedure ShowContinue; procedure ShowCountry; procedure ShowDateMode; procedure ShowDBCell; @@ -129,7 +131,8 @@ type public constructor Create(AOwner: TComponent); override; destructor Destroy; override; - procedure SetRecordType(ARecType: Word; ABuffer: TBIFFBuffer; AFormat: TsSpreadsheetFormat); + procedure SetBIFFNodeData(AData: TBIFFNodeData; ABuffer: TBIFFBuffer; AFormat: TsSpreadsheetFormat); +// procedure SetRecordType(ARecType: Word; ABuffer: TBIFFBuffer; AFormat: TsSpreadsheetFormat); published property OnDetails: TBIFFDetailsEvent read FOnDetails write FOnDetails; @@ -338,6 +341,8 @@ begin ShowPrintGridLines; $0031: ShowFont; + $003C: + ShowContinue; $003D: ShowWindow1; $003E, $023E: @@ -444,7 +449,7 @@ begin end; end; - + { procedure TBIFFGrid.SetRecordType(ARecType: Word; ABuffer: TBIFFBuffer; AFormat: TsSpreadsheetFormat); begin @@ -456,7 +461,20 @@ begin PopulateGrid; if Assigned(FOnDetails) then FOnDetails(self, FDetails); end; +} +procedure TBIFFGrid.SetBIFFNodeData(AData: TBIFFNodeData; ABuffer: TBIFFBuffer; + AFormat: TsSpreadsheetFormat); +begin + FFormat := AFormat; + FRecType := AData.RecordID; + FInfo := AData.Tag; + SetLength(FBuffer, Length(ABuffer)); + if Length(FBuffer) > 0 then + Move(ABuffer[0], FBuffer[0], Length(FBuffer)); + PopulateGrid; + if Assigned(FOnDetails) then FOnDetails(self, FDetails); +end; procedure TBIFFGrid.ShowBackup; var @@ -1137,6 +1155,44 @@ begin end; +procedure TBIFFGrid.ShowContinue; +var + numbytes: Integer; + s: String; + sa: ansistring; + sw: widestring; + ls: Integer; + i: Integer; +begin + case FInfo of + BIFFNODE_TXO_CONTINUE1: + begin + RowCount := FixedRows + 1; + numbytes := Length(FBuffer); + if FBuffer[FBufferIndex] = 0 then begin + ls := Length(FBuffer)-1; + SetLength(sa, ls); + Move(FBuffer[FBufferIndex+1], sa[1], ls); + s := AnsiToUTF8(sa); + end else + if FBuffer[FBufferIndex] = 1 then begin + ls := (Length(FBuffer) - 1) div SizeOf(WideChar); + SetLength(sw, ls); + Move(FBuffer[FBufferIndex+1], sw[1], ls*SizeOf(WideChar)); + s := UTF8Encode(sw); + end else + s := 'ERROR!!!'; + s := UTF8StringReplace(s, #$0A, '[/n]', [rfReplaceAll]); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Comment text'); + end; + + BIFFNODE_TXO_CONTINUE2: + begin + end; + end; +end; + + procedure TBIFFGrid.ShowCountry; var numBytes: Integer; @@ -3240,13 +3296,165 @@ procedure TBIFFGrid.ShowObj; var numBytes: Integer; w: Word; + dw: DWord; + savedBufferIndex: Integer; + fieldType: Word; + fieldSize: Word; + s: String; + i, n: Integer; + guid: TGUID; begin - RowCount := FixedRows + 5; + if FFormat = sfExcel8 then begin + n := 0; + RowCount := FixedRows + 1000; + while FBufferIndex < Length(FBuffer) do begin + savedBufferIndex := FBufferIndex; + numBytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + fieldType := WordLEToN(w); + case fieldType of + $00: s := 'ftEnd (End of OBJ record)'; + $01: s := '(Reserved)'; + $02: s := '(Reserved)'; + $03: s := '(Reserved)'; + $04: s := 'ftMacro (fmla-style macro)'; + $05: s := 'ftButton (Command button)'; + $06: s := 'ftGmo (Group marker)'; + $07: s := 'ftCf (Clipboard format)'; + $08: s := 'ftPioGrbit (Picture option flags)'; + $09: s := 'ftPictFmla (Picture fmla-style macro)'; + $0A: s := 'ftCbls (Checkbox link)'; + $0B: s := 'ftRbo (Radio button)'; + $0C: s := 'ftSbs (Scrollbar)'; + $0D: s := 'ftNts (Note structure)'; + $0E: s := 'ftSbsFmla (Scroll bar fmla-style macro)'; + $0F: s := 'ftGboData (Group box data)'; + $10: s := 'ftEdoData (Edit control data)'; + $11: s := 'ftRboData (Radio button data)'; + $12: s := 'ftCblsData (Check box data)'; + $13: s := 'ftLbsData (List box data)'; + $14: s := 'ftCblsFmla (Check box link fmla-style macro)'; + $15: s := 'ftCmo (Common object data)'; + else s := '(unknown)'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [fieldType]), + Format('Subrecord type: %s', [s])); + inc(n); + + Move(FBuffer[FBufferIndex], w, numBytes); + fieldSize := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(fieldSize), 'Size of subrecord'); + inc(n); + + case fieldType of + $000D: + begin // ftNts, from https://msdn.microsoft.com/en-us/library/office/dd951373%28v=office.12%29.aspx + numBytes := 16; + Move(FBuffer[FBufferIndex], guid, numBytes); + s := GuidToString(guid); + ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'GUID of comment'); + inc(n); + + numbytes := 2; + Move(FBuffer[FBufferIndex], w, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)), + 'Shared Note (0 = false, 1 = true)'); + inc(n); + + numBytes := 4; + Move(FBuffer[FBufferIndex], dw, numBytes); + ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), + 'Unused (undefined, must be ignored)'); + inc(n); + end; + + $0015: + begin // common object data + Move(FBuffer[FBufferIndex], w, numBytes); + w := WordLEToN(w); + case w of + $00: s := 'Group'; + $01: s := 'Line'; + $02: s := 'Rectangle'; + $03: s := 'Oval'; + $04: s := 'Arc'; + $05: s := 'Chart'; + $06: s := 'Text'; + $07: s := 'Button'; + $08: s := 'Picture'; + $09: s := 'Polybon'; + $0A: s := '(Reserved)'; + $0B: s := 'Checkbox'; + $0C: s := 'Option button'; + $0D: s := 'Edit box'; + $0E: s := 'Label'; + $0F: s := 'Dialog box'; + $10: s := 'Spinner'; + $11: s := 'Scrollbar'; + $12: s := 'List box'; + $13: s := 'Group box'; + $14: s := 'Combobox'; + $15: s := '(Reserved)'; + $16: s := '(Reserved)'; + $17: s := '(Reserved)'; + $18: s := '(Reserved)'; + $19: s := 'Comment'; + $1A: s := '(Reserved)'; + $1B: s := '(Reserved)'; + $1C: s := '(Reserved)'; + $1D: s := '(Reserved)'; + $1E: s := 'Microsoft Office drawing'; + else s := '(Unknown)'; + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]), + 'Object type: '+s); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.04x', [w]), + 'Object ID number'); + inc(n); + + Move(FBuffer[FBufferIndex], w, numbytes); + w := WordLEToN(w); + if Row = FCurrRow then begin + FDetails.Add('Option flags:'#13); + if w and $0001 <> 0 + then FDetails.Add('Bit $0001 = 1: Object is locked when sheet is protected.') + else FDetails.Add('Bit $0001 = 0: Object is NOT locked when sheet is protected.'); + if w and $000E <> 0 + then FDetails.Add('Bit $0002 <> 0: Reserved - must be zero - THIS SEEMS TO BE AN ERROR!') + else FDetails.Add('Bit $0002 = 0: Reserved - must be zero'); + if w and $0010 <> 0 + then FDetails.Add('Bit $0010 = 1: Image of this object is intended to be included when printing') + else FDetails.Add('Bit $0010 = 0: Image of this object is NOT intended to be included when printing'); + if w and $1FE0 <> 0 + then FDetails.Add('Bits 12-5 <> 0: Reserved - must be zero - THIS SEEMS TO BE AN ERROR!') + else FDetails.Add('Bits 12-5 = 0: Reserved - must be zero'); + if w and $2000 <> 0 + then FDetails.Add('Bit $2000 = 1: Object uses automatic fill style.') + else FDetails.Add('Bit $2000 = 0: Object does NOT use automatic fill style.'); + if w and $4000 <> 0 + then FDetails.Add('Bit $4000 = 1: Object uses automatic line style.') + else FDetails.Add('Bit $4000 = 0: Object does NOT use automatic line style.'); + if w and $8000 <> 0 + then FDetails.Add('Bit $8000 = 1: Reserved - must be zero - THIS SEEMS TO BE AN ERROR!') + else FDetails.Add('Bit $8000 = 0: Reserved - must be zero.'); + end; + ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]), + 'Option flags'); + inc(n); + end; + end; + FBufferIndex := savedBufferIndex + 4 + fieldSize; + end; + RowCount := FixedRows + n; + end else + if FFormat = sfExcel5 then begin + (* + - 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); @@ -3331,6 +3539,8 @@ begin end; ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]), 'Flags'); + *) + end; end; diff --git a/components/fpspreadsheet/reference/BIFFExplorer/bemain.pas b/components/fpspreadsheet/reference/BIFFExplorer/bemain.pas index be5670872..786823cc7 100644 --- a/components/fpspreadsheet/reference/BIFFExplorer/bemain.pas +++ b/components/fpspreadsheet/reference/BIFFExplorer/bemain.pas @@ -6,26 +6,16 @@ interface uses ActnList, Classes, ComCtrls, ExtCtrls, Grids, Menus, StdCtrls, SysUtils, - FileUtil, Forms, Controls, Graphics, Dialogs, Buttons, VirtualTrees, + FileUtil, Forms, Controls, Graphics, Dialogs, Buttons, Types, VirtualTrees, {$ifdef USE_NEW_OLE} fpolebasic, {$else} fpolestorage, {$endif} fpstypes, fpSpreadsheet, - mrumanager, beBIFFGrid, types; + mrumanager, beTypes, beBIFFGrid; type - { Virtual tree node data } - TBiffNodeData = class - Offset: Integer; - RecordID: Integer; - RecordName: String; - RecordDescription: String; - Index: Integer; - destructor Destroy; override; - end; - { TMainForm } TMainForm = class(TForm) @@ -152,6 +142,7 @@ type aState: TGridDrawState); procedure DumpToFile(const AFileName: String); procedure ExecFind(ANext, AKeep: Boolean); + function GetBIFFNodeData: TBiffNodeData; function GetNodeData(ANode: PVirtualNode): TBiffNodeData; function GetRecType: Word; procedure LoadFile(const AFileName: String); overload; @@ -204,16 +195,6 @@ type PObjectNodeData = ^TObjectNodeData; -{ TBiffNodeData } - -destructor TBiffNodeData.Destroy; -begin - Finalize(RecordName); - Finalize(RecordDescription); - inherited; -end; - - { TMainForm } procedure TMainForm.AcAboutExecute(Sender: TObject); @@ -703,6 +684,16 @@ begin end; +function TMainForm.GetBIFFNodeData: TBiffNodeData; +begin + Result := nil; + if BiffTree.FocusedNode <> nil then begin + Result := GetNodeData(BiffTree.FocusedNode); + if Result <> nil then + MemStream.Position := Result.Offset; + end; +end; + function TMainForm.GetNodeData(ANode: PVirtualNode): TBiffNodeData; var ptr: PObjectNodeData; @@ -907,7 +898,8 @@ end; procedure TMainForm.PopulateAnalysisGrid; begin - FAnalysisGrid.SetRecordType(GetRecType, FBuffer, FFormat); +// FAnalysisGrid.SetRecordType(GetRecType, FBuffer, FFormat); + FAnalysisGrid.SetBIFFNodeData(GetBiffNodeData, FBuffer, FFormat); end; @@ -1116,10 +1108,10 @@ var p0: Cardinal; s: String; i: Integer; - node: PVirtualNode; + node, prevnode: PVirtualNode; parentnode: PVirtualNode; ptr: PObjectNodeData; - parentdata, data: TBiffNodeData; + parentdata, data, prevdata: TBiffNodeData; w: word; crs: TCursor; begin @@ -1203,6 +1195,21 @@ begin node := BIFFTree.AddChild(parentnode); ptr := BIFFTree.GetNodeData(node); ptr^.Data := data; + // Store info on CONTINUE records + if recType = $003C then begin // CONTINUE record + prevnode := BIFFTree.GetPrevious(node); + prevdata := GetNodeData(prevnode); + if prevdata.RecordID = $01B6 then // TXO record + data.Tag := BIFFNODE_TXO_CONTINUE1 + else + if prevdata.RecordID = $003C then // CONTINUE record + begin + prevnode := BIFFTree.GetPrevious(prevnode); + prevdata := GetNodeData(prevnode); + if prevdata.RecordID = $01B6 then // TXO record + data.Tag := BIFFNODE_TXO_CONTINUE2; + end; + end; // advance stream pointer AStream.Position := AStream.Position + recSize; end; diff --git a/components/fpspreadsheet/reference/BIFFExplorer/betypes.pas b/components/fpspreadsheet/reference/BIFFExplorer/betypes.pas new file mode 100644 index 000000000..da81a3340 --- /dev/null +++ b/components/fpspreadsheet/reference/BIFFExplorer/betypes.pas @@ -0,0 +1,38 @@ +unit beTypes; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils; + +const + BIFFNODE_TXO_CONTINUE1 = 1; + BIFFNODE_TXO_CONTINUE2 = 2; + +type + { Virtual tree node data } + TBiffNodeData = class + Offset: Integer; + RecordID: Integer; + RecordName: String; + RecordDescription: String; + Index: Integer; + Tag: Integer; + destructor Destroy; override; + end; + +implementation + +{ TBiffNodeData } + +destructor TBiffNodeData.Destroy; +begin + Finalize(RecordName); + Finalize(RecordDescription); + inherited; +end; + +end. +