OPTION COLLAPSE TRUE
SPLIT ARGUMENT$ BY " " TO arg$ SIZE amount
IF amount < 2 THEN
PRINT "Usage: ", arg$[0], " <file>"
END
ENDIF
IF NOT(FILEEXISTS(arg$[1])) THEN
PRINT "File ", arg$[1], " not found!"
END 1
END IF
FileSize = FILELEN(arg$[1])
FileData = MEMORY(FileSize)
OPEN arg$[1] FOR READING AS myfile
GETBYTE FileData FROM myfile SIZE FileSize
CLOSE FILE myfile
offset = 0
IF ODD(FileSize) THEN offset = 1
DECLARE seqs[65536]
FOR x = 0 TO FileSize-1-offset STEP 2
INCR seqs[((int)PEEK(FileData+x)<<8)+(int)PEEK(FileData+x+1)]
NEXT
DECLARE idx_table[255]
FOR y = 0 TO 254
highest = 0
highest_x = 0
FOR x = 0 TO 65535
IF seqs[x] > highest AND seqs[x] > 0 THEN
highest = seqs[x]
highest_x = x
END IF
NEXT
idx_table[y] = highest_x
seqs[highest_x] = 0
NEXT
DECLARE idx_idx[256*256]
FOR x = 0 TO 65535
idx_idx[x] = -1
NEXT
FOR x = 0 TO 254
idx_idx[idx_table[x]] = x
NEXT
FileDest = MEMORY(FileSize*2+254*2)
FOR x = 0 TO 254
POKE FileDest+x*2, idx_table[x]>>8
POKE FileDest+x*2+1, idx_table[x]&255
NEXT
SUB Store_Raw_Data
POKE FileDest + pos, 255
POKE FileDest + pos + 1, amount
INCR pos, 2
FOR y = raw TO x
POKE FileDest + pos, PEEK(FileData+y)
INCR pos
NEXT
amount = 0
raw = -1
END SUB
pos = 254*2+2
amount = 0
raw = -1
FOR x = 0 TO FileSize-1-offset STEP 2
current = idx_idx[(int)PEEK(FileData+x)*256+(int)PEEK(FileData+x+1)]
IF current >= 0 THEN
IF raw > -1 THEN Store_Raw_Data()
POKE FileDest + pos, current
INCR pos
ELSE
IF raw = -1 THEN raw = x
INCR amount, 2
IF amount = 254 THEN Store_Raw_Data()
END IF
NEXT
IF offset THEN INCR amount
IF raw > -1 THEN
DECR x
Store_Raw_Data()
ELIF offset THEN
raw = x
Store_Raw_Data()
ENDIF
PRINT "Compression ratio: ", 100-pos*100/FileSize, "%. ";
newname$ = CONCAT$(arg$[1], ".lz77")
OPEN newname$ FOR WRITING AS myfile
PUTBYTE FileDest TO myfile SIZE pos
CLOSE FILE myfile
PRINT "File '", arg$[1], "' compressed with LZ77 and saved as '", newname$, "'."
FREE FileData
FREE FileDest
END