//---------------------------------------------------------------------------
//(R)CopyRight KivenSoft International ,inc 1999
//单元名称:数据库单元
//程序名称:电子书库
//作 者:李会文
//开始时间:1998.07.28
//最后修改:1999.07.22
//备注:此单元定义了SRM文件的读写接口
//---------------------------------------------------------------------------
unit SrmUnit;
interface
uses
Windows,SysUtils,Classes, SrmConst;
type
PCharArray=^TCharArray;
TCharArray=array[0..0] of char;
PDwordArray=^TDwordArray;
TDwordArray=array[0..0] of DWORD;
PIntArray=^TIntArray;
TIntArray=array[0..0] of integer;
PTreeData=^TTreeData;
TTreeData=record //索引信息
Pos: longint;
DataType: longint;
end;
PTdArray=^TTdArray;
TTdArray=array[0..0] of TTreeData;
PFileHead=^TFileHead;
TFileHead=record
OpenFlag :Longint; //数据库打开标志
LastViewPos :longint; //关闭时的位置
IndexPos :longint; //索引位置
EditDate :TDateTime; //最后修改日期
Key :DWORD; //密钥
Password :array[0..11] of char; //口令
Author :array[0..15] of char; //作者
BuildDate :TDateTime; //建库日期
DataType :longint; //数据库格式
DataFlag :array[0..7] of char; //数据库标志
Version :longint; //数据库版本
BookMerg :array[0..9] of longint; //10个书签
Keep :longint; //保留字段
end;
//DataType 0:Index 1:Normal Dir 2:Notmal TXT 3:Normal HTML
PIndexHead=^TIndexHead;
TIndexHead=record
DataType :longint; //段类型
Key :DWORD; //密钥
Count :longint; //标题数量
Size :longint; //标题长度
Keep :longint; //保留字段
end;
PDataHead=^TDataHead;
TDataHead=record
DataType :longint; //段类型
Author :array[0..15] of char; //作者
PubDate :TDateTime; //收录日期
Key :DWORD; //密钥
Num :longint; //附件数量
SearchKey :array[0..51] of char; //关键词
Password :array[0..11] of char; //口令
Keep :longint; //保留字段
end;
//------------------------------------------------------------------------
TSrmObject=class
private
FChangeFlag :boolean; //在文件后有增加新信息的变量
FDbChanged :boolean; //数据库属性有变动
FIndexChanged :boolean; //索引有变动
FItemHeadChanged :boolean; //标题头有变动
FItemDataChanged :boolean; //标题内容有变动
FFileHead :TFileHead;
FIndexHead :TIndexHead;
FDataHead :TDataHead;
FFileHandle :integer; //文件句柄
FFileName :string;
FActivePos :longint; //当前读出内容的位置
procedure Encypher(Cypher:DWORD;var Buf;Count:integer);
procedure Decypher(Cypher:DWORD;var Buf;Count:integer);
procedure CreateSrmFile; //建立一个空数据库
procedure SetDbChanged(Value:boolean);
procedure SetIndexChanged(Value:boolean);
procedure SetItemHeadChanged(Value:boolean);
procedure SetItemDataChanged(Value:boolean);
public
property FileName:string read FFileName;
property FileHead:TFileHead read FFileHead write FFileHead;
property IndexHead:TIndexHead read FIndexHead write FIndexHead;
property DataHead:TDataHead read FDataHead write FDataHead;
property ActivePos:longint read FActivePos write FActivePos;
property DbChanged:boolean read FDbChanged write SetDbChanged;
property IndexChanged:boolean read FIndexChanged write SetIndexChanged;
property ItemHeadChanged:boolean read FItemHeadChanged write SetItemHeadChanged;
property ItemDataChanged:boolean read FItemDataChanged write SetItemDataChanged;
constructor Create(Fn:string;Mode:Word);
destructor Destroy;override;
procedure Free;
procedure SaveSrmFile; //不关闭文件情况下保存文件
procedure LoadIndex(Msh,Msd:TMemoryStream); //读索引
procedure SaveIndex(Msh,Msd:TMemoryStream);//写索引
class function IsSrmFile(var Fn:string):boolean;//判断是否是SRM格式数据库文件
procedure ReadItemHead(Ps:longint); //读数据段头
procedure ReadItemData(DataStream:TMemoryStream); //读数据段内容
procedure EditItemHead(Ps:longint); //修改数据头
function AddItemHead:longint; //写数据段头
procedure AddItemData(DataStream:TMemoryStream); //写数据段内容
function GetItemPassword(Ps:longint):string; //得某标题口令
end;
//------------------------------------------------------------------------
implementation
//------------------------------------------------------------------------
procedure TSrmObject.Encypher(Cypher:DWORD;var Buf;Count:integer);
var
i:integer;
begin
Cypher:=not Cypher;
Dec(Count);
for i:=0 to Count do
TDwordArray(Buf)[i]:=TDwordArray(Buf)[i] xor Cypher;
end;
procedure TSrmObject.Decypher(Cypher:DWORD;var Buf;Count:integer);
var
i:integer;
begin
Cypher:=not Cypher;
Dec(Count);
for i:=0 to Count do
TDwordArray(Buf)[i]:=TDwordArray(Buf)[i] xor Cypher;
end;
//------------------------------------------------------------------------
procedure TSrmObject.CreateSrmFile; //建立一个空数据库
var
i:integer;
begin
FFileHandle:=FileCreate(FFileName);
if FFileHandle=-1 then raise Exception.Create(csCanNotCreate);
//添充缺省信息头
with FFileHead do
begin
OpenFlag:=0;
LastViewPos:=0;
IndexPos:=sizeof(TFileHead);
EditDate:=Now;
Key:=GetTickCount;
Password[0]:=#0;
Author[0]:=#0;
BuildDate:=EditDate;
DataType:=0;
Version:=3;
Keep:=0;
DataFlag[0]:='K';
DataFlag[1]:='i';
DataFlag[2]:='v';
DataFlag[3]:='e';
DataFlag[4]:='n';
for i:=0 to 9 do BookMerg[i]:=-1;
end;
//添充缺省索引
with FIndexHead do
begin
DataType:=0;
Key:=GetTickCount;
Count:=0;
Size:=0;
Keep:=0;
end;
//加密写入文件后解密
with FFileHead do
begin
Encypher(Key,Author,4);
Encypher(Key,Password,3);
FileWrite(FFileHandle,FFileHead,sizeof(TFileHead));
FileWrite(FFileHandle,FIndexHead,sizeof(TIndexHead));
Decypher(Key,Author,4);
Decypher(Key,Password,3);
end;
end;
constructor TSrmObject.Create(Fn:string;Mode:Word);
begin
FFileName:=Fn;
FChangeFlag:=false;
FDbChanged:=false;
FIndexChanged:=false;
FItemHeadChanged:=false;
FItemDataChanged:=false;
FDataHead.Keep:=0;
//打开文件时读文件头,否则建新文件
if Mode=fmOpenReadWrite then
begin
FFileHandle:=FileOpen(FFileName,fmOpenReadWrite);
FileRead(FFileHandle,FFileHead,sizeof(TFileHead));
with FFileHead do
begin
Decypher(Key,Password,3);
Decypher(Key,Author,4);
end;
end
else if Mode=fmCreate then
begin
CreateSrmFile;
end;
end;
destructor TSrmObject.Destroy;
begin
if FFileHandle<>-1 then
begin
FileClose(FFileHandle); //关闭文件
FFileHandle:=-1;
end;
inherited;
end;
procedure TSrmObject.Free;
begin
if Assigned(self) then Destroy;
end;
procedure TSrmObject.SaveSrmFile;
begin
//写入更新后的文件头
with FFileHead do
begin
FileSeek(FFileHandle,0,0);
EditDate:=Now;
Key:=GetTickCount;
Encypher(Key,Password,3);
Encypher(Key,Author,4);
FileWrite(FFileHandle,FFileHead,sizeof(TFileHead));
Decypher(Key,Password,3);
Decypher(Key,Author,4);
end;
end;
class function TSrmObject.IsSrmFile(var Fn:string):boolean;
var
Fh: TFileHead;
Fs: TFileStream;
begin
Result:=false;
Fs:=TFileStream.Create(Fn,fmOpenRead);
//根据文件长度判断
with Fs do
begin
if (Size<(sizeof(TFileHead)+sizeof(TIndexHead))) then
begin
Free;
Result:=false;
Exit;
end;
Read(Fh,sizeof(TFileHead));
Free;
end;
//根据文件标志判断
with Fh do
begin
if ( (DataFlag[0]='K') and (DataFlag[1]='i') and
(DataFlag[2]='v') and (DataFlag[3]='e') and
(DataFlag[4]='n') ) then
Result:=true;
end;
end;
procedure TSrmObject.LoadIndex(Msh,Msd:TMemoryStream);
begin
//读出索引头
FileSeek(FFileHandle,FFileHead.IndexPos,0);
FileRead(FFileHandle,FIndexHead,sizeof(TIndexHead));
//处理索引长度为0的状况,否则读出具体索引
if (FIndexHead.Size=0) then
begin
Msh.SetSize(0);
Msd.SetSize(0);
end
else
begin
Msh.SetSize(FIndexHead.Size);
Msd.SetSize(FIndexHead.Count*sizeof(longint)*2);
FileRead(FFileHandle,Msh.Memory^,Msh.Size);
FileRead(FFileHandle,Msd.Memory^,Msd.Size);
Decypher(FIndexHead.Key,Msh.Memory^,Msh.Size div 4);
end;
end;
procedure TSrmObject.SaveIndex(Msh,Msd:TMemoryStream);
begin
if FChangeFlag then //增加过新内容时
FFileHead.IndexPos:=FileSeek(FFileHandle,0,2)
else //无新增内容
FileSeek(FFileHandle,FFileHead.IndexPos,0);
//设置数据头
with FIndexHead do
begin
Key:=GetTickCount;
Size:=Msh.Size;
Count:=Msd.Size div (sizeof(longint)*2);
end;
//加密标题内容
Encypher(FIndexHead.Key,Msh.Memory^,Msh.Size div 4);
//写入索引
FileWrite(FFileHandle,FIndexHead,sizeof(TIndexHead));
FileWrite(FFileHandle,Msh.Memory^,Msh.Size);
FileWrite(FFileHandle,Msd.Memory^,Msd.Size);
end;
procedure TSrmObject.ReadItemHead(Ps:longint);
begin
FActivePos:=Ps; //保存当前数据位置
//读出并解密
with FDataHead do
begin
FileSeek(FFileHandle,Ps,0);
FileRead(FFileHandle,FDataHead,sizeof(TDataHead));
Decypher(Key,SearchKey,13);
Decypher(Key,Author,4);
Decypher(Key,Password,3);
end;
end;
procedure TSrmObject.ReadItemData(DataStream:TMemoryStream);
var
size:longint;
begin
//读出具体内容
FileRead(FFileHandle,size,sizeof(longint));
DataStream.SetSize(size);
FileRead(FFileHandle,DataStream.Memory^,size);
Decypher(FDataHead.Key,DataStream.Memory^,size div 4);
end;
procedure TSrmObject.EditItemHead(Ps:longint);
begin
//修改数据头
FileSeek(FFileHandle,Ps,0);
with FDataHead do
begin
Encypher(Key,SearchKey,13);
Encypher(Key,Author,4);
Encypher(Key,Password,3);
FileWrite(FFileHandle,FDataHead,sizeof(TDataHead));
Decypher(Key,SearchKey,13);
Decypher(Key,Author,4);
Decypher(Key,Password,3);
end;
end;
function TSrmObject.AddItemHead:longint;
begin
//新内容增加时移到文件尾添加
FChangeFlag:=true;
with FDataHead do
begin
Result:=FileSeek(FFileHandle,0,2);
Key:=GetTickCount;
PubDate:=Now;
Encypher(Key,SearchKey,13);
Encypher(Key,Author,4);
Encypher(Key,Password,3);
FileWrite(FFileHandle,FDataHead,sizeof(TDataHead));
Decypher(Key,SearchKey,13);
Decypher(Key,Author,4);
Decypher(Key,Password,3);
end;
end;
procedure TSrmObject.AddItemData(DataStream:TMemoryStream);
var
size:longint;
begin
size:=DataStream.Size;
FileWrite(FFileHandle,size,sizeof(longint));
Encypher(FDataHead.Key,DataStream.Memory^,size div 4);
FileWrite(FFileHandle,DataStream.Memory^,size);
end;
function TSrmObject.GetItemPassword(Ps:longint):string; //得某标题口令
var
Dh:TDataHead;
begin
//读出并解密
with Dh do
begin
FileSeek(FFileHandle,Ps,0);
FileRead(FFileHandle,Dh,sizeof(TDataHead));
Decypher(Key,Password,3);
end;
result:=string(Dh.Password);
end;
procedure TSrmObject.SetDbChanged(Value:boolean);
begin
FDbChanged:=Value;
end;
procedure TSrmObject.SetIndexChanged(Value:boolean);
begin
FIndexChanged:=Value;
if Value then FDbChanged:=Value;
end;
procedure TSrmObject.SetItemHeadChanged(Value:boolean);
begin
FItemHeadChanged:=Value;
if Value then
begin
FIndexChanged:=Value;
FDbChanged:=Value;
end;
end;
procedure TSrmObject.SetItemDataChanged(Value:boolean);
begin
FItemDataChanged:=Value;
if Value then
begin
FIndexChanged:=Value;
FDbChanged:=Value;
end;
end;
end.