'
' This is an implementation for Run Length Enconding (RLE).
'
' http://en.wikipedia.org/wiki/Run-length_encoding
'
' This type of encoding works good with large amounts of equal bytes, for example with BMP files.
' It does not work good with text files.
' (c) PvE - Oct 2012.
'--------------------------------------------------------------------------------------------

' Get the separate arguments
OPTION COLLAPSE TRUE
SPLIT ARGUMENT$ BY " " TO arg$ SIZE amount

' Show usage
IF amount < 2 THEN
    PRINT "Usage: ", arg$[0], " <file>"
    END
ENDIF

' Check if the file exists
IF NOT(FILEEXISTS(arg$[1])) THEN
    PRINT "File ", arg$[1], " not found!"
    END 1
END IF

' Get filesize
FileSize = FILELEN(arg$[1])

' Reserve memory
FileData = MEMORY(FileSize)

' Get the file into memory
OPEN arg$[1] FOR READING AS myfile
    GETBYTE FileData FROM myfile SIZE FileSize
CLOSE FILE myfile

' Memory for compressed data. As it is possible that the resulting compression is negative,
' we must reserve more memory. This encoder uses chunks of 127 for diverse data,
' and each chunk uses a separate byte as indicator, so we must reserve an extra filesize/127.
FileDest = MEMORY(FileSize+(FileSize/127))

' Initialize variables
pos = 0
amount_rle = 0
amount_raw = 0
chunk_rle = -1
chunk_raw = -1

' Start looping through memory
FOR x = 0 TO FileSize-2

    ' Do we have 3 subsequent bytes which are the same?
    IF PEEK(FileData+x) = PEEK(FileData+x+1) AND PEEK(FileData+x+1) = PEEK(FileData+x+2) THEN

        ' Yes we have, start writing previous block of diverse data
        IF amount_raw > 0 THEN
            POKE FileDest+pos, 128|amount_raw
            FOR y = chunk_raw TO x
                POKE FileDest+pos+1, PEEK(FileData+y)
                INCR pos
            NEXT
            chunk_raw = -1
            amount_raw = 0
        END IF

        ' Memorize the current data
        chunk_rle = PEEK(FileData+x)

        ' Increase counter
        INCR amount_rle

        ' If counter reaches 125 then we have 127 equal bytes
        IF amount_rle = 125 THEN
            POKE FileDest+pos, amount_rle+2
            POKE FileDest+pos+1, chunk_rle
            chunk_rle = -1
            amount_rle = 0
            INCR pos, 2
            INCR x, 2
        END IF
    ELSE
        ' We have diverse data, so start writing block of equal data
        IF amount_rle > 0 THEN
            POKE FileDest+pos, amount_rle+2
            POKE FileDest+pos+1, chunk_rle
            INCR pos, 2
            INCR x, 2
            chunk_rle = -1
            amount_rle = 0
        END IF

        ' Increase the counter for diverse data
        INCR amount_raw

        ' Memorize the position where the diverse data starts
        IF chunk_raw = -1 THEN chunk_raw = x

        ' We have 127 bytes of diverse data, store this block
        IF amount_raw = 127 THEN
            POKE FileDest+pos, 128|amount_raw
            FOR y = chunk_raw TO x
                INCR pos
                POKE FileDest+pos, PEEK(FileData+y)
            NEXT
            INCR pos
            amount_raw = 0
            chunk_raw = -1
        END IF
    ENDIF
NEXT

' Loop has ended, save diverse data if this was collected
IF amount_raw > 0 THEN
    POKE FileDest+pos, 128|(amount_raw+1)
    FOR y = chunk_raw TO x
        INCR pos
        POKE FileDest+pos, PEEK(FileData+y)
    NEXT
END IF

' End of loop where there is only 1 byte left
IF amount_raw = 0 THEN
    POKE FileDest+pos, 128|1
    INCR pos
    POKE FileDest+pos, PEEK(FileData+FileSize-1)
END IF

' End of loop, save equal data if this was collected
IF amount_rle > 0 THEN
    POKE FileDest+pos, amount_rle+2
    POKE FileDest+pos+1, chunk_rle
    INCR pos, 2
END IF

' Show some statistics
PRINT "Compression ratio: ", 100-pos*100/FileSize, "%. ";

' Determine new name
newname$ = CONCAT$(arg$[1], ".rle")

' Save compressed data from memory to file
OPEN newname$ FOR WRITING AS myfile
    PUTBYTE FileDest TO myfile SIZE pos
CLOSE FILE myfile

PRINT "File '", arg$[1], "' compressed with RLE and saved as '", newname$, "'."

' Free claimed memory
FREE FileData
FREE FileDest

END