SHOW:
|
|
- or go back to the newest paste.
| 1 | local inflate do | |
| 2 | ||
| 3 | local floor = math.floor | |
| 4 | local ceil = math.ceil | |
| 5 | local min = math.min | |
| 6 | local max = math.max | |
| 7 | ||
| 8 | -- utility functions | |
| 9 | ||
| 10 | local function memoize(f) | |
| 11 | local cache = {}
| |
| 12 | return function(...) | |
| 13 | local key = table.concat({...}, "-")
| |
| 14 | if not cache[key] then | |
| 15 | cache[key] = f(...) | |
| 16 | end | |
| 17 | return cache[key] | |
| 18 | end | |
| 19 | end | |
| 20 | ||
| 21 | local function int(bytes) | |
| 22 | local n = 0 | |
| 23 | for i = 1, #bytes do | |
| 24 | n = 256*n + bytes:sub(i, i):byte() | |
| 25 | end | |
| 26 | return n | |
| 27 | end | |
| 28 | int = memoize(int) | |
| 29 | ||
| 30 | local function bint(bits) | |
| 31 | return tonumber(bits, 2) or 0 | |
| 32 | end | |
| 33 | bint = memoize(bint) | |
| 34 | ||
| 35 | local function bits(b, width) | |
| 36 | local s = "" | |
| 37 | if type(b) == "number" then | |
| 38 | for i = 1, width do | |
| 39 | s = b%2 .. s | |
| 40 | b = floor(b/2) | |
| 41 | end | |
| 42 | else | |
| 43 | for i = 1, #b do | |
| 44 | s = s .. bits(b:byte(i), 8):reverse() | |
| 45 | assert(#s == i * 8, s) | |
| 46 | end | |
| 47 | end | |
| 48 | return s | |
| 49 | end | |
| 50 | bits = memoize(bits) | |
| 51 | ||
| 52 | local function fill(bytes, len) | |
| 53 | return bytes:rep(floor(len / #bytes)) .. bytes:sub(1, len % #bytes) | |
| 54 | end | |
| 55 | ||
| 56 | local function zip(t1, t2) | |
| 57 | local zipped = {}
| |
| 58 | for i = 1, max(#t1, #t2) do | |
| 59 | zipped[#zipped + 1] = {t1[i], t2[i]}
| |
| 60 | end | |
| 61 | return zipped | |
| 62 | end | |
| 63 | ||
| 64 | local function unzip(zipped) | |
| 65 | local t1, t2 = {}, {}
| |
| 66 | for i = 1, #zipped do | |
| 67 | t1[#t1 + 1] = zipped[i][1] | |
| 68 | t2[#t2 + 1] = zipped[i][2] | |
| 69 | end | |
| 70 | return t1, t2 | |
| 71 | end | |
| 72 | ||
| 73 | local function map(f, t) | |
| 74 | local mapped = {}
| |
| 75 | for i = 1, #t do | |
| 76 | mapped[#mapped + 1] = f(t[i], i) | |
| 77 | end | |
| 78 | return mapped | |
| 79 | end | |
| 80 | ||
| 81 | local function filter(pred, t) | |
| 82 | local filtered = {}
| |
| 83 | for i = 1, #t do | |
| 84 | if pred(t[i], i) then | |
| 85 | filtered[#filtered + 1] = t[i] | |
| 86 | end | |
| 87 | end | |
| 88 | return filtered | |
| 89 | end | |
| 90 | ||
| 91 | local function find(key, t) | |
| 92 | if type(key) == "function" then | |
| 93 | for i = 1, #t do | |
| 94 | if key(t[i]) then | |
| 95 | return i | |
| 96 | end | |
| 97 | end | |
| 98 | return nil | |
| 99 | else | |
| 100 | return find(function(x) return x == key end, t) | |
| 101 | end | |
| 102 | end | |
| 103 | ||
| 104 | local function slice(t, i, j, step) | |
| 105 | local sliced = {}
| |
| 106 | for k = i < 1 and 1 or i, i < 1 and #t + i or j or #t, step or 1 do | |
| 107 | sliced[#sliced + 1] = t[k] | |
| 108 | end | |
| 109 | return sliced | |
| 110 | end | |
| 111 | ||
| 112 | local function range(i, j) | |
| 113 | local r = {}
| |
| 114 | for k = j and i or 0, j or i - 1 do | |
| 115 | r[#r + 1] = k | |
| 116 | end | |
| 117 | return r | |
| 118 | end | |
| 119 | ||
| 120 | -- streams | |
| 121 | ||
| 122 | local function output_stream() | |
| 123 | local stream, buffer = {}, {}
| |
| 124 | local curr = 0 | |
| 125 | ||
| 126 | function stream:write(bytes) | |
| 127 | for i = 1, #bytes do | |
| 128 | buffer[#buffer + 1] = bytes:sub(i, i) | |
| 129 | end | |
| 130 | curr = curr + #bytes | |
| 131 | end | |
| 132 | ||
| 133 | function stream:back_read(offset, n) | |
| 134 | local read = {}
| |
| 135 | for i = curr - offset + 1, curr - offset + n do | |
| 136 | read[#read + 1] = buffer[i] | |
| 137 | end | |
| 138 | return table.concat(read) | |
| 139 | end | |
| 140 | ||
| 141 | function stream:back_copy(dist, len) | |
| 142 | local start, copied = curr - dist + 1, {}
| |
| 143 | for i = start, min(start + len, curr) do | |
| 144 | copied[#copied + 1] = buffer[i] | |
| 145 | end | |
| 146 | self:write(fill(table.concat(copied), len)) | |
| 147 | end | |
| 148 | ||
| 149 | function stream:pos() | |
| 150 | return curr | |
| 151 | end | |
| 152 | ||
| 153 | function stream:raw() | |
| 154 | return table.concat(buffer) | |
| 155 | end | |
| 156 | ||
| 157 | return stream | |
| 158 | end | |
| 159 | ||
| 160 | local function bit_stream(raw, offset) | |
| 161 | local stream = {}
| |
| 162 | local curr = 0 | |
| 163 | offset = offset or 0 | |
| 164 | ||
| 165 | function stream:read(n, reverse) | |
| 166 | local start = floor(curr/8) + offset + 1 | |
| 167 | local bb = "" | |
| 168 | for i = start, start + ceil(n/8) do | |
| 169 | local b = raw:byte(i) | |
| 170 | for j = 0, 7 do bb = bb .. bit32.extract(b, j, 1) end | |
| 171 | end | |
| 172 | local b = bb:sub(curr%8 + 1, curr%8 + n) | |
| 173 | curr = curr + n | |
| 174 | return reverse and b or b:reverse() | |
| 175 | end | |
| 176 | ||
| 177 | function stream:seek(n) | |
| 178 | if n == "beg" then | |
| 179 | curr = 0 | |
| 180 | elseif n == "end" then | |
| 181 | curr = #raw | |
| 182 | else | |
| 183 | curr = curr + n | |
| 184 | end | |
| 185 | return self | |
| 186 | end | |
| 187 | ||
| 188 | function stream:is_empty() | |
| 189 | return curr >= 8*#raw | |
| 190 | end | |
| 191 | ||
| 192 | function stream:pos() | |
| 193 | return curr | |
| 194 | end | |
| 195 | ||
| 196 | return stream | |
| 197 | end | |
| 198 | ||
| 199 | -- inflate | |
| 200 | ||
| 201 | local CL_LENS_ORDER = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
| |
| 202 | local MAX_BITS = 15 | |
| 203 | local PT_WIDTH = 8 | |
| 204 | ||
| 205 | local function cl_code_lens(stream, hclen) | |
| 206 | local code_lens = {}
| |
| 207 | for i = 1, hclen do | |
| 208 | code_lens[#code_lens + 1] = bint(stream:read(3)) | |
| 209 | end | |
| 210 | return code_lens | |
| 211 | end | |
| 212 | ||
| 213 | local function code_tree(lens, alphabet) | |
| 214 | alphabet = alphabet or range(#lens) | |
| 215 | local using = filter(function(x, i) return lens[i] and lens[i] > 0 end, alphabet) | |
| 216 | lens = filter(function(x) return x > 0 end, lens) | |
| 217 | local tree = zip(lens, using) | |
| 218 | table.sort(tree, function(a, b) | |
| 219 | if a[1] == b[1] then | |
| 220 | return a[2] < b[2] | |
| 221 | else | |
| 222 | return a[1] < b[1] | |
| 223 | end | |
| 224 | end) | |
| 225 | return unzip(tree) | |
| 226 | end | |
| 227 | ||
| 228 | local function codes(lens) | |
| 229 | local codes = {}
| |
| 230 | local code = 0 | |
| 231 | for i = 1, #lens do | |
| 232 | codes[#codes + 1] = bits(code, lens[i]) | |
| 233 | if i < #lens then | |
| 234 | code = (code + 1)*2^(lens[i + 1] - lens[i]) | |
| 235 | end | |
| 236 | end | |
| 237 | return codes | |
| 238 | end | |
| 239 | ||
| 240 | local prefix_table | |
| 241 | local function handle_long_codes(codes, alphabet, pt) | |
| 242 | local i = find(function(x) return #x > PT_WIDTH end, codes) | |
| 243 | local long = slice(zip(codes, alphabet), i) | |
| 244 | i = 0 | |
| 245 | repeat | |
| 246 | local prefix = long[i + 1][1]:sub(1, PT_WIDTH) | |
| 247 | local same = filter(function(x) return x[1]:sub(1, PT_WIDTH) == prefix end, long) | |
| 248 | same = map(function(x) return {x[1]:sub(PT_WIDTH + 1), x[2]} end, same)
| |
| 249 | pt[prefix] = {rest = prefix_table(unzip(same)), unused = 0}
| |
| 250 | i = i + #same | |
| 251 | until i == #long | |
| 252 | end | |
| 253 | ||
| 254 | function prefix_table(codes, alphabet) | |
| 255 | local pt = {}
| |
| 256 | if #codes[#codes] > PT_WIDTH then | |
| 257 | handle_long_codes(codes, alphabet, pt) | |
| 258 | end | |
| 259 | for i = 1, #codes do | |
| 260 | local code = codes[i] | |
| 261 | if #code > PT_WIDTH then | |
| 262 | break | |
| 263 | end | |
| 264 | local entry = {value = alphabet[i], unused = PT_WIDTH - #code}
| |
| 265 | if entry.unused == 0 then | |
| 266 | pt[code] = entry | |
| 267 | else | |
| 268 | for i = 0, 2^entry.unused - 1 do | |
| 269 | pt[code .. bits(i, entry.unused)] = entry | |
| 270 | end | |
| 271 | end | |
| 272 | end | |
| 273 | return pt | |
| 274 | end | |
| 275 | ||
| 276 | local function huffman_decoder(lens, alphabet) | |
| 277 | local base_codes = prefix_table(codes(lens), alphabet) | |
| 278 | return function(stream) | |
| 279 | local codes = base_codes | |
| 280 | local entry | |
| 281 | repeat | |
| 282 | entry = codes[stream:read(PT_WIDTH, true)] | |
| 283 | stream:seek(-entry.unused) | |
| 284 | codes = entry.rest | |
| 285 | until not codes | |
| 286 | return entry.value | |
| 287 | end | |
| 288 | end | |
| 289 | ||
| 290 | local function code_lens(stream, decode, n) | |
| 291 | local lens = {}
| |
| 292 | repeat | |
| 293 | local value = decode(stream) | |
| 294 | if value < 16 then | |
| 295 | lens[#lens + 1] = value | |
| 296 | elseif value == 16 then | |
| 297 | for i = 1, bint(stream:read(2)) + 3 do | |
| 298 | lens[#lens + 1] = lens[#lens] | |
| 299 | end | |
| 300 | elseif value == 17 then | |
| 301 | for i = 1, bint(stream:read(3)) + 3 do | |
| 302 | lens[#lens + 1] = 0 | |
| 303 | end | |
| 304 | elseif value == 18 then | |
| 305 | for i = 1, bint(stream:read(7)) + 11 do | |
| 306 | lens[#lens + 1] = 0 | |
| 307 | end | |
| 308 | end | |
| 309 | until #lens == n | |
| 310 | return lens | |
| 311 | end | |
| 312 | ||
| 313 | local function code_trees(stream) | |
| 314 | local hlit = bint(stream:read(5)) + 257 | |
| 315 | local hdist = bint(stream:read(5)) + 1 | |
| 316 | local hclen = bint(stream:read(4)) + 4 | |
| 317 | local cl_decode = huffman_decoder(code_tree(cl_code_lens(stream, hclen), CL_LENS_ORDER)) | |
| 318 | local ll_decode = huffman_decoder(code_tree(code_lens(stream, cl_decode, hlit))) | |
| 319 | local d_decode = huffman_decoder(code_tree(code_lens(stream, cl_decode, hdist))) | |
| 320 | return ll_decode, d_decode | |
| 321 | end | |
| 322 | ||
| 323 | local function extra_bits(value) | |
| 324 | if value >= 4 and value <= 29 then | |
| 325 | return floor(value/2) - 1 | |
| 326 | elseif value >= 265 and value <= 284 then | |
| 327 | return ceil(value/4) - 66 | |
| 328 | else | |
| 329 | return 0 | |
| 330 | end | |
| 331 | end | |
| 332 | extra_bits = memoize(extra_bits) | |
| 333 | ||
| 334 | local function decode_len(value, bits) | |
| 335 | assert(value >= 257 and value <= 285, "value out of range") | |
| 336 | assert(#bits == extra_bits(value), "wrong number of extra bits") | |
| 337 | if value <= 264 then | |
| 338 | return value - 254 | |
| 339 | elseif value == 285 then | |
| 340 | return 258 | |
| 341 | end | |
| 342 | local len = 11 | |
| 343 | for i = 1, #bits - 1 do | |
| 344 | len = len + 2^(i+2) | |
| 345 | end | |
| 346 | return floor(bint(bits) + len + ((value - 1) % 4)*2^#bits) | |
| 347 | end | |
| 348 | decode_len = memoize(decode_len) | |
| 349 | ||
| 350 | local function a(n) | |
| 351 | if n <= 3 then | |
| 352 | return n + 2 | |
| 353 | else | |
| 354 | return a(n-1) + 2*a(n-2) - 2*a(n-3) | |
| 355 | end | |
| 356 | end | |
| 357 | a = memoize(a) | |
| 358 | ||
| 359 | local function decode_dist(value, bits) | |
| 360 | assert(value >= 0 and value <= 29, "value out of range") | |
| 361 | assert(#bits == extra_bits(value), "wrong number of extra bits)") | |
| 362 | return bint(bits) + a(value - 1) | |
| 363 | end | |
| 364 | decode_dist = memoize(decode_dist) | |
| 365 | ||
| 366 | function inflate(data) | |
| 367 | local stream = bit_stream(data) | |
| 368 | local ostream = output_stream() | |
| 369 | repeat | |
| 370 | local bfinal, btype = bint(stream:read(1)), bint(stream:read(2)) | |
| 371 | assert(btype == 2, "compression method not supported") | |
| 372 | local ll_decode, d_decode = code_trees(stream) | |
| 373 | while true do | |
| 374 | local value = ll_decode(stream) | |
| 375 | if value < 256 then | |
| 376 | ostream:write(string.char(value)) | |
| 377 | elseif value == 256 then | |
| 378 | break | |
| 379 | else | |
| 380 | local len = decode_len(value, stream:read(extra_bits(value))) | |
| 381 | value = d_decode(stream) | |
| 382 | local dist = decode_dist(value, stream:read(extra_bits(value))) | |
| 383 | ostream:back_copy(dist, len) | |
| 384 | end | |
| 385 | end | |
| 386 | until bfinal == 1 | |
| 387 | return ostream:raw() | |
| 388 | end | |
| 389 | ||
| 390 | end | |
| 391 | local dfpwm = require "cc.audio.dfpwm" | |
| 392 | ||
| 393 | local hexstr = "0123456789abcdef" | |
| 394 | local file, err = http.get { url = "http://timuzkas.mywire.org:8000/video.32v", binary = true }
| |
| 395 | if not file then error(err) end | |
| 396 | if file.read(4) ~= "32VD" then file.close() error("Not a 32vid file") end
| |
| 397 | local width, height, fps, nStreams, flags = ("<HHBBH"):unpack(file.read(8))
| |
| 398 | local video, audio, subtitles | |
| 399 | for _ = 1, nStreams do | |
| 400 | local size, nFrames, frameType = ("<IIB"):unpack(file.read(9))
| |
| 401 | print(("%X"):format(file.seek()), size, nFrames, frameType)
| |
| 402 | if frameType == 0 and not video then | |
| 403 | local data = file.read(size) | |
| 404 | if bit32.band(flags, 3) == 2 then data = inflate(data) end | |
| 405 | video = {}
| |
| 406 | local pos = 1 | |
| 407 | local start = os.epoch "utc" | |
| 408 | for i = 1, nFrames do | |
| 409 | if i % 100 == 0 or i >= nFrames - 10 then print(i, os.epoch "utc" - start) start = os.epoch "utc" sleep(0) end | |
| 410 | local frame = {palette = {}}
| |
| 411 | local tmp, tmppos = 0, 0 | |
| 412 | local use5bit, customcompress = bit32.btest(flags, 16), bit32.band(flags, 3) == 3 | |
| 413 | local codetree = {}
| |
| 414 | local solidchar, runlen | |
| 415 | local function readField(isColor) | |
| 416 | if customcompress then | |
| 417 | if runlen then | |
| 418 | local c = solidchar | |
| 419 | runlen = runlen - 1 | |
| 420 | if runlen == 0 then runlen = nil end | |
| 421 | return c | |
| 422 | end | |
| 423 | if not isColor and solidchar then return solidchar end | |
| 424 | -- MARK: Huffman decoding | |
| 425 | local node = codetree | |
| 426 | while true do | |
| 427 | local n = bit32.extract(tmp, tmppos, 1) | |
| 428 | tmppos = tmppos - 1 | |
| 429 | if tmppos < 0 then tmp, pos, tmppos = data:byte(pos), pos + 1, 7 end | |
| 430 | if type(node) ~= "table" then error(("Invalid tree state: position %X, frame %d"):format(pos+file.seek()-size-1, i)) end
| |
| 431 | if type(node[n]) == "number" then | |
| 432 | local c = node[n] | |
| 433 | if isColor then | |
| 434 | if c > 15 then runlen = 2^(c-15)-1 return assert(solidchar) | |
| 435 | else solidchar = c end | |
| 436 | end | |
| 437 | return c | |
| 438 | else node = node[n] end | |
| 439 | end | |
| 440 | else | |
| 441 | local n | |
| 442 | if tmppos * 5 + 5 > 32 then n = bit32.extract(math.floor(tmp / 0x1000000), tmppos * 5 - 24, 5) | |
| 443 | else n = bit32.extract(tmp, tmppos * 5, 5) end | |
| 444 | tmppos = tmppos - 1 | |
| 445 | if tmppos < 0 then tmp, pos = (">I5"):unpack(data, pos) tmppos = 7 end
| |
| 446 | return n | |
| 447 | end | |
| 448 | end | |
| 449 | if customcompress then | |
| 450 | -- MARK: Huffman tree reconstruction | |
| 451 | -- read bit lengths | |
| 452 | local bitlen = {}
| |
| 453 | if use5bit then | |
| 454 | for j = 0, 15 do | |
| 455 | bitlen[j*2+1], bitlen[j*2+2] = {s = j*2, l = bit32.rshift(data:byte(pos+j), 4)}, {s = j*2+1, l = bit32.band(data:byte(pos+j), 0x0F)}
| |
| 456 | end | |
| 457 | pos = pos + 16 | |
| 458 | else | |
| 459 | for j = 0, 7 do | |
| 460 | tmp, pos = (">I5"):unpack(data, pos)
| |
| 461 | bitlen[j*8+1] = {s = j*8+1, l = math.floor(tmp / 0x800000000)}
| |
| 462 | bitlen[j*8+2] = {s = j*8+2, l = math.floor(tmp / 0x40000000) % 32}
| |
| 463 | for k = 3, 8 do bitlen[j*8+k] = {s = j*8+k, l = bit32.extract(tmp, (8-k)*5, 5)} end
| |
| 464 | end | |
| 465 | end | |
| 466 | do | |
| 467 | local j = 1 | |
| 468 | while j <= #bitlen do | |
| 469 | if bitlen[j].l == 0 then table.remove(bitlen, j) | |
| 470 | else j = j + 1 end | |
| 471 | end | |
| 472 | end | |
| 473 | if #bitlen == 0 then | |
| 474 | -- screen is solid character | |
| 475 | solidchar = data:byte(pos) | |
| 476 | pos = pos + 1 | |
| 477 | else | |
| 478 | -- reconstruct codes from bit lengths | |
| 479 | table.sort(bitlen, function(a, b) if a.l == b.l then return a.s < b.s else return a.l < b.l end end) | |
| 480 | bitlen[1].c = 0 | |
| 481 | for j = 2, #bitlen do bitlen[j].c = bit32.lshift(bitlen[j-1].c + 1, bitlen[j].l - bitlen[j-1].l) end | |
| 482 | -- create tree from codes | |
| 483 | for j = 1, #bitlen do | |
| 484 | local c = bitlen[j].c | |
| 485 | local node = codetree | |
| 486 | for k = bitlen[j].l - 1, 1, -1 do | |
| 487 | local n = bit32.extract(c, k, 1) | |
| 488 | if not node[n] then node[n] = {} end
| |
| 489 | node = node[n] | |
| 490 | if type(node) == "number" then error(("Invalid tree state: position %X, frame %d, #bitlen = %d, current entry = %d"):format(pos, i, #bitlen, j)) end
| |
| 491 | end | |
| 492 | local n = bit32.extract(c, 0, 1) | |
| 493 | node[n] = bitlen[j].s | |
| 494 | end | |
| 495 | -- read first byte | |
| 496 | tmp, tmppos, pos = data:byte(pos), 7, pos + 1 | |
| 497 | end | |
| 498 | else readField() end | |
| 499 | for y = 1, height do | |
| 500 | local line = {"", "", "", {}}
| |
| 501 | for x = 1, width do | |
| 502 | if pos + 5 + 1 >= #data then print(i, pos, x, y) error() end | |
| 503 | local n = readField() | |
| 504 | line[1] = line[1] .. string.char(128 + (n % 0x20)) | |
| 505 | line[4][x] = bit32.btest(n, 0x20) | |
| 506 | end | |
| 507 | frame[y] = line | |
| 508 | end | |
| 509 | if customcompress then | |
| 510 | if tmppos == 7 then pos = pos - 1 end | |
| 511 | codetree = {}
| |
| 512 | -- MARK: Huffman tree reconstruction | |
| 513 | -- read bit lengths | |
| 514 | local bitlen = {}
| |
| 515 | for j = 0, 11 do | |
| 516 | bitlen[j*2+1], bitlen[j*2+2] = {s = j*2, l = bit32.rshift(data:byte(pos+j), 4)}, {s = j*2+1, l = bit32.band(data:byte(pos+j), 0x0F)}
| |
| 517 | end | |
| 518 | pos = pos + 12 | |
| 519 | do | |
| 520 | local j = 1 | |
| 521 | while j <= #bitlen do | |
| 522 | if bitlen[j].l == 0 then table.remove(bitlen, j) | |
| 523 | else j = j + 1 end | |
| 524 | end | |
| 525 | end | |
| 526 | if #bitlen == 0 then | |
| 527 | -- screen is solid color | |
| 528 | solidchar = data:byte(pos) | |
| 529 | pos = pos + 1 | |
| 530 | runlen = math.huge | |
| 531 | else | |
| 532 | -- reconstruct codes from bit lengths | |
| 533 | table.sort(bitlen, function(a, b) if a.l == b.l then return a.s < b.s else return a.l < b.l end end) | |
| 534 | bitlen[1].c = 0 | |
| 535 | for j = 2, #bitlen do bitlen[j].c = bit32.lshift(bitlen[j-1].c + 1, bitlen[j].l - bitlen[j-1].l) end | |
| 536 | -- create tree from codes | |
| 537 | for j = 1, #bitlen do | |
| 538 | local c = bitlen[j].c | |
| 539 | local node = codetree | |
| 540 | for k = bitlen[j].l - 1, 1, -1 do | |
| 541 | local n = bit32.extract(c, k, 1) | |
| 542 | if not node[n] then node[n] = {} end
| |
| 543 | node = node[n] | |
| 544 | if type(node) == "number" then error(("Invalid tree state: position %X, frame %d, #bitlen = %d, current entry = %d"):format(pos, i, #bitlen, j)) end
| |
| 545 | end | |
| 546 | local n = bit32.extract(c, 0, 1) | |
| 547 | node[n] = bitlen[j].s | |
| 548 | end | |
| 549 | -- read first byte | |
| 550 | tmp, tmppos, pos = data:byte(pos), 7, pos + 1 | |
| 551 | end | |
| 552 | for y = 1, height do | |
| 553 | local line = frame[y] | |
| 554 | for x = 1, width do | |
| 555 | local c = readField(true) | |
| 556 | line[2] = line[2] .. hexstr:sub(c+1, c+1) | |
| 557 | end | |
| 558 | end | |
| 559 | runlen = nil | |
| 560 | for y = 1, height do | |
| 561 | local line = frame[y] | |
| 562 | for x = 1, width do | |
| 563 | local c = readField(true) | |
| 564 | line[3] = line[3] .. hexstr:sub(c+1, c+1) | |
| 565 | end | |
| 566 | end | |
| 567 | if tmppos == 7 then pos = pos - 1 end | |
| 568 | else | |
| 569 | for y = 1, height do | |
| 570 | local line = frame[y] | |
| 571 | for x = 1, width do | |
| 572 | local c = data:byte(pos) | |
| 573 | pos = pos + 1 | |
| 574 | if line[4][x] then c = bit32.bor(bit32.band(bit32.lshift(c, 4), 0xF0), bit32.rshift(c, 4)) end | |
| 575 | line[2] = line[2] .. hexstr:sub(bit32.band(c, 15)+1, bit32.band(c, 15)+1) | |
| 576 | line[3] = line[3] .. hexstr:sub(bit32.rshift(c, 4)+1, bit32.rshift(c, 4)+1) | |
| 577 | end | |
| 578 | line[4] = nil | |
| 579 | end | |
| 580 | end | |
| 581 | for n = 1, 16 do frame.palette[n], pos = {data:byte(pos) / 255, data:byte(pos+1) / 255, data:byte(pos+2) / 255}, pos + 3 end
| |
| 582 | video[i] = frame | |
| 583 | end | |
| 584 | elseif frameType == 1 and not audio then | |
| 585 | audio = {}
| |
| 586 | if bit32.band(flags, 12) == 0 then | |
| 587 | for i = 1, math.ceil(size / 48000) do | |
| 588 | local data | |
| 589 | if jit then | |
| 590 | data = {}
| |
| 591 | for j = 0, 5 do | |
| 592 | local t = {file.read(math.min(size - (i-1) * 48000 - j * 8000, 8000)):byte(1, -1)}
| |
| 593 | if #t > 0 then for k = 1, #t do data[j*8000+k] = t[k] end end | |
| 594 | end | |
| 595 | else data = {file.read(math.min(size - (i-1) * 48000, 48000)):byte(1, -1)} end
| |
| 596 | for j = 1, #data do data[j] = data[j] - 128 end | |
| 597 | audio[i] = data | |
| 598 | end | |
| 599 | elseif bit32.band(flags, 12) == 4 then | |
| 600 | local decode = dfpwm.make_decoder() | |
| 601 | for i = 1, math.ceil(size / 6000) do | |
| 602 | local data = file.read(math.min(size - (i-1)*6000, 6000)) | |
| 603 | if not data then break end | |
| 604 | audio[i] = decode(data) | |
| 605 | end | |
| 606 | end | |
| 607 | elseif frameType == 8 and not subtitles then | |
| 608 | subtitles = {}
| |
| 609 | for _ = 1, nFrames do | |
| 610 | local start, length, x, y, color, sz = ("<IIHHBxH"):unpack(file.read(16))
| |
| 611 | local text = file.read(sz) | |
| 612 | local sub = {text, hexstr:sub(bit32.band(color, 15)+1, bit32.band(color, 15)+1):rep(#text),
| |
| 613 | hexstr:sub(bit32.rshift(color, 4)+1, bit32.rshift(color, 4)+1):rep(#text), x = x, y = y} | |
| 614 | for n = start, start + length - 1 do | |
| 615 | subtitles[n] = subtitles[n] or {}
| |
| 616 | subtitles[n][#subtitles[n]+1] = sub | |
| 617 | end | |
| 618 | end | |
| 619 | end | |
| 620 | end | |
| 621 | file.close() | |
| 622 | if not video then error("No video stream found") end
| |
| 623 | sleep(0) | |
| 624 | ||
| 625 | local speaker = peripheral.find "speaker" | |
| 626 | term.clear() | |
| 627 | local ok, err = pcall(parallel.waitForAll, function() | |
| 628 | local start = os.epoch "utc" | |
| 629 | for f, image in ipairs(video) do | |
| 630 | for i, v in ipairs(image.palette) do term.setPaletteColor(2^(i-1), table.unpack(v)) end | |
| 631 | for y, r in ipairs(image) do | |
| 632 | term.setCursorPos(1, y) | |
| 633 | term.blit(table.unpack(r)) | |
| 634 | end | |
| 635 | if subtitles and subtitles[f-1] then | |
| 636 | for _, v in ipairs(subtitles[f-1]) do | |
| 637 | term.setCursorPos(v.x, v.y) | |
| 638 | term.blit(table.unpack(v)) | |
| 639 | end | |
| 640 | end | |
| 641 | while os.epoch "utc" < start + (f + 1) / fps * 1000 do sleep(1 / fps) end | |
| 642 | end | |
| 643 | end, function() | |
| 644 | if not speaker or not speaker.playAudio or not audio then return end | |
| 645 | for _, chunk in ipairs(audio) do | |
| 646 | while not speaker.playAudio(chunk) do repeat local ev, sp = os.pullEvent("speaker_audio_empty") until sp == peripheral.getName(speaker) end
| |
| 647 | end | |
| 648 | end) | |
| 649 | for i = 0, 15 do term.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end | |
| 650 | term.setBackgroundColor(colors.black) | |
| 651 | term.setTextColor(colors.white) | |
| 652 | term.setCursorPos(1, 1) | |
| 653 | term.clear() | |
| 654 | if not ok then printError(err) end |