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
This commit is contained in:
wp_xxyyzz
2015-02-04 23:33:57 +00:00
parent 38e4cf0e0b
commit e50643cc13
5 changed files with 296 additions and 35 deletions

View File

@ -111,7 +111,7 @@
<PackageName Value="LCL"/> <PackageName Value="LCL"/>
</Item4> </Item4>
</RequiredPackages> </RequiredPackages>
<Units Count="10"> <Units Count="11">
<Unit0> <Unit0>
<Filename Value="BIFFExplorer.lpr"/> <Filename Value="BIFFExplorer.lpr"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
@ -131,6 +131,7 @@
<Unit3> <Unit3>
<Filename Value="bebiffutils.pas"/> <Filename Value="bebiffutils.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="beBIFFUtils"/>
</Unit3> </Unit3>
<Unit4> <Unit4>
<Filename Value="behtml.pas"/> <Filename Value="behtml.pas"/>
@ -161,6 +162,11 @@
<Filename Value="beabout.lfm"/> <Filename Value="beabout.lfm"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
</Unit9> </Unit9>
<Unit10>
<Filename Value="betypes.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="beTypes"/>
</Unit10>
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>

View File

@ -8,7 +8,7 @@ uses
{$ENDIF}{$ENDIF} {$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset Interfaces, // this includes the LCL widgetset
Forms, virtualtreeview_package, laz_fpspreadsheet, beabout, bebiffgrid, Forms, virtualtreeview_package, laz_fpspreadsheet, beabout, bebiffgrid,
bebiffutils, behtml, beutils, mrumanager, beMain; bebiffutils, behtml, beutils, mrumanager, beMain, beTypes;
{$R *.res} {$R *.res}

View File

@ -5,7 +5,7 @@ unit beBIFFGrid;
interface interface
uses uses
Classes, SysUtils, Controls, Grids, fpstypes, fpspreadsheet; Classes, SysUtils, Controls, Grids, fpstypes, fpspreadsheet, beTypes;
type type
TBIFFBuffer = array of byte; TBIFFBuffer = array of byte;
@ -18,6 +18,7 @@ type
FBuffer: TBIFFBuffer; FBuffer: TBIFFBuffer;
FBufferIndex: LongWord; FBufferIndex: LongWord;
FFormat: TsSpreadsheetFormat; FFormat: TsSpreadsheetFormat;
FInfo: Integer;
FCurrRow: Integer; FCurrRow: Integer;
FDetails: TStrings; FDetails: TStrings;
FOnDetails: TBIFFDetailsEvent; FOnDetails: TBIFFDetailsEvent;
@ -37,6 +38,7 @@ type
procedure ShowCodePage; procedure ShowCodePage;
procedure ShowColInfo; procedure ShowColInfo;
procedure ShowColWidth; procedure ShowColWidth;
procedure ShowContinue;
procedure ShowCountry; procedure ShowCountry;
procedure ShowDateMode; procedure ShowDateMode;
procedure ShowDBCell; procedure ShowDBCell;
@ -129,7 +131,8 @@ type
public public
constructor Create(AOwner: TComponent); override; constructor Create(AOwner: TComponent); override;
destructor Destroy; 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 published
property OnDetails: TBIFFDetailsEvent read FOnDetails write FOnDetails; property OnDetails: TBIFFDetailsEvent read FOnDetails write FOnDetails;
@ -338,6 +341,8 @@ begin
ShowPrintGridLines; ShowPrintGridLines;
$0031: $0031:
ShowFont; ShowFont;
$003C:
ShowContinue;
$003D: $003D:
ShowWindow1; ShowWindow1;
$003E, $023E: $003E, $023E:
@ -444,7 +449,7 @@ begin
end; end;
end; end;
{
procedure TBIFFGrid.SetRecordType(ARecType: Word; ABuffer: TBIFFBuffer; procedure TBIFFGrid.SetRecordType(ARecType: Word; ABuffer: TBIFFBuffer;
AFormat: TsSpreadsheetFormat); AFormat: TsSpreadsheetFormat);
begin begin
@ -456,7 +461,20 @@ begin
PopulateGrid; PopulateGrid;
if Assigned(FOnDetails) then FOnDetails(self, FDetails); if Assigned(FOnDetails) then FOnDetails(self, FDetails);
end; 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; procedure TBIFFGrid.ShowBackup;
var var
@ -1137,6 +1155,44 @@ begin
end; 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; procedure TBIFFGrid.ShowCountry;
var var
numBytes: Integer; numBytes: Integer;
@ -3240,13 +3296,165 @@ procedure TBIFFGrid.ShowObj;
var var
numBytes: Integer; numBytes: Integer;
w: Word; w: Word;
dw: DWord;
savedBufferIndex: Integer;
fieldType: Word;
fieldSize: Word;
s: String;
i, n: Integer;
guid: TGUID;
begin 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; numBytes := 2;
Move(FBuffer[FBufferIndex], w, numBytes); Move(FBuffer[FBufferIndex], w, numBytes);
@ -3331,6 +3539,8 @@ begin
end; end;
ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]), ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]),
'Flags'); 'Flags');
*)
end;
end; end;

