📄 xqfilerw.pas
字号:
///////////////////////////////////////////////////////////////////////////////
//
// XQStduio Source Code (http://www.qipaile.net/xqstudio)
//
// Copyright (c) 1998-2008, DONG Shiwei (董世伟 or 过河象)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1) Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2) Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// 3) Neither the name of the XQStudio nor the names of its contributors
// may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////////
//
// Note: Some characters of this file are Simplified Chinese characters
// encoded with GB2312/GB18030 standard
//
unit XQFileRW;
interface
uses
Windows, SysUtils, Classes, Graphics, Forms, Controls, Menus,
StdCtrls, Dialogs, Buttons, Messages, ExtCtrls, ComCtrls, StdActns,
ActnList, ToolWin, ImgList, XQSystem, dDelphiS, XQDataT, XQPNode;
type
//-----------------------------------------------------------------------
// 象棋文件头记录的定义
//.......................................................................
dTXQFHead = packed record // 定义象棋棋谱文件头
Signature : dTWord; // 文件标记 'XQ' = $5158;
Version : dTByte; // 版本号
KeyMask : dTByte; // 加密掩码
ProductId : dTDWord; // 产品号(厂商的产品号)
KeyOrA : dTByte;
KeyOrB : dTByte;
KeyOrC : dTByte;
KeyOrD : dTByte;
KeysSum : dTByte; // 加密的钥匙和
KeyXY : dTByte; // 棋子布局位置钥匙
KeyXYf : dTByte; // 棋谱起点钥匙
KeyXYt : dTByte; // 棋谱终点钥匙
// = 16 bytes
QiziXY : dTXQZXY; // 32个棋子的原始位置
// = 48 bytes
PlayStepNo: dTWord; // 棋谱文件的开始步数
WhoPlay : dTByte; // 该谁下
PlayResult: dTByte; // 最终结果
PlayNodes : dTDWord; // 本棋谱一共记录了多少步
PTreePos : dTDWord; // 对弈树在文件中的起始位置
Reserved1 : array [1..4] of dTByte;
// = 64 bytes
CodeA : dTWord; // 对局类型(开,中,残等)
CodeB : dTWord; // 另外的类型
CodeC : dTWord; //
CodeD : dTWord;
CodeE : dTWord;
CodeF : dTWord;
CodeH : dTWord;
CodeG : dTWord;
// = 80 bytes
TitleA : String[63]; // 标题
TitleB : String[63];
// = 208 bytes
MatchName : String[63]; // 比赛名称
MatchTime : String[15]; // 比赛时间
MatchAddr : String[15]; // 比赛地点
RedPlayer : String[15]; // 红方姓名
BlkPlayer : String[15]; // 黑方姓名
// = 336 bytes
TimeRule : String[63]; // 开局类型
RedTime : String[15];
BlkTime : String[15];
Reservedh : String[31];
// = 464 bytes
RMKWriter : String[15]; // 棋谱评论员
Author : STring[15]; // 文件的作者
// = 496 bytes
Reserved2 : array [1..16] of dTByte;
// = 512 bytes
Reserved3 : array [1..512] of dTByte;
end;
//-----------------------------------------------------------------------
// 象棋文件对弈记录的定义
//.......................................................................
dTXQFPlayNode = packed record
XYf, XYt, ChildTag, Reserved: dTByte;
RemarkSize: dTDWord;
end;
//-----------------------------------------------------------------------
// 象棋文件类
//.......................................................................
dTXQFile = class
public
Name : String; // 文件名
XQPlayTree : dTXQPlayNode; // 象棋对局树
XQFHead : dTXQFHead; // 象棋文件头
isDisableRmk: dTBoolean;
isReverseH : dTBoolean;
KeyXY : dTByte; // 棋子32个位置加密因子
KeyXYf : dTByte; // 棋谱加密因子(起点)
KeyXYt : dTByte; // 棋谱加密因子(终点)
KeyRMKSize : dTWord; // 注解大小加密因子
function iLoadXQFile(OnlyHead:Boolean=False): dTINT32; // 读入XQF
procedure dSetRandomSecurityKeys; // 设置随机的加密因子
procedure dCalculateSecurityKeys; // 计算真正的加密密码
function isKeysSumZero: dTBoolean; // 检查密码的校验和为零否?
function iSaveXQFile: dTINT32; // 保存XQF
// 构造函数
constructor Create(sName:String; XQPTree: dTXQPlayNode);
end;
dTXqfStream = class (TFileStream)
private
FKeyBytes: array [1..4] of Byte;
F32Keys : array [1..32] of Byte;
FBuf1024 : packed array [0..1023 + 16] of Byte;
public
procedure SetKeyBytes(B1, B2, B3, B4: Byte);
function Read(var Buffer; Count: Longint): Longint; override;
function Write(const Buffer; Count: Longint): Longint; override;
end;
procedure dAddXqfToPlayTree(ATree: dTXQPlayNode; AXqf: string);
implementation
procedure dTXqfStream.SetKeyBytes(B1, B2, B3, B4: Byte);
begin
FKeyBytes[1] := B1; FKeyBytes[2] := B2;
FKeyBytes[3] := B3; FKeyBytes[4] := B4;
{ F32Keys = [(C) Copyright Mr. Dong Shiwei.] }
F32Keys[ 1] := Ord('[') and B1;
F32Keys[ 2] := Ord('(') and B2;
F32Keys[ 3] := Ord('C') and B3;
F32Keys[ 4] := Ord(')') and B4;
F32Keys[ 5] := Ord(' ') and B1;
F32Keys[ 6] := Ord('C') and B2;
F32Keys[ 7] := Ord('o') and B3;
F32Keys[ 8] := Ord('p') and B4;
F32Keys[ 9] := Ord('y') and B1;
F32Keys[10] := Ord('r') and B2;
F32Keys[11] := Ord('i') and B3;
F32Keys[12] := Ord('g') and B4;
F32Keys[13] := Ord('h') and B1;
F32Keys[14] := Ord('t') and B2;
F32Keys[15] := Ord(' ') and B3;
F32Keys[16] := Ord('M') and B4;
F32Keys[17] := Ord('r') and B1;
F32Keys[18] := Ord('.') and B2;
F32Keys[19] := Ord(' ') and B3;
F32Keys[20] := Ord('D') and B4;
F32Keys[21] := Ord('o') and B1;
F32Keys[22] := Ord('n') and B2;
F32Keys[23] := Ord('g') and B3;
F32Keys[24] := Ord(' ') and B4;
F32Keys[25] := Ord('S') and B1;
F32Keys[26] := Ord('h') and B2;
F32Keys[27] := Ord('i') and B3;
F32Keys[28] := Ord('w') and B4;
F32Keys[29] := Ord('e') and B1;
F32Keys[30] := Ord('i') and B2;
F32Keys[31] := Ord('.') and B3;
F32Keys[32] := Ord(']') and B4;
end;
function dTXqfStream.Read(var Buffer; Count: Longint): Longint;
var
ByteBuf: PByte;
p : PByte;
i, iPos: Integer;
KeyByte: Byte;
begin
Result := 0;
if (Count < 1) then Exit;
if (Count > 1024) then
begin
GetMem(ByteBuf, Count);
end
else
begin
ByteBuf := @FBuf1024;
end;
iPos := Self.Position; if (iPos < 0 ) then iPos := 0;
Result := inherited Read(ByteBuf^, Count);
p := ByteBuf;
for i:=0 to Count-1 do
begin
KeyByte := F32Keys[(iPos mod 32) + 1];
P^ := P^ - KeyByte;
Inc(p);
Inc(iPos);
end;
Move(ByteBuf^, Buffer, Count);
if (Count > 1024) then FreeMem(ByteBuf);
end;
function dTXqfStream.Write(const Buffer; Count: Longint): Longint;
var
ByteBuf: PByte;
p : PByte;
i, iPos: Integer;
KeyByte: Byte;
begin
Result := 0;
if Count < 1 then Exit;
if (Count > 1024) then
begin
GetMem(ByteBuf, Count);
end
else
begin
ByteBuf := @FBuf1024;
end;
Move(Buffer, ByteBuf^, Count);
iPos := Self.Position;
p := ByteBuf;
for i:=0 to Count-1 do
begin
KeyByte := F32Keys[(iPos mod 32) + 1];
P^ := P^ + KeyByte;
Inc(p);
Inc(iPos);
end;
Result := inherited Write(ByteBuf^, Count);
if (Count > 1024) then FreeMem(ByteBuf);
end;
//-------------------------------------------------------------------------
// 象棋文件类的构造函数
//.........................................................................
constructor dTXQFile.Create(sName:String; XQPTree: dTXQPlayNode);
begin
Name := sName; XQPlayTree := XQPTree; isReverseH := False;
end;
//-------------------------------------------------------------------------
// 装入象棋文件
//.........................................................................
function dTXQFile.iLoadXQFile(OnlyHead:Boolean): dTINT32;
var
fs : dTXqfStream; // 文件流
i, iRet : dTInt32;
// 子函数:读入注解
function slLoadRemark(fs:dTXqfStream; RmkSize:dTDWord):TStringList;
var
sl: TStringList;
ss: TStringStream; // 字符串流
begin
slLoadRemark := nil;
if (RmkSize=0) then Exit;
sl := TStringList.Create;
ss := TStringStream.Create('');
ss.CopyFrom(fs, RmkSize); ss.Position := 0;
sl.LoadFromStream(ss);
ss.Free;
slLoadRemark:=sl;
end;
// 子程序:将一个节点插入对弈树
procedure dInsertPNintoPlayTree(pt:dTXQPlayNode);
var
nd : dTXQPlayNode; // 对弈树节点
pn : dTXQFPlayNode; // 文件存放节点
b : dTByte;
begin
if pt=nil then Exit;
pn.RemarkSize := 0;
fs.Read(pn,sizeof(pn) - Sizeof(pn.RemarkSize));// 读入一个节点
if (XQFHead.Version <= $0A) then
begin
b := 0;
if ((pn.ChildTag and $F0) <> 0) then b := b or $80;
if ((pn.ChildTag and $0F) <> 0) then b := b or $40;
pn.ChildTag := b;
fs.Read(pn.RemarkSize, SizeOf(pn.RemarkSize));
end
else
begin
pn.ChildTag := pn.ChildTag and ($E0);
if ((pn.ChildTag and $20) <> 0) then
begin
fs.Read(pn.RemarkSize, SizeOf(pn.RemarkSize));
end;
end;
// 一步棋的起点和终点有简单的加密计算,读入时需要还原
pt.XYf := pn.XYf - $18 - KeyXYf; // 一步棋的起点
pt.XYt := pn.XYt - $20 - KeyXYt; // 一步棋的终点
if pt.LastStepNode <> nil then // 如果不是第一步节点
begin
pt.StepNo := pt.LastStepNode.StepNo + 1; // 步数
pt.QiziXY := pt.LastStepNode.QiziXY;
pt.StrRec := sGetPlayRecStr(pt.QiziXY, pt.XYf, pt.XYt, False);
end
else // 是第一步节点
begin
pt.XYf := 0;
case XQFHead.WhoPlay of // 判断谁先走棋
0: pt.XYt := 0;
1: pt.XYt := $FF;
end;
end;
if (pn.RemarkSize > 0) then // 如果有注解
begin
pn.RemarkSize := pn.RemarkSize-KeyRMKSize; // 还原注解的大小
end;
pt.Remark := slLoadRemark(fs, pn.RemarkSize); // 注解
// if pn.ChildTag=0 then Exit;
if ((pn.ChildTag and $80)<>0) then // 有左子树
begin
nd := dTXQPlayNode.Create(0,'',0,0,pt.QiziXY,nil,pt,nil,pt);
pt.dSetLChild(nd);
dInsertPNintoPlayTree(pt.LChild);
end;
if ((pn.ChildTag and $40)<>0) then // 有右子树
begin
nd := dTXQPlayNode.Create(0,'',0,0,pt.QiziXY,nil,
pt.LastStepNode,pt,nil);
pt.dSetRChild(nd);
dInsertPNintoPlayTree(pt.RChild);
end;
end;
begin // 装入象棋文件主程序
iLoadXQFile := -1;
iRet := -1;
fs := nil;
if XQPlayTree = nil then Exit;
// 将对弈树清空
if XQPlayTree.LChild <> nil then XQPlayTree.LChild.Free;
if XQPlayTree.RChild <> nil then XQPlayTree.RChild.Free;
try
try
fs := dTXqfStream.Create(Name, fmOpenRead);
fs.SetKeyBytes(0, 0, 0, 0);
// 读入文件头
if (fs.Read(XQFHead, SizeOf(XQFHead)) <> SizeOf(XQFHead)) then Exit;
// 检查文件标记
if (XQFHead.Signature <> $5158) then begin iRet:=-2; Exit; end;
// 检查密码校验和
if (not isKeysSumZero) then begin iRet := -3; Exit; end;
// 检查文件版本号
if (XQFHead.Version > dCFileVersion) then
begin
Application.MessageBox(
'这是一个高版本的XQF文件,您需要更高版本的XQStudio来' +
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -