Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local AUDIO_URL = "https://files.catbox.moe/4as46o.raw"
- local AUDIO_FILE = "audio.raw"
- local SCENE_PATH = "scene.bin"
- local SCENE_URL = "https://files.catbox.moe/r5mp9l.bin"
- local WIDTH, HEIGHT = term.getSize()
- local TARGET_FPS = 30
- local PLAYBACK_RATE = 16000
- local SPEAKER_RATE = 48000
- local TOTAL_FRAMES = 1800
- local stopAudio = false
- local frameCountGlobal = 0
- local speaker = peripheral.find("speaker")
- local useAudio = false
- if not speaker then
- print("No speaker attached! Rendering will proceed without audio.")
- else
- useAudio = true
- end
- local function downloadFile(url, filename)
- if not fs.exists(filename) then
- print("Downloading " .. filename .. "...")
- local response = http.get(url, nil, true)
- if not response then
- error("Failed to connect to " .. url)
- end
- local file = fs.open(filename, "wb")
- file.write(response.readAll())
- file.close()
- response.close()
- print(filename .. " downloaded.")
- end
- end
- if useAudio then
- downloadFile(AUDIO_URL, AUDIO_FILE)
- end
- downloadFile(SCENE_URL, SCENE_PATH)
- local function prepareAudio()
- if not useAudio then return nil end
- print("Processing audio...")
- local file = fs.open(AUDIO_FILE, "rb")
- local rawData = file.readAll()
- file.close()
- local samples = {}
- for i = 1, #rawData do
- samples[i] = string.byte(rawData, i) - 128
- end
- if PLAYBACK_RATE ~= SPEAKER_RATE then
- local resampled = {}
- local ratio = PLAYBACK_RATE / SPEAKER_RATE
- local numSamples = math.floor(#samples / ratio)
- for i = 1, numSamples do
- local src = (i - 1) * ratio + 1
- local idx = math.floor(src)
- local frac = src - idx
- local s1 = samples[idx] or 0
- local s2 = samples[idx + 1] or s1
- resampled[i] = math.floor(s1 + (s2 - s1) * frac)
- end
- samples = resampled
- end
- return samples
- end
- local audioSamples = prepareAudio()
- if not bit then bit = {} end
- local function toBits(num)
- local t = {}
- for i = 0, 31 do
- t[i] = num % 2
- num = math.floor(num / 2)
- end
- return t
- end
- local function fromBits(t)
- local num = 0
- for i = 0, 31 do num = num + (t[i] or 0) * 2^i end
- return num
- end
- function bit.band(a, b)
- local ta, tb = toBits(a), toBits(b)
- local t = {}
- for i = 0, 31 do t[i] = ta[i] * tb[i] end
- return fromBits(t)
- end
- function bit.bor(a, b)
- local ta, tb = toBits(a), toBits(b)
- local t = {}
- for i = 0, 31 do t[i] = (ta[i] + tb[i] > 0) and 1 or 0 end
- return fromBits(t)
- end
- function bit.lshift(a, n) return math.floor((a * 2^n) % 2^32) end
- function bit.rshift(a, n) return math.floor(a / 2^n) % 2^32 end
- local ccPalette = {
- colors.white, colors.orange, colors.magenta, colors.lightBlue,
- colors.yellow, colors.lime, colors.pink, colors.gray,
- colors.lightGray, colors.cyan, colors.purple, colors.blue,
- colors.brown, colors.green, colors.red, colors.black
- }
- local originalPalette = {}
- for i = 1, #ccPalette do
- local col = ccPalette[i]
- local r, g, b = term.getPaletteColor(col)
- originalPalette[i] = {r, g, b}
- end
- local backBuffer = {}
- local frontBuffer = {}
- for y = 1, HEIGHT do
- backBuffer[y] = {}
- frontBuffer[y] = {}
- for x = 1, WIDTH do
- backBuffer[y][x] = -1
- frontBuffer[y][x] = -1
- end
- end
- local function bufferSet(x, y, colorIdx)
- if x < 1 or x > WIDTH or y < 1 or y > HEIGHT then return end
- backBuffer[y][x] = colorIdx
- end
- local function flushBuffer(fps)
- for y = 1, HEIGHT do
- local lastColor = -2
- for x = 1, WIDTH do
- local backColor = backBuffer[y][x]
- if backColor ~= frontBuffer[y][x] then
- if backColor ~= lastColor then
- term.setCursorPos(x, y)
- term.setBackgroundColor(ccPalette[backColor + 1])
- lastColor = backColor
- else
- term.setCursorPos(x, y)
- end
- term.write(" ")
- frontBuffer[y][x] = backColor
- end
- end
- end
- term.setCursorPos(1, 1)
- term.setBackgroundColor(colors.black)
- term.setTextColor(colors.white)
- term.write(string.format("%.1f FPS | Frame %d/%d", fps, frameCountGlobal, TOTAL_FRAMES))
- end
- local sceneFile = fs.open(SCENE_PATH, "rb")
- local data = sceneFile.readAll()
- sceneFile.close()
- local pos = 1
- local function safeByte(offset)
- local b = data:byte(offset)
- return b or 0xFD
- end
- local function setCCPalette(ccIndex, r, g, b)
- term.setPaletteColor(ccPalette[ccIndex + 1], b / 255, g / 255, r / 255)
- end
- local function decode(raw)
- raw = bit.band(raw, 0xF)
- local bits = bit.bor(bit.band(bit.lshift(raw, 1), 0xE), bit.band(bit.rshift(raw, 3), 1))
- return bits * 17
- end
- local function fillPolygon(pts, count, colorIdx)
- if count < 3 then return end
- local minY, maxY = HEIGHT + 1, 0
- for i = 1, count do
- local y = pts[i*2]
- if y then
- minY = math.min(minY, y)
- maxY = math.max(maxY, y)
- end
- end
- if minY > HEIGHT or maxY < 1 then return end
- for y = math.max(1, math.ceil(minY)), math.min(HEIGHT, math.floor(maxY)) do
- local nodes = {}
- local nIdx = 1
- local j = count
- for i = 1, count do
- local iy = pts[i*2]; local ix = pts[i*2 - 1]
- local jy = pts[j*2]; local jx = pts[j*2 - 1]
- if ix and iy and jx and jy then
- if (iy < y and jy >= y) or (jy < y and iy >= y) then
- if math.abs(jy - iy) > 0.001 then
- nodes[nIdx] = ix + (y - iy) * (jx - ix) / (jy - iy)
- nIdx = nIdx + 1
- end
- end
- end
- j = i
- end
- if nIdx > 2 then
- table.sort(nodes)
- for i = 1, nIdx - 1, 2 do
- if nodes[i] and nodes[i + 1] then
- local x1 = math.max(1, math.floor(nodes[i] + 0.5))
- local x2 = math.min(WIDTH, math.floor(nodes[i + 1] + 0.5))
- if x1 <= x2 then
- for x = x1, x2 do bufferSet(x, y, colorIdx) end
- end
- end
- end
- end
- end
- end
- local function renderFrame()
- if pos > #data or frameCountGlobal >= TOTAL_FRAMES then
- return true
- end
- frameCountGlobal = frameCountGlobal + 1
- local flags = safeByte(pos)
- pos = pos + 1
- if bit.band(flags, 1) ~= 0 then
- for y = 1, HEIGHT do
- for x = 1, WIDTH do backBuffer[y][x] = 15 end
- end
- end
- if bit.band(flags, 2) ~= 0 then
- local mask = bit.bor(bit.lshift(safeByte(pos), 8), safeByte(pos + 1))
- pos = pos + 2
- for i = 0, 15 do
- if bit.band(mask, bit.lshift(1, 15 - i)) ~= 0 then
- local colorWord = bit.bor(bit.lshift(safeByte(pos), 8), safeByte(pos + 1))
- pos = pos + 2
- local r = decode(bit.band(colorWord, 0xF))
- local g = decode(bit.band(bit.rshift(colorWord, 4), 0xF))
- local b = decode(bit.band(bit.rshift(colorWord, 8), 0xF))
- setCCPalette(i, r, g, b)
- end
- end
- end
- local indexed = bit.band(flags, 4) ~= 0
- local points = {}
- if indexed then
- local vCount = safeByte(pos)
- pos = pos + 1
- for i = 0, vCount - 1 do
- points[i*2 + 1] = safeByte(pos) / 256 * WIDTH
- points[i*2 + 2] = safeByte(pos + 1) / 200 * HEIGHT
- pos = pos + 2
- end
- end
- while pos <= #data do
- local b = safeByte(pos)
- pos = pos + 1
- if b >= 0xFD then
- if b == 0xFE then pos = math.floor((pos - 2) / 65536) * 65536 + 65536 + 1 end
- break
- end
- local numPts = bit.band(b, 0xF)
- local colIdx = bit.rshift(b, 4)
- if numPts < 3 or colIdx > 15 then break end
- local poly = {}
- local validPoints = 0
- for i = 1, numPts do
- if indexed then
- local idx = safeByte(pos)
- pos = pos + 1
- if idx and idx < (#points/2) then
- poly[(i*2) - 1] = points[idx*2 + 1]
- poly[i*2] = points[idx*2 + 2]
- validPoints = validPoints + 1
- end
- else
- poly[(i*2) - 1] = safeByte(pos) / 256 * WIDTH
- poly[i*2] = safeByte(pos + 1) / 200 * HEIGHT
- validPoints = validPoints + 1
- pos = pos + 2
- end
- end
- if validPoints >= 3 then fillPolygon(poly, numPts, colIdx) end
- end
- return false
- end
- term.setBackgroundColor(colors.black)
- term.clear()
- term.setCursorPos(1,1)
- print("CC-NICCC! A ST-NICCC 2000 Port For ComputerCraft Made By nonogamer9!")
- print("This is")
- print("*NOT*")
- print("An OpenComputers Demo")
- sleep(1)
- local function playAudioThread()
- if not useAudio or not speaker or not audioSamples then return end
- local chunkSize = 1024
- local totalSamples = #audioSamples
- local playedSamples = 0
- while playedSamples < totalSamples and not stopAudio do
- local chunk = {}
- local chunkSamples = 0
- for i = 1, chunkSize do
- local idx = playedSamples + i
- if idx > totalSamples or stopAudio then break end
- chunk[i] = audioSamples[idx]
- chunkSamples = i
- end
- if chunkSamples == 0 then break end
- local success = speaker.playAudio(chunk)
- if not success then
- os.pullEvent("speaker_audio_empty")
- end
- playedSamples = playedSamples + chunkSamples
- if frameCountGlobal >= TOTAL_FRAMES then
- stopAudio = true
- break
- end
- end
- end
- local function renderThread()
- local startTime = os.clock()
- local lastFpsDisplay = 0
- local currentFps = 0
- while frameCountGlobal < TOTAL_FRAMES do
- local frameStart = os.clock()
- if renderFrame() then break end
- local now = os.clock()
- local elapsed = now - startTime
- if elapsed - lastFpsDisplay > 0.5 then
- currentFps = frameCountGlobal / elapsed
- lastFpsDisplay = elapsed
- end
- flushBuffer(currentFps)
- local frameTime = os.clock() - frameStart
- local sleepTime = (1 / TARGET_FPS) - frameTime
- if sleepTime > 0 then sleep(sleepTime) end
- end
- end
- local function mainThread()
- renderThread()
- end
- if useAudio then
- parallel.waitForAny(mainThread, playAudioThread)
- else
- mainThread()
- end
- if speaker then
- speaker.stop()
- end
- for i = 1, #ccPalette do
- local col = ccPalette[i]
- local rgb = originalPalette[i]
- term.setPaletteColor(col, rgb[1], rgb[2], rgb[3])
- end
- term.setBackgroundColor(colors.black)
- term.clear()
- term.setCursorPos(1, HEIGHT/2)
- print("Demo complete! Frame: " .. frameCountGlobal)
Add Comment
Please, Sign In to add comment