-
Notifications
You must be signed in to change notification settings - Fork 6
/
xymodem.e
1435 lines (1204 loc) · 39.8 KB
/
xymodem.e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
OPT LARGE,MODULE
MODULE 'dos/dos','*axenums','*bcd'
CONST MAX_PATH=512
CONST CTRL_Z=26
CONST CPMEOF=2 ->CPM End of file (^Z)
CONST XMODEM_MIN_BLOCK_SIZE=128
CONST XMODEM_MAX_BLOCK_SIZE=1024
CONST XMODEM_2K_BLOCK_SIZE=2048
CONST NOINP=-1
CONST FAILURE=-2
CONST NOT_YMODEM=-3
CONST NOT_XMODEM=-4
CONST SEND = 0
CONST RECV = 1
CONST XMODEM = 2
CONST YMODEM = 4
CONST ZMODEM = 8
CONST CRC = 16
CONST GMODE = 32
CONST B2K = 64
CONST OVERWRITE=512
CONST SUCCESS=0
CONST SOH=1
CONST STX=2
CONST ETX=3
CONST EOT=4
CONST ACK=6
CONST NAK=21
CONST CAN=24
EXPORT OBJECT xymodem_t
mode:CHAR
g_mode_supported:CHAR
crc_mode_supported:CHAR
iacEncode:CHAR
sendBuffer:PTR TO CHAR
sendBufferSize
sendBufferPtr:PTR TO CHAR
sendBufferEnd
cancelled
send_timeout
recv_timeout
byte_timeout
ack_timeout
success
crc16tbl:PTR TO INT
zm_lputs
zm_progress
zm_recv_byte
zm_is_connected
zm_is_cancelled
zm_upload_completed
zm_upload_failed
zm_download_completed
zm_dupecheck
zm_data_waiting
zm_flush
zm_duplicate_filename
zm_fopen
zm_fclose
zm_fread
zm_fwrite
zm_fseek
zm_firstfile
zm_nextfile
current_file_name[MAX_PATH]:ARRAY OF CHAR
new_file
transfer_start_time1
transfer_start_time2
current_file_pos
current_file_size
total_files
total_bytes[8]:ARRAY OF CHAR
files_remaining
bytes_remaining[8]:ARRAY OF CHAR
errors
fallback_to_xmodem
g_delay
max_errors:LONG
block_size:LONG
max_block_size:LONG
max_file_size:LONG /* 0 = unlimited */
log_level:PTR TO INT
user_data:LONG
/* Callbacks */
cbdata: PTR TO CHAR
ENDOBJECT
PROC memset(addr:PTR TO CHAR,val,len)
WHILE (len--)>=0 DO addr[]++:=val
ENDPROC
->#define ucrc16(ch,crc) (crc16tbl[((crc>>8)&0xff)^(unsigned char)ch]^(crc << 8))
PROC ucrc16(zm:PTR TO xymodem_t,ch,crc) IS Eor(zm.crc16tbl[Eor(Shr(crc,8) AND $ff,ch)],Shl(crc,8)) AND $ffff
PROC getXYmSystemTime()
DEF currDate: datestamp
DateStamp(currDate)
ENDPROC Mul(Mul(currDate.days,1440),60)+(currDate.minute*60)+(currDate.tick/50),Mod(currDate.tick,50)
PROC fexist(file)
DEF lh
IF lh:=Lock(file,ACCESS_READ)
UnLock(lh)
RETURN TRUE
ENDIF
ENDPROC FALSE
PROC lprintf(xym:PTR TO xymodem_t, level, str:PTR TO CHAR)
DEF p
IF(xym.zm_lputs=NIL) THEN RETURN -1
IF(xym.log_level<>NIL)
IF(level > xym.log_level) THEN RETURN 0
ENDIF
p:=xym.zm_lputs
ENDPROC p(level,str)
PROC is_connected(xym: PTR TO xymodem_t)
DEF p
p:=xym.zm_is_connected
IF(p<>NIL) THEN RETURN p()
ENDPROC TRUE
PROC is_cancelled(xym: PTR TO xymodem_t)
DEF p
p:=xym.zm_is_cancelled
IF(p<>NIL)
xym.cancelled:=xym.cancelled OR p()
RETURN xym.cancelled
ENDIF
ENDPROC xym.cancelled
PROC download_completed(xym:PTR TO xymodem_t,fname,fsize,sentsize)
DEF p
p:=xym.zm_download_completed
IF p<>NIL
RETURN p(fsize,sentsize)
ENDIF
ENDPROC
PROC upload_completed(xym:PTR TO xymodem_t,fname:PTR TO CHAR,filebytes)
DEF p
p:=xym.zm_upload_completed
IF (p<>NIL) THEN p(fname,filebytes)
ENDPROC
PROC upload_failed(xym:PTR TO xymodem_t,fname:PTR TO CHAR)
DEF p
p:=xym.zm_upload_failed
IF (p<>NIL) THEN p(fname)
ENDPROC
PROC dupe_check(xym:PTR TO xymodem_t,fname)
DEF res=FALSE
DEF p
p:=xym.zm_dupecheck
IF (p<>NIL) THEN res:=p(fname)
ENDPROC res
PROC xmodem_duplicate(xym:PTR TO xymodem_t,fname:PTR TO CHAR)
DEF res=FALSE
DEF p
p:=xym.zm_duplicate_filename
IF (p<>NIL) THEN res:=p(fname)
ENDPROC res
PROC xmodem_progress(xym:PTR TO xymodem_t)
DEF p
p:=xym.zm_progress
IF(p<>NIL) THEN p(xym.current_file_pos,xym.current_file_pos,xym.current_file_size,xym.transfer_start_time1,xym.transfer_start_time2,xym.errors,0,xym.current_file_name,xym.new_file,xym.block_size)
ENDPROC
PROC putcom(xym:PTR TO xymodem_t,ch)
IF (xym.iacEncode AND (ch=255))
IF xym.sendBufferPtr>(xym.sendBufferEnd-2) THEN xmodem_flush(xym)
xym.sendBufferPtr[]:=ch
xym.sendBufferPtr:=xym.sendBufferPtr+1
xym.sendBufferPtr[]:=ch
xym.sendBufferPtr:=xym.sendBufferPtr+1
IF xym.sendBufferPtr=xym.sendBufferEnd THEN xmodem_flush(xym)
ELSE
xym.sendBufferPtr[]:=ch
xym.sendBufferPtr:=xym.sendBufferPtr+1
IF xym.sendBufferPtr=xym.sendBufferEnd THEN xmodem_flush(xym)
ENDIF
ENDPROC
PROC getcom(xym:PTR TO xymodem_t,timeout)
DEF p
p:=xym.zm_recv_byte
ENDPROC p(timeout)
EXPORT PROC xmodem_flush(xym: PTR TO xymodem_t)
DEF p
IF (xym.sendBufferPtr>xym.sendBuffer)
p:=xym.zm_flush
IF(p<>NIL) THEN p(xym.sendBuffer,xym.sendBufferPtr-xym.sendBuffer)
xym.sendBufferPtr:=xym.sendBuffer
ENDIF
ENDPROC
PROC chr(ch,output)
SELECT ch
CASE SOH
StrCopy(output,'SOH')
RETURN
CASE STX
StrCopy(output,'STX')
RETURN
CASE ETX
StrCopy(output,'ETX')
RETURN
CASE EOT
StrCopy(output,'EOT')
RETURN
CASE ACK
StrCopy(output,'ACK')
RETURN
CASE NAK
StrCopy(output,'NAK')
RETURN
CASE CAN
StrCopy(output,'CAN')
RETURN
ENDSELECT
IF((ch>=" ") AND (ch<="~"))
StringF(output,'''\c'' (\h[2])',ch,ch)
ELSE
StringF(output,'\d (\h[2])',ch,ch)
ENDIF
ENDPROC
PROC xmodem_put_ack(xym:PTR TO xymodem_t)
WHILE ((getcom(xym,0)<>NOINP) AND (is_connected(xym)))
ENDWHILE
putcom(xym,ACK)
xmodem_flush(xym)
ENDPROC
PROC xmodem_put_nak(xym:PTR TO xymodem_t, block_num)
DEF tempstr[255]:STRING
DEF dump_count=0
/* wait for any trailing data */
WHILE(((getcom(xym,0))<>NOINP) AND (is_connected(xym)))
dump_count++
->StringF(tempstr,'Block \d: Dumping byte: \h[2]',block_num,i)
->lprintf(xym,LOG_DEBUG,tempstr)
Delay(1)
ENDWHILE
IF(dump_count)
StringF(tempstr,'Block \d: Dumped \d bytes',block_num, dump_count)
lprintf(xym,LOG_DEBUG,tempstr)
ENDIF
IF(block_num<=1)
IF((xym.mode AND (B2K OR GMODE))=(B2K OR GMODE)) -> A for X/Ymodem-G with 2-K block
StringF(tempstr,'Block \d: Requesting mode: Streaming, 16-bit CRC, 2K Block', block_num)
lprintf(xym,LOG_DEBUG,tempstr)
putcom(xym,"A")
ELSEIF((xym.mode) AND GMODE) -> G for X/Ymodem-G
StringF(tempstr,'Block \d: Requesting mode: Streaming, 16-bit CRC', block_num)
lprintf(xym,LOG_DEBUG,tempstr)
putcom(xym,"G")
ELSEIF ((xym.mode) AND CRC) -> C for CRC
StringF(tempstr,'Block \d: Requesting mode: 16-bit CRC', block_num)
lprintf(xym,LOG_DEBUG,tempstr)
putcom(xym,"C")
ELSE -> NAK for checksum
StringF(tempstr,'Block \d: Requesting mode: 8-bit Checksum', block_num)
lprintf(xym,LOG_DEBUG,tempstr)
putcom(xym,NAK)
ENDIF
ELSE
putcom(xym,NAK)
ENDIF
xmodem_flush(xym)
ENDPROC
PROC xmodem_cancel(xym:PTR TO xymodem_t)
DEF i
IF((is_cancelled(xym)=FALSE) AND (is_connected(xym)))
xym.cancelled:=TRUE
i:=0
WHILE(i<8) AND (is_connected(xym))
putcom(xym,CAN)
i++
ENDWHILE
i:=0
WHILE((i<10) AND (is_connected(xym)))
putcom(xym,2)
i++
ENDWHILE
ENDIF
xmodem_flush(xym)
ENDPROC SUCCESS
->****************************************************************************/
->* Return 0 on success */
->****************************************************************************/
PROC xmodem_get_block(xym:PTR TO xymodem_t, block:PTR TO CHAR, expected_block_num)
DEF block_num -> Block number received in header
DEF block_inv
DEF chksum,calc_chksum
DEF i,eot=0,can=0
DEF b,errors
DEF crc,calc_crc
DEF tempstr[255]:STRING
DEF tempstr2[255]:STRING
StringF(tempstr,'Requesting data block \d', expected_block_num)
lprintf(xym, LOG_DEBUG, tempstr)
errors:=0
WHILE((errors<=xym.max_errors) AND (is_connected(xym)))
i:=getcom(xym,IF expected_block_num<=1 THEN 3 ELSE 10)
IF(eot AND (i<>EOT) AND (i<>NOINP)) THEN eot:=0
IF(can AND (i<>CAN)) THEN can:=0
SELECT i
CASE SOH -> 128-byte blocks
xym.block_size:=XMODEM_MIN_BLOCK_SIZE
CASE ETX -> 2048-byte blocks
IF(xym.max_block_size < XMODEM_2K_BLOCK_SIZE)
StringF(tempstr,'Block \d: 2048-byte blocks not supported',expected_block_num)
lprintf(xym,LOG_WARN,tempstr)
RETURN FAILURE
ENDIF
xym.block_size:=XMODEM_2K_BLOCK_SIZE
CASE STX -> 1024-byte blocks
IF(xym.max_block_size < XMODEM_MAX_BLOCK_SIZE)
StringF(tempstr,'Block \d: 1024-byte blocks not supported',expected_block_num)
lprintf(xym,LOG_WARN,tempstr)
RETURN FAILURE
ENDIF
xym.block_size:=XMODEM_MAX_BLOCK_SIZE
CASE EOT
StringF(tempstr,'Block \d: EOT received', expected_block_num)
lprintf(xym,LOG_DEBUG,tempstr)
IF(eot=FALSE)
lprintf(xym,LOG_DEBUG,'NAKing first EOT')
eot:=1
xmodem_put_nak(xym,expected_block_num) -> chuck's double EOT trick
JUMP lp
ENDIF
RETURN EOT
CASE CAN
IF(can=FALSE) -> must get two CANs in a row
can:=1
StringF(tempstr,'Block \d: Received CAN Expected SOH, STX, or EOT',expected_block_num)
lprintf(xym,LOG_WARN,tempstr)
JUMP lp
ENDIF
StringF(tempstr,'Block \d: Canceled remotely', expected_block_num)
lprintf(xym,LOG_WARN,tempstr)
RETURN CAN
CASE NOINP -> Nothing came in
IF(eot) THEN RETURN EOT
RETURN NOINP
DEFAULT
chr(i,tempstr2)
StringF(tempstr,'Block \d: Received \s Expected SOH, STX, or EOT',expected_block_num,tempstr2)
lprintf(xym,LOG_WARN,tempstr)
IF (eot) THEN RETURN EOT
RETURN NOINP
ENDSELECT
IF ((i:=getcom(xym,xym.byte_timeout))=NOINP) THEN RETURN FAILURE
block_num:=i
IF ((i:=getcom(xym,xym.byte_timeout))=NOINP) THEN RETURN FAILURE
block_inv:=i
calc_crc:=0
calc_chksum:=0
b:=0
WHILE((b<xym.block_size) AND (is_connected(xym)))
IF((i:=getcom(xym,xym.byte_timeout))=NOINP) THEN RETURN FAILURE
block[b]:=i
IF (xym.mode AND B2K)=0
IF(xym.mode AND CRC)
calc_crc:=ucrc16(xym,block[b],calc_crc)
ELSE
calc_chksum:=calc_chksum+block[b]
ENDIF
ENDIF
b++
ENDWHILE
IF (b<xym.block_size) THEN RETURN FAILURE
IF(block_num<>(Eor(block_inv,255)))
StringF(tempstr,'Block \d: Block number bit error (0x\h[2] vs 0x\h[2])',expected_block_num, block_num,Eor(block_inv,255))
lprintf(xym,LOG_WARN,tempstr)
RETURN FAILURE
ENDIF
IF(xym.mode AND B2K)=0
IF(xym.mode AND CRC)
crc:=Shl(getcom(xym,xym.byte_timeout),8)
crc:=crc OR getcom(xym,xym.byte_timeout)
IF(crc<>calc_crc)
StringF(tempstr,'Block \d: CRC ERROR', block_num)
lprintf(xym,LOG_WARN,tempstr)
RETURN FAILURE
ENDIF
ELSE -> CHKSUM
chksum:=getcom(xym,xym.byte_timeout)
IF(chksum<>calc_chksum)
StringF(tempstr,'Block \d: CHECKSUM ERROR', block_num)
lprintf(xym,LOG_WARN,tempstr)
RETURN FAILURE
ENDIF
ENDIF
ENDIF
IF(block_num<>(expected_block_num AND 255))
StringF(tempstr,'Block number error (\d received, expected \d)',block_num,expected_block_num AND 255)
lprintf(xym,LOG_WARN,tempstr)
IF (((xym.mode) AND XMODEM) AND (expected_block_num=1) AND (block_num=0)) THEN RETURN NOT_XMODEM
IF ((expected_block_num=0) AND (block_num=1)) THEN RETURN NOT_YMODEM
IF((expected_block_num) AND (block_num=((expected_block_num-1) AND 255)))
JUMP lp -> silently discard repeated packets (ymodem.doc 7.3.2)
ENDIF
RETURN FAILURE
ENDIF
RETURN SUCCESS -> Success
lp:
errors++
ENDWHILE
ENDPROC FAILURE -> Failure
->****************/
-> Sends a block */
/*****************/
PROC xmodem_put_block(xym:PTR TO xymodem_t, block:PTR TO CHAR, block_size, block_num)
DEF ch,chksum
DEF crc
IF xym.sendBufferPtr>(xym.sendBufferEnd-(Shl(block_size+5,1))) THEN xmodem_flush(xym)
IF(block_size=XMODEM_MIN_BLOCK_SIZE)
putcom(xym,SOH)
ELSEIF block_size=XMODEM_MAX_BLOCK_SIZE
-> 1024
putcom(xym,STX)
ELSE
-> 2048
putcom(xym,ETX)
ENDIF
ch:=(block_num AND 255)
putcom(xym,ch)
putcom(xym,Eor(ch,255))
IF((xym.mode) AND B2K)=FALSE
IF((xym.mode) AND CRC)
crc:=0
WHILE (block_size--)>=0
putcom(xym,ch:=block[]++)
crc:=ucrc16(xym,ch,crc)
ENDWHILE
putcom(xym,(Shr(crc,8)))
putcom(xym,(crc AND 255))
ELSE
chksum:=0
WHILE (block_size--)>=0
putcom(xym,ch:=block[]++)
chksum:=(chksum+ch) AND 255
ENDWHILE
putcom(xym,chksum)
ENDIF
ELSE
WHILE (block_size--)>=0 DO putcom(xym,block[]++)
ENDIF
IF((xym.mode) AND GMODE)=FALSE THEN xmodem_flush(xym)
ENDPROC
->************************************************************/
->* Gets an acknowledgement - usually after sending a block */
->* Returns ACK if ack received */
->************************************************************/
PROC xmodem_get_ack(xym:PTR TO xymodem_t, tries, block_num)
DEF i=NOINP,can=0
DEF errors
DEF tempstr[255]:STRING
DEF tempstr2[255]:STRING
errors:=0
WHILE((errors<tries) AND (is_connected(xym)))
IF((xym.mode) AND GMODE) -> Don't wait for ACK on X/Ymodem-G
IF xym.g_delay>0 THEN Delay(xym.g_delay)
IF(getcom(xym,0)=CAN)
StringF(tempstr,'Block \d: !Canceled remotely', block_num)
lprintf(xym,LOG_WARN,tempstr)
xmodem_cancel(xym)
RETURN CAN
ENDIF
RETURN ACK
ENDIF
i:=getcom(xym,xym.ack_timeout)
IF(can AND (i<>CAN)) THEN can:=0
IF (i=ACK) THEN RETURN ACK
IF(i=CAN)
IF(can) -> 2 CANs in a row
StringF(tempstr,'Block \d: !Canceled remotely', block_num)
lprintf(xym,LOG_WARN,tempstr)
xmodem_cancel(xym)
RETURN CAN
ENDIF
can:=1
ENDIF
IF(i<>NOINP)
chr(i,tempstr2)
StringF(tempstr,'Block \d: !Received \s Expected ACK',block_num, tempstr2)
lprintf(xym,LOG_WARN,tempstr)
IF (i<>CAN) THEN RETURN i
ENDIF
IF (i<>CAN) THEN errors++
ENDWHILE
ENDPROC i
PROC xmodem_get_mode(xym:PTR TO xymodem_t)
DEF i
DEF errors
DEF can
DEF tempstr[255]:STRING
DEF tempstr2[255]:STRING
lprintf(xym,LOG_DEBUG,'Waiting for transfer mode request...')
xym.mode:=xym.mode AND Eor(GMODE OR CRC,255)
errors:=0
can:=0
WHILE((errors<=xym.max_errors) AND (is_connected(xym)))
lp2:
i:=getcom(xym,xym.recv_timeout)
IF(can AND (i<>CAN)) THEN can:=0
SELECT i
CASE NAK -> checksum
lprintf(xym,LOG_DEBUG,'Receiver requested mode: 8-bit Checksum')
xym.max_block_size:=XMODEM_MAX_BLOCK_SIZE
xym.block_size:=XMODEM_MAX_BLOCK_SIZE
RETURN TRUE
CASE "C"
lprintf(xym,LOG_DEBUG,'Receiver requested mode: 16-bit CRC')
IF(xym.crc_mode_supported=FALSE) THEN JUMP lp2
xym.max_block_size:=XMODEM_MAX_BLOCK_SIZE
xym.block_size:=XMODEM_MAX_BLOCK_SIZE
xym.mode:=xym.mode OR CRC
RETURN TRUE
CASE "A"
lprintf(xym,LOG_DEBUG,'Receiver requested mode: Streaming, 2k-block, 16-bit CRC')
IF ((xym.crc_mode_supported=FALSE) OR (xym.g_mode_supported=FALSE)) THEN JUMP lp2
xym.mode:=xym.mode OR (GMODE OR CRC OR B2K)
xym.max_block_size:=XMODEM_2K_BLOCK_SIZE
xym.block_size:=XMODEM_2K_BLOCK_SIZE
RETURN TRUE
CASE "G"
lprintf(xym,LOG_DEBUG,'Receiver requested mode: Streaming, 16-bit CRC')
IF ((xym.crc_mode_supported=FALSE) OR (xym.g_mode_supported=FALSE)) THEN JUMP lp2
xym.max_block_size:=XMODEM_MAX_BLOCK_SIZE
xym.block_size:=XMODEM_MAX_BLOCK_SIZE
xym.mode:=xym.mode OR (GMODE OR CRC)
RETURN TRUE
CASE CAN
IF(can)
lprintf(xym,LOG_WARN,'Canceled remotely')
RETURN FALSE
ENDIF
can:=1
CASE NOINP
->do nothing
DEFAULT
chr(i,tempstr2)
StringF(tempstr,'Received \s Expected NAK, C, or G',tempstr2)
lprintf(xym,LOG_WARN,tempstr)
ENDSELECT
errors++
ENDWHILE
lprintf(xym,LOG_ERROR,'Failed to get transfer mode request from receiver')
ENDPROC FALSE
PROC xmodem_put_eot(xym:PTR TO xymodem_t)
DEF ch
DEF errors
DEF cans=0
DEF tempstr[255]:STRING
DEF tempstr2[255]:STRING
errors:=0
WHILE((errors<=xym.max_errors) AND (is_connected(xym)))
StringF(tempstr,'Sending End-of-Text (EOT) indicator (\d)',errors+1)
lprintf(xym,LOG_DEBUG,tempstr)
WHILE(((ch:=getcom(xym,0))<>NOINP) AND (is_connected(xym)))
chr(ch,tempstr2)
lprintf(tempstr,'Throwing out received: \s',tempstr2)
lprintf(xym,LOG_DEBUG,tempstr)
ENDWHILE
putcom(xym,EOT)
xmodem_flush(xym)
IF((ch:=getcom(xym,xym.recv_timeout))=NOINP) THEN JUMP lp3
chr(ch,tempstr2)
StringF(tempstr,'Received \s',tempstr2)
lprintf(xym,LOG_DEBUG,tempstr)
IF(ch=ACK) THEN RETURN TRUE
IF ((ch=CAN) AND (cans>0)) THEN RETURN FALSE
IF((ch=NAK) AND (errors=0) AND (((xym.mode) AND (YMODEM OR GMODE))=YMODEM))
JUMP lp3 -> chuck's double EOT trick so don't complain
ENDIF
lprintf(xym,LOG_WARN,'Expected ACK')
lp3:
errors++
ENDWHILE
ENDPROC FALSE
PROC xmodem_send_file(xym:PTR TO xymodem_t, fname:PTR TO CHAR, sent:PTR TO LONG, timetaken:PTR TO LONG)
DEF success=FALSE
DEF sent_bytes=0
DEF block[XMODEM_2K_BLOCK_SIZE]: ARRAY OF CHAR
DEF block_len
DEF block_num
DEF i
DEF rd
DEF sent_header=FALSE
DEF fp,fsize
DEF t,t1,t2,t3,t4
DEF tempstr[255]:STRING
DEF tempstr2[255]:STRING
DEF ch
DEF p,r
DEF fname2:PTR TO CHAR
StringF(tempstr,'Sending file \s',fname)
lprintf(xym,LOG_ERROR,tempstr)
xym.success:=FALSE
fname2:=FilePart(fname)
IF StrLen(fname)>0
fp:=doOpen(xym,fname,MODE_OLDFILE)
IF fp=NIL
StringF(tempstr,'Error opening file \s',fname)
lprintf(xym,LOG_ERROR,tempstr)
RETURN FALSE
ENDIF
fsize:=getFileSize(xym,fp)
ELSE
fp:=0
fsize:=0
ENDIF
xym.current_file_size:=fsize
IF(xym.total_files=0) THEN xym.total_files:=1
IF(convertFromBCD(xym.total_bytes)=0) THEN convertToBCD(fsize,xym.total_bytes)
IF(xym.files_remaining=0) THEN xym.files_remaining:=1
IF(convertFromBCD(xym.bytes_remaining)=0) THEN convertToBCD(fsize,xym.bytes_remaining)
-> try
IF((xym.mode) AND YMODEM)
IF(xmodem_get_mode(xym)=FALSE) THEN JUMP sbr3
memset(block,0,XMODEM_MAX_BLOCK_SIZE)
AstrCopy(block,fname2,XMODEM_MAX_BLOCK_SIZE)
formatBCD(xym.bytes_remaining,tempstr2)
StringF(tempstr,'\d \d 0 0 \d \s'
,fsize
,0 ->(uintmax_t)st.st_mtime
,xym.files_remaining ->-xym.sent_files
,tempstr2 ->-xym.sent_bytes
)
AstrCopy(block+StrLen(block)+1,tempstr)
block_len:=StrLen(block)+1+StrLen(tempstr)
StringF(tempstr,'Sending YMODEM header block: ''\s''',block+StrLen(block)+1)
lprintf(xym,LOG_DEBUG,tempstr)
xym.errors:=0
WHILE((xym.errors<=xym.max_errors) AND (is_cancelled(xym)=FALSE) AND (is_connected(xym)))
WHILE(((ch:=getcom(xym,0))<>NOINP) AND (is_connected(xym)))
chr(ch,tempstr2)
StringF(tempstr,'Throwing out received: \s',tempstr2)
lprintf(xym,LOG_DEBUG,tempstr)
ENDWHILE
xmodem_put_block(xym, block, IF block_len <=XMODEM_MIN_BLOCK_SIZE THEN XMODEM_MIN_BLOCK_SIZE ELSE xym.block_size, 0)
xmodem_flush(xym)
IF((i:=xmodem_get_ack(xym,1, 0)) = ACK)
sent_header:=TRUE
JUMP sbr2
ENDIF
IF(((i=NAK) OR (i="C") OR (i="G")) AND (xym.fallback_to_xmodem) AND ((xym.errors+1) = xym.fallback_to_xmodem))
StringF(tempstr,'Falling back to XMODEM mode after \d attempts',xym.fallback_to_xmodem)
lprintf(xym,LOG_DEBUG,tempstr)
xym.mode:=xym.mode AND Not(YMODEM)
JUMP sbr2
ENDIF
xym.errors:=xym.errors+1
ENDWHILE
sbr2:
IF((xym.errors>xym.max_errors) OR (is_cancelled(xym)))
lprintf(xym,LOG_ERROR,'Failed to send header block')
JUMP sbr3
ENDIF
ENDIF
t1,t2:=getXYmSystemTime()
->file handle is
IF fp=0 THEN RETURN TRUE
IF(xmodem_get_mode(xym)=FALSE) THEN JUMP sbr3
p:=xym.zm_progress
IF(p<>NIL) THEN p(sent_bytes,sent_bytes,fsize,t1,t2,xym.errors,0,fname2,TRUE,xym.block_size)
block_num:=1
xym.errors:=0
WHILE((sent_bytes < fsize) AND (xym.errors<=xym.max_errors) AND (is_cancelled(xym)=FALSE) AND (is_connected(xym)))
doSeek(xym,fp,sent_bytes,OFFSET_BEGINNING)
IF(sent_header=FALSE)
IF(xym.block_size>XMODEM_MIN_BLOCK_SIZE)
IF((sent_bytes+xym.block_size) > fsize)
IF((sent_bytes+xym.block_size-XMODEM_MIN_BLOCK_SIZE) >= fsize)
lprintf(xym,LOG_DEBUG,'Falling back to 128-byte blocks for end of file')
xym.block_size:=XMODEM_MIN_BLOCK_SIZE
ENDIF
ENDIF
ENDIF
ENDIF
IF(((rd:=doRead(xym,fp,block,xym.block_size))<>xym.block_size) AND ((sent_bytes + rd) <> fsize))
StringF(tempstr,'ERROR reading \d bytes at file offset \d',xym.block_size,sent_bytes)
lprintf(xym,LOG_ERROR,tempstr)
xym.errors:=xym.errors+1
JUMP lp4
ENDIF
memset(block+rd,CPMEOF,xym.block_size-rd)
xmodem_put_block(xym, block, xym.block_size, block_num)
IF((r:=xmodem_get_ack(xym, 5,block_num)) <> ACK)
xym.errors:=xym.errors+1
StringF(tempstr,'Block \d: Error #\d at offset \d',block_num, xym.errors,sent_bytes)
lprintf(xym,LOG_WARN,tempstr)
IF (((r="C") OR (xym.errors=3)) AND (block_num=1) AND (xym.block_size>XMODEM_MIN_BLOCK_SIZE))
StringF(tempstr,'Block \d: Falling back to 128-byte blocks', block_num)
lprintf(xym,LOG_DEBUG,tempstr)
xym.block_size:=XMODEM_MIN_BLOCK_SIZE
ENDIF
ELSE
block_num++
sent_bytes:=sent_bytes+rd
ENDIF
p:=xym.zm_progress
IF(p<>NIL) THEN p(sent_bytes,sent_bytes,fsize,t1,t2,xym.errors,0,fname2,FALSE,xym.block_size)
lp4:
ENDWHILE
IF((sent_bytes >= fsize) AND (is_cancelled(xym)=FALSE) AND (is_connected(xym)))
->#if 0 /* !SINGLE_THREADED */
-> lprintf(LOG_DEBUG,"Waiting for output buffer to empty... ")
-> if(WaitForEvent(outbuf_empty,5000)<>WAIT_OBJECT_0)
-> lprintf(xym,LOG_WARN,"FAILURE")
->#endif
IF (xmodem_put_eot(xym)) -> end-of-text, wait for ACK
success:=TRUE
ENDIF
ENDIF
sbr3:
/* finally */
IF (success=FALSE) THEN xmodem_cancel(xym)
IF(sent<>NIL) THEN sent[]:=sent[]+sent_bytes
t3,t4:=getXYmSystemTime()
t:=Mul((t3-t1),50)+t4-t2
IF(timetaken<>NIL) THEN timetaken[]:=timetaken[]+t
xym.success:=success
doClose(xym,fp)
ENDPROC success
EXPORT PROC xymodem_send_files(ymodem,xym: PTR TO xymodem_t,sentptr: PTR TO LONG, timetaken:PTR TO LONG)
DEF p,res,init=TRUE
DEF fname[255]:STRING
DEF tempstr[255]:STRING
DEF tempstr2[255]:STRING
DEF ch
DEF sent
xym.mode:=(IF ymodem THEN YMODEM ELSE XMODEM) OR SEND
xym.files_remaining:=xym.total_files
CopyMem(xym.total_bytes,xym.bytes_remaining,8)
WHILE(((ch:=getcom(xym,0))<>NOINP) AND (is_connected(xym)))
chr(ch,tempstr2)
lprintf(tempstr,'Throwing out received: \s',tempstr2)
lprintf(xym,LOG_DEBUG,tempstr)
ENDWHILE
IF timetaken<>NIL THEN timetaken[]:=0
p:=xym.zm_firstfile
IF p<>NIL
IF p(fname)
REPEAT
sent:=0
res:=xmodem_send_file(xym, fname, {sent}, timetaken)
IF res=FALSE THEN RETURN res
IF (xym.success)
download_completed(xym,fname,xym.current_file_size,sent)
ENDIF
init:=FALSE
IF res
p:=xym.zm_nextfile
res:=FALSE
IF p<>NIL THEN res:=p(fname)
xym.files_remaining:=xym.files_remaining-1
subBCD(xym.bytes_remaining,sent)
IF xym.files_remaining<0 THEN xym.files_remaining:=0
IF xym.bytes_remaining[0]=$99 THEN convertToBCD(0,xym.bytes_remaining)
IF sentptr<>NIL THEN sentptr[]:=sentptr[]+sent
ENDIF
UNTIL res=FALSE
xmodem_send_file(xym, '', {sent}, timetaken)
xmodem_flush(xym)
ENDIF
ENDIF
ENDPROC TRUE
EXPORT PROC xymodem_recv_files(ymodem,xym: PTR TO xymodem_t, download_dir:PTR TO CHAR,bytes_received: PTR TO LONG,timetaken:PTR TO LONG)
DEF str[255]:STRING
DEF fname[255]:STRING
DEF logtmp[255]:STRING
DEF i=0
DEF fnum=0
DEF total_files=0
DEF cps,wr
DEF success=FALSE
DEF fmode
DEF serial_num=-1
DEF tmpftime
DEF file_bytes=0,file_bytes_left=0
DEF total_bytes=0
DEF fp=NIL
DEF t,t1,t2,ftime=0
DEF old_hold
DEF tmp,s,r,pos
DEF hold_update=FALSE
DEF block[2048]:ARRAY OF CHAR
DEF block_num
DEF fcount=0
IF ymodem
xym.mode:=YMODEM OR CRC OR GMODE OR B2K OR RECV OR OVERWRITE
ELSE
xym.mode:=XMODEM OR CRC OR RECV OR OVERWRITE
ENDIF
old_hold:=hold_update
IF timetaken<>NIL THEN timetaken[]:=0
IF bytes_received<>NIL THEN bytes_received[]:=0
xym.current_file_size:=0
WHILE(is_connected(xym))
IF(xym.mode AND XMODEM)
StrCopy(str,download_dir)
FOR i:=0 TO 20 DO StrAddChar(str,Rnd(26)+97)
StrCopy(fname,str)
file_bytes:=$7fffffff
file_bytes_left:=file_bytes
ELSE
lprintf(xym,LOG_DEBUG,'Fetching YMODEM header block')
xym.errors:=0
WHILE((xym.errors<=xym.max_errors) AND (xym.cancelled=FALSE))
xmodem_put_nak(xym,0)
i:=xmodem_get_block(xym, block, 0)
IF(i=SUCCESS)
IF((xym.mode AND GMODE)=FALSE)
putcom(xym,ACK)
xmodem_flush(xym)
ENDIF
JUMP rbr1
ENDIF
IF(((xym.errors+1)>(xym.max_errors/3)) AND (i=NOINP) AND ((xym.mode AND (B2K OR GMODE))=(B2K OR GMODE))) /* Timeout */
xym.mode:=xym.mode AND Not(B2K)
StringF(logtmp,'Falling back to Streaming \s',IF (xym.mode AND CRC) THEN 'CRC-16' ELSE 'Checksum')
lprintf(xym,LOG_WARN,logtmp)
xym.errors:=-1
ELSEIF(((xym.errors+1)>(xym.max_errors/3)) AND (i=NOINP) AND (xym.mode AND GMODE)) /* Timeout */
xym.mode:=xym.mode AND Not(GMODE)
StringF(logtmp,'Falling back to \s',IF (xym.mode AND CRC) THEN 'CRC-16' ELSE 'Checksum')
lprintf(xym,LOG_WARN,logtmp)
xym.errors:=-1
ENDIF
IF(i=NOT_YMODEM)
StringF(logtmp,'Falling back to XMODEM\s',IF (xym.mode AND GMODE) THEN '-g' ELSE '')
lprintf(xym,LOG_WARN,logtmp)
xym.mode:=xym.mode AND Not(YMODEM)
xym.mode:=xym.mode OR XMODEM OR CRC
hold_update:=0
->if(uifc.input(WIN_MID|WIN_SAV,0,0,"XMODEM Filename",fname,sizeof(fname),0)==-1) {
xmodem_cancel(xym);
JUMP end