--[[ 2djab decode/encode program Vague specification: - Probably little endian - 2djab files start with "2djab1" (the 1 is the version) - Then have a 4-byte unsigned integer dictating the number of posters in the file - Then, each poster sequentially follows - Each poster: - Starts with a 2-byte unsigned integer (dictating its length in bytes) - Has a UTF-8 label, string starting with a 2-byte unsigned integer for length in bytes - (length of 0 is assumed to be no label) - Has a UTF-8 tooltip, string starting with a 2-byte unsigned integer for length in bytes - (length of 0 is assumed to be no label) - Has a palette - Starts with a 1-byte unsigned integer, dictating its length in bytes - The amount of colors it contains is its length divided by 3 - Continues with many 3-byte integers, one for each color - Has an image data section - Starts with a 2-byte unsigned integer, dictating its length in bytes - Then has many 1-byte unsigned integers - If the 1-byte unsigned integer is within the range 0-63 inclusive: - it is a palette index - If the 1-byte unsigned integer is within the range 64-127 inclusive: - its value minus 64 is a palette index repeated twice - If the 1-byte unsigned integer is within the range 128-255 inclusive: - its value minus 128 is the amount of times that the previous palette index should be repeated - The previous palette index byte is still included - This byte must come directly after a byte within the 0-63 range. 2djabc files are just the 2djab file but compressed with LibDeflate:CompressZlib ]] local success, LibDeflate = pcall(require, "LibDeflate") if not success then print("Error in requiring LibDeflate. Install? [y/n] ") local s = read() if s:lower():find("y") then shell.run("wget https://raw.githubusercontent.com/MCJack123/CC-Archive/master/LibDeflate.lua") LibDeflate = require("LibDeflate") else return end end local function decode2djab(str) local ver, numPosters = ("c6I4"):unpack(str:sub(1, 10)) assert(ver == "2djab1") local pages = {} local out = { pages = pages } local posterStrs = {("s2"):rep(numPosters):unpack(str:sub(11))} for i = 1, numPosters do local posterStr = posterStrs[i] local label, tooltip, palette, data = ("s2s2s1s2"):unpack(posterStr) palette = {("I3"):rep(#palette / 3):unpack(palette)} palette[#palette] = nil data = data:gsub("([\0-\63])([\128-\255])", function(a, b) return a:rep(b:byte() - 127) end) data = data:gsub("[\64-\127]", function(a) return string.char(a:byte() - 64):rep(2) end) data = {data:byte(1, -1)} pages[#pages+1] = { label = label, tooltip = tooltip, palette = palette, pixels = data } end return out end local function encode2djab(input) local pages = input.pages local numPages = #pages local output = "2djab1" .. ("I4"):pack(numPages) for _i, page in ipairs(pages) do local data = page.pixels data[#data + 1] = -1 local d2 = '' local currentPixel = -1 local currentPixelCount = 0 for i, pix in ipairs(data) do if pix == currentPixel then currentPixelCount = currentPixelCount + 1 if currentPixelCount == 128 then d2 = d2 .. string.char(currentPixel) .. "\255" currentPixel, currentPixelCount = -1, 0 end elseif pix ~= currentPixel then if currentPixel ~= -1 then if currentPixelCount == 1 then d2 = d2 .. string.char(currentPixel) elseif currentPixelCount == 2 then d2 = d2 .. string.char(currentPixel + 64) else d2 = d2 .. string.char(currentPixel) .. string.char(currentPixelCount + 127) end end currentPixel, currentPixelCount = pix, 1 end end output = output .. ("s2"):pack(("s2s2s1s2"):pack( page.label or '', page.tooltip or '', ("I3"):rep(#page.palette):pack(unpack(page.palette)), d2 )) end return output end local function decode2djabc(input) return decode2djab(LibDeflate:DecompressZlib(input)) end local function encode2djabc(input) return LibDeflate:CompressZlib(encode2djab(input), {level = 9}) end local function readfile(name) local hn = fs.open(shell.resolve(name), "rb") assert(hn, "Input file does not exist") local text = hn.readAll() hn.close() return text end local function writefile(name, text) local hn = fs.open(shell.resolve(name), "wb") hn.write(text) hn.close() end local mode, inputfile, outputfile = arg[1], arg[2], arg[3] if not outputfile or not inputfile or (mode ~= "encode" and mode ~= "decode") then print("Usage: 2djab ") end if mode == "encode" then print("Reading file " .. inputfile .. "...") local text = readfile(inputfile) print("Decoding JSON...") local dat = textutils.unserializeJSON(text) print("Encoding file...") local res = outputfile:find("%.2djabc$") and encode2djabc(dat) or encode2djab(dat) print("Writing file...") writefile(outputfile, res) print("Done!") elseif mode == "decode" then print("Reading file " .. inputfile .. "...") local text = readfile(inputfile) print("Decoding 2djab...") local dat = inputfile:find("%.2djabc$") and decode2djabc(text) or decode2djab(text) print("Encoding JSON...") local res = textutils.serializeJSON(dat) print("Writing file...") writefile(outputfile, res) print("Done!") end