home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / FILE / JLPAK10.ZIP / JLPAK.BAS next >
Encoding:
BASIC Source File  |  1990-08-19  |  52.3 KB  |  1,645 lines

  1. DEFINT A-Z
  2.  
  3. DECLARE SUB LookForMatch (x, i)
  4. DECLARE SUB GetCommNames ()
  5. DECLARE SUB Resynch ()
  6. DECLARE SUB Normal ()
  7. DECLARE SUB need1(diff)
  8. DECLARE SUB need2(diff1,diff2)
  9. DECLARE FUNCTION fchangesok (x,ip,diff1, diff2, diff3, diff4)
  10. DECLARE SUB SetDiffer ( k, x)
  11. DECLARE FUNCTION FileExists! (testfile$)
  12. '  $INCLUDE: 'qb.bi'
  13.  
  14.  
  15. CONST IBUF% = 100
  16. REM IBUF% is the input buffer size for obtaining the ascii value of each
  17. REM byte.  Speed seems relatively independent of the IBUF% size.
  18.  
  19. CONST ISIZE% = 32000
  20. CONST ISIZE1% = 22000
  21. CONST ISIZE2% = ISIZE% - ISIZE1%
  22. CONST ISIZE3% = ISIZE1% - ISIZE2%
  23. REM This is the hunk of file that we work with.   We read in the first
  24. REM 32000 (or all if less),  and then try to obtain matches for the first
  25. REM 22000 of new.   Then we go again,   throwing away the 
  26. REM first 10000 (ISIZE2%),  and reading in the next 10000,  then matching
  27. REM from 12000 (ISIZE3%) up to 22000, and so on.
  28.  
  29. CONST ISEG% = 16
  30. REM This is the hunk we use for basic matching.
  31. REM ISEG%*256 must be less than 32K-1,  because we sum ISEG% values into
  32. REM an INTEGER value.
  33.  
  34. CONST MAXINSERT% = 1000
  35. REM This is the area of the search for matches from the current position.
  36. REM Speed is directly proportional to the size of this.
  37.  
  38. CONST SEGARRAYSIZE% = ISIZE% \ ISEG%
  39. CONST SEGARRAYSIZE1% = ISIZE1% \ ISEG%
  40. REM This is the size of the arrays holding details of matches
  41.  
  42. REM $DYNAMIC
  43.  
  44. DIM bytesold(ISIZE%)
  45. REM This is the array of bytes from the old version
  46.  
  47. DIM bytesnew(ISIZE%)
  48. REM This is the array of bytes from the new version
  49.  
  50. DIM matched(SEGARRAYSIZE%)
  51. REM This is the array that says where we have a match (0 if none)
  52. REM for each block of ISEG% bytes.
  53.  
  54. DIM pointerchange(SEGARRAYSIZE%)
  55. REM This array holds the change in the offset pointer needed for this
  56. REM match.
  57.  
  58. DIM matchtype(SEGARRAYSIZE%)
  59. REM This holds the type of the match,  1 for type A,  2 for type B,  and
  60. REM 3 for type C.
  61.  
  62. DIM subtype(SEGARRAYSIZE%)
  63. REM For type B and C matches,  this identifies the diffs to use.
  64. DIM differa(SEGARRAYSIZE%)
  65. DIM differb(SEGARRAYSIZE%)
  66. DIM differc(SEGARRAYSIZE%)
  67. DIM differd(SEGARRAYSIZE%)
  68. REM These hold the diffa and the diffb values for type B and type C
  69. REM matches.  The value is new - old.  Differc and differd are used
  70. REM for type D matches.  Note that for type D,  one differ may be
  71. REM zero.
  72.  
  73. DIM bitval(8)
  74. REM holds the values of the bits in an octet for easy setting in bitmaps
  75.  
  76. DIM gdiff(4),  gdiffcnt(4)
  77. REM Used to hold the global difference values we maintain during analysis
  78.  
  79. DIM gdiff2(4)
  80. REM Used to hold the global difference values we maintain during output
  81.  
  82. REM SHARED reffile$,  newfile$,  sendfile$,  diaglevel$,  diagoutput$
  83. REM SHARED recreffile$,  refnewfile$
  84.  
  85. COLOR 15, 1, 1
  86.  
  87. CALL GetCommNames
  88.  
  89. done& = 0
  90. usedold& = 0
  91. donesegs& = 0
  92. REM This is the amount of the new file that we have already done as a
  93. REM previous hunk.
  94.  
  95. dgf = 0
  96.  
  97. valjl = 1
  98. FOR i = 8 to 1 step -1
  99. bitval(i) = valjl
  100. valjl = valjl*2
  101. next i
  102.  
  103. IF INSTR(diaglevel$,"T") <> 0 THEN
  104. dgf = FREEFILE
  105. OPEN diagoutput$ FOR OUTPUT AS #dgf
  106. END IF
  107.  
  108. IF INSTR(diaglevel$,"P") <> 0 then
  109.    CLS
  110.    locate 2,26
  111.    color 14,4,1
  112.    PRINT "Forward compression program";
  113.    color 15,1,1
  114.    locate 5,1
  115.    PRINT "Compressing " + newfile$ + " using " + reffile$ + " as a reference file";
  116. END IF
  117.  
  118. IF INSTR(diaglevel$,"B") <> 0 THEN
  119. PRINT "JLPAK Copyright IT Institute 1990 - Forward compression program version 1.0"
  120. PRINT " "
  121. bline = CSRLIN
  122. locate bline,1
  123. PRINT "Percent done is: 0%     Reading in files for processing - please wait ..." ;
  124. END IF
  125.  
  126. fpold = FREEFILE
  127. buff$ = STRING$(IBUF%, " ")
  128. if fileexists(reffile$) then
  129.    OPEN reffile$ FOR BINARY AS #fpold
  130. else
  131. PRINT "**** ERROR - Reference file " + reffile$ + " does not exist"
  132.    if dgf <> 0 then close #dgf
  133.    end
  134. end if
  135.  
  136. checked = 0
  137. oldsumcheck1 = 0
  138. oldsumcheck2 = 0
  139. WHILE NOT EOF(fpold)
  140.        GET #fpold, ,buff$
  141. if checked < 10 then
  142.    checked = checked + 1
  143.    if not eof(fpold) then
  144.        for i = 1 to IBUF%
  145.        octval = ASC(MID$(buff$,i,1))
  146.        oldsumcheck1 = (oldsumcheck1 + octval) MOD 255
  147.        oldsumcheck2 = (oldsumcheck2 + oldsumcheck1) MOD 255
  148.        next i
  149.    else
  150.        lengthold& = loc(fpold)
  151.        for i = 1 to (lengthold& MOD IBUF%)
  152.        octval = ASC(MID$(buff$,i,1))
  153.        oldsumcheck1 = (oldsumcheck1 + octval) MOD 255
  154.        oldsumcheck2 = (oldsumcheck2 + oldsumcheck1) MOD 255
  155.        next i
  156.    end if
  157. end if
  158. WEND
  159. lengthold& = loc(fpold)
  160.  
  161. close #fpold
  162. OPEN reffile$ FOR BINARY AS #fpold
  163.  
  164. ICNT1 = 0
  165. WHILE NOT EOF(fpold) AND ICNT1 + IBUF% <= ISIZE%
  166.      GET #fpold, , buff$
  167.      FOR i = 1 TO IBUF%
  168.      ICNT1 = ICNT1 + 1
  169.      bytesold(ICNT1) = ASC(MID$(buff$, i, 1))
  170.      NEXT i
  171. WEND
  172.  
  173. endold = ISIZE%
  174. if lengthold& < ISIZE% then endold = lengthold&
  175.  
  176. IF INSTR(diaglevel$,"P") <> 0 THEN
  177.        locate 10,1
  178.        PRINT "First hunk of reference file loaded - total length is " + str$(lengthold&) + " bytes";
  179. END IF
  180.  
  181. fpnew = FREEFILE
  182. buff$ = STRING$(IBUF%, " ")
  183. if fileexists(newfile$) then
  184.    OPEN newfile$ FOR BINARY AS #fpnew
  185. else
  186. PRINT "**** ERROR - File to be compressed (" + newfile$ + ") does not exist"
  187.    if dgf <> 0 then close #dgf
  188.    close #fpold
  189.    end
  190. end if
  191.  
  192. sumcheck1 = 0
  193. sumcheck2 = 0
  194. WHILE NOT EOF(fpnew)
  195.        GET #fpnew, ,buff$
  196.    if not eof(fpnew) then
  197.        for i = 1 to IBUF%
  198.        octval = ASC(MID$(buff$,i,1))
  199.        sumcheck1 = (sumcheck1 + octval) MOD 255
  200.        sumcheck2 = (sumcheck2 + sumcheck1) MOD 255
  201.        next i
  202.    else
  203.        lengthnew& = loc(fpnew)
  204.        for i = 1 to (lengthnew& MOD IBUF%)
  205.        octval = ASC(MID$(buff$,i,1))
  206.        sumcheck1 = (sumcheck1 + octval) MOD 255
  207.        sumcheck2 = (sumcheck2 + sumcheck1) MOD 255
  208.        next i
  209.    end if
  210. WEND
  211. lengthnew& = loc(fpnew)
  212.  
  213. close #fpnew
  214. OPEN newfile$ FOR BINARY AS #fpnew
  215.  
  216. ICNT2 = 0
  217. WHILE NOT EOF(fpnew) AND ICNT2  + IBUF% <= ISIZE%
  218.      GET #fpnew, , buff$
  219.      FOR i = 1 TO IBUF%
  220.      ICNT2 = ICNT2 + 1
  221.      bytesnew(ICNT2) = ASC(MID$(buff$, i, 1))
  222.      NEXT i
  223. WEND
  224.  
  225. endnew = ISIZE%
  226. if lengthnew& < ISIZE% then endnew = lengthnew&
  227.  
  228. newblocks = endnew \ ISEG%
  229.  
  230. if lengthold& > ISIZE% or lengthnew& > ISIZE% then
  231.        if newblocks > SEGARRAYSIZE1% then newblocks = SEGARRAYSIZE1%
  232. end if
  233.  
  234. IF INSTR(diaglevel$,"P") <> 0 THEN
  235.       locate 11,1
  236.       PRINT "First hunk of main file loaded - total length is " + str$(lengthnew&) + " bytes";
  237. END IF
  238.  
  239. REM Now we initialise all variables,  ready to start the first hunk analysis
  240.  
  241. hunkstart = 0
  242. startoffset = 0
  243. factor = 1
  244. skipval = 1
  245. nomatchcnt = 0
  246. resyncstate = 0
  247. typeacnt = 0
  248. typebcnt = 0
  249. typeccnt = 0
  250. typed1cnt = 0
  251. typed2cnt = 0
  252. nomatches = 0
  253. ptrchs = 0
  254. ptr = 0
  255. for i = 1 to 4
  256. gdiff(i) = 0
  257. gdiff2(i) = 0
  258. gdiffcnt(i) = 0
  259. next i
  260. lastperc& = 0
  261. lasttypeacnt = 0
  262. lasttypebcnt = 0
  263. lasttypeccnt = 0
  264. lasttyped1cnt = 0
  265. lasttyped2cnt = 0
  266. lastnomatches = 0
  267. lastptrchs = 0
  268. lastfullmatch = 0
  269. investigate& = 0
  270. cumptr = 0
  271. outputcnt& = 0
  272. obuff$ = ""
  273. IF FileExists(sendfile$) THEN KILL sendfile$
  274. fp = FREEFILE
  275. OPEN sendfile$ FOR BINARY AS #fp
  276.  
  277. REM Output the preamble text
  278.  
  279. 'There will be an initial block containing:
  280. ' 1.  a length count (one octet)for the next item.
  281. ' 2.  the full path name of the file that is to be compressed
  282. '     (on decompression,  a file TEMP.TMP will be created in the current
  283. '     directory,  and then copied to the original destination path when
  284. '     decompression is complete,  unless overridden by a different target).
  285. ' 3.  two octets of sum check for the whole of this file;  the sum check
  286. '     is the standard one of SIGMA(Ai) and SIGMA(iAi),  all MOD 255.
  287. ' 4.  a length count for the next item.
  288. ' 5.  the full path name of the reference file (this can be overridden
  289. '     on decompression)
  290. ' 6.  two octets of sum check for the first 1K of the reference file.
  291.  
  292. obuff$ = obuff$ + CHR$(LEN(recnewfile$))
  293. obuff$ = obuff$ + recnewfile$
  294. obuff$ = obuff$ + CHR$(sumcheck1) + CHR$(sumcheck2)
  295. obuff$ = obuff$ + CHR$(LEN(recreffile$))
  296. obuff$ = obuff$ + recreffile$
  297. obuff$ = obuff$ + CHR$(oldsumcheck1) + CHR$(oldsumcheck2)
  298. outputcnt& = outputcnt& + LEN(recnewfile$) + LEN (recreffile$) + 6
  299.  
  300. PUT #fp, , obuff$
  301. obuff$ = ""
  302.  
  303. REM We try to find a match for each ISEG of octets.
  304. REM We start the search at the corresponding position in the
  305. REM old file,  offset by startoffset.   Startoffset is updated on each
  306. REM to migrate towards the offset of the match.  We will go up to
  307. REM MAXINSERT% * factor from startoffset,   working outwards in one byte
  308. REM units.
  309. REM Factor is initially one,  but gets adjusted if matches fail.
  310.  
  311. DO : REM This is the outer loop,  LOOP UNTIL finished,  for each hunk.
  312.  
  313. FOR i = 1 + hunkstart TO newblocks
  314. matched(i) = 0
  315. matchtype(i) = 0
  316. pointerchange(i) = 0
  317. REM Assume not matched.
  318. k = 0
  319. c1 = i * ISEG% + startoffset
  320. c2 = i * ISEG% + startoffset
  321. WHILE k <= (MAXINSERT% * factor) AND matched(i) = 0 AND (i MOD skipval) =  0
  322. IF c1 <= endold AND C1 >= ISEG% + lastfullmatch THEN
  323.      CALL LookForMatch (c1, i)
  324. END IF
  325. IF matched(i) <> 0 THEN
  326.            offset = c1 - i * ISEG%
  327.            pointerchange(i) = offset - ptr
  328.            ptr = offset
  329.            startoffset = (startoffset + offset) \ 2
  330. ELSE
  331.    IF c2 >= ISEG% + lastfullmatch AND c2 <= endold THEN
  332.         CALL LookForMatch(c2, i)
  333.    END IF
  334.    IF matched(i) <> 0 THEN
  335.            offset = c2 - i * ISEG%
  336.            pointerchange(i) = offset - ptr
  337.            ptr = offset
  338.            startoffset = (startoffset + offset) \ 2
  339.    END IF
  340. END IF
  341. k = k + 1
  342. c1 = c1 + 1
  343. c2 = c2 - 1
  344. WEND
  345. IF matched(i) = 0 THEN
  346.       nomatches = nomatches + 1
  347.       SELECT CASE resyncstate
  348.  
  349.       CASE IS = 0
  350.              nomatchcnt = nomatchcnt + 1
  351.              IF nomatchcnt = 10 then
  352.                   resyncstate = 1
  353.              end if
  354.  
  355.       CASE IS = 1
  356.              if (i mod skipval) = 0 then nomatchcnt = nomatchcnt + 1
  357.              skipval = nomatchcnt - 9
  358.              factor = (nomatchcnt - 8) \ 2
  359.              IF nomatchcnt = 20 then
  360.                     call resynch
  361.                     resyncstate = 2
  362.              end if
  363.       
  364.       CASE IS = 2
  365.  
  366.       END SELECT
  367. ELSE
  368.      SELECT CASE resyncstate
  369.   
  370.      CASE IS = 0
  371.              nomatchcnt = 0
  372.  
  373.      CASE IS = 1
  374.              nomatchcnt = nomatchcnt - 1
  375.              if nomatchcnt = 10 then call normal
  376.              skipval = 1
  377.  
  378.      CASE IS = 2
  379.              skipval = 1
  380.              resyncstate = 1
  381.  
  382.      END SELECT 
  383.  
  384.       if pointerchange(i) <> 0 then ptrchs = ptrchs + 1
  385. END IF
  386. IF INSTR(diaglevel$,"P") <> 0 THEN
  387.           perc& = i
  388.           realperc& = ((perc& * ISEG% + done&) * 1000)\lengthnew&
  389.           locate 20,1
  390.           PRINT "Percent done is: " + left$(STR$(realperc&/10.0),5) + "%     " ;
  391.           locate 21,1
  392.           PRINT "Resync state is: " + str$(resyncstate) + " No match count is: " + str$(nomatchcnt) + "          "
  393.           locate 22,1
  394.           PRINT "Number of no matches: " + STR$(nomatches) + " out of " + STR$(i + donesegs&) + " segments";
  395.           locate 23,1
  396.           PRINT "Type A: " + STR$(typeacnt) + "  Type B: " +STR$(typebcnt) + "  Type C: " + STR$(typeccnt) + " Type D1: " + STR$(typed1cnt) + " Type D2: " + STR$(typed2cnt);
  397.           locate 24,1
  398.           PRINT "Start offset is: " + str$(startoffset) + "               ";
  399. END IF
  400.  
  401. IF INSTR(diaglevel$,"B") <> 0 THEN
  402. perc& = i
  403. realperc& = ((perc& * ISEG% + done&) * 1000)\lengthnew&
  404. locate bline,1
  405. PRINT "Percent done is: " + left$(STR$(realperc&/10.0),5) + " %                                                      " ;
  406. END IF
  407.  
  408. NEXT i
  409.  
  410. IF INSTR(diaglevel$,"P") <> 0 THEN
  411. locate 15,1
  412. PRINT STRING$(80," ")
  413. locate 10,1
  414. PRINT STRING$(80," ");
  415. locate 10,1
  416. PRINT "Done analysis of a hunk of the file - outputing compressed format";
  417. locate 11,1
  418. PRINT STRING$(80," ");
  419. END IF
  420.  
  421. IF INSTR(diaglevel$,"B") <> 0 THEN
  422. perc& = i
  423. realperc& = ((perc& * ISEG% + done&) * 1000)\lengthnew&
  424. locate bline,1
  425. PRINT "Percent done is: " + left$(STR$(realperc&/10.0),5) + " %  Writing out compressed format - please wait ...    " ;
  426. END IF
  427.  
  428.  
  429. 'The receivers state is set up with ptroffset = 0,  diffa = 0,  and
  430. 'diffb = 0 and done& = 0 and usedold& = 0.   
  431. 'These values are retained unless explicitly 
  432. 'changed.  The difference is new - old
  433.  
  434. IF INSTR(diaglevel$,"I") <> 0 AND investigate& > = 0 THEN
  435. locate 13,1
  436. INPUT "Specify first i for investigation (0 end, -1 none, -2 all):",investigate&
  437.       IF investigate& = 0 THEN END
  438. END IF
  439.  
  440. 'We take each ISEG block in turn. 
  441. i = hunkstart
  442. WHILE i < newblocks
  443. i = i + 1
  444.  
  445. IF LEN(obuff$) > 100 THEN
  446.        PUT #fp, , obuff$
  447.        obuff$ = ""
  448. END IF
  449.  
  450. 'If there is no match, we output 10nnnnnn followed by the nnnnnn sets of
  451. '16 octets for up to 64 (nnnnnn of 0 means 64) segments.
  452. 'Note that 11nnnnnn is used for total matches (see below).  All other
  453. 'codes are 0xxxxxxx.
  454.  
  455. IF matched(i) = 0 THEN
  456.      nnn = 1
  457.      j = i + 1
  458.      WHILE matched(j) = 0 AND nnn < 64 AND j <= newblocks
  459.      j = j + 1
  460.      nnn = nnn + 1
  461.      WEND
  462.      k = nnn
  463.      IF nnn = 64 THEN nnn = 0
  464.      nnn = nnn + 128
  465.      obuff$ = obuff$ + CHR$(nnn)
  466.      newstart = (i -1) * ISEG%
  467.      FOR ik = 1 to k
  468.      FOR ii = 1 TO ISEG%
  469.      obuff$ = obuff$ + CHR$(bytesnew(newstart + ii))
  470.      NEXT ii
  471.      IF LEN(obuff$) > 100 THEN
  472.          PUT #fp, , obuff$
  473.          obuff$ = ""
  474.      END IF
  475.      newstart = newstart + ISEG%
  476.      next ik
  477.      i = i + k - 1
  478.      outputcnt& = outputcnt& + (ISEG% * k) + 1
  479.      IF INSTR(diaglevel$,"T") <> 0 THEN
  480.          PRINT #dgf, i + donesegs&;
  481.          PRINT #dgf,STR$(k) + " no match blocks"
  482. IF INSTR(diaglevel$,"I")<>0 and (investigate& = i + donesegs& or investigate& = -2) THEN
  483.                 newstart = (i-1) * ISEG%
  484.                 oldstart = newstart + cumptr
  485.                 FOR ii = 1 TO ISEG%
  486.                 PRINT #dgf, bytesnew(newstart + ii), bytesold(oldstart + ii)
  487.                 NEXT ii
  488.                 IF investigate& <> -2 THEN
  489. INPUT "Specify next i for investigation (0 end, -1 none, -2 all):",investigate&
  490.                 IF investigate& = 0 THEN END
  491.                 END IF
  492.           END IF
  493.      END IF
  494. ELSE
  495.  
  496. 'If there is a pointer change,  we need to signal it in the output.
  497. 'We have four cases for the value of the ptr change:
  498. '             positive and less than (or equals) 256
  499. '             negative and less than (or equals) 256
  500. '             positive and over 256
  501. '             negative and over 256
  502. 'We use 00000000,  00000001, 00010000, and 00010001 for these four cases.
  503. 'In the first two cases,  we follow with a single octet,  and in the last
  504. 'two with two octets.  In the case of the single octet,  zero means 256.
  505.  
  506. IF pointerchange(i) <> 0 THEN
  507.        ptr2 = pointerchange(i)
  508.        cumptr = cumptr + pointerchange(i)
  509.        ptrcase = 0
  510.        IF ptr2 < 0 THEN
  511.             ptr2 = -ptr2
  512.             ptrcase = 1
  513.        END IF
  514.        IF ptr2 = 256 THEN ptr2 = 0
  515.        IF ptr2 <= 256 THEN
  516.           obuff$ = obuff$ + CHR$(ptrcase) + CHR$(ptr2)
  517.           outputcnt& = outputcnt& + 2
  518.           IF INSTR(diaglevel$,"T") <> 0 THEN
  519.                PRINT #dgf, i + donesegs&;
  520.                PRINT #dgf, " Pointer change to " + STR$(cumptr)
  521.           END IF
  522.        ELSE
  523.  obuff$ = obuff$ + CHR$(ptrcase + 16) + CHR$(ptr2 \ 256) + CHR$(ptr2 MOD 256)
  524.           outputcnt& = outputcnt& + 3
  525.           IF INSTR(diaglevel$,"T") <> 0 THEN
  526.               PRINT #dgf, i + donesegs&;
  527.               PRINT #dgf, " Pointer change to " + STR$(cumptr)
  528.           END IF
  529.        END IF
  530. END IF
  531.  
  532. IF matched(i) + usedold& - i*ISEG% - done&  <> cumptr THEN
  533.           PRINT "**** ERROR - Cumulative pointer error (bug):", matched(i), cumptr
  534.           END
  535. END IF
  536.  
  537. IF INSTR(diaglevel$,"T") <> 0 THEN
  538. PRINT #dgf, "Dealing with a matchtype of "; matchtype(i)
  539. END IF
  540.  
  541. 'Next we look at whether a diffa, diffb, diffc,  diffd change is needed.
  542. 'If a change of a diff is needed,  we
  543. 'have the following cases for the diff:
  544. '             positive and less than 256
  545. '             negative and less than 256
  546. 'We use 00100000 and 00100001 for these cases for diffa,  00110000 and 
  547. '00110001 for these cases for diffb,  01000000 and 01000001 for diffc,
  548. 'and 0101000 and 01010001 for diffd,  followed by a single byte which gives
  549. 'the new diff value.
  550.  
  551. SELECT CASE matchtype(i)
  552.  
  553. CASE 1: REM Type A - changes never needed, no action
  554.  
  555. CASE 2: REM Type B - may need a diffx change,  x depending on the sybtype
  556.  
  557.          k = subtype(i)
  558.          CALL SetDiffer (k, differa(i))
  559.  
  560. CASE 3: REM Type C - may need a diffx and/or a diffy change,  depending
  561.         REM on the subtype (1 to 6)
  562.  
  563.         k = subtype(i)
  564.         SELECT CASE k
  565.         CASE 1 TO 3 : 
  566.              CALL SetDiffer (1, differa(i))
  567.              CALL SetDiffer (k + 1, differb(i))
  568.         CASE 4 TO 5:
  569.              CALL SetDiffer (2, differa(i))
  570.              CALL SetDiffer (k - 1, differb(i))
  571.         CASE 6:
  572.              CALL SetDiffer (3, differa(i))
  573.              CALL SetDiffer (4, differb(i))
  574.         END SELECT
  575.  
  576. CASE 4: REM Type D - may need up to two changes, could be any ones.  Check
  577.         REM each one.
  578.  
  579.         CALL SetDiffer (1,differa(i))
  580.         CALL SetDiffer (2,differb(i))
  581.         CALL SetDiffer (3,differc(i))
  582.         CALL SetDiffer (4,differd(i))
  583.  
  584. END SELECT
  585.  
  586. SELECT CASE matchtype(i)
  587.  
  588. CASE 1:  REM Type A - total match
  589.  
  590. 'For a type A match,  we look
  591. 'to see if the next block is also type A with no pointer change.  If so,
  592. 'we count the number of blocks we can merge in,  up to a maximum of 64,
  593. 'and output 11nnnnnn,  where n is the count of the number of merged-in
  594. 'blocks
  595. '(nnnnnn all zeros means 64).
  596.  
  597. nnn = 1
  598. j = i + 1
  599. WHILE matchtype(j) = 1 AND nnn < 64 AND pointerchange(j) = 0 AND j <= newblocks
  600.       j = j + 1
  601.       nnn = nnn + 1
  602. WEND
  603. k = nnn
  604. i = i + k - 1
  605. IF nnn = 64 THEN nnn = 0
  606. nnn = nnn + 128 + 64
  607. obuff$ = obuff$ + CHR$(nnn)
  608. outputcnt& = outputcnt& + 1
  609. IF INSTR(diaglevel$,"T") <> 0 THEN
  610.          PRINT #dgf, i + donesegs&;
  611.        PRINT #dgf,STR$(k) + " type A blocks"
  612. END IF
  613.  
  614. CASE 2: REM Type B - match with a single diff value.
  615.  
  616. 'For a type B match,  we determine whether to use diffa,  b, c,  or d (based
  617. 'on this segment alone,  and possibly with a diff change).  We then merge in
  618. 'up to 8 segments (nnn = 000 means 8) with the same pointer and diff value 
  619. '(after juggling diffs if necessary).  The coding is 0nnn0010 for use of diffa
  620. '0nnn0011 for use of diffb,  0nnn0100 for use of diffc,  and 0nnn0101 for use of
  621. 'diffd.  nnn is the number of segments merged in.  We then follow with a 
  622. 'string of bits,  one per byte in each of the segments,  where 0 means no
  623. 'addition, and 1 means add in the selected diff.   Bits up to the next octet
  624. 'boundary are ignored.
  625.  
  626. nnn = 1
  627. j = i + 1
  628. WHILE matchtype(j) = 2 AND nnn < 8 AND pointerchange(j) = 0 AND differa(j) = differa(i) AND j<=newblocks
  629.       j = j + 1
  630.       nnn = nnn + 1
  631. WEND
  632. k = nnn
  633. IF nnn = 8 THEN nnn = 0
  634. testdiff = gdiff2(subtype(i))
  635. dcase = subtype(i) + 1
  636. obuff$ = obuff$ + CHR$(nnn*16 + dcase)
  637. outputcnt& = outputcnt& + 1
  638. IF INSTR(diaglevel$,"T") <> 0 THEN
  639.          PRINT #dgf, i + donesegs&;
  640.     PRINT #dgf, STR$(k) + " type B blocks.  Difference is " + str$(testdiff)
  641. END IF
  642.  
  643. REM We now output k sets of 2 octets,  showing where testdiff is to be
  644. REM added in for each of the k segments.
  645. newstart = (i -1) * ISEG%
  646. oldstart = matched(i) - ISEG%
  647. FOR ik = 1 to 2 * k
  648. bitmap = 0
  649. FOR ii = 1 TO ISEG%\ 2
  650.     IF bytesnew(newstart + ii) <> bytesold(oldstart + ii) THEN
  651.          IF bytesnew(newstart + ii) - bytesold(oldstart+ii) <> testdiff THEN
  652.                 PRINT "**** ERROR - Type B difference is not differa (bug)"
  653.                 END
  654.          END IF
  655.          bitmap = bitmap + bitval(ii)
  656.     END IF
  657. NEXT ii
  658. oldstart = oldstart + ISEG% \2
  659. newstart = newstart + ISEG% \2
  660. obuff$ = obuff$ + chr$(bitmap)
  661. outputcnt& = outputcnt& + 1
  662. next ik
  663. i = i + k -1
  664.  
  665. CASE 3: REM Type C - match with two diff values
  666.  
  667. 'For a type C match,  we have six possible diff selections to use, coded as
  668. 'follows (again,  merging in up to 8 possible segments):
  669. '           diffa and diffb    0nnn0110
  670. '           diffa and diffc    0nnn0111
  671. '           diffa and diffd    0nnn1000
  672. '           diffb and diffc    0nnn1001
  673. '           diffb and diffd    0nnn1010
  674. '           diffc and diffd    0nnn1011
  675. 'We then follow with a string of bits,  one per byte in each of the segments,
  676. 'where 0 means no addition,  and 10 means the first mentioned diff is added in,
  677. 'and 11 means the second mentionned diff is added in.
  678.  
  679. diff1 = differa(i)
  680. diff2 = differb(i)
  681. nnn = 1
  682. j = i + 1
  683. WHILE matchtype(j) = 3 AND nnn < 8 AND pointerchange(j) = 0 AND differa(j) = diff1 AND differb(j) = diff2 AND j<=newblocks
  684.       j = j + 1
  685.       nnn = nnn + 1
  686. WEND
  687. k = nnn
  688. IF nnn = 8 THEN nnn = 0
  689. nnn = nnn * 16 + subtype(i) + 5
  690. obuff$ = obuff$ + CHR$(nnn)
  691. outputcnt& = outputcnt& + 1
  692. IF INSTR(diaglevel$,"T") <> 0 THEN
  693.          PRINT #dgf, i + donesegs&;
  694. PRINT #dgf, STR$(k) + " type C blocks.  Differences are " + str$(diff1) " and " + str$(diff2)
  695. END IF
  696.  
  697. REM We now output k sets of bitstrings,  showing where diff1 (10) and 
  698. REM diff2 (11) or neither (0) are to be added in for each of the k segments.
  699. newstart = (i -1) * ISEG%
  700. oldstart = matched(i) - ISEG%
  701. bitstring$ = ""
  702. FOR ik = 1 to k
  703. FOR ii = 1 TO ISEG%
  704.   SELECT CASE bytesnew(newstart + ii) - bytesold(oldstart + ii)
  705.   CASE IS = 0 
  706.      bitstring$ = bitstring$ + "0"
  707.   CASE IS = diff1
  708.      bitstring$ = bitstring$ + "10"
  709.   CASE IS = diff2
  710.      bitstring$ = bitstring$ + "11"
  711.   CASE ELSE
  712.      PRINT  "**** ERROR - Type C difference is not differa or differb (bug)"
  713.      END
  714.   END SELECT
  715. NEXT ii
  716. oldstart = oldstart + ISEG% 
  717. newstart = newstart + ISEG% 
  718. next ik
  719. bitstring$ = bitstring$ + "0000000"
  720. blen = len(bitstring$)
  721. blen = 8 * (blen\8)
  722. bitstring$ = left$(bitstring$,blen)
  723. outc = 0
  724. j = 0
  725. jj = 0
  726. bitmap = 0
  727. while j<blen
  728.    j=j+1
  729.    jj = jj + 1
  730.    if mid$(bitstring$,j,1) = "1" THEN bitmap = bitmap + bitval(jj)
  731.    if jj = 8 then
  732.         jj = 0
  733.         obuff$ = obuff$ + chr$(bitmap)
  734.         outputcnt& = outputcnt& + 1
  735.         bitmap = 0
  736.    end if
  737. wend
  738. i = i + k -1
  739.  
  740.  
  741. CASE 4: REM Type D 
  742.  
  743. 'For a type D match,  we encode the segments
  744. 'as 0nnn1100 (for up to 8 segments) where we have four differs,  and 
  745. '0nnn1101 where diffa is omitted, 0nnn1110 where diffb is omitted,  0nnn1111
  746. 'where diffc is omitted.  For diffd omitted,  we have no code space left,
  747. 'and treat that as if it were a four differ (code 0nnn1100).  
  748. 'This is followed by a bitstring for each
  749. 'octet in each of the segments,  where we have 0 if no diff is to
  750. 'be added in,  then,  for the three differs in use,
  751. '100 if the first is to be added in, 101 for the second,  and 11 for the third.
  752. 'For four differs,  we have
  753. '100 if diffa is to be added in,  101 for diffb,  110 for diffc,
  754. '111 for diffd.
  755.  
  756. diff1 = differa(i)
  757. diff2 = differb(i)
  758. diff3 = differc(i)
  759. diff4 = differd(i)
  760.  
  761. nnn = 1
  762. j = i + 1
  763. WHILE matchtype(j) = 4 AND nnn < 8 AND pointerchange(j) = 0 AND differa(j) = diff1 AND differb(j) = diff2 AND differc(j) = diff3 AND differd(j) = diff4 AND j<=newblocks
  764.       if subtype(j) <> subtype(i) then subtype(i) = 0 :REM use all four if necessary
  765.       j = j + 1
  766.       nnn = nnn + 1
  767. WEND
  768.  
  769. if subtype(i) = 4 then subtype(i) = 0 :REM This is because we ran out of code space
  770.  
  771. SELECT CASE subtype(i)
  772.  
  773. CASE 0
  774. CASE 1
  775.    diff1 = differb(i)
  776.    diff2 = differc(i)
  777.    diff3 = differd(i)
  778.    diff4 = 0
  779.  
  780. CASE 2 
  781.    diff2 = differc(i)
  782.    diff3 = differd(i)
  783.    diff4 = 0
  784.  
  785. CASE 3
  786.    diff3 = differd(i)
  787.    diff4 = 0
  788.    
  789. CASE 4 
  790.  
  791. END SELECT
  792.  
  793. k = nnn
  794. IF nnn = 8 THEN nnn = 0
  795. nnn = nnn * 16 + 12
  796. nnn = nnn + subtype(i)
  797. obuff$ = obuff$ + CHR$(nnn)
  798. outputcnt& = outputcnt& + 1
  799. IF INSTR(diaglevel$,"T") <> 0 THEN
  800.          PRINT #dgf, i + donesegs&;
  801.          IF subtype(i) = 0 THEN
  802. PRINT #dgf, STR$(k) + " type D2 blocks.  Differences are " + str$(diff1) " and " + str$(diff2) + " and " + str$(diff3) + " and " str$(diff4)
  803.          ELSE
  804. PRINT #dgf, STR$(k) + " type D1 blocks.  Differences are " + str$(diff1) " and " + str$(diff2) + " and " + str$(diff3) + " and " str$(diff4)
  805.          END IF
  806. END IF
  807.  
  808. REM We now output k sets of bitstrings,  showing where diff1 (100),
  809. REM diff2 (101),  diff3 (110 - or 11 if only three differs), diff4 (111) 
  810. REM or neither (0) are to be added in for each of the k segments.
  811.  
  812. diff3str$ = "110"
  813. if subtype(i) <> 0 then diff3str$ = "11"
  814. newstart = (i -1) * ISEG%
  815. oldstart = matched(i) - ISEG%
  816. bitstring$ = ""
  817. FOR ik = 1 to k
  818. FOR ii = 1 TO ISEG%
  819.   SELECT CASE bytesnew(newstart + ii) - bytesold(oldstart + ii)
  820.   CASE IS = 0 
  821.      bitstring$ = bitstring$ + "0"
  822.   CASE IS = diff1
  823.      bitstring$ = bitstring$ + "100"
  824.   CASE IS = diff2
  825.      bitstring$ = bitstring$ + "101"
  826.   CASE IS = diff3
  827.      bitstring$ = bitstring$ + diff3str$
  828.   CASE IS = diff4
  829.      bitstring$ = bitstring$ + "111"
  830.   CASE ELSE
  831.      PRINT  "**** ERROR - Type D difference is not diffa or diffb or diffc or diffd (bug)"
  832.      END
  833.   END SELECT
  834. NEXT ii
  835. oldstart = oldstart + ISEG% 
  836. newstart = newstart + ISEG% 
  837. next ik
  838. bitstring$ = bitstring$ + "0000000"
  839. blen = len(bitstring$)
  840. blen = 8 * (blen\8)
  841. bitstring$ = left$(bitstring$,blen)
  842. j = 0
  843. jj = 0
  844. bitmap = 0
  845. while j<blen
  846.    j=j+1
  847.    jj = jj + 1
  848.    if mid$(bitstring$,j,1) = "1" THEN bitmap = bitmap + bitval(jj)
  849.    if jj = 8 then
  850.         jj = 0
  851.         obuff$ = obuff$ + chr$(bitmap)
  852.         outputcnt& = outputcnt& + 1
  853.         bitmap = 0
  854.    end if
  855. wend
  856. i = i + k -1
  857.  
  858. END SELECT
  859.  
  860. END IF
  861.  
  862. WEND
  863.  
  864. REM Now loop to do another hunk if necessary
  865. finished = 0
  866. IF lengthnew& - (newblocks + donesegs&)*ISEG < ISEG% THEN 
  867.      finished = 1
  868.      close #fpold
  869.      close #fpnew
  870. ELSE
  871.  
  872. REM Prepare for next hunk
  873.  
  874. hunkstart = ISIZE3% \ ISEG%
  875.  
  876. REM move new array down by ISIZE2%,  and oldarray down by 
  877. REM ISIZE2% + startoffset,  and decrement ptr and startoffset by startoffset
  878. REM and signal the move in the sendfile.
  879.  
  880. done& = done& + ISIZE2%
  881. donesegs& = done& \ ISEG%
  882.  
  883. move = ISIZE2% + startoffset
  884. if move < 0 then move = 0
  885. usedold& = usedold& + move
  886.  
  887. REM Signal a new hunk
  888. 'Where we have discarded some old file and refilled the buffers,  we 
  889. 'signal this
  890. 'in the output by putting out a single octet of 01100000 followed by
  891. 'two octets giving the amount of the move.
  892.  
  893. obuff$ = obuff$ + chr$(96) + chr$(move \ 256) + chr$(move MOD 256)
  894. outputcnt& = outputcnt& + 3
  895.  
  896. ptr = ptr + (ISIZE2% - move)
  897. startoffset = startoffset + (ISIZE2% - move)
  898. lastfullmatch = lastfullmatch - move
  899.  
  900. IF endold < move + ISEG% THEN
  901.     PRINT "**** ERROR - Files are too different"
  902.     if dgf <> 0 then CLOSE #dgf
  903.     close #fp
  904.     close #fpnew
  905.     close #fpold
  906.     IF FileExists(sendfile$) THEN KILL sendfile$
  907.     END
  908. END IF
  909.  
  910. endold = endold - move
  911. for i = 1 to endold
  912. bytesold(i) = bytesold(i + move)
  913. next i
  914.  
  915. endnew = endnew - ISIZE2%
  916. for i = 1 to endnew
  917. bytesnew(i) = bytesnew(i + ISIZE2%)
  918. next i
  919.  
  920. IF INSTR(diaglevel$,"T") <> 0 THEN
  921. PRINT #dgf, "Move of new by ";ISIZE2%;" and of old by ";move
  922. END IF
  923.  
  924. REM Now read in as much as possible of the files to fill the buffers.
  925.  
  926. buff$ = STRING$(IBUF%, " ")
  927. ICNT1 = endold
  928. WHILE NOT EOF(fpold) AND ICNT1  + IBUF% <= ISIZE%
  929.      GET #fpold, , buff$
  930.      FOR i = 1 TO IBUF%
  931.      ICNT1 = ICNT1 + 1
  932.      bytesold(ICNT1) = ASC(MID$(buff$, i, 1))
  933.      NEXT i
  934. WEND
  935.  
  936. endold = ISIZE%
  937. if lengthold& - usedold& < ISIZE% then endold = lengthold& - usedold&
  938.  
  939. buff$ = STRING$(IBUF%, " ")
  940. ICNT2 = endnew
  941. WHILE NOT EOF(fpnew) AND ICNT2  + IBUF% <= ISIZE%
  942.      GET #fpnew, , buff$
  943.      FOR i = 1 TO IBUF%
  944.      ICNT2 = ICNT2 + 1
  945.      bytesnew(ICNT2) = ASC(MID$(buff$, i, 1))
  946.      NEXT i
  947. WEND
  948.  
  949. endnew = ISIZE%
  950. if lengthnew& - done& < ISIZE% then endnew = lengthnew& - done&
  951.  
  952. newblocks = endnew \ ISEG%
  953.  
  954. if lengthold& - usedold& > ISIZE% or lengthnew& - done& > ISIZE% then
  955.        if newblocks > SEGARRAYSIZE1% then newblocks = SEGARRAYSIZE1%
  956. end if
  957.  
  958. IF INSTR(diaglevel$,"P") <> 0 THEN
  959.       locate 10,1
  960.       PRINT STRING$(80," ");
  961.       locate 10,1
  962.       PRINT "Next hunk of files loaded - proceeding with analysis";
  963.       locate 11,1
  964.       PRINT STRING$(80," ");
  965. END IF
  966.  
  967. REM This ends the preparation for the next hunk.
  968. END IF
  969.  
  970. LOOP UNTIL finished = 1
  971.  
  972. 'Finally,  we have to cope with the residual set of up to 15 octets at the
  973. 'end of the file.   We simply output 01100001,  then a single octet saying
  974. 'how many follow,  then the octets.  If there are none,  we still output
  975. 'the single octet and the null count.
  976.  
  977. extras = endnew - newblocks * ISEG%
  978.  
  979. obuff$ = obuff$ + chr$(97) + chr$(extras)
  980. outputcnt& = outputcnt& + 2
  981. for i = 1 to extras
  982. obuff$ = obuff$ + chr$(bytesnew(newblocks * ISEG% + i))
  983. next i
  984. outputcnt& = outputcnt& + extras
  985.    
  986. PUT #fp, , obuff$
  987. CLOSE #fp
  988.  
  989. IF INSTR(diaglevel$,"T") <> 0 THEN
  990. PRINT #dgf,
  991. PRINT #dgf,"Statistics for the compression (done using" + STR$(ISEG%) + " byte segments) are"
  992. percent& = typeacnt
  993. percent& = (percent& * ISEG% * 1000) \ lengthnew&
  994. pstr$ = left$(STR$(percent&/10),5) + "%"
  995. PRINT #dgf,"         Type A matches: " + pstr$
  996. percent& = typebcnt
  997. percent& = (percent& * ISEG% * 1000) \ lengthnew&
  998. pstr$ = left$(STR$(percent&/10),5) + "%"
  999. PRINT #dgf,"         Type B matches: "+pstr$
  1000. percent& = typeccnt
  1001. percent& = (percent& * ISEG% * 1000) \ lengthnew&
  1002. pstr$ = left$(STR$(percent&/10),5) + "%"
  1003. PRINT #dgf,"         Type C matches: " + pstr$
  1004. percent& = typed1cnt
  1005. percent& = (percent& * ISEG% * 1000) \ lengthnew&
  1006. pstr$ = left$(STR$(percent&/10),5) + "%"
  1007. PRINT #dgf,"         Type D1 matches: " + pstr$
  1008. percent& = typed2cnt
  1009. percent& = (percent& * ISEG% * 1000) \ lengthnew&
  1010. pstr$ = left$(STR$(percent&/10),5) + "%"
  1011. PRINT #dgf,"         Type D2 matches: " + pstr$
  1012. percent& = nomatches
  1013. percent& = (percent& * ISEG% * 1000) \ lengthnew&
  1014. pstr$ = left$(STR$(percent&/10),5) + "%"
  1015. PRINT #dgf,"         Failed matches: "+pstr$
  1016. PRINT #dgf,"         Pointer changes:",ptrchs
  1017. PRINT #dgf,
  1018. END IF
  1019.  
  1020. IF INSTR(diaglevel$,"P") <> 0 THEN 
  1021. locate 13,1
  1022. PRINT STRING$(80," ");
  1023. locate 13,1
  1024. color 14,4,1
  1025. PRINT "Compression complete.  Number of octets output is ", outputcnt&;
  1026. outjl& = lengthnew&
  1027. outjl& = (outjl& * 10) \ outputcnt&
  1028. locate 15,18
  1029. PRINT "This is a compression ratio of "+str$(outjl&/10) +" to 1";
  1030. locate 16,18
  1031. PRINT "Conversion to .ARC will give a little more.";
  1032. locate 18,1
  1033. PRINT "Forward compression finished.";
  1034. locate 24,1
  1035. END IF
  1036.  
  1037. IF INSTR(diaglevel$,"T") <> 0 THEN
  1038. PRINT #dgf, "Compression complete.  Number of octets output is ", outputcnt&
  1039. outjl& = lengthnew&
  1040. outjl& = (outjl& * 10) \ outputcnt&
  1041. PRINT #dgf, "                  This is a compression ratio of "+str$(outjl&/10) +" to 1"
  1042. PRINT #dgf, "                  Conversion to .ARC will give a little more."
  1043. END IF
  1044.  
  1045. IF INSTR(diaglevel$,"B") <> 0 THEN
  1046. locate bline,1
  1047. outjl& = lengthnew&
  1048. outjl& = (outjl& * 10) \ outputcnt&
  1049. PRINT STRING$(80," ");
  1050. locate bline,1
  1051. PRINT "Forward compression complete.  Compression ratio is " + str$(outjl&/10) +" to 1"
  1052. END IF
  1053.  
  1054. close #dgf
  1055. END
  1056.  
  1057. SUB LookForMatch (x, i)
  1058.  
  1059. SHARED bytesold(), bytesnew(), matched()
  1060. SHARED pointerchange(), matchtype(),  subtype()
  1061. SHARED differa(), differb(), differc(), differd()
  1062. SHARED typeacnt, typebcnt, typeccnt, typed1cnt, typed2cnt
  1063. SHARED ptr, gdiff(), gdiffcnt (),  lastfullmatch,  factor
  1064.         
  1065.          diffa = 0
  1066.          diffb = 0
  1067.          diffc = 0
  1068.          diffd = 0
  1069.          diffe = 0
  1070.          startnew = (i * ISEG%) - ISEG%
  1071.          startold = x - ISEG%
  1072.          j = 1
  1073.          WHILE j <= ISEG% AND diffa = 0
  1074.               diff = bytesnew(j + startnew) - bytesold(j + startold)
  1075.               IF diff <> 0 THEN diffa = diff
  1076.               j = j + 1
  1077.          WEND
  1078.          WHILE j <= ISEG% AND diffb = 0
  1079.               diff = bytesnew(j + startnew) - bytesold(j + startold)
  1080.               IF diff <> 0 AND diff <> diffa THEN diffb = diff
  1081.               j = j + 1
  1082.          WEND
  1083.          WHILE j <= ISEG% AND diffc = 0
  1084.               diff = bytesnew(j + startnew) - bytesold(j + startold)
  1085. IF diff <> 0 AND diff <> diffa AND diff <> diffb THEN diffc = diff
  1086.               j = j + 1
  1087.          WEND
  1088.          WHILE j <= ISEG% AND diffd = 0
  1089.               diff = bytesnew(j + startnew) - bytesold(j + startold)
  1090. IF diff <> 0 AND diff <> diffa AND diff <> diffb AND diff <> diffc THEN diffd = diff
  1091.               j = j + 1
  1092.          WEND
  1093.          WHILE j <= ISEG% AND diffe = 0
  1094.               diff = bytesnew(j + startnew) - bytesold(j + startold)
  1095. IF diff <> 0 AND diff <> diffa AND diff <> diffb AND diff <> diffc AND diff <> diffd THEN diffe = diff
  1096.               j = j + 1
  1097.          WEND
  1098.          IF diffe = 0 THEN
  1099.                matched(i) = x
  1100.                IF diffa = 0 THEN
  1101.                     matchtype(i) = 1
  1102.                     typeacnt = typeacnt + 1
  1103.                     IF factor = 1 THEN lastfullmatch = x
  1104.                ELSE
  1105.                     IF diffb = 0 THEN
  1106.                         matchtype(i) = 2
  1107.                         typebcnt = typebcnt + 1
  1108.                         differa(i) = diffa
  1109.                         CALL need1(diffa)
  1110.                         subtype(i) = 0
  1111.                         for ii = 1 to 4
  1112.                             if diffa = gdiff(ii) then subtype (i) = ii
  1113.                         next ii
  1114.                         if subtype(i) = 0 then PRINT "**** ERROR - In LookForMatch (bug)"
  1115.                     ELSE
  1116.                         IF diffc = 0 THEN
  1117.                           matchtype(i) = 3
  1118.                           typeccnt = typeccnt + 1
  1119.                           CALL need2 (diffa, diffb)
  1120.                           if gdiff(1) = diffa then
  1121.                             switch = 0
  1122.                             if diffb = gdiff(2) then subtype(i) = 1
  1123.                             if diffb = gdiff(3) then subtype(i) = 2
  1124.                             if diffb = gdiff(4) then subtype(i) = 3
  1125.                           else
  1126.                             if gdiff(1) = diffb then
  1127.                                 switch = 1
  1128.                                 if diffa = gdiff(2) then subtype(i) = 1
  1129.                                 if diffa = gdiff(3) then subtype(i) = 2
  1130.                                 if diffa = gdiff(4) then subtype(i) = 3
  1131.                             else
  1132.                              if gdiff(2) = diffa then
  1133.                              switch = 0
  1134.                              if diffb = gdiff(3) then subtype(i) = 4
  1135.                              if diffb = gdiff(4) then subtype(i) = 5
  1136.                              else
  1137.                                 if gdiff(2) = diffb then
  1138.                                     switch = 1
  1139.                                     if diffa = gdiff(3) then subtype(i) = 4
  1140.                                     if diffa = gdiff(4) then subtype(i) = 5
  1141.                                 else
  1142.                                   subtype(i) = 6
  1143.                                   if gdiff(3) = diffa then
  1144.                                      switch = 0                                    
  1145.                                   else 
  1146.                                      switch = 1
  1147.                                   end if
  1148.                                 end if
  1149.                              end if
  1150.                             end if
  1151.                           end if
  1152.                           if switch = 1 then
  1153.                                      sw = diffa
  1154.                                      diffa = diffb
  1155.                                      diffb = sw
  1156.                           end if
  1157.                           differa(i) = diffa
  1158.                           differb(i) = diffb                         
  1159.                         ELSE
  1160. REM If we have more than one pointer and one diff,  or two diff changes
  1161. REM needed for these options,  we treat them as a no match.
  1162.                           chngesok = fchangesok (x,i,diffa, diffb, diffc, diffd)
  1163.                           if chngesok = 0 then
  1164.                              matchtype(i) = 0
  1165.                              matched(i) = 0
  1166.                           else
  1167.                              matchtype(i) = 4
  1168.                              if diffd = 0 then
  1169.                                 typed1cnt = typed1cnt + 1
  1170.                              else
  1171.                                 typed2cnt = typed2cnt + 1
  1172.                              end if
  1173.                              differa(i) = gdiff(1)
  1174.                              differb(i) = gdiff(2)
  1175.                              differc(i) = gdiff(3)
  1176.                              differd(i) = gdiff(4)
  1177.                           end if
  1178.                         END IF
  1179.                     END IF
  1180.                END IF
  1181.          END IF
  1182. REM For a type B match,  we have in differa the new gdiffx value to be forced,
  1183. REM depending on the subtype.
  1184. REM For a type C match,  we have in differa and differb the new gdiffx and 
  1185. REM gdiffy values to be forced,  depending on the subtype.
  1186. REM For a type D match,  we have in differa to differd the new  gidffx
  1187. REM values to be forced (except where differd = 0).
  1188. if matched(i) <> 0 then
  1189.      for ii = 1 to 4
  1190.      gdiffcnt(ii) = gdiffcnt(ii)\2
  1191.      next ii
  1192. end if
  1193. END SUB
  1194.  
  1195. SUB need1(diff)
  1196.  
  1197. SHARED ptr, gdiff(), gdiffcnt()
  1198.  
  1199. gotit = 0
  1200. FOR i = 1 to 4                     
  1201. if diff = gdiff(i) then
  1202.      gdiffcnt(i) = gdiffcnt(i) + 32
  1203.      gotit = 1
  1204. end if
  1205. next i
  1206. if gotit <> 1 then
  1207.        mincnt = 1
  1208.        for i = 1 to 4
  1209.            if gdiffcnt(i) < gdiffcnt(mincnt) then mincnt = i
  1210.        next i
  1211.        gdiffcnt (mincnt) = gdiffcnt(mincnt) + 32
  1212.        gdiff (mincnt) = diff
  1213. end if
  1214.  
  1215. END SUB
  1216.  
  1217. SUB need2(diff1,diff2)
  1218.  
  1219. SHARED ptr, gdiff(), gdiffcnt()
  1220.  
  1221. gotit1 = 0
  1222. FOR i = 1 to 4                     
  1223. if diff1 = gdiff(i) then
  1224.      gdiffcnt(i) = gdiffcnt(i) + 32
  1225.      gotit1 = 1
  1226. end if
  1227. next i
  1228. gotit2 = 0
  1229. FOR i = 1 to 4                     
  1230. if diff2 = gdiff(i) then
  1231.      gdiffcnt(i) = gdiffcnt(i) + 32
  1232.      gotit2 = 1
  1233. end if
  1234. next i
  1235. if gotit1 <> 1 then
  1236.        mincnt = 1
  1237.        for i = 1 to 4
  1238.            if gdiffcnt(i) < gdiffcnt(mincnt) then mincnt = i
  1239.        next i
  1240.        gdiffcnt (mincnt) = gdiffcnt(mincnt) + 32
  1241.        gdiff (mincnt) = diff1
  1242. end if
  1243. if gotit2 <> 1 then
  1244.        mincnt = 1
  1245.        for i = 1 to 4
  1246.            if gdiffcnt(i) < gdiffcnt(mincnt) then mincnt = i
  1247.        next i
  1248.        gdiffcnt (mincnt) = gdiffcnt(mincnt) + 32
  1249.        gdiff (mincnt) = diff2
  1250. end if
  1251.  
  1252. END SUB
  1253.  
  1254. FUNCTION fchangesok (x,ip,diff1, diff2, diff3, diff4)
  1255.  
  1256. DIM diff(4),  gotit(4)
  1257. SHARED ptr, gdiff(), gdiffcnt(),factor, resyncstate, subtype()
  1258.  
  1259. diff(1) = diff1
  1260. diff(2) = diff2
  1261. diff(3) = diff3
  1262. diff(4) = diff4
  1263. m = 4
  1264. subtype(ip) = 0
  1265. if diff4 = 0 then m = 3
  1266. matchcnt = 0
  1267. for j = 1 to m
  1268. gotit(j) = 0
  1269. FOR i = 1 to 4
  1270. if diff(j) = gdiff(i) then 
  1271.        gotit(j) = i
  1272.        matchcnt = matchcnt + 1
  1273. end if
  1274. next i
  1275. next j
  1276.  
  1277. for i = 1 to 4
  1278. if gdiffcnt(i) = 0 then matchcnt = matchcnt + 1
  1279. next i
  1280.  
  1281. nok = 0
  1282. ok = 1
  1283.  
  1284. changesok = ok
  1285. if x - ip * ISEG% <> ptr and matchcnt < m - 2 and resyncstate = 0 then changesok = nok
  1286. REM If we have a pointer move,  we require no more than two new diffs unless
  1287. REM we are resynching,  when anything goes.
  1288.  
  1289. if changesok = 1 then
  1290.      for j = 1 to m
  1291.      k = gotit(j)
  1292.      if k <> 0 then gdiffcnt(k) = gdiffcnt(k) + 32
  1293.      next j
  1294.      for j = 1 to m
  1295.         if gotit(j) = 0 then
  1296.               mincnt = 1
  1297.               for i = 1 to 4
  1298.               if gdiffcnt(i) < gdiffcnt(mincnt) then mincnt = i
  1299.               next i
  1300.               gdiffcnt (mincnt) = gdiffcnt(mincnt) + 32
  1301.               gdiff (mincnt) = diff(j)
  1302.         end if
  1303.      next j
  1304. end if
  1305.  
  1306. if m <> 4 then
  1307.      for i = 1 to 4
  1308.        notneeded = 1
  1309.        for j = 1 to m
  1310.        if gdiff(i) = diff(j) then notneeded = 0
  1311.        next j
  1312.        if notneeded = 1 then subtype(ip) = i
  1313.      next i
  1314. end if
  1315.  
  1316. fchangesok = changesok
  1317.  
  1318. END FUNCTION
  1319.  
  1320. SUB GetCommNames
  1321.  
  1322. SHARED reffile$,  newfile$,  sendfile$,  diaglevel$,  diagoutput$
  1323. SHARED recnewfile$,  recreffile$
  1324.     
  1325.      DIM z$(7)
  1326.      Maxargs = 7
  1327.      Numargs = 0: in = 0
  1328.      Cl$ = COMMAND$
  1329.      l = LEN(Cl$)
  1330.      FOR i = 1 TO l
  1331.        c$ = MID$(Cl$, i, 1)
  1332.        IF (c$ <> " " AND c$ <> CHR$(9)) THEN
  1333.             IF in = 0 THEN
  1334.                         IF Numargs = Maxargs THEN EXIT FOR
  1335.                         Numargs = Numargs + 1
  1336.                         in = 1
  1337.             END IF
  1338.             z$(Numargs) = z$(Numargs) + c$
  1339.        ELSE
  1340.             in = 0
  1341.        END IF
  1342.      NEXT i
  1343.  
  1344. IF Numargs < 4 THEN
  1345.         PRINT "You have to specify (as command line parameters separated by space):
  1346.         PRINT "        the file to be compressed (the new version);"
  1347.         PRINT "        the reference file (the old version);"
  1348.         PRINT "        the verbosity level (B or P),  and optionally T;"
  1349.         PRINT "        (B is brief,  P is progress messages, T is trace);"
  1350.         PRINT "        the file to hold the fcm format for transmission."
  1351.         PRINT " "
  1352.         PRINT "If the verbosity level includes T,  you must next specify the file "
  1353.         PRINT "to hold the trace information reporting details of matches."
  1354.         PRINT " "
  1355.         PRINT "Finally,  you may optionally include two further parameters which"
  1356.         PRINT "specify first the file name of the file to be generated (by default)"
  1357.         PRINT "on the receiver's system,  and secondly the filename to be used (by"
  1358.         PRINT "default) as the reference file (the old version) on the receiver's"
  1359.         PRINT "system.  If these are omitted,  tbey both default to the name supplied"
  1360.         PRINT "for the (new) file to be compressed."
  1361.         PRINT
  1362.         END
  1363. END IF
  1364.  
  1365. Newfile$ = UCASE$(z$(1))
  1366.  
  1367. Reffile$ = UCASE$(z$(2))
  1368.  
  1369. diaglevel$ = UCASE$(z$(3))
  1370.  
  1371. IF INSTR(diaglevel$,"T") <> 0 THEN
  1372.     IF INSTR(diaglevel$,"P") <> 0 THEN
  1373.           diaglevel$ = "TP"
  1374.     ELSE
  1375.           diaglevel$ = "TB"
  1376.     END IF
  1377. ELSE
  1378.     IF INSTR(diaglevel$,"P") <> 0 THEN
  1379.           diaglevel$ = "P"
  1380.     ELSE
  1381.           diaglevel$ = "B"
  1382.     END IF
  1383. END IF
  1384.  
  1385. Sendfile$ = UCASE$(z$(4))
  1386.  
  1387. IF INSTR(diaglevel$,"T") <> 0 AND Numargs < 5 THEN
  1388.      PRINT "Tracing requested but no trace file name.  Please respecify"
  1389. END IF
  1390.  
  1391. arginc = 0
  1392. IF INSTR(diaglevel$,"T") <> 0 THEN 
  1393.      diagoutput$ = UCASE$(z$(5))
  1394.      arginc = 1
  1395. END IF
  1396.  
  1397. recnewfile$ = newfile$
  1398.  
  1399. recreffile$ = newfile$
  1400.  
  1401. IF numargs >(4 + arginc) then recnewfile$= ucase$(z$(5+arginc))
  1402.  
  1403. IF numargs >(5 + arginc) then recreffile$ = ucase$(z$(6+arginc))
  1404.  
  1405. END SUB
  1406.  
  1407. SUB SetDiffer ( k, x)
  1408.  
  1409. SHARED gdiff2(),  obuff$,  outputcnt&, dgf, diaglevel$, i, donesegs&
  1410.  
  1411.          if x <> gdiff2(k) then
  1412.               REM We need to change gdiff2(k) to x
  1413.               diffid = (k + 1) * 16
  1414.               gdiff2(k) = x
  1415.               sendiff = x
  1416.               dcase = 0
  1417.               IF sendiff < 0 THEN
  1418.                   dcase = 1
  1419.                   sendiff = -sendiff
  1420.               END IF
  1421. IF sendiff > 255 THEN
  1422. PRINT "**** ERROR - Difference is out of range (bug): ";sendiff
  1423. sendiff = 0
  1424. END IF
  1425.               obuff$ = obuff$ + CHR$(dcase + diffid) + CHR$(sendiff)
  1426.               outputcnt& = outputcnt& + 2
  1427.               IF INSTR(diaglevel$,"T") <> 0 THEN
  1428.               PRINT #dgf, i + donesegs&;
  1429.     PRINT #dgf, "Change of diff " + str$(k) + " to " + str$(x)
  1430.               END IF
  1431.           end if
  1432.  
  1433. END SUB
  1434.  
  1435. FUNCTION FileExists! (testfile$)
  1436.  
  1437. DIM InRegs AS RegType, OutRegs AS RegType
  1438.  
  1439. checkname$ = testfile$ + CHR$(0)
  1440.  
  1441. InRegs.ax = &H4300
  1442. InRegs.dx = SADD(checkname$)
  1443.  
  1444. CALL INTERRUPT(&H21, InRegs, OutRegs)
  1445.  
  1446. IF (&H1 AND OutRegs.flags) <> 0 THEN
  1447.        FileExists = 0
  1448. ELSE
  1449.        FileExists = 1
  1450. END IF
  1451.  
  1452. END FUNCTION
  1453.  
  1454. SUB Resynch
  1455.  
  1456. SHARED factor, skipval, lastfullmatch, lengthold&,  lengthnew&, i, usedold&
  1457. SHARED startoffset, diaglevel$, done&, resyncstate
  1458.  
  1459. REM We have had twenty successive no matches.
  1460. REM Adjust factor and skipval
  1461.  
  1462.           resyncstate = 2
  1463.           lastfullmatch = 0
  1464. estimatedposn& = 2.0 * (lengthold& - lengthnew&)*((1.0*i*ISEG% + done&)/(lengthnew& + lengthold&))
  1465.           startoffset = estimatedposn& - usedold& + done&
  1466.  
  1467. END SUB
  1468.  
  1469. SUB Normal
  1470. SHARED factor, skipval, lastfullmatch, lengthold&,  lengthnew&, i, done&
  1471. SHARED startoffset, resyncstate
  1472.  
  1473.           factor = 1
  1474.           skipval = 1
  1475.           resyncstate = 0
  1476. END SUB
  1477.  
  1478. 'REM This is part of the eventual documentation,  and describes the
  1479. 'compressed format based on this approach. 
  1480. '
  1481. 'There will be an initial block containing:
  1482. ' 1.  a length count (one octet)for the next item.
  1483. ' 2.  the full path name of the file that is to be compressed
  1484. '     (on decompression,  a file TEMP.TMP will be created in the current
  1485. '     directory,  and then copied to the original destination path when
  1486. '     decompression is complete,  unless overridden by a different target).
  1487. ' 3.  two octets of sum check for the whole of this file;  the sum check
  1488. '     is the standard one of SIGMA(Ai) and SIGMA(iAi),  all MOD 255.
  1489. ' 4.  a length count for the next item.
  1490. ' 5.  the full path name of the reference file (this can be overridden
  1491. '     on decompression)
  1492. ' 6.  two octets of sum check for the first 1K of the reference file.
  1493. '
  1494. 'Decompression will be abandonned if the reference file is not found
  1495. 'on the receiving system,  with the correct sumcheck,  and TEMP.TMP will
  1496. 'not be copied unless the sum-checks of the original file match with the
  1497. 'decompressed one.
  1498. '
  1499. 'The receivers state is set up with ptroffset = 0, gdiff2(1) = 0, gdiff2(2) = 0,
  1500. 'gdiff2(3) = 0,  gdiff2(4) = 0.   These values are retained unless explicitly
  1501. 'changed.  The target value is old plus difference. (Difference is new - old).
  1502. 'The old values to be used are at cnt + ptroffset + 1 to
  1503. 'cnt + ptroffset + ISEG%,  where cnt is the number
  1504. 'of new octets generated so far.  A positive pointer change is an addition
  1505. 'to ptroffset.
  1506. '(A ptroffset is old posn - new posn for compression).
  1507. '
  1508. 'The following algorithm determines the compressed format:
  1509. '
  1510. 'We take each ISEG block in turn.
  1511. '
  1512. 'If there is no match, we output 10nnnnnn followed by the nnnnnn sets of
  1513. '16 octets for up to 64 (nnnnnn of 0 means 64) segments.
  1514. 'Note that 11nnnnnn is used for total matches (see below).  All other
  1515. 'codes are 0xxxxxxx.
  1516. 'On decompression,  we branch on bit 1 equals 1 or 0,  and if bit 1 is
  1517. '1,  then we branch on 10 or 11.  For 10,  we pick up nnnnnn,  and copy
  1518. 'ISEG% * nnnnnn octetcs from the fcm to the target.
  1519. '
  1520. 'If there is a pointer change,  we need to signal it in the output.
  1521. 'We have four cases for the value of the ptr change:
  1522. '             positive and less than (or equals) 256
  1523. '             negative and less than (or equals) 256
  1524. '             positive and over 256
  1525. '             negative and over 256
  1526. 'We use 00000000,  00000001, 00010000, and 00010001 for these four cases.
  1527. 'In the first two cases,  we follow with a single octet,  and in the last
  1528. 'two with two octets.  In the case of the single octet,  zero means 256.
  1529. 'On decompression,  we are in the first bit zero case,  and branch first on
  1530. 'the bottom four bits,  taking the 0 and 1 cases here.  For zero,  we are
  1531. 'collecting a positive value, and for 1 a negative.  Next we branch on the
  1532. 'value of nnn in 0nnn000x.  For 0 we have a single octet following and
  1533. 'a value to be added (after negation if necessary) to ptroffset.  For 1
  1534. 'we have a two octet value (most significant first).
  1535. '
  1536. 'Next we look at whether a diffa, diffb, diffc,  diffd change is needed.
  1537. 'If a change of a diff is needed,  we
  1538. 'have the following cases for the diff:
  1539. '             positive and less than 256
  1540. '             negative and less than 256
  1541. 'We use 00100000 and 00100001 for these cases for diffa,  00110000 and
  1542. '00110001 for these cases for diffb,  01000000 and 01000001 for diffc,
  1543. 'and 0101000 and 01010001 for diffd,  followed by a single byte which gives
  1544. 'the fcm diff value.
  1545. 'On decompression, this is the nnn = 2 (diffa), 3 (diffb), 4 (diffc) and
  1546. '5 (diffd) cases described under pointer above.
  1547. '
  1548. 'This completes the use of the 0mmm0000 and 0mmm0001 values
  1549. 'except for 110 which is used in termination below,  and 111 which is spare.
  1550. 'Remaining codes are
  1551. '0mmmxxxx where xxxx is above 0001.
  1552. '
  1553. 'In all cases,  apart from no match,  we first code any pointer change
  1554. 'needed,  then any one or more diff changes that are needed,  then we code
  1555. 'the type.
  1556. '
  1557. 'For a type A match,  we look
  1558. 'to see if the next block is also type A with no pointer change.  If so,
  1559. 'we count the number of blocks we can merge in,  up to a maximum of 64,
  1560. 'and output 11nnnnnn,  where n is the count of the number of merged-in
  1561. 'blocks
  1562. '(nnnnnn all zeros means 64).
  1563. 'On decompression,  this is the branch from first bit 1,  first two 11.
  1564. 'We copy ISEG% * nnnnnn octets from the old file,  starting at cnt +
  1565. 'ptroffset + 1.
  1566. '
  1567. 'For a type B match,  we determine whether to use diffa,  b, c,  or d (based
  1568. 'on this segment alone,  and possibly with a diff change).  We then merge in
  1569. 'up to 8 segments (nnn = 000 means 8) with the same pointer and diff value
  1570. '(after juggling diffs if necessary).  The coding is 0nnn0010 for use of diffa
  1571. '0nnn0011 for use of diffb,  0nnn0100 for use of diffc,  and 0nnn0101 for use of
  1572. 'diffd.  nnn is the number of segments merged in.  We then follow with a
  1573. 'string of bits,  one per byte in each of the segments,  where 0 means no
  1574. 'addition, and 1 means add in the selected diff.   Bits up to the next octet
  1575. 'boundary are ignored.
  1576. 'On decompression,  we use nnn to determine the number of octets to be
  1577. 'produced (16 * nnn),  and then take as many octets as necessary to give
  1578. 'us the additions of diffa,  b etc.  We start the old at cnt + ptroffset + 1
  1579. 'and we set new to old + diff. (Diff was defined as new - old)
  1580. '
  1581. 'For a type C match,  we have six possible diff selections to use, coded as
  1582. 'follows (again,  merging in up to 8 possible segments):
  1583. '           diffa and diffb    0nnn0110
  1584. '           diffa and diffc    0nnn0111
  1585. '           diffa and diffd    0nnn1000
  1586. '           diffb and diffc    0nnn1001
  1587. '           diffb and diffd    0nnn1010
  1588. '           diffc and diffd    0nnn1011
  1589.  
  1590. 'We then follow with a string of bits,  one per byte in each of the segments,
  1591. 'where 0 means no addition,  and 10 means the first mentioned diff is added in,
  1592. 'and 11 means the second mentionned diff is added in.
  1593. 'On decompression,  this is values 6 to 11 of the bottom four octets. We
  1594. 'proceed as for case B, except that we need to use two diffs,  diff1 and diff2
  1595. 'taken from diffa to diffd according to the case being considered.
  1596. '
  1597. 'For a type D match,  we encode the segments
  1598. 'as 0nnn1100 (for up to 8 segments) where we have four differs,  and
  1599. '0nnn1101 where diffa is omitted, 0nnn1110 where diffb is omitted,  0nnn1111
  1600. 'where diffc is omitted.  For diffd omitted,  we have no code space left,
  1601. 'and treat that as if it were a four differ (code 0nnn1100).
  1602. 'This is followed by a bitstring for each
  1603. 'octet in each of the segments,  where we have 0 if no diff is to
  1604. 'be added in,  then,  for the three differs in use,
  1605. '100 if the first is to be added in, 101 for the second,  and 11 for the third.
  1606. 'For four differs,  we have
  1607. '100 if diffa is to be added in,  101 for diffb,  110 for diffc,
  1608. '111 for diffd.
  1609. 'On decompression,  this is again similar to CASE C,  except that we have
  1610. 'three or four differs depending on the value 12 (four) or 13 to 16 (three)
  1611. 'of the case.  The value of the case says which differ is omitted.
  1612. '
  1613. 'Finally,  we have to cope with the residual set of up to 15 octets at the
  1614. 'end of the file.   We simply output 00001110,  then a single octet saying
  1615. 'how many follow,  then the octets.  If there are none,  we still output
  1616. 'the single octet and the null count.  On decompression,  we copy octets
  1617. 'across.
  1618. '
  1619. 'Where we have discarded some old file and refilled the buffers,  we
  1620. 'signal this
  1621. 'in the output by putting out a single octet of 00011110 followed by
  1622. 'two octets giving the amount of the move.
  1623. 'On decompression,  this is a signal to move the new file by the full
  1624. '12K,  and to move the old file by the specified amount.
  1625. '
  1626. 'This ends the use of the 0nnnxxxx codes.  The value 1111 of xxxx is
  1627. 'spare.
  1628. '
  1629. 'Thus,  we get the following counts:
  1630. '    first no match                  17
  1631. '    subsequent no match (up to 128) 16
  1632. '    ptr change (<256)                2
  1633. '    ptr change (>256)                3
  1634. '    diff change                      2
  1635. '    type A                           1 (?+3 - ptr change)
  1636. '    subsequent A (up to 128)         0
  1637. '    type B                           3 (?+5 - ptr + 1 diff)
  1638. '    subsequent B (up to 8)           2
  1639. '    type C                           4 to 5 bytes (?+7 - ptr + 2 diff)
  1640. '    subsequent C (up to 8)           2 to 4 bytes
  1641. '    type D or E                      4 to 7 bytes (?+5/4 - ptr/diff +  diff)
  1642. 'NOTE - We recognise D or E only if there is at most 1 ptr and one diff,  or
  1643. 'two diff changes.
  1644. '
  1645.