-- raw-player from sanjuuni
-- Licensed under the MIT license

local b64str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
local function base64decode(str)
    local retval = ""
    for s in str:gmatch "...." do
        if s:sub(3, 4) == '==' then
            retval = retval .. string.char(bit32.bor(bit32.lshift(b64str:find(s:sub(1, 1)) - 1, 2), bit32.rshift(b64str:find(s:sub(2, 2)) - 1, 4)))
        elseif s:sub(4, 4) == '=' then
            local n = (b64str:find(s:sub(1, 1))-1) * 4096 + (b64str:find(s:sub(2, 2))-1) * 64 + (b64str:find(s:sub(3, 3))-1)
            retval = retval .. string.char(bit32.extract(n, 10, 8)) .. string.char(bit32.extract(n, 2, 8))
        else
            local n = (b64str:find(s:sub(1, 1))-1) * 262144 + (b64str:find(s:sub(2, 2))-1) * 4096 + (b64str:find(s:sub(3, 3))-1) * 64 + (b64str:find(s:sub(4, 4))-1)
            retval = retval .. string.char(bit32.extract(n, 16, 8)) .. string.char(bit32.extract(n, 8, 8)) .. string.char(bit32.extract(n, 0, 8))
        end
    end
    return retval
end

local file, err = fs.open(shell.resolve(...), "r")
if not file then error(err) end
if file.readLine() ~= "32Vid 1.1" then error("Unsupported file") end
local fps = tonumber(file.readLine())
local first, second = file.readLine(), file.readLine()
if second == "" or second == nil then fps = 0 end
term.clear()
while true do
    local frame
    if first then frame, first = first, nil
    elseif second then frame, second = second, nil
    else frame = file.readLine() end
    if frame == "" or frame == nil then break end
    local mode = frame:match("^!CP([CD])")
    if not mode then error("Invalid file") end
    local b64data
    if mode == "C" then
        local len = tonumber(frame:sub(5, 8), 16)
        b64data = frame:sub(9, len + 8)
    else
        local len = tonumber(frame:sub(5, 16), 16)
        b64data = frame:sub(17, len + 16)
    end
    local data = base64decode(b64data)
    -- TODO: maybe verify checksums?
    assert(data:sub(1, 4) == "\0\0\0\0" and data:sub(9, 16) == "\0\0\0\0\0\0\0\0", "Invalid file")
    local width, height = ("HH"):unpack(data, 5)
    local c, n, pos = string.unpack("c1B", data, 17)
    local text = {}
    for y = 1, height do
        text[y] = ""
        for x = 1, width do
            text[y] = text[y] .. c
            n = n - 1
            if n == 0 then c, n, pos = string.unpack("c1B", data, pos) end
        end
    end
    c = c:byte()
    for y = 1, height do
        local fg, bg = "", ""
        for x = 1, width do
            fg, bg = fg .. ("%x"):format(bit32.band(c, 0x0F)), bg .. ("%x"):format(bit32.rshift(c, 4))
            n = n - 1
            if n == 0 then c, n, pos = string.unpack("BB", data, pos) end
        end
        term.setCursorPos(1, y)
        term.blit(text[y], fg, bg)
    end
    pos = pos - 2
    local r, g, b
    for i = 0, 15 do
        r, g, b, pos = string.unpack("BBB", data, pos)
        term.setPaletteColor(2^i, r / 255, g / 255, b / 255)
    end
    if fps == 0 then read() break
    else sleep(1 / fps) end
end
file.close()
for i = 0, 15 do term.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end
term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.clear()
term.setCursorPos(1, 1)