include "inc/cmdsys.plh"
include "inc/args.plh"
include "inc/fileio.plh"
include "inc/lz4.plh"

const ERASE_DST = 1
word arg
byte ioflags
byte inref, outref
char[64] infile, outfile
word[2] eof

def lz4ReadBlock(flags)#2
    word size[2], block, data, len

    len = fileio:read(inref, @size, 4)
    if len <> 4 or size[0] == 0 or size[1] & $7FFF
        return NULL, 0
    fin
    block = heapalloc(size[0])
    if block
        len = fileio:read(inref, block, size[0])
        if len <> size[0]
            heaprelease(block)
            return NULL, 0
        fin
    else
        return NULL, 0
    fin
    if size[1] & $8000
        //
        // Uncompressed block
        //
        data = block
    else
        //
        // Decompress block
        //
        len  = heapavail - 256 // Allocate almost entire heap to decompress into
        data = heapalloc(len)
        if data
            len = lz4Unpack(block, block + size[0], data, data + len)
            memcpy(block, data, len)
            data = block
        else
            len = 0
        fin
        heaprelease(block + len)
    fin
    if flags & $10 // Block Checksum
        fileio:read(inref, @size, 4)
    fin
    return data, len
end
def lz4ReadFrame#0
    word data, len
    byte header[t_lz4header], opt

    fileio:read(inref, @header, t_lz4header)
    if header:lz4magic[1] <> $184D or header:lz4magic[0] <> $2204
        puts("Not LZ4 file.\n")
        return
    fin
    if header.lz4FLG & $C0 <> $40
        puts("Wrong LZ4 version.\n")
        return
    fin
    if header.lz4BD & $70 <> $40
        puts("Not 64K block size.\n")
        return
    fin
    opt = 1
    if header.lz4FLG & $08 // Content Size
        opt = opt + 8
    fin
    if header.lz4FLG & $01 // Dictionary ID
        opt = opt + 4
    fin
    fileio:read(inref, heapmark, opt) // Read rest of header and throw away
    repeat
        data, len = lz4ReadBlock(header.lz4FLG)
        if len
            if outref
                fileio:write(outref, data, len)
            else
                while len
                    putc(^data <> $0A ?? ^data :: $0D)
                    data++
                    len--
                loop
            fin
            heaprelease(data)
        fin
    until not data
    if header.lz4FLG & $04 // Content Checksun
        fileio:read(inref, heapmark, 4)
    fin
end

arg = argNext(argFirst)
if ^arg
    while ^(arg + 1) == '-'
        when toupper(^(arg + 2))
            is 'E'
                ioflags = ioflags | ERASE_DST
                break
        wend
        arg = argNext(arg)
    loop
    strcpy(@infile, arg)
    arg = argNext(arg)
    if ^arg
        strcpy(@outfile, arg)
    fin
    if infile
        fileio:iobufalloc(2) // Reserve two I/O buffers
        inref = fileio:open(@infile)
        if inref
            if outfile
                if ioflags & ERASE_DST
                    fileio:destroy(@outfile)
                fin
                outref = fileio:open(@outfile)
                if outref
                    eof[0], eof[1] = fileio:geteof(outref)#2
                    fileio:setmark(outref, eof[0], eof[1])
                else
                    if fileio:create(@outfile, 0, 0) == FILE_ERR_OK
                        outref = fileio:open(@outfile)
                    else
                        puts("Error creating:"); puts(@outfile); putln
                    fin
                fin
            fin
            lz4ReadFrame
            fileio:close(0)
            return 0
        else
            puts("Error reading:"); puts(@infile); putln
        fin
    fin
fin
puts("Usage: lz4cat <lz4 File> [output file]\n")
done
