nonogamer9

OC-NICCC: ST-NICCC 2000 Port For OpenComputers (Version 1.1)

Jul 20th, 2024 (edited)
412
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.82 KB | Software | 0 0
  1. local component = require("component")
  2. local gpu = component.gpu
  3. local internet = require("internet")
  4. local event = require("event")
  5. local unicode = require("unicode")
  6. local computer = require("computer")
  7. local bit32 = require("bit32")
  8.  
  9. local SCREEN_WIDTH, SCREEN_HEIGHT = 160, 50
  10. local ORIGINAL_WIDTH, ORIGINAL_HEIGHT = 256, 200
  11. local scaleX, scaleY = SCREEN_WIDTH / ORIGINAL_WIDTH, SCREEN_HEIGHT / ORIGINAL_HEIGHT
  12.  
  13. local screenBuffer = {}
  14. for y = 1, SCREEN_HEIGHT do
  15.     screenBuffer[y] = {}
  16.     for x = 1, SCREEN_WIDTH do
  17.         screenBuffer[y][x] = 0
  18.     end
  19. end
  20.  
  21. local function isLua53OrHigher()
  22.     return _VERSION and _VERSION >= "Lua 5.3"
  23. end
  24.  
  25. local function stringUnpack(format, str, pos)
  26.     if isLua53OrHigher() then
  27.         return table.unpack(table.pack(string.unpack(format, str, pos)))
  28.     else
  29.         error("String unpacking not supported in this Lua version")
  30.     end
  31. end
  32.  
  33. local function yieldIfNeeded()
  34.     if computer.freeMemory() < 1024 then
  35.         os.sleep(0)
  36.     end
  37. end
  38.  
  39. local function hasMagicalMemory()
  40.     return computer.totalMemory() >= 2097152
  41. end
  42.  
  43. local function decodeBMPMode2(imageData)
  44.     local width = stringUnpack("<I4", imageData, 19)
  45.     local height = stringUnpack("<I4", imageData, 23)
  46.     local bitsPerPixel = stringUnpack("<I2", imageData, 29)
  47.     local compression = stringUnpack("<I4", imageData, 31)
  48.     local dataOffset = stringUnpack("<I4", imageData, 11)
  49.  
  50.     if bitsPerPixel ~= 24 or compression ~= 0 then
  51.         error("Unsupported BMP format. Only 24-bit uncompressed BMPs are supported.")
  52.     end
  53.  
  54.     local screenWidth, screenHeight = gpu.getResolution()
  55.     local targetWidth = screenWidth * 2
  56.     local targetHeight = screenHeight * 4
  57.  
  58.     local decodedImage = {}
  59.     local rowSize = math.floor((bitsPerPixel * width + 31) / 32) * 4
  60.     local xRatio = width / targetWidth
  61.     local yRatio = height / targetHeight
  62.  
  63.     for y = 0, targetHeight - 1 do
  64.         for x = 0, targetWidth - 1 do
  65.             local srcX = math.floor(x * xRatio)
  66.             local srcY = math.floor(y * yRatio)
  67.             local rowStart = dataOffset + (height - 1 - srcY) * rowSize
  68.             local pixelStart = rowStart + srcX * 3
  69.             local b, g, r = stringUnpack("BBB", imageData, pixelStart + 1)
  70.             table.insert(decodedImage, {r, g, b})
  71.             yieldIfNeeded()
  72.         end
  73.     end
  74.  
  75.     return decodedImage, targetWidth, targetHeight
  76. end
  77.  
  78. local function getBrailleCharacter(pixels)
  79.     local brailleBase = 0x2800
  80.     local pattern = 0
  81.     local dotWeights = {1, 2, 4, 8, 16, 32, 64, 128}
  82.  
  83.     local function adjustBrightness(brightness)
  84.         return math.pow(brightness / 255, 0.5) * 255
  85.     end
  86.  
  87.     local thresholds = {}
  88.     for i = 1, 8 do
  89.         local brightness = (pixels[i][1] + pixels[i][2] + pixels[i][3]) / 3
  90.         thresholds[i] = adjustBrightness(brightness)
  91.     end
  92.  
  93.     local avgThreshold = 0
  94.     for i = 1, 8 do
  95.         avgThreshold = avgThreshold + thresholds[i]
  96.     end
  97.     avgThreshold = avgThreshold / 8
  98.  
  99.     for i = 1, 8 do
  100.         if thresholds[i] > avgThreshold then
  101.             pattern = bit32.bor(pattern, dotWeights[i])
  102.         end
  103.     end
  104.  
  105.     return unicode.char(brailleBase + pattern)
  106. end
  107.  
  108. local function rgbToColor(r, g, b)
  109.     return bit32.bor(bit32.lshift(r, 16), bit32.lshift(g, 8), b)
  110. end
  111.  
  112. local function averageColor(colors)
  113.     local r, g, b, count = 0, 0, 0, #colors
  114.     for _, color in ipairs(colors) do
  115.         r = r + color[1]
  116.         g = g + color[2]
  117.         b = b + color[3]
  118.     end
  119.     if count > 0 then
  120.         return {math.floor(r / count), math.floor(g / count), math.floor(b / count)}
  121.     else
  122.         return {0, 0, 0}
  123.     end
  124. end
  125.  
  126. local function blendColors(c1, c2, factor)
  127.     return {
  128.         math.floor(c1[1] * (1 - factor) + c2[1] * factor),
  129.         math.floor(c1[2] * (1 - factor) + c2[2] * factor),
  130.         math.floor(c1[3] * (1 - factor) + c2[3] * factor)
  131.     }
  132. end
  133.  
  134. local function displayImageMode2(decodedImage, x, y, width, height)
  135.     for py = 0, height / 4 - 1 do
  136.         for px = 0, width / 2 - 1 do
  137.             local pixels = {}
  138.             local colors = {}
  139.            
  140.             for dy = 0, 3 do
  141.                 for dx = 0, 1 do
  142.                     local ix = px * 2 + dx
  143.                     local iy = py * 4 + dy
  144.                     local index = iy * width + ix + 1
  145.                     local pixel = decodedImage[index]
  146.                     pixels[dy * 2 + dx + 1] = pixel
  147.                     table.insert(colors, pixel)
  148.                 end
  149.             end
  150.  
  151.             local char = getBrailleCharacter(pixels)
  152.             local fgColor = averageColor(colors)
  153.            
  154.             local darkestColor = colors[1]
  155.             for i = 2, #colors do
  156.                 if colors[i][1] + colors[i][2] + colors[i][3] < darkestColor[1] + darkestColor[2] + darkestColor[3] then
  157.                     darkestColor = colors[i]
  158.                 end
  159.             end
  160.             local avgColor = averageColor(colors)
  161.             local bgColor = blendColors(darkestColor, avgColor, 0.3)
  162.            
  163.             gpu.setForeground(rgbToColor(fgColor[1], fgColor[2], fgColor[3]))
  164.             gpu.setBackground(rgbToColor(bgColor[1], bgColor[2], bgColor[3]))
  165.             gpu.set(x + px, y + py, char)
  166.         end
  167.         yieldIfNeeded()
  168.     end
  169. end
  170.  
  171. local function decodeBMPMode1(imageData)
  172.   local width = stringUnpack("<I4", imageData, 19)
  173.   local height = stringUnpack("<I4", imageData, 23)
  174.   local bitsPerPixel = stringUnpack("<I2", imageData, 29)
  175.   local compression = stringUnpack("<I4", imageData, 31)
  176.   local dataOffset = stringUnpack("<I4", imageData, 11)
  177.  
  178.   if bitsPerPixel ~= 24 or compression ~= 0 then
  179.     error("Unsupported BMP format. Only 24-bit uncompressed BMPs are supported.")
  180.   end
  181.  
  182.   local decodedImage = {}
  183.   local rowSize = math.floor((bitsPerPixel * width + 31) / 32) * 4
  184.   local xRatio = width / 160
  185.   local yRatio = height / 50
  186.  
  187.   for y = 0, 49 do
  188.     for x = 0, 159 do
  189.       local srcX = math.floor(x * xRatio)
  190.       local srcY = math.floor(y * yRatio)
  191.       local rowStart = dataOffset + (height - 1 - srcY) * rowSize
  192.       local pixelStart = rowStart + srcX * 3
  193.       local b, g, r = stringUnpack("BBB", imageData, pixelStart + 1)
  194.       table.insert(decodedImage, {r, g, b})
  195.       yieldIfNeeded()
  196.     end
  197.   end
  198.  
  199.   return decodedImage, 160, 50
  200. end
  201.  
  202. local function decodeTIImageMode1(imageData)
  203.   local width = stringUnpack("<I4", imageData, 5)
  204.   local height = stringUnpack("<I4", imageData, 9)
  205.   local pixelData = imageData:sub(21)
  206.  
  207.   local decodedImage = {}
  208.   for i = 1, #pixelData, 2 do
  209.     local pixel = stringUnpack("<I2", pixelData, i)
  210.     local r = bit32.lshift(bit32.band(pixel, 0x7C00), 10) * 8
  211.     local g = bit32.lshift(bit32.band(pixel, 0x03E0), 5) * 8
  212.     local b = bit32.band(pixel, 0x001F) * 8
  213.     table.insert(decodedImage, {r, g, b})
  214.     yieldIfNeeded()
  215.   end
  216.  
  217.   return decodedImage, width, height
  218. end
  219.  
  220. local function decodeImageMode1(imageData)
  221.   if not imageData or imageData == "" then
  222.     error("No image data received")
  223.   end
  224.  
  225.   local format = imageData:sub(1, 2)
  226.   if format == "BM" then
  227.     return decodeBMPMode1(imageData)
  228.   elseif format == "TI" then
  229.     return decodeTIImageMode1(imageData)
  230.   else
  231.     error("Unsupported image format")
  232.   end
  233. end
  234.  
  235. local function packColor(r, g, b)
  236.   return bit32.bor(bit32.lshift(r, 16), bit32.lshift(g, 8), b)
  237. end
  238.  
  239. local function displayImageMode1(decodedImage, x, y, width, height)
  240.   for i = 1, #decodedImage do
  241.     local pixel = decodedImage[i]
  242.     local px = (i - 1) % width + x
  243.     local py = math.floor((i - 1) / width) + y
  244.     local color = packColor(pixel[1], pixel[2], pixel[3])
  245.     gpu.setBackground(color)
  246.     gpu.set(px, py, " ")
  247.     yieldIfNeeded()
  248.   end
  249. end
  250.  
  251. local function downloadAndDisplayLoadingScreen()
  252.     if not isLua53OrHigher() then
  253.         print("Loading screen disabled for Lua versions below 5.3")
  254.         return
  255.     end
  256.  
  257.     print("Downloading loading screen image...")
  258.     local url = "https://files.catbox.moe/qtxc7b.bmp"
  259.     local response = internet.request(url)
  260.     local imageData = ""
  261.     for chunk in response do
  262.         imageData = imageData .. chunk
  263.     end
  264.     print("Image downloaded. Decoding...")
  265.  
  266.     local decodedImage, width, height
  267.     if hasMagicalMemory() then
  268.         decodedImage, width, height = decodeBMPMode2(imageData)
  269.         print("Image decoded. Displaying in Mode 2...")
  270.         gpu.setResolution(160, 50)
  271.         displayImageMode2(decodedImage, 1, 1, width, height)
  272.     else
  273.         decodedImage, width, height = decodeImageMode1(imageData)
  274.         print("Image decoded. Displaying in Mode 1...")
  275.         gpu.setResolution(160, 50)
  276.         displayImageMode1(decodedImage, 1, 1, width, height)
  277.     end
  278.    
  279.     gpu.setForeground(0xFFFFFF)
  280.     print("OC-NICCC! A ST-NICCC 2000 Port For OpenComputers Made By nonogamer9!")
  281.     print("This is")
  282.     print("*NOT*")
  283.     print("A ComputerCraft Demo")
  284. end
  285.  
  286. local function downloadFile(url, path)
  287.     if not component.filesystem.exists(path) then
  288.         print("Downloading " .. url .. " to " .. path)
  289.         local result = ""
  290.         for chunk in internet.request(url) do
  291.             result = result .. chunk
  292.         end
  293.         local file = io.open(path, "wb")
  294.         file:write(result)
  295.         file:close()
  296.         print("Download complete")
  297.     end
  298. end
  299.  
  300. local function decode(raw)
  301.     raw = bit32.band(raw, 0xF)
  302.     local bits = bit32.band(bit32.rshift(raw, 3), 1)
  303.     bits = bit32.bor(bits, bit32.band(bit32.lshift(raw, 1), 0xE))
  304.     return bits * 17
  305. end
  306.  
  307. local function readByte(file)
  308.     return string.byte(file:read(1))
  309. end
  310.  
  311. local function readWord(file)
  312.     local b1, b2 = readByte(file), readByte(file)
  313.     return bit32.bor(bit32.lshift(b1, 8), b2)
  314. end
  315.  
  316. local function readColor(file)
  317.     local color = readWord(file)
  318.     local b = decode(bit32.band(color, 0xF))
  319.     local g = decode(bit32.band(bit32.rshift(color, 4), 0xF))
  320.     local r = decode(bit32.band(bit32.rshift(color, 8), 0xF))
  321.     return bit32.bor(bit32.lshift(r, 16), bit32.lshift(g, 8), b)
  322. end
  323.  
  324. local function scalePoint(x, y)
  325.     return math.floor(x * scaleX) + 1, math.floor(y * scaleY) + 1
  326. end
  327.  
  328. local function setPixel(x, y, color)
  329.     if x >= 1 and x <= SCREEN_WIDTH and y >= 1 and y <= SCREEN_HEIGHT then
  330.         screenBuffer[y][x] = color
  331.     end
  332. end
  333.  
  334. local function fillPolygon(points, color)
  335.     if #points < 3 then return end
  336.  
  337.     local minY, maxY = math.huge, -math.huge
  338.     local edges = {}
  339.  
  340.     for i = 1, #points do
  341.         local j = i % #points + 1
  342.         local p1, p2 = points[i], points[j]
  343.         local x1, y1 = scalePoint(p1.x, p1.y)
  344.         local x2, y2 = scalePoint(p2.x, p2.y)
  345.         if y1 ~= y2 then
  346.             if y1 > y2 then x1, y1, x2, y2 = x2, y2, x1, y1 end
  347.             local dx = (x2 - x1) / (y2 - y1)
  348.             table.insert(edges, {y = y1, x = x1, dx = dx, ymax = y2})
  349.             minY = math.min(minY, y1)
  350.             maxY = math.max(maxY, y2)
  351.         end
  352.     end
  353.  
  354.     for y = math.max(1, math.floor(minY)), math.min(SCREEN_HEIGHT, math.ceil(maxY)) do
  355.         local active = {}
  356.         for _, edge in ipairs(edges) do
  357.             if y >= edge.y and y < edge.ymax then
  358.                 table.insert(active, edge.x)
  359.                 edge.x = edge.x + edge.dx
  360.             end
  361.         end
  362.         table.sort(active)
  363.         for i = 1, #active - 1, 2 do
  364.             local x1, x2 = math.ceil(active[i]), math.floor(active[i+1])
  365.             for x = math.max(1, x1), math.min(SCREEN_WIDTH, x2) do
  366.                 setPixel(x, y, color)
  367.             end
  368.         end
  369.     end
  370. end
  371.  
  372. local function renderBuffer()
  373.     for y = 1, SCREEN_HEIGHT do
  374.         local lastColor = screenBuffer[y][1]
  375.         local startX = 1
  376.         for x = 2, SCREEN_WIDTH do
  377.             if screenBuffer[y][x] ~= lastColor then
  378.                 gpu.setBackground(lastColor)
  379.                 gpu.fill(startX, y, x - startX, 1, " ")
  380.                 lastColor = screenBuffer[y][x]
  381.                 startX = x
  382.             end
  383.         end
  384.         gpu.setBackground(lastColor)
  385.         gpu.fill(startX, y, SCREEN_WIDTH - startX + 1, 1, " ")
  386.     end
  387. end
  388.  
  389. local function main()
  390.     if isLua53OrHigher() then
  391.         downloadAndDisplayLoadingScreen()
  392.     else
  393.         print("OC-NICCC! A ST-NICCC 2000 Port For OpenComputers Made By nonogamer9!")
  394.         print("This is")
  395.         print("*NOT*")
  396.         print("A ComputerCraft Demo")
  397.         print("Loading screen disabled for Lua versions below 5.3")
  398.     end
  399.     downloadFile("https://raw.githubusercontent.com/pulkomandy/beniccc/master/scene1.bin", "scene1.bin")
  400.  
  401.     local file = io.open("scene1.bin", "rb")
  402.     if not file then error("Could not open scene1.bin file") end
  403.  
  404.     gpu.setResolution(SCREEN_WIDTH, SCREEN_HEIGHT)
  405.  
  406.     local colors = {}
  407.     for i = 1, 16 do colors[i] = 0x000000 end
  408.  
  409.     local running = true
  410.  
  411.     while running do
  412.         local flags = readByte(file)
  413.         local alignStream = false
  414.  
  415.         if bit32.band(flags, 2) ~= 0 then
  416.             local colorMask = readWord(file)
  417.             for i = 1, 16 do
  418.                 if bit32.band(colorMask, bit32.lshift(1, 16 - i)) ~= 0 then
  419.                     colors[i] = readColor(file)
  420.                     gpu.setPaletteColor(i - 1, colors[i])
  421.                 end
  422.             end
  423.         end
  424.  
  425.         if bit32.band(flags, 1) ~= 0 then
  426.             for y = 1, SCREEN_HEIGHT do
  427.                 for x = 1, SCREEN_WIDTH do
  428.                     screenBuffer[y][x] = colors[1]
  429.                 end
  430.             end
  431.         end
  432.  
  433.         if bit32.band(flags, 4) ~= 0 then
  434.             local vertexCount = readByte(file)
  435.             local vertices = {}
  436.             for i = 1, vertexCount do vertices[i] = {x=readByte(file), y=readByte(file)} end
  437.  
  438.             while true do
  439.                 local bits = readByte(file)
  440.                 if bits == 0xFF then
  441.                     alignStream = false
  442.                     break
  443.                 elseif bits == 0xFE then
  444.                     alignStream = true
  445.                     break
  446.                 elseif bits == 0xFD then
  447.                     running = false
  448.                     break
  449.                 else
  450.                     local colorIndex = bit32.rshift(bits, 4)
  451.                     local color = colors[colorIndex + 1]
  452.                     local polygon = {}
  453.                     for i = 1, bit32.band(bits, 0xF) do
  454.                         local index = readByte(file) + 1
  455.                         table.insert(polygon, vertices[index])
  456.                     end
  457.                     fillPolygon(polygon, color)
  458.                 end
  459.             end
  460.         else
  461.             while true do
  462.                 local bits = readByte(file)
  463.                 if bits == 0xFF then
  464.                     alignStream = false
  465.                     break
  466.                 elseif bits == 0xFE then
  467.                     alignStream = true
  468.                     break
  469.                 elseif bits == 0xFD then
  470.                     running = false
  471.                     break
  472.                 else
  473.                     local colorIndex = bit32.rshift(bits, 4)
  474.                     local color = colors[colorIndex + 1]
  475.                     local polygon = {}
  476.                     for i = 1, bit32.band(bits, 0xF) do
  477.                         table.insert(polygon, {x = readByte(file), y = readByte(file)})
  478.                     end
  479.                     fillPolygon(polygon, color)
  480.                 end
  481.             end
  482.         end
  483.  
  484.         renderBuffer()
  485.  
  486.         if alignStream then
  487.             local position = file:seek()
  488.             position = bit32.bor(bit32.band(position, bit32.bnot(0xFFFF)), 0x10000)
  489.             file:seek("set", position)
  490.         end
  491.  
  492.         local _, _, _, code = event.pull(0, "key_down")
  493.         if code == 16 then
  494.             running = false
  495.         end
  496.     end
  497.  
  498.     file:close()
  499. end
  500.  
  501. main()
  502.  
Add Comment
Please, Sign In to add comment