home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.wwiv.com
/
ftp.wwiv.com.zip
/
ftp.wwiv.com
/
pub
/
PROTOCOL
/
TPHYD100.ZIP
/
HYDRA.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1993-09-03
|
81KB
|
2,256 lines
{$A+,B-,D+,E-,F+,I-,L+,N-,O+,R-,S+,V-}
{$UnDef H_DEBUG}
(******************************************************************************)
(* Hydra Bi-directional Protocol *)
(* ───────────────────────── *)
(* *)
(* *)
(* BY: Adam Blake Wandoo Valley Software *)
(* Arjen Lentz and Lentz Software *)
(* VER: 1.00 Development *)
(* DATE: 5th August 1993 (c) Copyright 1993 *)
(* LANGUAGE: Turbo Pascal v6.0 All Rights Reserved Worldwide *)
(******************************************************************************)
Unit Hydra;
Interface
uses
Dos, TpCrt, TpString,
h_Comms, h_Date, h_File, h_String, h_Timers, h_Win;
{$I Hydra.inc}
type
ByteArrayP = ^ByteArray;
ByteArray = array[0..0] of byte;
FuncType = Procedure( var Data; Len : word );
DevString = string[H_FLAGLEN];
const
Hydra_TxWindow : longint = 0;
Hydra_RxWindow : longint = 0;
Originator : boolean = true; (*Are we the orig side?*)
HdxSession : boolean = false;
Hydra_Speed : longint = 2400;
var
LastTimer,
ChatTimer : h_Timer;
ChatFill : word;
Function Hydra_Init( Want_Options : longint ) : boolean;
Procedure Hydra_DeInit( CloseWindow : boolean );
Function Hydra_Send( TxPathName, TxAlias : string ) : integer;
Function Hydra_DevFree : boolean;
Function Hydra_DevSend( Dev : DevString; Buffer : ByteArrayP; Len : word ) : boolean;
Function Hydra_DevFunc( Dev : DevString; Func : FuncType ) : boolean;
(******************************************************************************)
Implementation
uses
Crc;
var
BatchesDone : integer; (* No. HYDRA batches done *)
HdxLink : boolean; (* Hdx link & not orig side *)
Options : longint; (* INIT options hydra_init() *)
TimeOut : word; (* General TimeOut in secs *)
const
PROGNAME : string[20] = 'TP-Hydra';
VERSION : string[5] = '1.00';
H_OS : string[10] = 'PC';
PktPrefix : string[H_PKTPREFIX] = '';
HdxMsg : string[60] = 'Fallback to one-way xfer';
AutoStr : array[1..6] of char = ('h', 'y', 'd', 'r', 'a', #13);
AbortStr : array[1..18] of byte = (24, 24, 24, 24, 24, 24, 24, 24,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8);
var
Crc16Tab : Crc16Table; (* CRC-16 table, CCITT *)
Crc32Tab : Crc32Table; (* CRC-32 table *)
TxBuf,
RxBuf : ByteArrayP; (* packet buffers *)
TxOptions,
RxOptions : longint; (* HYDRA options (INIT seq) *)
TxPktPrefix : string[H_PKTPREFIX]; (* pkt prefix str they want *)
TxWindow,
RxWindow : longint; (* window size (0=streaming) *)
BrainDead : h_timer; (* BrainDead timer *)
TxBufIn : ByteArrayP; (* read data from disk here *)
TxLastC, (* last byte put in txbuf *)
RxDLE, (* count of received H_DLEs *)
RxPktFormat : byte; (* format of pkt receiving *)
RxBufPtr, (* current position in RxBuf *)
RxBufMax : integer; (* highwatermark of RxBuf *)
TxFName,
RxFName : string[12]; (* fname of current files *)
RxPathName : string; (* pointer to rx pathname *)
TxFTime,
RxFTime, (* file timestamp (UNIX) *)
TxFSize,
RxFSize : longint; (* file length *)
TxFd,
RxFd : file; (* file handles *)
RxPktLen, (* length of last packet *)
RxBlkLen : word; (* len of last good data blk *)
TxState,
RxState : byte; (* xmit/recv states *)
TxPos,
RxPos : longint; (* current position in files *)
TxBlkLen, (* length of last block sent *)
TxMaxBlkLen : word; (* max block length allowed *)
TxLastAck, (* last dataack received *)
TxStart,
RxStart, (* time we started this file *)
TxOffset,
RxOffset : longint; (* offset in file we begun *)
TxTimer,
RxTimer : h_timer; (* retry timers *)
TxRetries,
RxRetries : word; (* retry counters *)
RxLastSync, (* filepos last sync retry *)
TxSyncID,
RxSyncID : longint; (* id of last resync *)
TxGoodNeeded, (* to send before larger blk *)
TxGoodBytes : word; (* no. sent at this blk size *)
type
_h_Flags = record
St : DevString;
Val : longint;
end;
const
H_FLAGNUM = 10;
h_Flags : array[0..h_FlagNum] of _h_Flags = (( St : 'XON'; Val : HOPT_XONXOFF ),
( St : 'TLN'; Val : HOPT_TELENET ),
( St : 'CTL'; Val : HOPT_CTLCHRS ),
( St : 'HIC'; Val : HOPT_HIGHCTL ),
( St : 'HI8'; Val : HOPT_HIGHBIT ),
( St : 'BRK'; Val : HOPT_CANBRK ),
( St : 'ASC'; Val : HOPT_CANASC ),
( St : 'UUE'; Val : HOPT_CANUUE ),
( St : 'C32'; Val : HOPT_CRC32 ),
( St : 'DEV'; Val : HOPT_DEVICE ),
( St : 'FPT'; Val : HOPT_FPT ));
(*---------------------------------------------------------------------------*)
Procedure Hydra_MsgDev( var Data; Len : word ); (*hWin_Message Window*)
var
St : string;
begin
If Len > 255 then Len := 255;
Move(Data, St[1], Len);
St[0] := Chr(Len);
hWin_Message('REMOTE HYDRA: ' + St);
end; (*Hydra_MsgDev*)
Procedure Hydra_ConDev( var Data; Len : word ); (*Remote Chat Window*)
begin
hWin_RemChat(Data, Len);
(*Strings can be up to 2kb in length, handled by hWin_RemChat*)
end; (*Hydra_ConDev*)
Procedure Hydra_NilDev( var Data; Len : word );
begin
end; (*Hydra_NilDev*)
(*---------------------------------------------------------------------------*)
var
DevTxState : word; (* dev xmit state *)
DevTxTimer : h_timer; (* dev xmit retry timer *)
DevTxRetries : word; (* dev xmit retry counter *)
DevTxID,
DevRxID : longint; (* id of last devdata pkt *)
DevTxDev : DevString; (* xmit device ident flag *)
DevTxBuf : ByteArrayP; (* ptr to usersupplied dbuf *)
DevTxLen : word; (* len of data in xmit buf *)
type
_h_Dev = record
Dev : DevString;
Func : FuncType;
end;
const
H_DEVNUM = 3;
h_Dev : array[0..h_DevNum] of _h_Dev = (( Dev : 'MSG'; Func : Hydra_MsgDev ),
( Dev : 'CON'; Func : Hydra_ConDev ),
( Dev : 'PRN'; Func : Hydra_NilDev ),
( Dev : 'ERR'; Func : Hydra_NilDev ));
(* 0: Internal protocol msg *)
(* 1: Text to console (chat) *)
(* 2: Data to printer *)
(* 3: Text to error output *)
{$I h_Crc.inc}
{$I h_Long.inc}
{$I h_Encode.inc}
(*---------------------------------------------------------------------------*)
Function Hydra_DevFree : boolean;
begin
If (DevTxState or not(TxOptions and HOPT_DEVICE) or TxState >= HTX_END) then
Hydra_DevFree := false (* busy or not allowed *)
else
Hydra_DevFree := true; (* allowed to send a new pkt *)
end; (*Hydra_DevFree*)
(*---------------------------------------------------------------------------*)
Function Hydra_DevSend( Dev : DevString; Buffer : ByteArrayP; Len : word ) : boolean;
begin
Hydra_DevSend := false;
If (Len < 1) or (not Hydra_DevFree) then
Exit;
DevTxDev := StUpCase(Dev);
DevTxBuf := Buffer;
If Len > H_MAXBLKLEN then DevTxLen := H_MAXBLKLEN
else DevTxLen := Len;
Inc(DevTxID);
DevTxTimer := h_Timer_Reset;
DevTxRetries := 0;
DevTxState := HTD_DATA;
(* Special for chat, only prolong life If our side keeps typing! *)
If (ChatTimer > 0) and (DevTxDev = 'CON') and (TxState = HTX_REND) then
BrainDead := h_Timer_Set(H_BRAINDEAD);
Hydra_DevSend := true;
end; (*Hydra_DevSend*)
(*---------------------------------------------------------------------------*)
Function Hydra_DevFunc( Dev : DevString; Func : FuncType ) : boolean;
var
Count : integer;
begin
Hydra_DevFunc := false;
For Count := 0 to h_DevNum do
begin
If Dev = h_Dev[Count].Dev then
begin
h_Dev[Count].Func := Func;
Hydra_DevFunc := true;
end;
end;
end; (*Hydra_DevFunc*)
(*---------------------------------------------------------------------------*)
Procedure Hydra_DevRecv;
var
Len : word;
Count : integer;
DevStr : DevString;
Buffer : ByteArrayP;
begin
Len := RxPktLen;
Buffer := RxBuf;
Inc(Buffer,SizeOf(longint)); (* Skip the ID longint *)
Dec(Len,SizeOf(longint));
Move(Buffer^[0], DevStr[1], H_FLAGLEN);
DevStr[0] := Chr(H_FLAGLEN);
Dec(Len, NulSearch(Buffer^) + 1); (* sub devstr len *)
Inc(Buffer, NulSearch(Buffer^) + 1); (* skip devtag *)
Count := 0;
Repeat (* walk through devs *)
If h_Dev[Count].Dev = DevStr then
h_Dev[Count].Func(Buffer^, Len); (* call output func *)
Inc(Count);
Until (Count > h_DevNum) or (h_Dev[Count - 1].Dev = DevStr);
end; (*Hydra_DevRecv*)
(*---------------------------------------------------------------------------*)
Function Put_Flags( Buffer : ByteArrayP; Value : longint ) : integer;
var
Count,
Counter : integer;
begin
Count := 0;
For Counter := 0 to H_FLAGNUM do
begin
If (Value and h_Flags[Counter].Val) <> 0 then
begin
If Count > 0 then
begin
Buffer^[Count] := byte(',');
Inc(Count);
end;
Move(h_Flags[Counter].St[1], Buffer^[Count], H_FLAGLEN);
Inc(Count,H_FLAGLEN);
end;
end;
Buffer^[Count] := 0; (*Nul*)
Inc(Count);
Put_Flags := Count;
end; (*Put_Flags*)
(*---------------------------------------------------------------------------*)
Function Get_Flags( Buffer : ByteArrayP ) : longint;
var
Len,
Count : integer;
Value : longint;
St : array[1..3] of char;
begin
Value := 0;
Len := NulSearch(Buffer^);
For Count := 0 to H_FLAGNUM do
begin
(***DEBUG***)
Move(h_Flags[Count].St[1], St, H_FLAGLEN);
If Search(Buffer^, Len, St, H_FLAGLEN) <> $FFFF then
Value := Value or h_Flags[Count].Val;
end;
Get_Flags := Value;
end; (*Get_Flags*)
(*---------------------------------------------------------------------------*)
Procedure Put_BinByte( var Index : word; Ch : byte );
var
N : byte;
begin
N := Ch;
If (TxOptions and HOPT_HIGHCTL) <> 0 then
N := N and $7f;
If ((N = H_DLE) or
((TxOptions and HOPT_XONXOFF <> 0) and ((N = XON) or (N = XOFF)) ) or
((TxOptions and HOPT_TELENET <> 0) and (N = 13) and (TxLastC = byte('@'))) or
((TxOptions and HOPT_CTLCHRS <> 0) and ((N < 32) or (n = 127)))) then
begin
TxBuf^[Index] := H_DLE;
Inc(Index);
Ch := Ch xor $40;
end;
TxBuf^[Index] := Ch;
Inc(Index);
TxLastC := N;
end; (*Put_BinByte*)
(*---------------------------------------------------------------------------*)
Procedure TxPkt( Len : word; PktType : char );
var
Format : byte;
C,
N,
CrcW,
IndexIn,
IndexOut : word;
CrcL : longint;
Crc32 : boolean;
begin
Crc32 := false;
TxBufIn^[Len] := byte(PktType);
Inc(Len);
Case PktType of
HPKT_START,
HPKT_INIT,
HPKT_INITACK,
HPKT_END,
HPKT_IDLE : Format := byte(HCHR_HEXPKT);
else
begin
(* COULD do smart format selection depending on data and options! *)
If (TxOptions and HOPT_HIGHBIT) <> 0 then
begin
If ((TxOptions and HOPT_CTLCHRS <> 0) and (TxOptions and HOPT_CANUUE <> 0)) then
Format := byte(HCHR_UUEPKT)
else If (TxOptions and HOPT_CANASC) <> 0 then
Format := byte(HCHR_ASCPKT)
else
Format := byte(HCHR_HEXPKT);
end
else
Format := byte(HCHR_BINPKT);
end;
end;
If ((Format <> byte(HCHR_HEXPKT)) and (TxOptions and HOPT_CRC32 <> 0)) then
Crc32 := true;
{$IfDef H_DEBUG}
If (loglevel=0) {
char *s1, *s2, *s3, *s4;
hWin_Message(0,' -> PKT (format='%c' type='%c' crc=%d len=%d)',
format, type, crc32 ? 32 : 16, len - 1);
switch (type) {
case HPKT_START: hWin_Message(0,' <autostr>START');
break;
case HPKT_INIT: s1 := ((char *) TxBufIn) + ((integer) strlen((char *) TxBufIn)) + 1;
s2 := s1 + ((integer) strlen(s1)) + 1;
s3 := s2 + ((integer) strlen(s2)) + 1;
s4 := s3 + ((integer) strlen(s3)) + 1;
hWin_Message(0,' INIT (appinfo='%s' can='%s' want='%s' options='%s' pktprefix='%s')',
(char *) TxBufIn, s1, s2, s3, s4);
break;
case HPKT_INITACK: hWin_Message(0,' INITACK');
break;
case HPKT_FINFO: hWin_Message(0,' FINFO (%s)',TxBufIn);
break;
case HPKT_FINFOACK: If (RxFd >= 0) {
If (RxPos > 0) s1 := 'Result';
else s1 := 'BOF';
}
else If (RxPos = -1) s1 := 'HAVE';
else If (RxPos = -2) s1 := 'SKIP';
else s1 := 'EOB';
hWin_Message(0,' FINFOACK (pos=%ld %s RxState=%d RxFd=%d)',
RxPos,s1,RxState,RxFd);
break;
case HPKT_DATA: hWin_Message(0,' DATA (ofs=%ld len=%d)',
h_long1(TxBufIn), len - 5);
break;
case HPKT_DATAACK: hWin_Message(0,' DATAACK (ofs=%ld)',
h_long1(TxBufIn));
break;
case HPKT_RPOS: hWin_Message(0,' RPOS (pos=%ld%s blklen=%ld syncid=%ld)',
RxPos, RxPos < 0 ? ' SKIP' : '',
h_long2(TxBufIn), RxSyncID);
break;
case HPKT_EOF: hWin_Message(0,' EOF (ofs=%ld%s)',
TxPos, TxPos < 0 ? ' SKIP' : '');
break;
case HPKT_EOFACK: hWin_Message(0,' EOFACK');
break;
case HPKT_IDLE: hWin_Message(0,' IDLE');
break;
case HPKT_END: hWin_Message(0,' END');
break;
case HPKT_DEVDATA: hWin_Message(0,' DEVDATA (id=%ld dev='%s' len=%u)',
DevTxID, devtxdev, devtxlen);
break;
case HPKT_DEVDACK: hWin_Message(0,' DEVDACK (id=%ld)',
h_long1(RxBuf));
break;
default: (* This couldn't possibly happen! ;-) *)
break;
}
}
{$EndIf}
If Crc32 then
begin
CrcL := Crc32Post(Crc32Block(Crc32Tab,CRC32_INIT,TxBufIn,Len));
Move(CrcL, TxBufIn^[Len], SizeOf(longint));
Inc(Len,SizeOf(longint));
end
else
begin
CrcW := Crc16Post(Crc16Block(Crc16Tab,CRC16_INIT,TxBufIn,Len));
Move(CrcW, TxBufIn^[Len], SizeOf(word));
Inc(Len,SizeOf(word));
end;
IndexIn := 0;
IndexOut := 0;
TxLastC := 0;
TxBuf^[IndexOut] := H_DLE;
Inc(IndexOut);
TxBuf^[IndexOut] := Format;
Inc(IndexOut);
Case char(Format) of
HCHR_HEXPKT :
begin
While (Len > 0) do
begin
If (TxBufIn^[IndexIn] and $80) <> 0 then
begin
TxBuf^[IndexOut] := byte('\');
Inc(IndexOut);
TxBuf^[IndexOut] := Ord(HexDigit[(TxBufIn^[IndexIn] shr 4) and $0f]);
Inc(IndexOut);
TxBuf^[IndexOut] := Ord(HexDigit[TxBufIn^[IndexIn] and $0f]);
Inc(IndexOut);
end
else If (TxBufIn^[IndexIn] < 32) or (TxBufIn^[IndexIn] = 127) then
begin
TxBuf^[IndexOut] := H_DLE;
Inc(IndexOut);
TxBuf^[IndexOut] := TxBufIn^[IndexIn] xor $40;
Inc(IndexOut);
end
else If TxBufIn^[IndexIn] = byte('\') then
begin
TxBuf^[IndexOut] := byte('\');
Inc(IndexOut);
TxBuf^[IndexOut] := byte('\');
Inc(IndexOut);
end
else
begin
TxBuf^[IndexOut] := TxBufIn^[IndexIn];
Inc(IndexOut);
end;
Inc(IndexIn);
Dec(Len);
end;
end;
HCHR_BINPKT:
begin
While (Len > 0) do
begin
Put_BinByte(IndexOut,TxBufIn^[IndexIn]);
Inc(IndexIn);
Dec(Len);
end;
end;
HCHR_ASCPKT:
begin
N := 0;
C := 0;
While (Len > 0) do
begin
C := C or (TxBufIn^[IndexIn] shl N);
Put_BinByte(IndexOut,(byte(C) and $7f));
C := C shr 7;
Inc(N);
If (N >= 7) then
begin
Put_BinByte(IndexOut,(byte(C) and $7f));
N := 0;
C := 0;
end;
Inc(IndexIn);
Dec(Len);
end;
If (N > 0) then
Put_BinByte(IndexOut,(byte(C) and $7f));
end;
HCHR_UUEPKT:
begin
While (Len >= 3) do
begin
TxBuf^[IndexOut] := h_UUEnc(TxBufIn^[IndexIn] shr 2);
TxBuf^[IndexOut + 1] := h_UUEnc( ((TxBufIn^[IndexIn] shl 4) and $30) or
((TxBufIn^[IndexIn + 1] shr 4) and $0f) );
TxBuf^[IndexOut + 2] := h_UUEnc( ((TxBufIn^[IndexIn + 1] shl 2) and $3c) or
((TxBufIn^[IndexIn + 2] shr 6) and $03) );
TxBuf^[IndexOut + 3] := h_UUEnc(TxBufIn^[IndexIn + 2] and $3f);
Inc(IndexOut,4);
Inc(IndexIn,3);
Dec(Len,3);
end;
If (Len > 0) then
begin
TxBuf^[IndexOut] := h_UUEnc(TxBufIn^[IndexIn] shr 2);
TxBuf^[IndexOut + 1] := h_UUEnc(((TxBufIn^[IndexIn] shl 4) and $30) or ((TxBufIn^[IndexIn + 1] shr 4) and $0f));
Inc(IndexOut,2);
If (Len = 2) then
begin
TxBuf^[IndexOut] := h_UUEnc((TxBufIn^[IndexIn + 1] shl 2) and $3c);
Inc(IndexOut);
end;
end;
end;
end;
TxBuf^[IndexOut] := H_DLE;
TxBuf^[IndexOut + 1] := byte(HCHR_PKTEND);
Inc(IndexOut,2);
If (PktType <> HPKT_DATA) and (Format <> byte(HCHR_BINPKT)) then
begin
TxBuf^[IndexOut] := 13; (*CR*)
TxBuf^[IndexOut + 1] := 10; (*LF*)
Inc(IndexOut,2);
end;
If Length(TxPktPrefix) > 0 then
For N := 1 to Length(TxPktPrefix) do
begin
Case Ord(TxPktPrefix[N]) of
221 : Com_Break; (* Transmit break signal for one second *)
222 : Delay(1000);
223 : Com_TxByte(0);
else
Com_TxByte(byte(TxPktPrefix[N]));
end;
end;
Com_TxBlock(TxBuf^,IndexOut);
end; (*TxPkt*)
(*---------------------------------------------------------------------------*)
Function RxPkt : integer;
label
Continue;
var
C,
N,
I,
Count,
Index : integer;
OK,
Done : boolean;
P,
Q : ByteArrayP;
begin
If hWin_KeyCheck then
begin
RxPkt := H_SYSABORT;
Exit;
end;
If not Com_Carrier then
begin
RxPkt := H_CARRIER;
Exit;
end;
If (h_Timer_Running(BrainDead)) and (h_Timer_Expired(BrainDead)) then
begin
{$IfDef H_DEBUG}
If (LogLevel = 0) then
hWin_Message(0,' <- BrainDead (timer=%08lx time=%08lx)', BrainDead,tnow);
{$EndIf}
RxPkt := H_BRAINTIME;
Exit;
end;
If (h_Timer_Running(TxTimer)) and (h_Timer_Expired(TxTimer)) then
begin
{$IfDef H_DEBUG}
If (LogLevel = 0) then
hWin_Message(0,' <- TxTimer (timer=%08lx time=%08lx)', TxTimer,tnow);
{$EndIf}
RxPkt := H_TXTIME;
Exit;
end;
If (h_Timer_Running(DevTxTimer)) and (h_Timer_Expired(DevTxTimer)) then
begin
{$IfDef H_DEBUG}
If (LogLevel = 0) then
hWin_Message(0,' <- DevTxTimer (timer=%08lx time=%08lx)', DevTxTimer,tnow);
{$EndIf}
RxPkt := H_DEVTXTIME;
Exit;
end;
While Com_RxReady do
begin
C := Com_RxByte;
If (RxOptions and HOPT_HIGHBIT) <> 0 then
C := C and $7f;
N := C;
If (RxOptions and HOPT_HIGHCTL) <> 0 then
N := N and $7f;
If ((N <> H_DLE) and
(((RxOptions and HOPT_XONXOFF <> 0) and ((N = XON) or (N = XOFF))) or
((RxOptions and HOPT_CTLCHRS <> 0) and ((N < 32) or (N = 127))) )) then
Goto Continue;
If (RxDLE > 0) or (C = H_DLE) then
begin
Case char(C) of
char(H_DLE) :
begin
Inc(RxDLE);
If RxDLE >= 5 then
begin
RxPkt := H_CANCEL;
Exit;
end;
end;
HCHR_PKTEND :
begin
Case char(RxPktFormat) of
HCHR_BINPKT :
begin
Index := RxBufPtr;
end;
HCHR_HEXPKT :
begin
Count := 0;
Index := 0;
Done := false;
While (Count < RxBufPtr) and (not Done) do
begin
If (RxBuf^[Count] = byte('\')) then
begin
Inc(Count);
If (RxBuf^[Count] <> byte('\')) then
begin
I := RxBuf^[Count];
N := RxBuf^[Count + 1];
Inc(Count);
Dec(I,48);
If (I > 9) then Dec(I,39);
Dec(N,48);
If (N > 9) then Dec(N,39);
If (I and $FFF0 <> 0) or (N and $FFF0 <> 0) then
begin
C := H_NOPKT;
Done := true;
end;
RxBuf^[Index] := (I shl 4) or N;
Inc(Index);
end
else
begin
RxBuf^[Index] := RxBuf^[Count];
Inc(Index);
end;
end
else
begin
RxBuf^[Index] := RxBuf^[Count];
Inc(Index);
end;
Inc(Count);
end;
If Count > RxBufPtr then
C := H_NOPKT;
end;
HCHR_ASCPKT :
begin
N := 0;
I := 0;
Count := 0;
Index := 0;
While Count < RxBufPtr do
begin
I := I or ((RxBuf^[Count] and $7f) shl N);
Inc(N,7);
If N >= 8 then
begin
RxBuf^[Index] := byte(I) and $FF;
Inc(Index);
I := I shr 8;
Dec(N,8);
end;
Inc(Count);
end;
end;
HCHR_UUEPKT :
begin
N := RxBufPtr;
Count := 0;
Index := 0;
Done := false;
While (N >= 4) and (not Done) do
begin
If ((RxBuf^[Count] <= byte(' ')) or (RxBuf^[Count] >= byte('a')) or
(RxBuf^[Count + 1] <= byte(' ')) or (RxBuf^[Count + 1] >= byte('a')) or
(RxBuf^[Count + 2] <= byte(' ')) or (RxBuf^[Count + 2] >= byte('a')) or
(RxBuf^[Count + 3] <= byte(' ')) or (RxBuf^[Count + 3] >= byte('a'))) then
begin
C := H_NOPKT;
Done := true;
end
else
begin
RxBuf^[Index] := (h_uudec(RxBuf^[Count]) shl 2) or (h_uudec(RxBuf^[Count + 1]) shr 4);
RxBuf^[Index + 1] := (h_uudec(RxBuf^[Count + 1]) shl 4) or (h_uudec(RxBuf^[Count + 2]) shr 2);
RxBuf^[Index + 2] := (h_uudec(RxBuf^[Count + 2]) shl 6) or h_uudec(RxBuf^[Count + 3]);
Inc(Count,4);
Inc(Index,3);
Dec(N,4);
end;
end;
If (n >= 2) and (not Done) then
begin
If (RxBuf^[Count] <= byte(' ')) or (RxBuf^[Count] >= byte('a')) or
(RxBuf^[Count + 1] <= byte(' ')) or (RxBuf^[Count + 1] >= byte('a')) then
C := H_NOPKT
else
begin
RxBuf^[Index] := (h_uudec(RxBuf^[Count]) shl 2) or (h_uudec(RxBuf^[Count + 1]) shr 4);
Inc(Index);
If (N = 3) then
begin
If (RxBuf^[Count + 2] <= byte(' ')) or (RxBuf^[Count + 2] >= byte('a')) then
C := H_NOPKT
else
begin
RxBuf^[Index] := (h_uudec(RxBuf^[Count + 1]) shl 4) or
(h_uudec(RxBuf^[Count + 2]) shr 2);
Inc(Index);
end;
end;
end;
end;
end;
else (*CASE - This'd mean an internal fluke *)
begin
{$IfDef H_DEBUG}
If (LogLevel = 0) then
hWin_Message(0,' <- <PKTEND> (pktformat='%c' dec=%d hex=%02x) ??',
RxPktFormat, RxPktFormat, RxPktFormat);
{$EndIf}
C := H_NOPKT;
end;
end; (*CASE RxPktFormat*)
RxBufPtr := -1;
If (c = H_NOPKT) then
Goto Continue;
RxPktLen := Index;
If (RxPktFormat <> byte(HCHR_HEXPKT)) and (RxOptions and HOPT_CRC32 <> 0) then
begin
If (RxPktLen < 5) then
begin
C := H_NOPKT;
Goto Continue;
end;
OK := h_Crc32Test(Crc32Block(Crc32Tab,CRC32_INIT,RxBuf,RxPktLen));
Dec(RxPktLen, SizeOf(longint)); (*Remove CRC-32*)
end
else
begin
If (RxPktLen < 3) then
begin
C := H_NOPKT;
Goto Continue;
end;
OK := h_Crc16Test(Crc16Block(Crc16Tab, CRC16_INIT, RxBuf, RxPktLen));
Dec(RxPktLen, SizeOf(word)); (*Remove CRC-16*)
end;
Dec(RxPktLen); (*Remove Pkt type*)
If OK then
begin
{$IfDef H_DEBUG}
If (loglevel=0) {
char *s1, *s2, *s3, *s4;
hWin_Message(0,' <- PKT (format='%c' type='%c' len=%d)',
RxPktFormat, (integer) RxBuf[RxPktLen], RxPktLen);
switch (RxBuf[RxPktLen]) {
case HPKT_START: hWin_Message(0,' START');
break;
case HPKT_INIT: s1 := ((char *) RxBuf) + ((integer) strlen((char *) RxBuf)) + 1;
s2 := s1 + ((integer) strlen(s1)) + 1;
s3 := s2 + ((integer) strlen(s2)) + 1;
s4 := s3 + ((integer) strlen(s3)) + 1;
hWin_Message(0,' INIT (appinfo='%s' can='%s' want='%s' options='%s' pktprefix='%s')',
(char *) RxBuf, s1, s2, s3, s4);
break;
case HPKT_INITACK: hWin_Message(0,' INITACK');
break;
case HPKT_FINFO: hWin_Message(0,' FINFO ('%s' RxState=%d)',RxBuf,RxState);
break;
case HPKT_FINFOACK: hWin_Message(0,' FINFOACK (pos=%ld TxState=%d TxFd=%d)',
h_long1(RxBuf), TxState, TxFd);
break;
case HPKT_DATA: hWin_Message(0,' DATA (RxState=%d pos=%ld len=%u)',
RxState, h_long1(RxBuf),
(word) (RxPktLen - ((integer) sizeof (longint))));
break;
case HPKT_DATAACK: hWin_Message(0,' DATAACK (RxState=%d pos=%ld)',
RxState, h_long1(RxBuf));
break;
case HPKT_RPOS: hWin_Message(0,' RPOS (pos=%ld%s blklen=%u->%ld syncid=%ld%s TxState=%d TxFd=%d)',
h_long1(RxBuf),
h_long1(RxBuf) < 0 ? ' SKIP' : '',
TxBlkLen, h_long2(RxBuf),
h_long3(RxBuf),
h_long3(RxBuf) = RxSyncID ? ' DUP' : '',
TxState, TxFd);
break;
case HPKT_EOF: hWin_Message(0,' EOF (RxState=%d pos=%ld%s)',
RxState, h_long1(RxBuf),
h_long1(RxBuf) < 0 ? ' SKIP' : '');
break;
case HPKT_EOFACK: hWin_Message(0,' EOFACK (TxState=%d)', TxState);
break;
case HPKT_IDLE: hWin_Message(0,' IDLE');
break;
case HPKT_END: hWin_Message(0,' END');
break;
case HPKT_DEVDATA: s1 := ((char *) RxBuf) + ((integer) sizeof (longint));
hWin_Message(0,' DEVDATA (id=%ld dev=%s len=%u',
h_long1(RxBuf), s1,
RxPktLen - (((integer) sizeof (longint)) + ((integer) strlen(s1)) + 1));
break;
case HPKT_DEVDACK: hWin_Message(0,' DEVDACK (DevTxState=%d id=%ld)',
DevTxState, h_long1(RxBuf));
break;
default: hWin_Message(0,' Unkown PktType %d (TxState=%d RxState=%d)',
(integer) RxBuf[RxPktLen], TxState, RxState);
break;
}
}
{$EndIf}
RxPkt := integer(RxBuf^[RxPktLen]);
Exit;
end; (*Good Pkt*)
(* {$IfDef H_DEBUG} *)
hWin_Message('Bad CRC (format=' + Chr(RxPktFormat) +
' type=' + Chr(RxBuf^[RxPktLen]) +
' len=' + Long2Str(RxPktLen) + ')');
(* {$EndIf} *)
end; (*HCHR_PKTEND*)
HCHR_BINPKT,
HCHR_HEXPKT,
HCHR_ASCPKT,
HCHR_UUEPKT :
begin
{$IfDef H_DEBUG}
hWin_Message('<- <PKTSTART> (pktformat=' + Chr(C) + ')');
{$EndIf}
RxPktFormat := C;
RxBufPtr := 0;
RxDLE := 0;
end;
else
begin
If RxBufPtr >= 0 then
begin
If RxBufPtr < RxBufMax then
begin
RxBuf^[RxBufPtr] := byte(C) xor $40;
Inc(RxBufPtr);
end
else
begin
(* {$IfDef H_DEBUG} *)
hWin_Message('<- Pkt too long - discarded');
(* {$EndIf} *)
RxBufPtr := -1;
end;
end;
RxDLE := 0;
end;
end; (*CASE C*)
end (*If*)
else
begin
If RxBufPtr >= 0 then
begin
If (RxBufPtr < RxBufMax) then
begin
RxBuf^[RxBufPtr] := byte(C);
Inc(RxBufPtr);
end
else
begin
(* {$IfDef H_DEBUG} *)
hWin_Message('<- Pkt too long - discarded');
(* {$EndIf} *)
RxBufPtr := -1;
end;
end;
end;
Continue:
end;
RxPkt := H_NOPKT;
end; (*RxPkt*)
(*---------------------------------------------------------------------------*)
Procedure Hydra_Status( Xmit : boolean );
begin
If Xmit then
hWin_ShowTxBytes(TxPos,TxStart)
else
hWin_ShowRxBytes(RxPos,RxStart);
end; (*Hydra_Status*)
(*---------------------------------------------------------------------------*)
Procedure Hydra_Pct( Xmit : boolean );
begin
If Xmit then
hWin_Complete(TxFName,TxFSize - TxOffset)
else
hWin_Complete(RxFName,RxFSize - RxOffset);
end; (*Hydra_Pct*)
(*---------------------------------------------------------------------------*)
Procedure Hydra_BadXfer;
begin
If (FileRec(RxFd).Mode <> fmClosed) then
begin
Close(RxFd);
FileRec(RxFd).Mode := fmClosed;
If hFile_Bad(RxPathName,RxFSize) then
hWin_Message('Bad transfer - Recovery info saved')
else
hWin_Message('Bad transfer - File deleted');
end;
end; (*Hydra_BadXfer*)
(*---------------------------------------------------------------------------*)
Function Hydra_Init( Want_Options : longint ) : boolean;
begin
Hydra_Init := false;
TxBuf := nil;
RxBuf := nil;
Crc16Tab := nil;
Crc32Tab := nil;
GetMem(TxBuf,H_BUFLEN);
GetMem(RxBuf,H_BUFLEN);
New(Crc16Tab);
New(Crc32Tab);
If (TxBuf = nil) or (RxBuf = nil) or (Crc16Tab = nil) or (Crc32Tab = nil) then
begin
If TxBuf <> nil then FreeMem(TxBuf,H_BUFLEN);
If RxBuf <> nil then FreeMem(RxBuf,H_BUFLEN);
If Crc16Tab <> nil then Dispose(Crc16Tab);
If Crc32Tab <> nil then Dispose(Crc32Tab);
Exit;
end;
TxBufIn := TxBuf;
Inc(TxBufIn,(H_MAXBLKLEN + H_OVERHEAD + 5) * 2);
RxBufMax := H_MAXPKTLEN;
Crc16Init(Crc16Tab,CRC16_POLY);
Crc32Init(Crc32Tab,CRC32_POLY);
BatchesDone := 0;
If Originator then
HdxLink := false
else If HdxSession then
HdxLink := true;
Options := (Want_Options and HCAN_OPTIONS) and (not HUNN_OPTIONS);
TimeOut := Trunc(40960 / Hydra_Speed);
If (TimeOut < H_MINTIMER) then
TimeOut := H_MINTIMER
else If (TimeOut > H_MAXTIMER) then
TimeOut := H_MAXTIMER;
TxMaxBlkLen := Trunc(Hydra_Speed / 300) * 128;
If (TxMaxBlkLen < 256) then
TxMaxBlkLen := 256
else If (TxMaxBlkLen > H_MAXBLKLEN) then
TxMaxBlkLen := H_MAXBLKLEN;
If Hydra_Speed < 2400 then
TxBlkLen := 256
else
TxBlkLen := 512;
RxBlkLen := TxBlkLen;
TxGoodBytes := 0;
TxGoodNeeded := TxMaxBlkLen;
TxState := HTX_DONE;
If HdxLink then
hWin_OpenWindow
else
hWin_OpenWindow;
ChatFill := 0;
ChatTimer := -1;
LastTimer := 0;
Hydra_Init := true;
end; (*Hydra_Init*)
(*---------------------------------------------------------------------------*)
Procedure Hydra_DeInit( CloseWindow : boolean );
begin
If TxBuf <> nil then FreeMem(TxBuf,H_BUFLEN);
If RxBuf <> nil then FreeMem(RxBuf,H_BUFLEN);
If Crc16Tab <> nil then Dispose(Crc16Tab);
If Crc32Tab <> nil then Dispose(Crc32Tab);
If CloseWindow then hWin_CloseWindow;
end; (*Hydra_DeInit*)
(*---------------------------------------------------------------------------*)
Function Hydra_Send( TxPathName, TxAlias : string) : integer;
label
Break;
var
St : string;
Result,
PktType,
Counter,
Count,
Index : integer;
WorkPtr : ByteArrayP;
FileStat : SearchRec;
begin
Hydra_Send := XFER_ABORT;
hWin_ClearTxWindow;
(*-------------------------------------------------------------------------*)
If (TxState = HTX_DONE) then
begin
hWin_ShowTxFileName('-Initialising-');
TxState := HTX_START;
TxOptions := HTXI_OPTIONS;
TxPktPrefix := '';
hWin_ClearRxWindow;
hWin_ShowRxFileName('-Initialising-');
RxState := HRX_INIT;
RxOptions := HRXI_OPTIONS;
FileRec(RxFd).Mode := fmClosed;
RxDLE := 0;
RxBufPtr := -1;
RxTimer := h_Timer_Reset;
DevTxID := 0;
DevRxID := 0;
DevTxTimer := h_Timer_Reset;
DevTxState := HTD_DONE;
BrainDead := h_Timer_Set(H_BRAINDEAD);
end
else
TxState := HTX_FINFO;
TxTimer := h_Timer_Reset;
TxRetries := 0;
(*-------------------------------------------------------------------------*)
If TxPathName <> '' then
begin
TxPathName := StUpCase(TxPathName);
TxAlias := StUpCase(TxAlias);
TxFName := JustFilename(TxPathName);
FindFirst(TxPathName,(ReadOnly and Archive),FileStat);
TxFSize := FileStat.Size;
TxFTime := h_ToUnixDate(FileStat.Time);
FileMode := RO or DenyNone;
Assign(TxFd, TxPathName);
Reset(TxFd,1);
If IOResult <> 0 then
begin
hWin_Message('Unable to open ' + TxFName);
Hydra_Send := XFER_SKIP;
Exit;
end;
{
If isatty(TxFd) then
begin
hWin_Message(TxFName + ' is a device name!');
Close(TxFd);
Hydra_Send := XFER_SKIP;
Exit;
end;
}
TxStart := 0;
TxSyncID := 0;
end
else
begin
TxFName := '';
FileRec(TxFd).Mode := fmClosed;
end;
(*-------------------------------------------------------------------------*)
Repeat
Case (DevTxState) of
HTD_DATA : begin
If (TxState > HTX_RINIT) then
begin
Count := 0;
h_PutLong(1,DevTxID);
Inc(Count,SizeOf(longint));
(***DEBUG***) Move(DevTxDev[1], TxBufIn^[Count], H_FLAGLEN);
TxBufIn^[Count + H_FLAGLEN] := 0;
Inc(Count, H_FLAGLEN + 1);
Move(DevTxBuf^[0], TxBufIn^[Count], DevTxLen);
Inc(Count,DevTxLen);
TxPkt(Count,HPKT_DEVDATA);
If (RxState = HRX_DONE) and (TxState = HTX_REND) then
DevTxTimer := h_Timer_Set(TimeOut shr 1)
{***DEBUG*** DevTxTimer := h_Timer_Set(Trunc(TimeOut / 2))}
else
DevTxTimer := h_Timer_Set(TimeOut);
DevTxState := HTD_DACK;
end;
end;
end; (*CASE DevTxState*)
(*-----------------------------------------------------------------------*)
Case (TxState) of
HTX_START :
begin
Com_TxBlock(AutoStr,SizeOf(AutoStr));
TxPkt(0,HPKT_START);
TxTimer := h_Timer_Set(H_START);
TxState := HTX_SWAIT;
end;
(*---------------------------------------------------------------------*)
HTX_INIT :
begin
Count := 0;
h_PutHex(Count,H_REVSTAMP);
Inc(Count,8);
Move(PROGNAME[1], TxBufIn^[Count], Length(PROGNAME));
Inc(Count,Length(PROGNAME));
TxBufIn^[Count] := byte(',');
Inc(Count);
Move(VERSION[1], TxBufIn^[Count], Length(VERSION));
Inc(Count,Length(VERSION));
TxBufIn^[Count] := byte(' ');
Inc(Count);
Move(H_OS[1], TxBufIn^[Count], Length(H_OS));
Inc(Count,Length(H_OS));
TxBufIn^[Count] := 0;
Inc(Count);
WorkPtr := TxBufIn;
Inc(WorkPtr,Count);
Inc(Count,Put_Flags(WorkPtr,HCAN_OPTIONS)); (*What we CAN do*)
WorkPtr := TxBufIn;
Inc(WorkPtr,Count);
Inc(Count,Put_Flags(WorkPtr,Options)); (*What we WANT*)
h_PutHex(Count,Hydra_TxWindow);
Inc(Count,8);
h_PutHex(Count,Hydra_RxWindow);
Inc(Count,8);
TxBufIn^[Count] := 0;
Inc(Count);
Move(PktPrefix[1], TxBufIn^[Count], Length(PktPrefix));
Inc(Count,Length(PktPrefix));
TxBufIn^[Count] := 0;
Inc(Count);
TxOptions := HTXI_OPTIONS;
TxPkt(Count, HPKT_INIT);
TxOptions := RxOptions;
TxTimer := h_Timer_Set(TimeOut shr 1);
(* TxTimer := h_Timer_Set(Trunc(TimeOut / 2)); ***DEBUG***)
TxState := HTX_INITACK;
end;
(*---------------------------------------------------------------------*)
HTX_FINFO :
begin
If (FileRec(TxFd).Mode <> fmClosed) then
begin
If (TxRetries = 0) then
begin
If (TxAlias <> '') then
hWin_ShowTxFileName(TxFName + '(' + TxAlias + ')')
else
hWin_ShowTxFileName(TxFName);
hWin_ShowTxFileSize(TxFSize);
TxFName := StLoCase(TxFName);
end;
h_PutHex( 0,TxFTime);
h_PutHex( 8,TxFSize);
h_PutHex(16,0);
h_PutHex(24,0);
h_PutHex(32,0);
Count := 5 * 8;
If TxAlias <> '' then
begin
Move(TxAlias[1], TxBufIn^[Count], Length(TxAlias));
Inc(Count,Length(TxAlias));
end
else
begin
Move(TxFName[1], TxBufIn^[Count], Length(TxFName));
Inc(Count,Length(TxFName));
end;
TxBufIn^[Count] := 0;
Inc(Count);
end
else
begin
If (TxRetries = 0) then
hWin_ShowTxFileName('-End Of Batch-');
TxBufIn^[0] := 0;
Count := 1;
end;
TxPkt(Count,HPKT_FINFO);
If TxRetries <> 0 then
TxTimer := h_Timer_Set(TimeOut shr 1)
(* TxTimer := h_Timer_Set(Trunc(TimeOut / 2)) ***DEBUG***)
else
TxTimer := h_Timer_Set(TimeOut);
TxState := HTX_FINFOACK;
end; (*HTX_FINFO*)
(*---------------------------------------------------------------------*)
HTX_XDATA :
begin
If (Com_TxChars^ < TxMaxBlkLen) then
begin
If (TxPos < 0) then
Counter := -1 (*Skip*)
else
begin
h_PutLong(1,TxPos);
Count := SizeOf(longint);
BlockRead(TxFd,TxBufIn^[Count],TxBlkLen,Counter);
Inc(Count,Counter);
If IOResult <> 0 then
begin
Counter := -1;
hWin_Message('File read error');
Close(TxFd);
FileRec(TxFd).Mode := fmClosed;
TxPos := -2; (*Skip*)
end;
end;
If (Counter > 0) then
begin
Inc(TxPos,Counter);
TxPkt(Count,HPKT_DATA);
If (TxBlkLen < TxMaxBlkLen) then
begin
Inc(TxGoodBytes,Counter);
If (TxGoodBytes >= TxGoodNeeded) then
begin
TxBlkLen := TxBlkLen shl 1;
If (TxBlkLen >= TxMaxBlkLen) then
begin
TxBlkLen := TxMaxBlkLen;
TxGoodNeeded := 0;
end;
TxGoodBytes := 0;
end;
end;
If (TxWindow > 0) and (TxPos >= (TxLastAck + TxWindow)) then
begin
If TxRetries <> 0 then
TxTimer := h_Timer_Set(TimeOut shr 1)
(* TxTimer := h_Timer_Set(Trunc(TimeOut / 2)) ***DEBUG***)
else
TxTimer := h_Timer_Set(TimeOut);
TxState := HTX_DATAACK;
end;
If (TxStart = 0) then
TxStart := h_Timer_Get;
Hydra_Status(true);
end
else
begin
TxState := HTX_EOF; (*Fallthrough to HTX_EOF*)
end;
end;
end; (*HTX_XDATA*)
(*---------------------------------------------------------------------*)
HTX_EOF :
begin
h_PutLong(1,TxPos);
TxPkt(SizeOf(longint),HPKT_EOF);
If TxRetries <> 0 then
TxTimer := h_Timer_Set(TimeOut shr 1)
(* TxTimer := h_Timer_Set(Trunc(TimeOut / 2)) ***DEBUG***)
else
TxTimer := h_Timer_Set(TimeOut);
TxState := HTX_EOFACK;
end;
(*---------------------------------------------------------------------*)
HTX_END :
begin
TxPkt(0,HPKT_END);
TxPkt(0,HPKT_END);
TxTimer := h_Timer_Set(TimeOut shr 1);
(* TxTimer := h_Timer_Set(Trunc(TimeOut / 2)); ***DEBUG***)
TxState := HTX_ENDACK;
end;
end; (*CASE TxState*)
(*-----------------------------------------------------------------------*)
PktType := RxPkt;
(*-----------------------------------------------------------------------*)
Case PktType of
(*---------------------------------------------------------------------*)
H_CARRIER,
H_CANCEL,
H_SYSABORT,
H_BRAINTIME :
begin
Case PktType of
H_CARRIER : hWin_Message('Carrier lost');
H_CANCEL : hWin_Message('Transfer aborted by remote console');
H_SYSABORT : hWin_Message('Transfer aborted by local console');
H_BRAINTIME : hWin_Message('Remote has expired');
end;
TxState := HTX_DONE;
Result := XFER_ABORT;
end; (*H_BRAINTIME*)
(*---------------------------------------------------------------------*)
H_TXTIME :
begin
If (TxState = HTX_XWAIT) or (TxState = HTX_REND) then
begin
TxPkt(0,HPKT_IDLE);
TxTimer := h_Timer_Set(H_IDLE);
end
else
begin
Inc(TxRetries);
If (TxRetries > H_RETRIES) then (***DEBUG***)
{ If (TxRetries > H_RETRIES) and (TxState <> HTX_SWAIT) then}
begin
hWin_Message('Aborting - Too many errors');
TxState := HTX_DONE;
Result := XFER_ABORT;
Goto Break;
end;
Str(TxRetries,St);
hWin_Message('TimeOut - Retry ' + St);
TxTimer := h_Timer_Reset;
Case (TxState) of
HTX_SWAIT : TxState := HTX_START;
HTX_INITACK : TxState := HTX_INIT;
HTX_FINFOACK : TxState := HTX_FINFO;
HTX_DATAACK : TxState := HTX_XDATA;
HTX_EOFACK : TxState := HTX_EOF;
HTX_ENDACK : TxState := HTX_END;
end;
end;
end; (*H_TXTIME*)
(*---------------------------------------------------------------------*)
H_DEVTXTIME :
begin
Inc(DevTxRetries);
If (DevTxRetries > H_RETRIES) then
begin
hWin_Message('Aborting - Too many errors');
TxState := HTX_DONE;
Result := XFER_ABORT;
Goto Break;
end;
Str(DevTxRetries,St);
hWin_Message('TimeOut - Retry ' + St);
DevTxTimer := h_Timer_Reset;
DevTxState := HTD_DATA;
end; (*H_DEVTXTIME*)
(*---------------------------------------------------------------------*)
integer(HPKT_START) :
begin
If (TxState = HTX_START) or (TxState = HTX_SWAIT) then
begin
TxTimer := h_Timer_Reset;
TxRetries := 0;
TxState := HTX_INIT;
BrainDead := h_Timer_Set(H_BRAINDEAD);
end;
end; (*HPKT_START*)
(*---------------------------------------------------------------------*)
integer(HPKT_INIT) :
begin
If (RxState = HRX_INIT) then
begin
Count := NulSearch(RxBuf^) + 1;
WorkPtr := RxBuf;
Inc(WorkPtr,Count);
Index := Count + NulSearch(WorkPtr^) + 1;
RxOptions := Options or HUNN_OPTIONS;
WorkPtr := RxBuf;
Inc(WorkPtr,Index);
RxOptions := RxOptions or Get_Flags(WorkPtr);
WorkPtr := RxBuf;
Inc(WorkPtr,Count);
RxOptions := RxOptions and Get_Flags(WorkPtr);
RxOptions := RxOptions and HCAN_OPTIONS;
If RxOptions < (Options and HNEC_OPTIONS) then
begin
hWin_Message('Incompatible on this link');
TxState := HTX_DONE;
Result := XFER_ABORT;
Goto Break;
end;
WorkPtr := RxBuf;
Inc(WorkPtr,Index);
Count := Index + NulSearch(WorkPtr^) + 1;
WorkPtr := RxBuf;
Inc(WorkPtr,Count);
If NulSearch(WorkPtr^) < 16 then
begin
TxWindow := 0;
RxWindow := 0;
end
else
begin
TxWindow := h_GetHex(Count + 8);
RxWindow := h_GetHex(Count);
end;
If (RxWindow < 0) then RxWindow := 0;
If (Hydra_RxWindow <> 0) and ((RxWindow = 0) or (Hydra_RxWindow < RxWindow)) then
RxWindow := Hydra_RxWindow;
If (TxWindow < 0) then TxWindow := 0;
If (Hydra_TxWindow <> 0) and ((TxWindow = 0) or (Hydra_TxWindow < TxWindow)) then
TxWindow := Hydra_TxWindow;
WorkPtr := RxBuf;
Inc(WorkPtr,Count);
Index := Count + NulSearch(WorkPtr^) + 1;
WorkPtr := RxBuf;
Inc(WorkPtr,Index);
Count := NulSearch(WorkPtr^);
If Count > H_PKTPREFIX then Count := H_PKTPREFIX;
Move(RxBuf^[Index], TxPktPrefix[1], Count);
TxPktPrefix[0] := Chr(Count);
If (BatchesDone = 0) then
begin
{
longint revstamp;
p := (char *) RxBuf;
sscanf(p,'%08lx',&revstamp);
hWin_Message(0,'*HYDRA: Other's HydraRev=%s',
h_revdate(revstamp));
p += 8;
If ((q := strchr(p,',')) <> NULL) *q := ' ';
If ((q := strchr(p,',')) <> NULL) *q := '/';
hWin_Message(0,'*HYDRA: Other's App.Info '%s'',p);
put_flags((char *) RxBuf,h_flags,RxOptions);
hWin_Message(1,'*HYDRA: Using link options '%s'',RxBuf);
If (TxWindow or RxWindow)
hWin_Message(0,'*HYDRA: Window tx=%ld rx=%ld',
TxWindow,RxWindow);
}
end;
If (RxOptions and HOPT_DEVICE <> 0) then
ChatTimer := 0
else
ChatTimer := -2;
TxOptions := RxOptions;
RxState := HRX_FINFO;
end;
TxPkt(0,HPKT_INITACK);
end; (*HPKT_INIT*)
(*---------------------------------------------------------------------*)
integer(HPKT_INITACK) :
begin
If (TxState = HTX_INIT) or (TxState = HTX_INITACK) then
begin
BrainDead := h_Timer_Set(H_BRAINDEAD);
TxTimer := h_Timer_Reset;
TxRetries := 0;
TxState := HTX_RINIT;
end;
end; (*HPKT_INITACK*)
(*---------------------------------------------------------------------*)
integer(HPKT_FINFO) :
begin
If (RxState = HRX_FINFO) then
begin
BrainDead := h_Timer_Set(H_BRAINDEAD);
If (RxBuf^[0] = 0) then
begin
hWin_ClearRxWindow;
hWin_ShowRxFileName('-End Of Batch-');
RxPos := 0;
RxState := HRX_DONE;
Inc(BatchesDone);
end
else
begin
RxFTime := h_GetHex(0);
RxFSize := h_GetHex(8);
Index := 5 * 8;
WorkPtr := RxBuf;
Inc(WorkPtr,Index);
Count := NulSearch(WorkPtr^);
Move(WorkPtr^[0], RxFName[1], Count);
RxFName[0] := Chr(Count);
RxFName := StUpCase(RxFName);
hWin_ClearRxWindow;
hWin_ShowRxFileName(RxFName);
RxPathName := hFile_Check(RxFName,RxFSize,RxFTime);
If (RxPathName = '') then (* Already have file *)
begin
hWin_Message('Already have ' + RxFName + ', Skipping');
RxPos := -1;
end
else
begin
If FileCheck(RxPathName) then (*Resuming?*)
begin
FileMode := RW or DenyAll;
Assign(RxFd,RxPathName);
Reset(RxFd,1);
If (IOResult <> 0) then
begin
hWin_Message('Unable to re-open ' + RxPathName + ', Skipping');
RxPos := -2;
end;
end
else
begin
FileMode := RW or DenyAll;
Assign(RxFd,RxPathName);
Rewrite(RxFd,1);
If (IOResult <> 0) then
begin
hWin_Message('Unable to create ' + RxPathName + ', Skipping');
RxPos := -2;
end;
end;
If (FileRec(RxFd).Mode <> fmClosed) then
begin
hWin_ShowRxFileSize(RxFSize);
Seek(RxFd,FileSize(RxFd));
If (IOResult <> 0) then
begin
hWin_Message('File seek error');
Hydra_BadXfer;
RxPos := -2;
end
else
begin
RxOffset := FilePos(RxFd);
RxPos := RxOffset;
If (IOResult <> 0) then
begin
hWin_Message('File positioning error');
Hydra_BadXfer;
RxPos := -2;
end
else If ((RxFSize - RxOffset) + 10240) > DiskFree(0) then
begin
hWin_Message('Not enough diskspace for ' + RxFName);
Hydra_BadXfer;
RxPos := -2;
end
else
begin
RxStart := 0;
RxTimer := h_Timer_Reset;
RxRetries := 0;
RxLastSync := 0;
RxSyncID := 0;
Hydra_Status(false);
If (RxPos > 0) then
hWin_Message('Resuming file');
RxState := HRX_DATA;
end;
end;
end;
end;
end;
end
else If (RxState = HRX_DONE) then
begin
If RxBuf^[0] = 0 then
RxPos := 0
else
RxPos := -2;
end;
h_PutLong(1,RxPos);
TxPkt(SizeOf(longint),HPKT_FINFOACK);
end; (*HPKT_FINFO*)
(*-------------------------------------------------------------------*)
integer(HPKT_FINFOACK) :
begin
If (TxState = HTX_FINFO) or (TxState = HTX_FINFOACK) then
begin
BrainDead := h_Timer_Set(H_BRAINDEAD);
TxRetries := 0;
If TxFName = '' then
begin
TxTimer := h_Timer_Set(H_IDLE);
TxState := HTX_REND;
end
else
begin
TxTimer := h_Timer_Reset;
TxPos := h_GetLong(1);
If (TxPos >= 0) then
begin
TxOffset := TxPos;
TxLastAck := TxPos;
Hydra_Status(true);
If (TxPos > 0) then
begin
hWin_Message('Transmit resuming file');
Seek(TxFd,TxPos);
If (IOResult <> 0) then
begin
hWin_Message('File seek error');
Close(TxFd);
FileRec(TxFd).Mode := fmClosed;
TxPos := -2;
TxState := HTX_EOF;
Goto Break;
end;
end;
TxState := HTX_XDATA;
end
else
begin
Close(TxFd);
If (TxPos = -1) then
begin
hWin_Message('Remote skipped file ' + TxFName);
Hydra_Send := XFER_OK;
Exit;
end
else
begin (* (TxPos < -1) file not sent *)
hWin_Message('Remote temporarily skipped ' + TxFName);
Hydra_Send := XFER_SKIP;
Exit;
end;
end;
end;
end;
end; (*HPKT_FINFOACK*)
(*-------------------------------------------------------------------*)
integer(HPKT_DATA) :
begin
If (RxState = HRX_DATA) then
begin
If (h_GetLong(1) <> RxPos) or (h_GetLong(1) < 0) then
begin
If (h_GetLong(1) <= RxLastSync) then
begin
RxTimer := h_Timer_Reset;
RxRetries := 0;
end;
RxLastSync := h_GetLong(1);
If (not h_Timer_Running(RxTimer)) or (h_Timer_Expired(RxTimer)) then
begin
If (RxRetries > 4) then
begin
If (TxState < HTX_REND) and (not Originator) and (not HdxLink) then
begin
HdxLink := true;
RxRetries := 0;
end;
end;
Inc(RxRetries);
If (RxRetries > H_RETRIES) then
begin
hWin_Message('Too many errors');
TxState := HTX_DONE;
Result := XFER_ABORT;
Goto Break;
end;
If (RxRetries = 1) or (RxRetries = 4) then
Inc(RxSyncID);
RxBlkLen := (RxBlkLen shr 1);
(***DEBUG*** RxBlkLen := Trunc(RxBlkLen / 2); *)
Counter := RxBlkLen;
If (Counter <= 64) then Counter := 64
else If (Counter <= 128) then Counter := 128
else If (Counter <= 256) then Counter := 256
else If (Counter <= 512) then Counter := 512
else Counter := 1024;
hWin_Message('Bad packet at ' + Long2Str(RxPos) + ' - Retry ' + Long2Str(RxRetries));
h_PutLong(1,RxPos);
h_PutLong(2,longint(Counter));
h_PutLong(3,RxSyncID);
TxPkt(3 * SizeOf(longint),HPKT_RPOS);
RxTimer := h_Timer_Set(TimeOut);
end;
end
else
begin
BrainDead := h_Timer_Set(H_BRAINDEAD);
Dec(RxPktLen,SizeOf(longint));
RxBlkLen := RxPktLen;
WorkPtr := RxBuf;
Inc(WorkPtr,SizeOf(longint));
BlockWrite(RxFd,WorkPtr^,RxBlkLen);
If (IOResult <> 0) then
begin
hWin_Message('File write error');
Hydra_BadXfer;
RxPos := -2;
RxRetries := 1;
Inc(RxSyncID);
h_PutLong(1,RxPos);
h_PutLong(2,0);
h_PutLong(3,RxSyncID);
TxPkt(3 * SizeOf(longint),HPKT_RPOS);
RxTimer := h_Timer_Set(TimeOut);
Goto Break;
end;
RxRetries := 0;
RxTimer := h_Timer_Reset;
RxLastSync := RxPos;
Inc(RxPos,RxPktLen);
If (RxWindow > 0) then
begin
h_PutLong(1,RxPos);
TxPkt(SizeOf(longint),HPKT_DATAACK);
end;
If (RxStart = 0) then
RxStart := h_Timer_Get - Trunc((RxPktLen * 10) / Hydra_Speed);
Hydra_Status(false);
end;
end; (*RxState=HRX_DATA*)
end; (*HPKT_DATA*)
(*-------------------------------------------------------------------*)
integer(HPKT_DATAACK) :
begin
If (TxState = HTX_XDATA) or (TxState = HTX_DATAACK) or (TxState = HTX_XWAIT) or
(TxState = HTX_EOF) or (TxState = HTX_EOFACK) then
begin
If (TxWindow > 0) and (h_GetLong(1) > TxLastAck) then
begin
TxLastAck := h_GetLong(1);
If (TxState = HTX_DATAACK) and (TxPos < (TxLastAck + TxWindow)) then
begin
TxState := HTX_XDATA;
TxRetries := 0;
TxTimer := h_Timer_Reset;
end;
end;
end;
end; (*HPKT_DATAACK*)
(*-------------------------------------------------------------------*)
integer(HPKT_RPOS) :
begin
If (TxState = HTX_XDATA) or (TxState = HTX_DATAACK) or (TxState = HTX_XWAIT) or
(TxState = HTX_EOF) or (TxState = HTX_EOFACK) then
begin
If (h_GetLong(3) <> TxSyncID) then
begin
TxSyncID := h_GetLong(3);
TxRetries := 1;
end
else
begin
Inc(TxRetries);
If (TxRetries > H_RETRIES) then
begin
hWin_Message('Too many errors');
TxState := HTX_DONE;
Result := XFER_ABORT;
Goto Break;
end;
If (TxRetries <> 4) then Goto Break;
end;
TxTimer := h_Timer_Reset;
TxPos := h_GetLong(1);
If (TxPos < 0) then
begin
If (FileRec(TxFd).Mode <> fmClosed) then
begin
hWin_Message('Skipping ' + TxFName);
Close(TxFd);
FileRec(TxFd).Mode := fmClosed;
TxState := HTX_EOF;
end;
TxPos := -2;
Goto Break;
end;
If (TxBlkLen > h_GetLong(2)) then
TxBlkLen := h_GetLong(2)
else
TxBlkLen := TxBlkLen shr 1;
If (TxBlkLen <= 64) then TxBlkLen := 64
else If (TxBlkLen <= 128) then TxBlkLen := 128
else If (TxBlkLen <= 256) then TxBlkLen := 256
else If (TxBlkLen <= 512) then TxBlkLen := 512
else TxBlkLen := 1024;
TxGoodBytes := 0;
Inc(TxGoodNeeded, TxMaxBlkLen * 2);
If (TxGoodNeeded > (TxMaxBlkLen * 8)) then
TxGoodNeeded := (TxMaxBlkLen * 8);
Hydra_Status(true);
hWin_Message('Resending from offset ' + Long2Str(TxPos));
Seek(TxFd,TxPos);
If (IOResult <> 0) then
begin
hWin_Message('File seek error');
Close(TxFd);
FileRec(TxFd).Mode := fmClosed;
TxPos := -2;
TxState := HTX_EOF;
Goto Break;
end;
If (TxState <> HTX_XWAIT) then
TxState := HTX_XDATA;
end;
end; (*HPKT_RPOS*)
(*-------------------------------------------------------------------*)
integer(HPKT_EOF) :
begin
If (RxState = HRX_DATA) then
begin
If (h_GetLong(1) < 0) then
begin
Hydra_BadXfer;
hWin_Message('Remote skipping ' + RxFName);
RxState := HRX_FINFO;
BrainDead := h_Timer_Set(H_BRAINDEAD);
end
else If (h_GetLong(1) <> RxPos) then
begin
If (h_GetLong(1) <= RxLastSync) then
begin
RxTimer := h_Timer_Reset;
RxRetries := 0;
end;
RxLastSync := h_GetLong(1);
If (not h_Timer_Running(RxTimer)) or (h_Timer_Expired(RxTimer)) then
begin
Inc(RxRetries);
If (RxRetries > H_RETRIES) then
begin
hWin_Message('Too many errors');
TxState := HTX_DONE;
Result := XFER_ABORT;
Goto Break;
end;
If (RxRetries = 1) or (RxRetries = 4) then
Inc(RxSyncID);
RxBlkLen := (RxBlkLen shr 1);
(***DEBUG*** RxBlkLen := Trunc(RxBlkLen / 2); *)
Counter := RxBlkLen;
If (Counter <= 64) then Counter := 64
else If (Counter <= 128) then Counter := 128
else If (Counter <= 256) then Counter := 256
else If (Counter <= 512) then Counter := 512
else Counter := 1024;
hWin_Message('Bad EOF at ' + Long2Str(RxPos) + ' - Retry ' + Long2Str(RxRetries));
h_PutLong(1,RxPos);
h_PutLong(2,Counter);
h_PutLong(3,RxSyncID);
TxPkt(3 * SizeOf(longint),HPKT_RPOS);
RxTimer := h_Timer_Set(TimeOut);
end;
end
else
begin
RxFSize := RxPos;
SetFTime(RxFd,h_FromUnixDate(RxFTime));
Close(RxFd);
FileRec(RxFd).Mode := fmClosed;
Hydra_Pct(false);
If hFile_Okay(RxPathName,RxFTime) then;
Hydra_Status(false);
hWin_Message('Received ' + RxFName);
RxState := HRX_FINFO;
BrainDead := h_Timer_Set(H_BRAINDEAD);
end;
end; (*RxState=HRX_DATA*)
If (RxState = HRX_FINFO) then
TxPkt(0,HPKT_EOFACK);
end; (*HPKT_EOF*)
(*-------------------------------------------------------------------*)
integer(HPKT_EOFACK) :
begin
If (TxState = HTX_EOF) or (TxState = HTX_EOFACK) then
begin
BrainDead := h_Timer_Set(H_BRAINDEAD);
If (FileRec(TxFd).Mode <> fmClosed) then
begin
TxFSize := TxPos;
Close(TxFd);
Hydra_Pct(true);
Hydra_Send := XFER_OK;
Exit;
end
else
begin
Hydra_Send := XFER_SKIP;
Exit
end;
end;
end; (*HPKT_EOFACK*)
(*-------------------------------------------------------------------*)
integer(HPKT_IDLE) :
begin
If (TxState = HTX_XWAIT) then
begin
HdxLink := false;
TxTimer := h_Timer_Reset;
TxRetries := 0;
TxState := HTX_XDATA;
end
else If (TxState >= HTX_FINFO) and (TxState < HTX_REND) then
BrainDead := h_Timer_Set(H_BRAINDEAD);
end; (*HPKT_IDLE*)
(*-------------------------------------------------------------------*)
integer(HPKT_END) :
begin
(* special for chat, other side wants to quit *)
If (ChatTimer > 0) and (TxState = HTX_REND) then
begin
ChatTimer := -3;
Goto Break;
end;
If (TxState = HTX_END) or (TxState = HTX_ENDACK) then
begin
TxPkt(0,HPKT_END);
TxPkt(0,HPKT_END);
TxPkt(0,HPKT_END);
hWin_Message('Session completed');
TxState := HTX_DONE;
Result := XFER_OK;
end;
end; (*HPKT_END*)
(*-------------------------------------------------------------------*)
integer(HPKT_DEVDATA) :
begin
If (DevRxID <> h_GetLong(1)) then
begin
Hydra_DevRecv;
DevRxID := h_GetLong(1);
end;
h_PutLong(1,h_GetLong(1));
TxPkt(SizeOf(longint),HPKT_DEVDACK);
end; (*HPKT_DEVDATA*)
(*-------------------------------------------------------------------*)
integer(HPKT_DEVDACK) :
begin
If (DevTxState <> HTD_DONE) and (DevTxID = h_GetLong(1)) then
begin
DevTxTimer := h_Timer_Reset;
DevTxState := HTD_DONE;
end;
end; (*HPKT_DEVDACK*)
(*-----------------------------------------------------------------------*)
(* ELSE: Unknown packet types: IGNORE, no error! *)
end; (*CASE PktType*)
Break:
(*-----------------------------------------------------------------------*)
Case (TxState) of
HTX_START,
HTX_SWAIT :
begin
If (RxState = HRX_FINFO) then
begin
TxTimer := h_Timer_Reset;
TxRetries := 0;
TxState := HTX_INIT;
end;
end;
(*---------------------------------------------------------------------*)
HTX_RINIT :
begin
If (RxState = HRX_FINFO) then
begin
TxTimer := h_Timer_Reset;
TxRetries := 0;
TxState := HTX_FINFO;
end;
end;
(*---------------------------------------------------------------------*)
HTX_XDATA :
begin
If (RxState <> HRX_DONE) and (HdxLink) then
begin
hWin_Message(HdxMsg);
WorkPtr := @HdxMsg;
Inc(WorkPtr);
If Hydra_DevSend('MSG', WorkPtr, Length(HdxMsg)) then;
TxTimer := h_Timer_Set(H_IDLE);
TxState := HTX_XWAIT;
end;
end;
(*---------------------------------------------------------------------*)
HTX_XWAIT :
begin
If (RxState = HRX_DONE) then
begin
TxTimer := h_Timer_Reset;
TxRetries := 0;
TxState := HTX_XDATA;
end;
end;
(*---------------------------------------------------------------------*)
HTX_REND :
begin
If (RxState = HRX_DONE) and (DevTxState = HTD_DONE) then
begin
(*Special for chat, BrainDead will protect*)
If (ChatTimer <= 0) then
begin
If (ChatTimer = 0) Then ChatTimer := -3;
TxTimer := h_Timer_Reset;
TxRetries := 0;
TxState := HTX_END;
end;
end;
end;
end; (*CASE TxState*)
Until TxState = HTX_DONE;
If FileRec(TxFd).Mode <> fmClosed then
Close(TxFd);
Hydra_BadXfer;
If (Result = XFER_ABORT) then
begin
Com_TxFlush;
If (Com_Carrier) then
begin
Com_TxBlock(AbortStr,SizeOf(AbortStr));
Repeat
Until (Com_TxEmpty) or (not Com_Carrier);
end;
Com_RxFlush;
end
else
begin
Repeat
Until (Com_TxEmpty) or (not Com_Carrier);
end;
Hydra_Send := Result;
end; (*Hydra*)
(**********************************MAINLINE************************************)
end.