View File

@ -6,26 +6,16 @@ interface
uses uses
ActnList, Classes, ComCtrls, ExtCtrls, Grids, Menus, StdCtrls, SysUtils, 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} {$ifdef USE_NEW_OLE}
fpolebasic, fpolebasic,
{$else} {$else}
fpolestorage, fpolestorage,
{$endif} {$endif}
fpstypes, fpSpreadsheet, fpstypes, fpSpreadsheet,
mrumanager, beBIFFGrid, types; mrumanager, beTypes, beBIFFGrid;
type type
{ Virtual tree node data }
TBiffNodeData = class
Offset: Integer;
RecordID: Integer;
RecordName: String;
RecordDescription: String;
Index: Integer;
destructor Destroy; override;
end;
{ TMainForm } { TMainForm }
TMainForm = class(TForm) TMainForm = class(TForm)
@ -152,6 +142,7 @@ type
aState: TGridDrawState); aState: TGridDrawState);
procedure DumpToFile(const AFileName: String); procedure DumpToFile(const AFileName: String);
procedure ExecFind(ANext, AKeep: Boolean); procedure ExecFind(ANext, AKeep: Boolean);
function GetBIFFNodeData: TBiffNodeData;
function GetNodeData(ANode: PVirtualNode): TBiffNodeData; function GetNodeData(ANode: PVirtualNode): TBiffNodeData;
function GetRecType: Word; function GetRecType: Word;
procedure LoadFile(const AFileName: String); overload; procedure LoadFile(const AFileName: String); overload;
@ -204,16 +195,6 @@ type
PObjectNodeData = ^TObjectNodeData; PObjectNodeData = ^TObjectNodeData;
{ TBiffNodeData }
destructor TBiffNodeData.Destroy;
begin
Finalize(RecordName);
Finalize(RecordDescription);
inherited;
end;
{ TMainForm } { TMainForm }
procedure TMainForm.AcAboutExecute(Sender: TObject); procedure TMainForm.AcAboutExecute(Sender: TObject);
@ -703,6 +684,16 @@ begin
end; 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; function TMainForm.GetNodeData(ANode: PVirtualNode): TBiffNodeData;
var var
ptr: PObjectNodeData; ptr: PObjectNodeData;
@ -907,7 +898,8 @@ end;
procedure TMainForm.PopulateAnalysisGrid; procedure TMainForm.PopulateAnalysisGrid;
begin begin
FAnalysisGrid.SetRecordType(GetRecType, FBuffer, FFormat); // FAnalysisGrid.SetRecordType(GetRecType, FBuffer, FFormat);
FAnalysisGrid.SetBIFFNodeData(GetBiffNodeData, FBuffer, FFormat);
end; end;
@ -1116,10 +1108,10 @@ var
p0: Cardinal; p0: Cardinal;
s: String; s: String;
i: Integer; i: Integer;
node: PVirtualNode; node, prevnode: PVirtualNode;
parentnode: PVirtualNode; parentnode: PVirtualNode;
ptr: PObjectNodeData; ptr: PObjectNodeData;
parentdata, data: TBiffNodeData; parentdata, data, prevdata: TBiffNodeData;
w: word; w: word;
crs: TCursor; crs: TCursor;
begin begin
@ -1203,6 +1195,21 @@ begin
node := BIFFTree.AddChild(parentnode); node := BIFFTree.AddChild(parentnode);
ptr := BIFFTree.GetNodeData(node); ptr := BIFFTree.GetNodeData(node);
ptr^.Data := data; 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 // advance stream pointer
AStream.Position := AStream.Position + recSize; AStream.Position := AStream.Position + recSize;
end; end;

View File

@ -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.