2023-10-15 03:13:42 +03:00
|
|
|
|
2024-01-18 16:26:57 +03:00
|
|
|
local decoders = {}
|
2023-10-15 03:13:42 +03:00
|
|
|
|
2024-01-18 16:26:57 +03:00
|
|
|
local function read_palette_full(palette, fp)
|
2023-10-15 03:13:42 +03:00
|
|
|
for i = 1, 16 do
|
2024-01-18 16:26:57 +03:00
|
|
|
palette[i] = bit.blshift(string.byte(fp:read(1)), 16)
|
|
|
|
palette[i] = bit.bor(palette[i], bit.blshift(string.byte(fp:read(1)), 8))
|
|
|
|
palette[i] = bit.bor(palette[i], string.byte(fp:read(1)))
|
2023-10-15 03:13:42 +03:00
|
|
|
end
|
2024-01-18 16:26:57 +03:00
|
|
|
end
|
2023-10-15 03:13:42 +03:00
|
|
|
|
2024-01-18 16:26:57 +03:00
|
|
|
local function read_pixeldata_v0(image, fp)
|
2023-10-15 03:13:42 +03:00
|
|
|
for y = 1, image.h do
|
|
|
|
local line = { s = "", bg = "", fg = "" }
|
|
|
|
for x = 1, image.w do
|
2024-01-18 15:12:24 +03:00
|
|
|
local data = fp:read(2)
|
2024-01-18 16:26:57 +03:00
|
|
|
if data == nil or #data == 0 then
|
2024-01-18 15:12:24 +03:00
|
|
|
return nil, string.format("Failed to read character at x=%d y=%d", x, y)
|
|
|
|
end
|
|
|
|
|
|
|
|
line.s = line.s .. data:sub(1, 1)
|
|
|
|
local color = string.byte(data, 2, 2)
|
|
|
|
|
|
|
|
if color == nil then
|
2024-01-03 20:30:16 +03:00
|
|
|
return nil, string.format("Failed to read color data for x=%d y=%d", x, y)
|
2023-12-20 19:59:28 +03:00
|
|
|
end
|
2024-01-03 20:30:16 +03:00
|
|
|
line.bg = line.bg .. string.format("%x", bit.band(0xF, color))
|
|
|
|
line.fg = line.fg .. string.format("%x", bit.band(0xF, bit.brshift(color, 4)))
|
2023-10-15 03:13:42 +03:00
|
|
|
end
|
|
|
|
table.insert(image.lines, line)
|
|
|
|
end
|
2024-01-18 16:26:57 +03:00
|
|
|
return true
|
|
|
|
end
|
2023-10-15 03:13:42 +03:00
|
|
|
|
2024-01-18 16:26:57 +03:00
|
|
|
local function read_varint(fp)
|
|
|
|
local value = 0
|
|
|
|
local current = 0
|
|
|
|
local offset = 0
|
|
|
|
repeat
|
|
|
|
if offset >= 5 then return nil, "varint too long" end
|
|
|
|
current = string.byte(fp:read(1))
|
|
|
|
value = bit.bor(value, bit.blshift(bit.band(current, 0x7f), offset * 7))
|
|
|
|
offset = offset + 1
|
|
|
|
until bit.band(current, 0x80) == 0
|
|
|
|
return value
|
|
|
|
end
|
|
|
|
|
|
|
|
decoders[0] = function(image, fp)
|
|
|
|
image.w, image.h = string.byte(fp:read(1)), string.byte(fp:read(1))
|
|
|
|
image.scale = 0.5 + string.byte(fp:read(1)) * 5 / 255
|
|
|
|
read_palette_full(image.palette, fp)
|
|
|
|
local success, err = read_pixeldata_v0(image, fp)
|
|
|
|
if not success then return false, err end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
decoders[1] = function(image, fp)
|
|
|
|
image.w = read_varint(fp)
|
|
|
|
image.h = read_varint(fp)
|
|
|
|
image.scale = 0.5 -- CPIv1 doesn't have a scale property
|
|
|
|
read_palette_full(image.palette, fp)
|
|
|
|
local success, err = read_pixeldata_v0(image, fp)
|
|
|
|
if not success then return false, err end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
local function load(path)
|
|
|
|
local fp, err = io.open(path, "rb")
|
|
|
|
if not fp then return nil, err end
|
2023-10-15 03:13:42 +03:00
|
|
|
|
2024-01-18 16:26:57 +03:00
|
|
|
local res
|
|
|
|
local image = { w = 0, h = 0, scale = 1.0, palette = {}, lines = {} }
|
|
|
|
|
|
|
|
local magic = fp:read(4)
|
|
|
|
if magic == "CCPI" then
|
|
|
|
res, err = decoders[0](image, fp)
|
|
|
|
elseif magic:sub(1, 3) == "CPI" then
|
|
|
|
local version = magic:byte(4, 4)
|
|
|
|
if decoders[version] == nil then
|
|
|
|
fp:close()
|
|
|
|
return nil, string.format("Invalid CPI version 0x%02x", version)
|
|
|
|
end
|
|
|
|
res, err = decoders[version](image, fp)
|
|
|
|
else
|
|
|
|
fp:close()
|
|
|
|
return nil, "Invalid header: expected CCPI got " .. magic
|
|
|
|
end
|
|
|
|
|
|
|
|
fp:close()
|
|
|
|
if not res then return false, err end
|
2023-10-15 03:13:42 +03:00
|
|
|
return image
|
|
|
|
end
|
|
|
|
|
|
|
|
local function draw(img, ox, oy, monitor)
|
2024-01-03 20:30:16 +03:00
|
|
|
-- todo: add expect()
|
2023-10-15 03:13:42 +03:00
|
|
|
local t = monitor or term.current()
|
|
|
|
ox = ox or 1
|
|
|
|
oy = oy or 1
|
|
|
|
|
2024-01-18 15:12:24 +03:00
|
|
|
if not t.setPaletteColor then
|
|
|
|
return nil, "setPaletteColor is not supported on this term"
|
|
|
|
end
|
|
|
|
|
|
|
|
if not t.setTextScale then
|
|
|
|
return nil, "setTextScale is not supported on this term"
|
|
|
|
end
|
|
|
|
|
2023-10-15 03:13:42 +03:00
|
|
|
for i = 1, 16 do
|
2024-01-03 20:30:16 +03:00
|
|
|
t.setPaletteColor(bit.blshift(1, i - 1), img.palette[i])
|
2023-10-15 03:13:42 +03:00
|
|
|
end
|
|
|
|
|
2023-10-15 03:26:13 +03:00
|
|
|
t.setTextScale(img.scale)
|
2023-10-15 03:24:29 +03:00
|
|
|
|
2023-10-15 03:13:42 +03:00
|
|
|
for y = 1, img.h do
|
|
|
|
t.setCursorPos(ox, oy + y - 1)
|
|
|
|
t.blit(img.lines[y].s, img.lines[y].fg, img.lines[y].bg)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return {
|
|
|
|
load = load,
|
|
|
|
draw = draw
|
|
|
|
}
|