home *** CD-ROM | disk | FTP | other *** search
/ PC World 2000 August / PCWorld_2000-08_cd.bin / Software / TemaCD / xbasic / xbpro.exe / xb / zap.x < prev   
Text File  |  1999-08-16  |  32KB  |  944 lines

  1. '
  2. ' ####################
  3. ' #####  PROLOG  #####
  4. ' ####################
  5. '
  6. PROGRAM    "zap"
  7. VERSION    "0.0033"
  8. '
  9. IMPORT    "xst"
  10. '
  11. ' This program was contributed by an XBasic programmer.  This program
  12. ' is not similar to other compression programs or algorithms except by
  13. ' coincidence, so this program will not decompress files compressed by
  14. ' any other program or utility.  You are free to snarf any part or all
  15. ' of this program for any purpose whatever with the understanding that
  16. ' this program may contain bugs and no person or entity represents the
  17. ' code in this program as suitable for any purpose whatever.  You are
  18. ' fully responsible for any use of this program and no other person is
  19. ' responsible or liable in any way under any circumstances.
  20. '
  21. ' If you improve the Implode() and/or Explode() functions in the program
  22. ' to achieve greater compression of text and binary files, we encourage
  23. ' you to contribute your new and improved routines for future releases.
  24. '
  25. ' If you run this program in the environment, the Entry() function fakes
  26. ' command line arguments to compress "zap.x" into "zap.zap".  Feel free
  27. ' to change "zap.x" and "zap.zap" to other filenames to test this program.
  28. ' If the first argument has a ".zap" suffix, this program decompresses,
  29. ' otherwise it compresses (unless you insert "-i" or "-o" arguments).
  30. '
  31. ' If this program is compiled to a binary executable program (not library),
  32. ' execution begins in the Entry() function which looks at the command line
  33. ' arguments for the following arguments:
  34. '
  35. '   zap [-i] [-o] infilename outfilename
  36. '
  37. ' "zap" is the name of the program
  38. ' "-i" means "Implode" or compress the non-compressed "infilename" file
  39. '      to create the compressed "outfilename"
  40. ' "-e" means "Explode" or decompress the compressed "infilename" file
  41. '      to create the uncompressed "outfilename"
  42. ' the "-i" and "-e" switches are not necessary since zap recognizes
  43. '      already compressed files from a "zap" header in the file
  44. '
  45. ' If this program is compiled to a object/binary library (not program),
  46. ' the entry function does nothing.  To compress data your program calls
  47. ' Implode(), and to decompress data your program calls Explode().
  48. ' Alternatively you can copy all functions in this program except Entry()
  49. ' into your program and declare all functions with INTERNAL FUNCTION.
  50. '
  51. '
  52. '
  53. ' Compress data.  Transfer data to result array:
  54. '   1. Compress strings that already occured in the data into offset and length.
  55. '   2. Compress strings of repeated bytes, like  "##########", into length and byte.
  56. '   3. Insert uncompressable bytes 0x00 to 0xFF as 0x000 to 0x0FF.
  57. '   4. Insert uncompressable strings >= 4 bytes after header and length.
  58. '
  59. ' data in file.zap - what it means (after initial 8 to 16 bytes header)
  60. '
  61. '   0x000 to 0x0FF - a byte with the same value as the 12-bit value.
  62. '   0x100 to 0xFDF - relative offset to the byte after the end of a
  63. '                    string of bytes to insert at the current location.
  64. '                    0x0100 is subtracted from the 12-bit value to get
  65. '                    the actual offset.  A 12-bit value of 0x100 is
  66. '                    therefore an offset of 0x000 and refers to a
  67. '                    string of bytes whose last byte is the byte
  68. '                    before the current location.
  69. '                    Following every relative offset is a 4-bit value
  70. '                    that specifies the length of the string to insert
  71. '                    at the current location, except that 3 is added
  72. '                    to this value to get the actual string length.
  73. '                    The length can be 0x0 = 3 to 0xF = 18 bytes.
  74. '   0xFE0 to 0xFF7 - string of literal bytes stored as inverted bytes
  75. '                    where the length is (header - 0xFE0 + 4), where
  76. '                    header is 0xFE0 to 0xFF7 (4 to 27 bytes).
  77. '            0xFFC - string of literal bytes stored as inverted bytes
  78. '                    followed by an 8-bit length (0-255 mean 16-271)
  79. '                    followed by the bytes (inverted by NOT operator)
  80. '            0xFF8 - reserved
  81. '            0xFF9 - reserved
  82. '            0xFFA - says the following 12-bit value is the same as the
  83. '                    0x100 to 0xFDF case except the length is 8-bit,
  84. '                    and the offset is not adjusted by 0x100.
  85. '            0xFFB - says the following 12-bit value is the same as the
  86. '                    0x100 to 0xFDF case except the length is 12-bit.
  87. '                    and the offset is not adjusted by 0x100.
  88. '            0xFFD - string of identical bytes
  89. '                    followed by an 4-bit length (0-15 means 3-18)
  90. '                    followed by repeated byte
  91. '            0xFFE - string of identical bytes
  92. '                    followed by an 8-bit length (0-255 means 16-271)
  93. '                    followed by repeated byte
  94. '            0xFFF - end of file
  95. '
  96. '
  97. INTERNAL FUNCTION  Entry       ()
  98. INTERNAL FUNCTION  GetPrefix   (@prefix$)
  99. INTERNAL FUNCTION  GetString   (addr, bytes, @string$)
  100. '
  101. EXPORT
  102. DECLARE FUNCTION  CommandLine (UBYTE @i[], @ofile, @ilength, @direction, @version)
  103. DECLARE FUNCTION  Implode (addr, bytes, @count, @version, UBYTE @data[])
  104. DECLARE FUNCTION  Explode (addr, bytes, @count, @version, UBYTE @data[])
  105. END EXPORT
  106. EXTERNAL FUNCTION  XstFindMemoryMatch (@start, after, match, min, max)
  107. '
  108. $$Version     =    2  ' non-zero algorithm version # follows "zap" signature
  109. $$MatchMin    =    3
  110. $$MatchMax    =   18
  111. $$MatchMax4   =   18
  112. $$MatchMax8   =  274
  113. $$MatchMax12  = 4370
  114.  
  115. $$Reference   = 4096
  116. '
  117. '
  118. ' ######################
  119. ' #####  Entry ()  #####
  120. ' ######################
  121. '
  122. FUNCTION  Entry ()
  123.     UBYTE  i[]
  124.     UBYTE  o[]
  125. '
  126. ' Define codes relative to #Basis so only #Basis
  127. ' needs to be changed to change the code size from
  128. ' the current 12-bits to other values - generally
  129. ' in the 9-bit to 12-bit range.
  130. '
  131.     #Basis                        = 0x1000                ' 0x1000 = 4096
  132.     #RelativeBase            = 0x0100                ' 0x0100 =  256
  133.     #RelativeMax            = #Basis - 33        ' 0x0FDF = 4063
  134.     #CodeLiteral4            = #Basis - 32        ' 0x0FE0 = 4064
  135.     #CodeLiteral8            = #Basis -  8        ' 0x0FF8 = 4088
  136.     #CodeReserved0        = #Basis -  7        ' 0x0FF9 = 4089
  137.     #CodeRelative8        = #Basis -  6        ' 0x0FFA = 4090
  138.     #CodeRelative12        = #Basis -  5        ' 0x0FFB = 4091
  139.     #CodeRunLength4        = #Basis -  4        ' 0x0FFC = 4092
  140.     #CodeRunLength8        = #Basis -  3        ' 0x0FFD = 4093
  141.     #CodeReserved1        = #Basis -  2        ' 0x0FFE = 4094
  142.     #CodeEndOfFile        = #Basis -  1        ' 0x0FFF = 4095
  143. '
  144. '
  145. ' if compiled as a library, this function does nothing
  146. '
  147.     IF LIBRARY (0) THEN RETURN
  148. '
  149. ' to run in environment to test, the following section sets up
  150. ' the command line arguments to look like the zap program was
  151. ' executed with two arguments.  you can test on various files
  152. ' by changing argv$[1] and argv$[2].
  153. '
  154.     XstGetApplicationEnvironment (@standalone, @extra)
  155. '
  156. ' if running in the environment, fake command line arguments to test
  157. '
  158. ' pass = 0 compresses "zap.x" into "zap.zap"
  159. ' pass = 1 decompresses "zap.zap" into "zap.xxx"
  160. '
  161. ' 1: run with pass = 0 to create compressed file "zap.zap"
  162. ' 2: run with pass = 1 to create uncompressed file "zap.xxx"
  163. ' 3: make sure uncompressed file "zap.xxx" is the same as original file "zap.x"
  164. '
  165.     pass = 1
  166.     pass = 0                            ' comment out second time
  167. '
  168.     IFZ standalone THEN
  169.         IFZ pass THEN
  170.             argc = 4
  171.             DIM argv$[3]
  172.             argv$[0] = "zap"
  173.             argv$[1] = "-i"
  174.             argv$[2] = "/xb/zap.x"
  175.             argv$[3] = "/xb/zap.zap"
  176.             XstSetCommandLineArguments (argc, @argv$[])
  177.         ELSE
  178.             argc = 4
  179.             DIM argv$[3]
  180.             argv$[0] = "zap"
  181.             argv$[1] = "-e"
  182.             argv$[2] = "/xb/zap.zap"
  183.             argv$[3] = "/xb/zap.xxx"
  184.             XstSetCommandLineArguments (argc, @argv$[])
  185.         END IF
  186.     END IF
  187. '
  188. ' normal standalone execution begins below
  189. '
  190.     XstGetSystemTime (@preload)
  191.     error = CommandLine (@i[], @ofile, @ilength, @direction, @version)
  192.     IF error THEN RETURN ($$TRUE)
  193. '
  194. ' call the function that implodes or explodes i[] to create o[]
  195. '
  196.     XstGetSystemTime (@first)
  197.     SELECT CASE direction
  198.         CASE 'i'    :    Implode (&i[], ilength, @count, version, @o[])
  199.         CASE 'e'    :    Explode (&i[], ilength, @count, version, @o[])
  200.         CASE ELSE    : RETURN ($$TRUE)
  201.     END SELECT
  202.     XstGetSystemTime (@final)
  203.     PRINT final-first; " = "; final; " -"; first; " : "; first-preload; " ="; first; " -"; preload
  204. '
  205.     WRITE [ofile], o[]                                    ' save o[] result file
  206.     CLOSE (ofile)
  207. END FUNCTION
  208. '
  209. '
  210. ' ##########################
  211. ' #####  GetPrefix ()  #####
  212. ' ##########################
  213. '
  214. FUNCTION  GetPrefix (prefix$)
  215. '
  216. ' short prefix with 3865 bytes
  217. '
  218. '    prefix$ = " whether extracted shortages measurements thoughts exported imported facility sounds fixed lateral stores opens until patrols explorers personal externals interactions internals scales experiences adapters ships surplus classify targets active finds deleted integrated units beginnings minimums overviews regions loads fields ranges respectively forget products surely profiled passed around shared represents determines wasted significantly calling digital usually combinations receives kinds backgrounds wells doubles really pages always records edited loops anything accounts positions focu
  219. '    PRINT LEN (prefix$)
  220. '
  221. ' long prefix with 4132 bytes
  222. '
  223.     prefix$ = "once storage true reason close final words much were exceptions continue modify depends incorrect smaller rather elastic inserted validated books goods synchronized governmental typically thus thinks frames consistency furthermore shells essentials notify manipulated whether extracted shortages measurements thoughts exported imported facility sounds fixed lateral stores opens until patrols explorers personal externals interactions internals scales experiences adapters ships surplus classify targets active finds deleted integrated units beginnings minimums overviews regions loads fields ranges respectively forget products surely profiled passed around shared represents determines wasted significantly calling digital usually combinations receives kinds backgrounds wells doubles really pages always records edited loops anything accounts positions focuses results arguments various converts path eventually knowledge accumulates worlds insiders left increases searchers bytes obtains executes symbols earlier rectangles checks dialogs reality templates actually disable wants helpful collections techniques cases immediately apparatus providers indicates viewers effects downstream threesome rotates patents sensations monitors visible itself invents tests during customs contextual statements children individuals levels sizes keyboards pictures technology means mighty pixels steps enhanced indentical certainly calculated interfaces many longer appearances shows partners sequences headers spaces working seconds groups iterates namesake sends contents given couldn't probably mental aspects such items panels still completed two boxed themselves foundation details below blocks connected promptly universal locations features remotes differences updated basics encoded graphics separates wouldn't quality chapters layers beings binary cannot numbers something directly outlines generally likely toggles bothers locals justice attributes status removes screens brains everywhere sincerely lengthy operations makers videos containers allowances previously printers "
  224.     prefix$ = prefix$ + "lines mostly summary points designers those while scripts accessed drivers utility managers above surroundings dismay exchanged disks corresponds thanks written handles resources errors describes doesn't inputs been perceptions commons questions orders within hastle seen assigns fundamentals automation limited creates throughout they newly textures expressed values formats similar people chooses performs needs includes runs understands existing addresses however necessary correctly lists ones components languages button must related methods originals requires anyone transforms subjects already pleased property granted based notes implements concepts points reserved times extensions directory what clicks comments hardware generic created several instructions library parameters theirs types buildings switches perceptions appropriately devices characters because multiples references installed routines mains options performances entity into copyrights introductions declares singles visuals enters arithmetic additional important where returns before bits sets releases distributed same starts associated modules buffers automatics outputs sections between either each simpler more lines first compress supports after problems colors contacts understands machines compatible selects develops creates compilers variables calls then sources currents settings buttons changes instances purposes changes demonstrates consciousness another names modes users provides builds also documentation defines can values memory used software includes configures strings algorithms only drawings descriptions running codes without defaults therefore images processes supports particulars standards computers coordinates objects when others structures about numbers have these different services data implements controls not contains examples displays commands should specifics are systems using initial which you from available will follows programs functions with messages versions samples files directory for that information this and applications windows the "
  225. '    PRINT LEN (prefix$)
  226. '
  227. ' alternatively load the prefix string from a disk file
  228. '
  229. '    zfile = OPEN ("zapwords.txt", $$RD)
  230. '    IF (zfile <= 0) THEN RETURN
  231. '    zlength = LOF (zfile)
  232. '    IF (zlength < 3840) THEN RETURN
  233. '    prefix$ = NULL$ (zlength)
  234. '    READ [zfile], prefix$
  235. '    CLOSE (zfile)
  236. '
  237. '    PRINT "LEN (prefix$) = "; LEN (prefix$)
  238. END FUNCTION
  239. '
  240. '
  241. ' ##########################
  242. ' #####  GetString ()  #####
  243. ' ##########################
  244. '
  245. FUNCTION  GetString (addr, count, string$)
  246. '
  247.     string$ = ""
  248.     IF (count <= 0) THEN RETURN
  249.     string$ = NULL$(count)
  250. '
  251.     FOR d = 0 TO count-1
  252.         string${d} = UBYTEAT(addr)
  253.         INC addr
  254.     NEXT d
  255. END FUNCTION
  256. '
  257. '
  258. ' ############################
  259. ' #####  CommandLine ()  #####
  260. ' ############################
  261. '
  262. ' command line syntax
  263. '
  264. '   zap [-i|-e] inputfilename outputfilename"
  265. '
  266. ' -i switch means "Implode() aka compress the input file"
  267. ' -e switch means "Explode() aka decompress the input file"
  268. ' if no switch given, figure out implode vs explode from file header
  269. '
  270. ' CommandLine() gets the command line arguments, gets the data
  271. ' from the input file into UBYTE array i[], determines direction
  272. ' of operation (implode vs explode), and returns these results.
  273. '
  274. ' if input file is a compressed file with a "zap" signature,
  275. ' version is the algorithm version taken from the input file.
  276. '
  277. '
  278. FUNCTION  CommandLine (UBYTE i[], ofile, ilength, direction, version)
  279. '
  280.     DIM i[]                                                            ' return no data unless read in
  281.     ofile$ = ""                                                    ' return no ofile$ unless found
  282.     version = 0                                                    ' return no version unless found
  283.     direction = 0                                                ' return no direction unless found
  284. '
  285.     XstGetCommandLineArguments (@argc, @argv$[])        ' get command line
  286. '
  287. ' get [-switch], inputfilename, outputfilename from command line
  288. '
  289.     IF (argc > 1) THEN
  290.         FOR i = 1 TO argc-1                                ' for all command line arguments
  291.             arg$ = TRIM$(argv$[i])                    ' get next argument
  292.             IF arg$ THEN                                        ' if not empty
  293.                 char = arg${0}                                ' get 1st byte
  294.                 IF (char = '-') THEN                    ' command line switch?
  295.                     next = arg${1}                                    ' get switch character
  296.                     SELECT CASE next                                ' which switch?
  297.                         CASE 'i'    : direction = 'i'        ' implode = compress
  298.                         CASE 'e'    : direction = 'e'        ' explode = decompress
  299.                     END SELECT
  300.                 ELSE                                                    ' not a switch argument
  301.                     IFZ ifile$ THEN                            ' if 1st filename not yet given
  302.                         ifile$ = arg$                            ' get 1st filename aka source file
  303.                     ELSE
  304.                         IFZ ofile$ THEN                        ' if 2nd filename not yet given
  305.                             ofile$ = arg$                        ' get 2nd filename aka result file
  306.                         END IF
  307.                     END IF
  308.                 END IF
  309.             END IF
  310.         NEXT i
  311.     END IF
  312. '
  313. ' see if we have valid input file and output filename : read input file
  314. '
  315.     IF ifile$ THEN                                            ' need input filename
  316.         IF ofile$ THEN                                        ' and output filename
  317.             ifile = OPEN (ifile$, $$RD)
  318.             IF (ifile <= 0) THEN RETURN            ' can't open input filename
  319.             ilength = LOF (ifile)                        ' input filename length
  320.             IF (ilength <= 0) THEN                    ' bogus file
  321.                 CLOSE (ifile)
  322.                 RETURN (1)
  323.             END IF
  324.             upper = ilength-1
  325.             DIM i[upper]
  326.             READ [ifile], i[]                                ' read input file into i[]
  327.             CLOSE (ifile)
  328.             IF (upper >= 8) THEN                        ' zap file head is 8+ bytes
  329.                 IF (i[0] = 'z') THEN
  330.                     IF (i[1] = 'a') THEN
  331.                         IF (i[2] = 'p') THEN            ' "zap" begins compressed files
  332.                             IFZ direction THEN            ' if direction is not specified
  333.                                 direction = 'e'                ' explode already compressed files
  334.                             END IF
  335.                         END IF
  336.                     END IF
  337.                 END IF
  338.             END IF
  339.         END IF
  340.     END IF
  341. '
  342.     IF (direction = 'e') THEN                        ' Explode() this "zap" file
  343.         version = i[3]                                        ' get zap.x version number
  344.     ELSE                                                                ' no "zap" header, so compress
  345.         version = $$Version                                ' algorithm version number
  346.         direction = 'i'                                        ' say Implode()
  347.     END IF
  348. '
  349.     IF i[] THEN                                                    ' if we have input data
  350.         IF ofile$ THEN                                        ' if we have output filename
  351.             ofile = OPEN (ofile$, $$WRNEW)    ' open output file
  352.             IF (ofile > 0) THEN                            ' if output file opened okay
  353.                 RETURN ($$FALSE)                            ' then return arguments - no error
  354.             END IF
  355.         END IF
  356.     END IF
  357. '
  358. ' an error occured, clear out arguments and return error = $$TRUE
  359. '
  360.     DIM i[]                                                            ' no input data
  361.     version = 0                                                    ' invalid version
  362.     direction = 0                                                ' invalid direction
  363.     IF (ofile > 0) THEN                                    ' output file open?
  364.         CLOSE (ofile)                                            ' close output file
  365.         ofile = 0                                                    ' clear argument
  366.     END IF
  367.     RETURN ($$TRUE)                                            ' error = $$TRUE
  368. END FUNCTION
  369. '
  370. '
  371. ' ########################
  372. ' #####  Implode ()  #####
  373. ' ########################
  374. '
  375. FUNCTION  Implode (addr, bytes, count, version, UBYTE o[])
  376.     SHARED  words$
  377.     UBYTE  i[]
  378. '
  379.     DIM o[]
  380.     count = 0
  381.     length = bytes
  382.     IF (length <= 0) THEN RETURN ($$TRUE)        ' error
  383. '
  384.     IFZ words$ THEN GetPrefix (@words$)            ' get prefix data
  385.     zlength = LEN(words$)
  386. '
  387.     DIM i[zlength+bytes-1]
  388.     FOR i = 0 TO zlength-1
  389.         i[i] = words${i}                                            ' put prefix data in i[]
  390.     NEXT i
  391. '
  392.     at = addr
  393.     FOR z = zlength TO zlength+bytes-1
  394.         i[z] = UBYTEAT (at)                                        ' append input data to prefix
  395.         INC at
  396.     NEXT z
  397. '
  398.     upper = bytes - 1                '
  399.     count = 0                                ' result count starts at zero
  400.     addra = addr                        ' address of first byte to compress
  401.     addrz = addra + upper        ' address of last byte to compress
  402.     addra = &i[] + zlength    '
  403.     addrz = addra + upper        '
  404. '
  405.     output = 0                            ' output data offset
  406.     outhalf = 0                            '
  407.     input = addra                        ' address to get bytes from input data
  408.     DIM o[upper+upper+255]    ' let result be up to twice as many bytes
  409. '
  410.     o[0] = 'z'                            ' signature = "zap"
  411.     o[1] = 'a'                            '
  412.     o[2] = 'p'                            '
  413.     o[3] = version                    ' algorithm version # byte
  414.     output = 8                            ' leave room for length, etc
  415. '
  416. ' compress input bytes to create o[]
  417. '
  418. '    XstGetSystemTime (@msa)
  419. '
  420.     DO WHILE (input <= addrz)                        ' compress all input bytes
  421.         start = input - #Basis + 0x0120        ' compute beginning of window
  422. '        IF (start < addra) THEN start = addra        ' can't start before beginning
  423.         matchFirst = UBYTEAT (input)            ' first byte to match
  424.         matchLength = -1                                    ' no match found yet
  425.         matchIndex = -1                                        ' no match found yet
  426.         index = start                                            ' start at beginning of window
  427.         run = input                                                ' start of runlength check
  428.         runs = 0
  429. '
  430.         DO WHILE (run < addrz)                        ' don't exceed input data
  431.             INC run                                                    ' check next byte
  432.             INC runs                                                ' # of identical bytes
  433.             IF (runs = 258) THEN EXIT DO        ' no more than 258 in a row
  434.         LOOP WHILE (UBYTEAT(run) = matchFirst)
  435. '
  436. ' look for previous match of $$MatchMin to $$MatchMax bytes
  437. '
  438.         ss = start
  439.         zz = input
  440.         min = $$MatchMin
  441.         max = addrz - input + 1
  442.         IF (max > $$MatchMax12) THEN max = $$MatchMax12
  443.         match = $$FALSE
  444.         DO
  445.             match = XstFindMemoryMatch (@ss, zz, input, @min, @max)
  446.             IF match THEN
  447.                 matchLength = min
  448.                 matchIndex = ss
  449.                 INC min
  450.             END IF
  451.         LOOP WHILE match
  452. '
  453. ' the short routine above replaces the following longer/slower routine
  454. '
  455. '        DO WHILE (index <= (input-$$MatchMin))
  456. '            IF (UBYTEAT(index) = matchFirst) THEN
  457. '                l = 0                                                    ' length of match = 1
  458. '                a = input                                            ' first byte of match in input
  459. '                b = index                                            ' first byte of match in check
  460. '                DO WHILE (a < addrz)                    ' match up to end of input data
  461. '                    INC a
  462. '                    INC b
  463. '                    INC l
  464. '                    IF (UBYTEAT(a) != UBYTEAT(b)) THEN EXIT DO
  465. '                LOOP WHILE (b < input)
  466. '                IF (l >= $$MatchMin) THEN
  467. '                    IF (l > matchLength) THEN
  468. '                        matchLength = l
  469. '                        matchIndex = index
  470. '                        IF (matchLength >= $$MatchMax12) THEN
  471. '                            matchLength = $$MatchMax12
  472. '                            EXIT DO
  473. '                        END IF
  474. '                    END IF
  475. '                END IF
  476. '            END IF
  477. '            INC index
  478. '        LOOP
  479. '
  480.         IF (runs > 2) THEN
  481.             IF (matchLength < 0) OR (runs > (matchLength + 1)) THEN
  482. '                INC rrrr : PRINT rrrr;; runs;; matchLength;; HEX$(matchFirst,2)
  483.                 IF (ogot >= 4) THEN GOSUB Flush    ' store literal string efficiently
  484.                 SELECT CASE TRUE
  485.                     CASE (runs <= 18)
  486.                                 data = #CodeRunLength4    ' header : runlength encode 3-18 bytes
  487.                                 GOSUB Out12                            ' output header
  488.                                 data = runs - 3                    ' length = length - 3
  489.                                 GOSUB Out4                            ' output length
  490.                     CASE (runs <= 271)
  491.                                 data = #CodeRunLength8    ' header : runlength encode 16-271 bytes
  492.                                 GOSUB Out12                            ' output header
  493.                                 data = runs - 16                ' length = length - 16
  494.                                 GOSUB Out8                            ' output length
  495.                     CASE ELSE
  496.                                 PRINT "Implode() : RunLength : Disaster : (runs > 271)"
  497.                 END SELECT
  498. '
  499.                 data = matchFirst                                ' data = repeated byte
  500.                 GOSUB Out8                                            '
  501.                 input = input + runs                        '
  502. '                PRINT "      got : "; RJUST$(STRING$(runs),6); " consectutive '"; CHR$(matchFirst); "' bytes"
  503.                 ogot = 0
  504.                 DO LOOP
  505.             END IF
  506.         END IF
  507. '
  508. ' find a string that matches bytes at current location
  509. '
  510.         IF (matchLength < $$MatchMin) THEN
  511.             IF (oinput = input) THEN
  512.                 IF (ogot >= 270) THEN GOSUB Flush        ' flush max length literal
  513.             END IF
  514.             IF (oinput = input) THEN        ' repeated individual byte out
  515.                 INC ogot                                    ' count repeated individual bytes
  516.             ELSE
  517.                 ogot = 1                                    ' no previous got
  518.                 iaddr = input                            ' capture start of individual bytes
  519.                 oaddr = output                        ' capture start of stored bytes
  520.                 ohalf = outhalf                        ' capture half byte state
  521.             END IF
  522. '            PRINT RJUST$("OutByte : ",12); RJUST$(STRING$(upper),6);; RJUST$(STRING$(input-addra),6);; RJUST$(STRING$(output),6);;;;;;;;;;; HEX$(matchFirst,4);;; CHR$(matchFirst)
  523.             data = matchFirst
  524.             GOSUB Out12
  525.             INC input
  526.             INC obs
  527.             oinput = input
  528.         ELSE
  529.             IF (ogot >= 4) THEN GOSUB Flush
  530.             ogot = 0
  531.             GOSUB OutBytes
  532.             input = input + matchLength
  533.         END IF
  534.     LOOP
  535. '
  536.     IF (ogot >= 4) THEN GOSUB Flush
  537. '
  538.     data = #CodeEndOfFile
  539.     GOSUB Out12
  540.     REDIM o[output]
  541.     count = output+1
  542. '    XstGetSystemTime (@msb)
  543.     o[4] = length AND 0x00FF
  544.     o[5] = (length >> 8) AND 0x00FF
  545.     o[6] = (length >> 16) AND 0x00FF
  546.     o[7] = (length >> 24) AND 0x00FF
  547. '
  548. '    PRINT output+1, msb-msa
  549. '    PRINT upper, input-addra, output, outhalf, osave, obs, obsx, obs-obsx
  550. '    PRINT
  551. '
  552.     RETURN ($$FALSE)
  553. '
  554. '
  555. ' *****  Flush  *****
  556. '
  557. SUB Flush
  558.     obsx = obsx + ogot
  559.     osave = osave + (ogot - 4)
  560. '    GetString (iaddr, ogot, @i$)
  561. '    PRINT RJUST$("Literals : ",12); RJUST$(STRING$(upper),6);; RJUST$(STRING$(input-addra),6);; RJUST$(STRING$(output),6);;;;;;;;; RJUST$(STRING$(ogot),6);; "\""; i$; "\"  ::: save "; (ogot - 4);; " nybbles"
  562.     output = oaddr                        ' back up output to start of literal string
  563.     outhalf = ohalf                        ' ditto
  564.     IF outhalf THEN o[output] = o[output] AND 0x0F
  565. '
  566.     SELECT CASE TRUE
  567.         CASE (ogot <= 27)
  568.                     data = #CodeLiteral4 + ogot - 4        ' header : literal string of 4 to 27 bytes
  569.                     GOSUB Out12                                                ' output header : length imbedded in header
  570.         CASE (ogot <= 271)
  571.                     data = #CodeLiteral8                            ' header : literal string of 16 to 271 bytes
  572.                     GOSUB Out12                                                ' output header
  573.                     data = ogot - 16                                    ' length = length - 16
  574.                     GOSUB Out8                                                ' output length
  575.         CASE ELSE
  576.                     PRINT "Implode() : Flush : Disaster : (ogot > 271)"
  577.     END SELECT
  578. '
  579.     s = iaddr                                    ' source addr
  580.     FOR s = iaddr TO iaddr+ogot-1
  581.         data = UBYTEAT(s)                ' data byte
  582.         data = NOT data                    ' invert byte to make file unreadable
  583.         data = data AND 0x00FF    ' must keep data <= 0x00FF or bug city
  584.         GOSUB Out8
  585.     NEXT s
  586.     ogot = 0                                    ' done
  587.     oinput = -1                                ' done
  588.     data = 0                                    ' zero byte
  589.     GOSUB Out8                                ' clear garbage
  590.     DEC output                                ' restore output offset
  591. END SUB
  592. '
  593. '
  594. ' *****  Out4  *****
  595. '
  596. SUB Out4
  597.     IFZ outhalf THEN
  598.         outhalf = $$TRUE
  599.         o[output] = data AND 0x0F
  600.     ELSE
  601.         outhalf = $$FALSE
  602.         outbyte = o[output]
  603.         outbyte = outbyte OR (data << 4)
  604.         o[output] = outbyte
  605.         INC output
  606.     END IF
  607. '    PRINT RJUST$("Out4 : ",12); RJUST$(STRING$(upper),6);; RJUST$(STRING$(input-addra),6);; RJUST$(STRING$(output),6);; RJUST$(STRING$(data),6)
  608. END SUB
  609. '
  610. '
  611. ' *****  Out8  *****
  612. '
  613. SUB Out8
  614.     IFZ outhalf THEN
  615.         o[output] = data
  616.         INC output
  617.     ELSE
  618.         outbyte = o[output]
  619.         outbyte = outbyte OR (data << 4)
  620.         o[output] = outbyte
  621.         INC output
  622.         o[output] = (data >> 4) AND 0x0F
  623.     END IF
  624. '    PRINT RJUST$("Out8 : ",12); RJUST$(STRING$(upper),6);; RJUST$(STRING$(input-addra),6);; RJUST$(STRING$(output),6);; RJUST$(STRING$(data),6);; CHR$(data AND 0x00FF)
  625. END SUB
  626. '
  627. '
  628. ' *****  Out12  *****
  629. '
  630. SUB Out12
  631.     IFZ outhalf THEN
  632.         o[output] = data
  633.         outhalf = $$TRUE
  634.         INC output
  635.         o[output] = (data >> 8) AND 0x000F
  636.     ELSE
  637.         outbyte = o[output]
  638.         outbyte = outbyte OR (data << 4)
  639.         o[output] = outbyte
  640.         INC output
  641.         outhalf = $$FALSE
  642.         o[output] = data >> 4
  643.         INC output
  644.     END IF
  645. '    PRINT RJUST$("Out12 : ",12); RJUST$(STRING$(upper),6);; RJUST$(STRING$(input-addra),6);; RJUST$(STRING$(output),6);; RJUST$(STRING$(data),6);; CHR$(data AND 0x00FF)
  646. END SUB
  647. '
  648. '
  649. ' *****  OutBytes  *****
  650. '
  651. SUB OutBytes
  652.     SELECT CASE TRUE
  653.         CASE (matchLength <= $$MatchMax)
  654.                     offset = input - matchIndex - matchLength
  655.                     data = offset + 0x0100
  656.                     GOSUB Out12
  657.                     data = matchLength - $$MatchMin
  658.                     GOSUB Out4
  659.         CASE (matchLength <= $$MatchMax8)
  660.                     data = #CodeRelative8                        ' header for 8 bit length
  661.                     GOSUB Out12
  662.                     offset = input - matchIndex - matchLength
  663.                     data = offset
  664.                     GOSUB Out12
  665.                     data = matchLength - $$MatchMax - 1
  666.                     GOSUB Out8
  667.         CASE (matchLength <= $$MatchMax12)
  668.                     data = #CodeRelative12                    ' header for 12 bit length
  669.                     GOSUB Out12
  670.                     offset = input - matchIndex - matchLength
  671.                     data = offset
  672.                     GOSUB Out12
  673.                     data = matchLength - $$MatchMax8 - 1
  674.                     GOSUB Out12
  675.         CASE ELSE
  676.                     PRINT "Implode() : OutBytes : Disaster : "; matchLength
  677.     END SELECT
  678. '
  679. '    GetString (matchIndex, matchLength, @i$)
  680. '    PRINT RJUST$("OutBytes : ",12); RJUST$(STRING$(upper),6);; RJUST$(STRING$(input-addra),6);; RJUST$(STRING$(output),6);; RJUST$(STRING$(matchIndex-addra),6);; RJUST$(STRING$(matchLength),6);; "\""; i$; "\""
  681. '    jjj = kkk
  682. '    kkk = jjj
  683. END SUB
  684. END FUNCTION
  685. '
  686. '
  687. ' ########################
  688. ' #####  Explode ()  #####
  689. ' ########################
  690. '
  691. FUNCTION  Explode (addr, bytes, count, version, UBYTE o[])
  692.     SHARED  words$
  693. '
  694.     do = 0
  695.     DIM o[]
  696.     count = 0
  697.     length = bytes
  698.     upper = length - 1
  699.     IF (upper < 0) THEN RETURN
  700. '
  701.     addra = addr
  702.     addrz = addra + upper
  703.     input = addra
  704.     inhalf = 0
  705.     output = 0
  706. '
  707.     a = UBYTEAT(input)    : input = input + 1
  708.     b = UBYTEAT(input)     : input = input + 1
  709.     c = UBYTEAT(input)    : input = input + 1
  710.     d = UBYTEAT(input)    : input = input + 1
  711.     e = XLONGAT(input)    : input = input + 4
  712. '
  713.     IF (a != 'z') THEN RETURN ($$TRUE)
  714.     IF (b != 'a') THEN RETURN ($$TRUE)
  715.     IF (c != 'p') THEN RETURN ($$TRUE)
  716.     IF (d = 0x00) THEN RETURN ($$TRUE)    ' non-zero version #s only
  717.     IF (e <=  0 ) THEN RETURN ($$TRUE)    ' e = length of uncompressed file
  718. '
  719. '    PRINT "Explode() : length in header ="; e; " bytes : bytes = "; bytes
  720. '
  721.     IFZ words$ THEN GetPrefix (@words$)
  722.     zlength = LEN (words$)
  723.     DIM o[zlength+e+255]
  724.     output = zlength
  725. '
  726.     FOR i = 0 TO zlength-1
  727.         o[i] = words${i}
  728.     NEXT i
  729. '
  730. ' ??? should (data > #CodeLiteral4) be (data >= #CodeLiteral4) ???
  731. '
  732.     DO WHILE (input <= addrz)
  733.         GOSUB In12
  734.         SELECT CASE TRUE
  735.             CASE (data = #CodeEndOfFile)    :    EXIT DO
  736.             CASE (data = #CodeReserved1)    : GOSUB Reserved
  737.             CASE (data = #CodeRunLength8)    : GOSUB ExplodeIdenticalLong
  738.             CASE (data = #CodeRunLength4)    : GOSUB ExplodeIdenticalShort
  739.             CASE (data = #CodeRelative12)    : GOSUB ExplodePreviousString12
  740.             CASE (data = #CodeRelative8)    : GOSUB ExplodePreviousString8
  741.             CASE (data = #CodeReserved0)    : GOSUB Reserved
  742.             CASE (data = #CodeLiteral8)        : GOSUB ExplodeLiteralLong
  743.             CASE (data > #CodeLiteral4)        : GOSUB ExplodeLiteralShort
  744.             CASE (data = #CodeLiteral4)        : GOSUB ExplodeLiteralShort
  745.             CASE (data > 0x00FF)                    : GOSUB ExplodePreviousString4
  746.             CASE (data < 0x0100)                    : GOSUB ExplodeLiteralByte
  747.             CASE ELSE                                            : GOSUB Reserved
  748.         END SELECT
  749.     LOOP
  750. '
  751.     count = e
  752.     DEC output
  753.     FOR i = 0 TO e-1
  754.         o[i] = o[i+zlength]
  755.     NEXT i
  756. '
  757.     REDIM o[count-1]
  758.     output = output - zlength
  759.     RETURN ($$FALSE)
  760. '
  761. '
  762. ' *****  Reserved  *****
  763. '
  764. SUB Reserved
  765.     PRINT "Implode() : Reserved : Disaster : (unrecognized header : ignore)"
  766. END SUB
  767. '
  768. '
  769. ' *****  In4  *****
  770. '
  771. SUB In4
  772.     IFZ inhalf THEN
  773.         data = UBYTEAT(input) AND 0x000F
  774.         inhalf = $$TRUE
  775.     ELSE
  776.         data = (UBYTEAT(input) AND 0x00F0) >> 4
  777.         inhalf = $$FALSE
  778.         INC input
  779.     END IF
  780. END SUB
  781. '
  782. '
  783. ' *****  In8  *****
  784. '
  785. SUB In8
  786.     IFZ inhalf THEN
  787.         data = UBYTEAT(input)
  788.         INC input
  789.     ELSE
  790.         data = (UBYTEAT(input) AND 0x00F0) >> 4
  791.         INC input
  792.         data = data OR ((UBYTEAT(input) AND 0x000F) << 4)
  793.     END IF
  794. END SUB
  795. '
  796. '
  797. ' *****  In12  *****
  798. '
  799. SUB In12
  800.     IFZ inhalf THEN
  801.         data = UBYTEAT(input)
  802.         INC input
  803.         data = data OR ((UBYTEAT(input) AND 0x000F) << 8)
  804.         inhalf = $$TRUE
  805.     ELSE
  806.         data = (UBYTEAT(input) AND 0x00F0) >> 4
  807.         bottom = (UBYTEAT(input) AND 0x00F0) >> 4
  808.         INC input
  809.         data = data OR (UBYTEAT(input) << 4)
  810.         top = UBYTEAT(input) << 4
  811.         inhalf = $$FALSE
  812.         INC input
  813.     END IF
  814. END SUB
  815. '
  816. '
  817. ' *****  ExplodeIdenticalLong  *****
  818. '
  819. SUB ExplodeIdenticalLong
  820.     IF do THEN PRINT "eil : "
  821.     GOSUB In8
  822.     length = data + 16                        ' length of string
  823.     GOSUB In8
  824.     FOR o = 1 TO length
  825.         o[output] = data
  826.         INC output
  827.     NEXT o
  828. END SUB
  829. '
  830. '
  831. ' *****  ExplodeIdenticalShort  *****
  832. '
  833. SUB ExplodeIdenticalShort
  834.     IF do THEN PRINT "eis : "
  835.     GOSUB In4
  836.     length = data + 3                            ' length of string
  837.     GOSUB In8
  838.     FOR o = 1 TO length
  839.         o[output] = data
  840.         INC output
  841.     NEXT o
  842. END SUB
  843. '
  844. '
  845. ' *****  ExplodeLiteralLong  *****
  846. '
  847. SUB ExplodeLiteralLong
  848.     IF do THEN PRINT "ell : "
  849.     GOSUB In8
  850.     length = data + 16                        ' length of string
  851.     FOR o = 1 TO length
  852.         GOSUB In8
  853.         data = NOT data
  854.         data = data AND 0x00FF
  855.         o[output] = data
  856.         INC output
  857.     NEXT o
  858. END SUB
  859. '
  860. '
  861. ' *****  ExplodeLiteralShort  *****
  862. '
  863. SUB ExplodeLiteralShort
  864.     IF do THEN PRINT "els : "
  865.     xdata = data
  866.     length = data - #CodeLiteral4 + 4        ' length of string
  867.     FOR o = 1 TO length
  868.         GOSUB In8
  869.         data = NOT data
  870.         data = data AND 0x00FF
  871.         o[output] = data
  872.         INC output
  873.     NEXT o
  874.     IF do THEN GetString (&o[output-length], length, @aaa$)
  875.     IF do THEN PRINT HEX$(&o[at],4);; length;; xdata;; "\""; aaa$; "\""
  876. END SUB
  877. '
  878. '
  879. ' *****  ExplodePreviousString4  *****
  880. '
  881. SUB ExplodePreviousString4
  882.     IF do THEN PRINT "eps4 : "
  883.     at = output - (data - 256)
  884.     GOSUB In4
  885.     length = data + $$MatchMin        ' length of string
  886.     at = at - length
  887.     IF do THEN GetString (&o[at], length, @aaa$)
  888.     IF do THEN PRINT HEX$(&o[at],4);; length;; data;; "\""; aaa$; "\""
  889.     FOR o = 1 TO length
  890.         o[output] = o[at]
  891.         INC output
  892.         INC at
  893.     NEXT o
  894. END SUB
  895. '
  896. '
  897. ' *****  ExplodePreviousString8  *****
  898. '
  899. SUB ExplodePreviousString8
  900.     IF do THEN PRINT "eps8 : "
  901.     GOSUB In12
  902.     at = output - data
  903.     GOSUB In8
  904.     length = data + $$MatchMax4 + 1            ' length of string
  905.     at = at - length
  906.     IF do THEN GetString (&o[at], length, @aaa$)
  907.     IF do THEN PRINT HEX$(&o[at],4);; length;; data;; "\""; aaa$; "\""
  908.     FOR o = 1 TO length
  909.         o[output] = o[at]
  910.         INC output
  911.         INC at
  912.     NEXT o
  913. END SUB
  914. '
  915. '
  916. ' *****  ExplodePreviousString12  *****
  917. '
  918. SUB ExplodePreviousString12
  919.     IF do THEN PRINT "eps12 : "
  920.     GOSUB In12
  921.     at = output - data
  922.     GOSUB In12
  923.     length = data + $$MatchMax8 + 1        ' length of string
  924.     at = at - length
  925.     IF do THEN GetString (&o[at], length, @aaa$)
  926.     IF do THEN PRINT HEX$(&o[at],4);; length;; data;; "\""; aaa$; "\""
  927.     FOR o = 1 TO length
  928.         o[output] = o[at]
  929.         INC output
  930.         INC at
  931.     NEXT o
  932. END SUB
  933. '
  934. '
  935. ' *****  ExplodeLiteralByte  *****
  936. '
  937. SUB ExplodeLiteralByte
  938.     IF do THEN PRINT "elb : "
  939.     o[output] = data
  940.     INC output
  941. END SUB
  942. END FUNCTION
  943. END PROGRAM
  944.