1
--[[
2
2djab decode/encode program
3
Vague specification:
4
- Probably little endian
5
- 2djab files start with "2djab1" (the 1 is the version)
6
- Then have a 4-byte unsigned integer dictating the number of posters in the file
7
- Then, each poster sequentially follows
8
- Each poster:
9
- Starts with a 2-byte unsigned integer (dictating its length in bytes)
10
- Has a UTF-8 label, string starting with a 2-byte unsigned integer for length in bytes
11
- (length of 0 is assumed to be no label)
12
- Has a UTF-8 tooltip, string starting with a 2-byte unsigned integer for length in bytes
13
- (length of 0 is assumed to be no label)
14
- Has a palette
15
- Starts with a 1-byte unsigned integer, dictating its length in bytes
16
- The amount of colors it contains is its length divided by 3
17
- Continues with many 3-byte integers, one for each color
18
- Has an image data section
19
- Starts with a 2-byte unsigned integer, dictating its length in bytes
20
- Then has many 1-byte unsigned integers
21
- If the 1-byte unsigned integer is within the range 0-63 inclusive:
22
- it is a palette index
23
- If the 1-byte unsigned integer is within the range 64-127 inclusive:
24
- its value minus 64 is a palette index repeated twice
25
- If the 1-byte unsigned integer is within the range 128-255 inclusive:
26
- its value minus 128 is the amount of times that the previous palette index should be repeated
27
- The previous palette index byte is still included
28
- This byte must come directly after a byte within the 0-63 range.
29
2djabc files are just the 2djab file but compressed with LibDeflate:CompressZlib
30
]]
31
local success, LibDeflate = pcall(require, "LibDeflate")
32
33
if not success then
34
print("Error in requiring LibDeflate. Install? [y/n] ")
35
local s = read()
36
if s:lower():find("y") then
37
shell.run("wget https://raw.githubusercontent.com/MCJack123/CC-Archive/master/LibDeflate.lua")
38
LibDeflate = require("LibDeflate")
39
else
40
return
41
end
42
end
43
44
local function decode2djab(str)
45
local ver, numPosters = ("c6I4"):unpack(str:sub(1, 10))
46
47
assert(ver == "2djab1")
48
local pages = {}
49
local out = {
50
pages = pages
51
}
52
53
local posterStrs = {("s2"):rep(numPosters):unpack(str:sub(11))}
54
for i = 1, numPosters do
55
local posterStr = posterStrs[i]
56
local label, tooltip, palette, data = ("s2s2s1s2"):unpack(posterStr)
57
palette = {("I3"):rep(#palette / 3):unpack(palette)}
58
palette[#palette] = nil
59
data = data:gsub("([\0-\63])([\128-\255])", function(a, b) return a:rep(b:byte() - 127) end)
60
data = data:gsub("[\64-\127]", function(a) return string.char(a:byte() - 64):rep(2) end)
61
data = {data:byte(1, -1)}
62
pages[#pages+1] = {
63
label = label, tooltip = tooltip, palette = palette, pixels = data
64
}
65
end
66
67
return out
68
end
69
70
local function encode2djab(input)
71
local pages = input.pages
72
local numPages = #pages
73
local output = "2djab1" .. ("I4"):pack(numPages)
74
75
for _i, page in ipairs(pages) do
76
local data = page.pixels
77
data[#data + 1] = -1
78
local d2 = ''
79
80
local currentPixel = -1
81
local currentPixelCount = 0
82
for i, pix in ipairs(data) do
83
if pix == currentPixel then
84
currentPixelCount = currentPixelCount + 1
85
if currentPixelCount == 128 then
86
d2 = d2 .. string.char(currentPixel) .. "\255"
87
currentPixel, currentPixelCount = -1, 0
88
end
89
elseif pix ~= currentPixel then
90
if currentPixel ~= -1 then
91
if currentPixelCount == 1 then
92
d2 = d2 .. string.char(currentPixel)
93
elseif currentPixelCount == 2 then
94
d2 = d2 .. string.char(currentPixel + 64)
95
else
96
d2 = d2 .. string.char(currentPixel) .. string.char(currentPixelCount + 127)
97
end
98
end
99
currentPixel, currentPixelCount = pix, 1
100
end
101
end
102
103
output = output .. ("s2"):pack(("s2s2s1s2"):pack(
104
page.label or '',
105
page.tooltip or '',
106
("I3"):rep(#page.palette):pack(unpack(page.palette)),
107
d2
108
))
109
end
110
111
return output
112
end
113
114
local function decode2djabc(input)
115
return decode2djab(LibDeflate:DecompressZlib(input))
116
end
117
118
local function encode2djabc(input)
119
return LibDeflate:CompressZlib(encode2djab(input), {level = 9})
120
end
121
122
local function readfile(name)
123
local hn = fs.open(shell.resolve(name), "rb")
124
assert(hn, "Input file does not exist")
125
local text = hn.readAll()
126
hn.close()
127
return text
128
end
129
130
local function writefile(name, text)
131
local hn = fs.open(shell.resolve(name), "wb")
132
hn.write(text)
133
hn.close()
134
end
135
136
local mode, inputfile, outputfile = arg[1], arg[2], arg[3]
137
if not outputfile or not inputfile or (mode ~= "encode" and mode ~= "decode") then
138
print("Usage: 2djab <encode|decode> <infile> <outfile>")
139
end
140
141
if mode == "encode" then
142
print("Reading file " .. inputfile .. "...")
143
local text = readfile(inputfile)
144
print("Decoding JSON...")
145
local dat = textutils.unserializeJSON(text)
146
print("Encoding file...")
147
local res = outputfile:find("%.2djabc$") and encode2djabc(dat) or encode2djab(dat)
148
print("Writing file...")
149
writefile(outputfile, res)
150
print("Done!")
151
elseif mode == "decode" then
152
print("Reading file " .. inputfile .. "...")
153
local text = readfile(inputfile)
154
print("Decoding 2djab...")
155
local dat = inputfile:find("%.2djabc$") and decode2djabc(text) or decode2djab(text)
156
print("Encoding JSON...")
157
local res = textutils.serializeJSON(dat)
158
print("Writing file...")
159
writefile(outputfile, res)
160
print("Done!")
161
end