include "inc/cmdsys.plh"
include "inc/lz4.plh"
include "inc/fileio.plh"
asm incs
        !SOURCE    "vmsrc/plvmzp.inc"
end
//
// Always forward copy memory - important for overlapping match sequences
//
asm bcpy(dst, src, len)#0
        INX
        INX
        INX
        LDA     ESTKL-3,X
        ORA     ESTKH-3,X
        BEQ     CPYEX
        LDA     ESTKL-1,X
        STA     DSTL
        LDA     ESTKH-1,X
        STA     DSTH
        LDA     ESTKL-2,X
        STA     SRCL
        LDA     ESTKH-2,X
        STA     SRCH
        LDY     ESTKL-3,X
        BEQ     CPYLP
        INC     ESTKH-3,X
        LDY     #$00
CPYLP   LDA     (SRC),Y
        STA     (DST),Y
        INY
        BNE     +
        INC     DSTH
        INC     SRCH
+       DEC     ESTKL-3,X
        BNE     CPYLP
        DEC     ESTKH-3,X
        BNE     CPYLP
CPYEX   RTS
end
//
// Unpack LZ4 sequence into buffer, return unpacked length
//
export def lz4Unpack(seq, seqend, buff, buffend)
    word data, len, match, i
    byte token

    data = buff
    while isult(seq, seqend)
        token = ^seq
        seq++
        len = token >> 4
        if len
            //
            // Literal sequence
            //
            if len == 15
                while ^seq == 255
                    len = len + 255
                    seq++
                loop
                len = len + ^seq
                seq++
            fin
            if isuge(data + len, buffend); return 0; fin
            bcpy(data, seq, len)
            data = data + len
            seq  = seq  + len
        fin
        len = token & $0F
        if len or isult(seq, seqend)
            //
            // Match sequence
            //
            match =  data - *seq
            seq   = seq + 2
            len   = len + 4
            if len == 19 // $0F + 4
                while ^seq == 255
                    len = len + 255
                    seq++
                loop
                len = len + ^seq
                seq++
            fin
            if isuge(data + len, buffend); return 0; fin
            bcpy(data, match, len)
            data = data + len
        fin
    loop
    return data - buff
end
def lz4ReadBlock(ref, flags, dstbuf, dstend, unpackbuf, unpackend)#1
    word size[2], block, len

    len = fileio:read(ref, @size, 4)
    if len <> 4 or size[0] == 0 or size[1] & $7FFF
        return 0
    fin
    if size[1] & $8000
        //
        // Uncompressed block, read directly into dst buffer
        //
        if dstbuf + size[0] > dstend
            size[0] = dstend - dstbuf
        fin
        len = fileio:read(ref, dstbuf, size[0])
    else
        //
        // Decompress from end of destination buffer
        //
        block = unpackend - size[0]
        fileio:read(ref, block, size[0])
        len = lz4Unpack(block, block + size[0], dstbuf, dstend)
    fin
    if flags & $10 // Block Checksum
        fileio:read(ref, @size, 4)
    fin
    return len
end
export def lz4Read(filestr, dstbuf, dstend, unpackbuf, unpackend)#1
    word len
    byte ref, header[t_lz4header], opt

    ref = fileio:open(filestr)
    if not ref; return LZ4_INVALID; fin
    fileio:read(ref, @header, t_lz4header)
    if header:lz4magic[1] <> $184D or header:lz4magic[0] <> $2204
        return LZ4_INVALID
    fin
    if header.lz4FLG & $C0 <> $40
        return LZ4_WRONG_VERSION
    fin
    if header.lz4BD & $70 <> $40
        return LZ4_NOT_64K
    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(ref, heapmark, opt) // Read rest of header and throw away
    repeat
        len    = lz4ReadBlock(ref, header.lz4FLG, dstbuf, dstend, unpackbuf, unpackend)
        dstbuf = len + dstbuf
    until len == 0
    fileio:close(ref)
    return LZ4_OK
end
done
