///
/*
* ZmodemReceiver - receive files using zmodem.
*
* A c# class developed as a library for conducting serial interface
* file transfers, developed by Chuck Forsberg. This code is strongly
* based on the lrzsz linux library, and is an expanded port of that
* code, released under
* GNU GPL.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Developed by Eric Martin MBARI 2016
*
*/
using System;
using System.Text;
using System.Threading;
using System.IO;
using System.IO.Ports;
using System.Diagnostics;
using Microsoft.SPOT;
using Microsoft.SPOT.IO;
namespace CPF.FileTransfer
{
public class ZmodemReceiver : Zmodem
{
//Constants
private const int ZRQ_MAX = 10;
private const UInt16 MAX_BLOCK = 8192;
private UInt16 MAX_TXFRAME = 1024;
//Variables
private SerialPort sPort;
private byte Lzmanag; /*Local File Management Request*/
private bool rxClob;
private bool rxBinary;
private bool rxASCII;
private bool ThisBinary;
private bool renameExistingFiles;
private bool zctlEsc;
private bool skipifnotfound;
private STATES state;
private int zrTimeout = 2;
private byte tryzhdrtype = ZRINIT;
private FileMode openmode;
private byte zconv; //ZMODEM file conversion request;
private byte zmanag; //ZMODEM file management request;
private byte ztrans; //ZMODEM file transport request;
private byte[] rxbuff;
public string filePrefix = "";
//STATES
private enum STATES
{
INIT, // Waiting for any message from the transmitter
RECVFILE,
DONE,
TIMED_OUT
};
public enum ManageOpts
{
NEWORLONGER = 1, // Transfer if source is newer or longer
CRC, // Transfer if different file CRC or length
APPEND, // Append Contents to Existing FIle
CLOBBER, // Replace Existing File
NEWER // Transfer if source is newer
};
//Properties
/// Use 16BIT Crc in reply messages
public bool CrcEnable;
/// Disable timeouts during
public bool DisableTimeouts;
/// Enable the resume file functionality.
public bool ResumeEnable;
///
/// Enable Clobbering of the Received File, disables ResumeEnable if true.
///
public bool RxClob
{
get { return rxClob; }
set
{
if (value)
ResumeEnable = true;
rxClob = value;
}
}
/// Sets unit to receive data in ascii
/// format, disables binary transfer.
public bool RxASCII
{
get { return rxASCII; }
set
{
rxBinary = !value;
rxASCII = value;
}
}
/// Sets unit to receive data in binary
/// format, disables ascii transfer.
public bool RxBinary
{
get { return rxBinary; }
set
{
rxASCII = (!value);
rxBinary = value;
}
}
/// Enable/Disable Escape sequences
/// for control characters.
public bool ZctlEsc
{
get { return zctlEsc; }
set
{
zctlEsc = value;
Init(zctlEsc);
}
}
///
/// Rename any files already existing, this disables
/// file resume functionality.
///
public bool RenameExistingFiles
{
get { return renameExistingFiles; }
set
{
renameExistingFiles = value;
if (value)
ResumeEnable = false;
}
}
/// Set timeout in tenths of seconds.
public int Timeout
{
get { return zrTimeout; }
set
{
if (value == 0)
DisableTimeouts = true;
zrTimeout = value;
}
}
public ManageOpts LocalManagement
{
get { return (ManageOpts) Lzmanag; }
set { Lzmanag = (byte)value; }
}
///
/// Constructor for single or multiple file transfers through serialports using
/// the ZMODEM protocol.
///
/// SerialPort object. Must be open for r/w.
/// Enable the ability to resume a file partially downloaded in a
/// previous session. CRC on all packets will be checked.
///
/// This is how you conduct a receive operation. Default property settings are:
/// - No local management options
/// - No CRC enabled (reduces bandwidth.
/// - Standard timeouts enabled
/// - Binary transmission
/// - Skip if not found is disabled.
///
/// SerialPort sp = new SerialPort("COM1", 115200, Parity.None, 8, StopBits.One);
/// ZmodemReceiver zr = new ZmodemReceiver(sp, true); //allow resume functionality
/// while (zr.doTasks()) {
/// // this does not return regularly, just between phases of the transfer.
/// }
/// //Transfer Complete
///
public ZmodemReceiver(SerialPort sp, bool tryResume)
{
this.Lzmanag = 0; // no local management options
this.CrcEnable = false;
this.DisableTimeouts = false;
this.RxBinary = false;
this.RxASCII = false;
this.ThisBinary = false;
this.skipifnotfound = false;
this.rxbuff = new byte[MAX_BLOCK];
// this sets Init(false);
this.ZctlEsc = false;
this.sPort = sp;
this.state = STATES.INIT;
this.ResumeEnable = true;
}
private int zrqinits_received = 0;
private int init_loops = 0;
private UInt32 rxpos = 0;
private ushort nbytes = 0;
private bool pollOnNext = true;
private int errors = 0;
public bool doTasks()
{
int c;
byte cmdzack1flg;
switch (state) {
case STATES.INIT:
// Test if we're under the max zrq's and that we haven't timed out too many times.
if ((init_loops - zrqinits_received) < ZRQ_MAX && (zrqinits_received < ZRQ_MAX)) {
if (pollOnNext) {
stohdr(1024);
Txhdr[ZF0] |=
CANFC32 | CANFDX| CANOVIO;
//Txhdr[ZF0] = 0;
//Txhdr[ZF1] = 0;
//todo P2 timesync flag
if (zctlEsc)
Txhdr[ZF0] |= TESCCTL;
zshhdr(sPort, tryzhdrtype, ref Txhdr);
init_loops++;
if (tryzhdrtype == ZSKIP)
tryzhdrtype = ZRINIT;
pollOnNext = false;
}
switch (c = zgethdr(sPort, ref Rxhdr, 0, ref rxpos)) {
case ZRQINIT:
zrqinits_received++;
pollOnNext = true;
return true;
case ZEOF:
Debug.Print("GOT ZEOF");
pollOnNext = true;
return true;
case TIMEOUT:
pollOnNext = true;
return true;
case ZFILE: //This is what we're looking for.
zconv = Rxhdr[ZF0];
if ((Rxhdr[ZF1] & ZF1_ZMSKNOLOC) > 0) {
Rxhdr[ZF1] = (byte)(Rxhdr[ZF1] & ~ZF1_ZMSKNOLOC);
skipifnotfound = true;
}
zmanag = Rxhdr[ZF1];
ztrans = Rxhdr[ZF2];
tryzhdrtype = ZRINIT;
// clear the rx array
Array.Clear(rxbuff, 0, rxbuff.Length);
nbytes = 0;
c = zrdata(sPort, ref rxbuff, ref nbytes);
if (c == GOTCRCW) {
state = STATES.RECVFILE;
return true;
}
zshhdr(sPort, ZNAK, ref Txhdr);
return true;
case ZSINIT:
zctlEsc |= (TESCCTL & Rxhdr[ZF0]) != 0;
nbytes = 0;
if (zrdata(sPort, ref Attn, ref nbytes) == GOTCRCW) {
stohdr(1);
zshhdr(sPort, ZACK, ref Txhdr);
return true;
}
zshhdr(sPort, ZNAK, ref Txhdr);
return true;
case ZFREECNT:// right now this doesn't work...
stohdr(getfree());
zshhdr(sPort, ZACK, ref Txhdr);
return true;
case ZCOMMAND:
//Not enabled in C# protocol
cmdzack1flg = Rxhdr[ZF0];
zshhdr(sPort, ZACK, ref Txhdr);
state = STATES.DONE;
return true;
case ZCOMPL:
return true;
case ZFIN:
ackbibi();
state = STATES.DONE;
return true;
case ZRINIT:
return false;
case ZCAN:
return false;
default:
return true;
}
}
break;
case STATES.RECVFILE:
if (rzfiles(ref zi) == ERROR)
return false;
break;
case STATES.TIMED_OUT:
break;
case STATES.DONE:
Debug.Print("Transfer Session Complete");
return false;
}
return true;
}
private int rzfiles(ref zfileinfo zm)
{
int c;
for (; ; ) {
///Debug.Print("rzfiles(): opening new file.");
c = rzfile(ref zm);
switch (c) {
case ZEOF:
//Debug.Print("rzfiles(): ZEOF received.\n");
state = STATES.INIT;
pollOnNext = true;
return c;
case ZSKIP:
//Some debug printing ... TODO
state = STATES.INIT;
pollOnNext = true;
return c;
default:
return c;
case ERROR:
return c;
}
//if (state != STATES.RECVFILE)
// break;
}
//TODO P2 return OK;
}
private int rzfile(ref zfileinfo zm)
{
int c, n;
//long last_rxbytes = 0;
//ulong last_bps;
//long not_printed = 0;
//uint bytes_in_block = 0;
zm.eof_seen = false;
n = 10;
if (procheader(ref rxbuff, nbytes, ref zm) == ERROR) {
tryzhdrtype = ZSKIP;
return ZSKIP;
}
if (processZFileInfo(ref zm) == ERROR) {
tryzhdrtype = ZSKIP;
return ZSKIP;
}
for (; ; ) {
repos:
stohdr(zm.bytes_received);
zshhdr(sPort, ZRPOS, ref Txhdr);
nxthdr:
//wait for reply
c = zgethdr(sPort, ref Rxhdr, 0, ref rxpos);
switch (c) {
default:
Debug.Print("rzfile(): zgethdr error");
return ERROR;
case ZNAK:
case TIMEOUT:
if (--n < 0) {
Debug.Print("rzfile(): error: ZNAK or TIMEOUT");
return ERROR;
}
continue;
case ZFILE:
nbytes = 0;
if (zrdata(sPort, ref rxbuff, ref nbytes) == ERROR)
Debug.Print("rzfile(): error on zrdata from ZFILE header.");
continue;
case ZEOF:
if (rclhdr(ref Rxhdr) != zm.bytes_received) {
errors = 0; goto nxthdr;
}
//close file if ()
if (!closeit(ref zm)) {
return ERROR;
}
return c;
case ERROR:
if (--n < 0) {
return ERROR;
}
zmputs(ref Attn);
continue;
case ZSKIP:
if (!closeit(ref zm)) return ERROR;
Debug.Print("rzfile(): sender skipped file\r\n");
return c;
case ZDATA:
if (rclhdr(ref Rxhdr) != zm.bytes_received) {
if (--n < 0) {
Debug.Print("rzfile(): out of sync\r\n");
return ERROR;
}
zmputs(ref Attn);
continue;
}
moredata:
Debug.Print("BYTES SO FAR: " + zm.bytes_received +
" of "+zm.bytes_total);
// skipped a bunch of bps checks
nbytes = 0;
switch (c = zrdata(sPort, ref rxbuff, ref nbytes)) {
case ZCAN:
Debug.Print("rzfile(): zrdata returned ZCAN\r\n");
return ERROR;
case ERROR:
if (--n < 0) {
Debug.Print("rzfile(): zrdata returned ERROR\r\n");
return ERROR;
}
else {
//Dump the buffer
Debug.Print("rzfile(): dumping buffer and sending new ZRPOS.");
sPort.DiscardInBuffer();
zmputs(ref Attn);
goto repos;
}
case TIMEOUT:
if (--n < 0) {
Debug.Print("rzfile(): zrdata returned TIMEOUT\r\n");
return ERROR;
}
else continue;
case GOTCRCW:
n = 20;
if (putsec(ref zm, ref rxbuff, nbytes) == OK) {
zm.bytes_received += nbytes;
stohdr(zm.bytes_received);
zshhdr(sPort, (ZACK | 0x80), ref Txhdr);
goto nxthdr;
}
else { return ERROR; }
case GOTCRCQ:
n = 20;
if (putsec(ref zm, ref rxbuff, nbytes) == OK) {
zm.bytes_received += nbytes;
stohdr(zm.bytes_received);
zshhdr(sPort, (ZACK), ref Txhdr);
goto moredata;
}
else { return ERROR; }
case GOTCRCG:
n = 20;
if (putsec(ref zm, ref rxbuff, nbytes) == OK) {
zm.bytes_received += nbytes;
goto moredata;
}
else { return ERROR; }
case GOTCRCE:
n = 20;
if (putsec(ref zm, ref rxbuff, nbytes) == OK) {
zm.bytes_received += nbytes;
stohdr(zm.bytes_received);
zshhdr(sPort, (ZACK), ref Txhdr);
goto nxthdr;
}
else { return ERROR; }
default:
Debug.Print("rzfile(): unhandled Reply");
break;
}
break;
}
}
}
///
/// Assumes the file is open, and writes a sector to it.
///
///
///
///
///
private int putsec(ref zfileinfo zm, ref byte[] buff, ushort nbytes)
{
if (nbytes == 0)
return OK;
if (zm.fs == null) {
Debug.Print("putsec(): File not open.\r\n");
return ERROR;
}
if (ThisBinary) {
try {
zm.fs.Write(buff, 0, nbytes);
}
catch (Exception e) {
Debug.Print("Putsec(): writing binary data: " + e.ToString());
return ERROR;
}
}
else {
if (zm.eof_seen)
return OK;
for (int n = 0; n < nbytes; n++) {
if (buff[n] == 0x0D) // \r
continue;
if (buff[n] == CPMEOF) {
zm.eof_seen = true;
return OK;
}
try {
zm.fs.WriteByte(buff[n]);
}
catch (Exception e) {
Debug.Print("Putsec(): writing ascii byte. " + e.ToString());
}
}
}
return OK;
}
private void zmputs(ref byte[] s)
{
for (int n = 0; n < s.Length; n++ ) {
if (s[n] == 0xDD | s[n] == 0xDE)
sPort.Write(s, n, 1);
else if (s[n] == 0xDE)
Thread.Sleep(1000);
else if (s[n] == 0xDD) {
//sPort.BreakState = true;
//Thread.Sleep(10);
//sPort.BreakState = false;
}
}
}
private bool closeit(ref zfileinfo zm)
{
try {
zm.fs.Close();
zm.fs = null;
}
catch {
return false;
}
Debug.Print("Closing File:" + zm.fname);
return true;
}
private ushort Readnum = MAX_BLOCK;
private void ackbibi()
{
//Debug.Print("Ackbibi:");
Readnum = 1;
stohdr(0);
for (int n = 0; n < 3; n++) {
//purgeline(0);
zshhdr(sPort, ZFIN, ref Txhdr);
switch (readline(sPort, 100)) {
case 'O':
readline(sPort, 1); /*Discard second 'O'*/
//Debug.Print("Transfer complete");
return;
case RCDO:
return;
case TIMEOUT:
default:
break;
}
}
}
internal struct zfileinfo
{
public string fname;
public DateTime modtime;
public uint mode;
public uint bytes_total;
public uint bytes_sent;
public uint bytes_received;
public uint bytes_skipped;
public uint bytes_remaining;
public uint files_remaining;
public uint serial_number;
public bool eof_seen;
public FileStream fs;
}
private zfileinfo zi;
///
/// Process incoming file information header
///
///
///
///
private int procheader(ref byte[] buff, uint nbytes, ref zfileinfo zm)
{
//string flags;
int nullidx = Array.IndexOf(buff, (byte)0);
if (nullidx <= 0) return ERROR;
StringBuilder sb = new StringBuilder();
zm.fname = sb.Clear().Append(this.filePrefix).Append(Encoding.UTF8.GetChars(buff, 0, nullidx)).ToString() ;
Debug.Print("Receiving File: " + zm.fname + "\r\n");
int nullidx2 = Array.IndexOf(buff, (byte)0, nullidx + 1);
StringBuilder flags = new StringBuilder();
flags.Append(Encoding.UTF8.GetChars(buff, nullidx + 1, nullidx2 - nullidx - 1));
Debug.Print("GOT FLAGS: [" + flags.ToString() + "]\r\n");
//Parse the flags
if (nullidx2 <= 0) {
return OK;
}
int tokstart = nullidx + 1;
int toklen = bytesToNextSpace(buff, tokstart);
try {
//Parse Length
zm.bytes_total = uint.Parse(sb.Clear().Append(Encoding.UTF8.GetChars(buff, tokstart, toklen)).ToString());
sb.Clear();
tokstart += toklen + 1;
if (tokstart >= nullidx2) return OK;
//Parse Modification Date
toklen = bytesToNextSpace(buff, tokstart);
int modtime = convertOctalStringToDec(ref buff, tokstart, toklen);
// convert from octal
zm.modtime = UnixEpoch.AddSeconds(modtime);
tokstart += toklen + 1;
if (tokstart >= nullidx2) return OK;
//Parse File Mode
toklen = bytesToNextSpace(buff, tokstart);
zm.mode = (UInt32)convertOctalStringToDec(ref buff, tokstart,toklen);
tokstart += toklen + 1;
if (tokstart >= nullidx2) return OK;
//Parse Serial Number
toklen = bytesToNextSpace(buff, tokstart);
zm.serial_number = uint.Parse(sb.Clear().Append(Encoding.UTF8.GetChars(buff, tokstart, toklen)).ToString());
tokstart += toklen + 1;
if (tokstart >= nullidx2) return OK;
// Parse Files Remaining
toklen = bytesToNextSpace(buff, tokstart);
zm.files_remaining = uint.Parse(sb.Clear().Append(Encoding.UTF8.GetChars(buff, tokstart, toklen)).ToString());
tokstart += toklen + 1;
if (tokstart >= nullidx2) return OK;
// Parse Bytes Remaining
toklen = bytesToNextSpace(buff, tokstart);
zm.bytes_remaining = uint.Parse(sb.Clear().Append(Encoding.UTF8.GetChars(buff, tokstart, toklen)).ToString());
//There is one more field (file type, but that is not really used.
}
catch {
return ERROR;
}
return OK;
}
private int convertOctalStringToDec(ref byte [] buff, int offset, int len)
{
int result=0;
int pow = 0;
int n;
StringBuilder sb = new StringBuilder();
//Debug.Print("CONVERTING :" + sb.Clear().Append(Encoding.UTF8.GetChars(buff, offset, len)).ToString());
for (n = len-1; n >= 0 ; n--)
{
result += (int)System.Math.Pow(8, pow++) * int.Parse(sb.Clear().Append(Encoding.UTF8.GetChars(buff, n + offset, 1)).ToString());
}
//Debug.Print("RESULT: " + result.ToString());
return result;
}
private int buffersize = 32768;
private int processZFileInfo(ref zfileinfo zm)
{
//Set default parameters
openmode = FileMode.OpenOrCreate;
ThisBinary = (!rxASCII) || rxBinary;
if (Lzmanag > 0)
zmanag = Lzmanag;
if (!rxBinary && (zconv == ZCNL))
ThisBinary = false;
if (zconv == ZCBIN)
ThisBinary = true;
if (ThisBinary && (zconv == ZCBIN) && ResumeEnable)
zconv = ZCRESUM;
if ((zmanag == ZF1_ZMAPND && zconv != ZCRESUM))
openmode = FileMode.Append;
if (skipifnotfound)
openmode = FileMode.Open;
if ((zm.mode & UNIXFILE) != 0)
ThisBinary = true;
// Check for existing file
if (zconv != ZCRESUM && !RxClob &&
(zmanag & ZF1_ZMMASK) != ZF1_ZMCLOB &&
(zmanag & ZF1_ZMMASK) != ZF1_ZMAPND &&
File.Exists(zm.fname)
) {
//Look for newer or not
if (zmanag == ZF1_ZMNEW || zmanag == ZF1_ZMNEWL) {
bool isLonger = new System.IO.FileInfo(zm.fname).LastWriteTimeUtc > zm.modtime;
if (zmanag == ZF1_ZMNEW) {
if (isLonger) {
Debug.Print("Skipping: " + zm.fname + ", newer file exists.");
return ERROR;
}
}
else { // newer or longer
long fLength = new System.IO.FileInfo(zm.fname).Length;
if (isLonger && fLength > zm.bytes_total) {
Debug.Print("Skipping: " + zm.fname + ", newer and longer file exists.");
return ERROR;
}
}
}
else if (zmanag == ZF1_ZMCRC) {
int r = do_crc_check(ref zm, 0);
if (r == ERROR)
return ERROR;
if (r != ZCRC_DIFFERS) {
Debug.Print("Skipping, CRC match on local and remote files.");
return ERROR; // skips
}
else {
openmode = FileMode.Create;
Debug.Print("CRC Mismatch, redownloading entire file.");
}
}
else {
if ((zmanag & ZF1_ZMMASK) != ZF1_ZMCHNG) {
Debug.Print("Skipping: " + zm.fname + ", file exists");
return ERROR;
}
// try to rename
int i = 1;
do {
if (!File.Exists(zm.fname + i.ToString())) {
zm.fname += i.ToString();
break;
}
} while (i < 1000);
if (File.Exists(zm.fname)) {
Debug.Print("Skipping: " + zm.fname + ", file could not be renamed.");
return ERROR;
}
}
}
// NOT IMPLEMENTING TIMESYNC
// NOT IMPLEMENTING LOWERCASING
// NOT IMPLEMENTING PIPE
try {
if (zm.fs == null)
zm.fs = File.Open(zm.fname, openmode, FileAccess.ReadWrite);
}
catch {
Debug.Print("processZFileInfo(): File open failed.\r\n");
}
if (ThisBinary && zconv == ZCRESUM) {
if (zm.fs != null) {
bool can_resume = true;
if (zmanag == ZF1_ZMCRC) {
int r = do_crc_check(ref zm, zm.fs.Length);
if (r == ERROR) {
zm.fs.Close();
return ZFERR;
}
if (r == ZCRC_DIFFERS) {
can_resume = false;
}
}
if (zm.fs.Length > zm.bytes_total) {
can_resume = false;
}
/* Retransfer Whole Blocks */
zm.bytes_skipped = (uint)((zm.fs.Length) & ~(1023));
if (can_resume) {
try {
zm.fs.Seek(zm.bytes_skipped, SeekOrigin.Begin);
}
catch {
zm.fs.Close();
return ZFERR;
}
}
else
zm.bytes_skipped = 0; /*resume impossible, file has changed */
goto buffer_it;
}
zm.bytes_skipped = 0;
}
try {
//Deal with directory structure if required
string directoryName = Path.GetDirectoryName(zm.fname);
if ((directoryName.Length) > 0 && (!Directory.Exists(directoryName)))
Directory.CreateDirectory(directoryName);
}
catch { Debug.Print("processZfileInfo(): Cannot Create Directory for file, " + zm.fname + " \r \n"); }
if (zm.fs == null) {
Debug.Print("processZfileInfo(): Cannot open file, " + zm.fname + " \r \n");
return ERROR;
}
/*
uint last_length = 0;
if (buffersize == -1) {
if (zm.bytes_total > last_length) {
last_length = 0;
}
}
if (buffersize != 0) {
last_length = 32768;
if (buffersize == -1) {
if (zm.bytes_total > 0)
last_length = zm.bytes_total;
}
else last_length = (uint)buffersize;
//buffer '4096' byte space
last_length = (last_length + 4095) & 0xFFFFF000;
}
*/
// zm.fs = File.Open(zm.fname, openmode, FileAccess.ReadWrite);
//}
buffer_it:
zm.bytes_received = zm.bytes_skipped;
return OK;
}
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
private const int ZCRC_DIFFERS = ERROR + 1;
private const int ZCRC_EQUAL = ERROR + 2;
private int do_crc_check(ref zfileinfo zm, long check_bytes)
{
UInt32 crc, rcrc;
long n;
int c;
int t1 = 0, t2 = 0;
bool iOpened;
if (check_bytes > UInt32.MaxValue) {
Debug.Print("do_crc_check(): filesize too big");
return ERROR;
}
//Check File size
long fLength = new FileInfo(zm.fname).Length;
if (check_bytes == 0 && fLength != zm.bytes_total)
return ZCRC_DIFFERS;
//open if it's not already
try {
if (zm.fs == null) {
zm.fs = File.Open(zm.fname, FileMode.Open, FileAccess.ReadWrite);
iOpened = true;
}
else iOpened = false;
}
catch { return ERROR; }
crc = 0xFFFFFFFF;
n = check_bytes;
if (n == 0)
n = fLength;
while ((n-- > 0) && ((c = zm.fs.ReadByte()) != -1)) {
crc = CRC.UPDC32((byte)c, crc);
}
crc = ~crc;
zm.fs.Seek(0, 0);
if (iOpened) {
zm.fs.Close();
zm.fs = null;
}
while (t1 < 3) {
stohdr((uint)check_bytes);
zshhdr(sPort, ZCRC, ref Txhdr);
while (t2 < 3) {
uint tmp = 0;
c = zgethdr(sPort, ref Rxhdr, 0, ref tmp);
rcrc = tmp;
switch (c) {
default: //ignore
break;
case ZFIN:
return ERROR;
case ZRINIT:
return ERROR;
case ZCAN:
Debug.Print("do_crc_check(): got ZCAN\r\n");
return ERROR;
case ZCRC:
if (crc != rcrc)
return ZCRC_DIFFERS;
return ZCRC_EQUAL;
}
}
}
return ERROR;
}
private static int bytesToNextSpace(byte[] buff, int tokstart)
{
int toklen = Array.IndexOf(buff, (byte)32, tokstart) - tokstart;
// can't find another space... check for null, maybe its the last field.
if (toklen < 0) {
toklen = Array.IndexOf(buff, (byte)0, tokstart) - tokstart;
}
return toklen;
}
/*private int rzfile()
{
zi.eof_seen = false;
}
*/
private ushort getfree()
{
return (UInt16)(0xFFFF);
}
/*
private int sys2(byte c)
{
return 0;// currently not implemented
}
*/
private int wcreceive()
{
return 0;
}
}
}