From 56f03d0dbea76e9707a7df2d3d7ab4c5f709a436 Mon Sep 17 00:00:00 2001 From: Igor Timofeev Date: Sat, 17 Mar 2018 00:00:38 +0300 Subject: [PATCH] =?UTF-8?q?2=D0=BA-=D0=BF=D0=BE=D0=BC=D0=BE=D0=B9=D0=BA?= =?UTF-8?q?=D1=83=20=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=20=D0=BD=D0=B0=20=D1=85?= =?UTF-8?q?=D1=83=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CompressedPaletteGenerator/Colors.lua | 258 - .../CompressedPaletteGenerator/Generator.lua | 86 - .../CompressedPaletteGenerator/cyka.lua | 18 - Applications/Exam/school.lua | 163 - Applications/Exam/schoolQuestions.lua | 56 - Applications/Home.lua | 262 - Applications/Matrix/About/English.txt | 1 - Applications/Matrix/About/Russian.txt | 1 - Applications/Matrix/Icon.pic | Bin 293 -> 0 bytes Applications/Matrix/Matrix.lua | 60 - Applications/MineOSPacker/MineOS.pkg | 33539 ---------------- Applications/MineOSPacker/MineOSPacker.lua | 93 - Applications/Motd/English.txt | 28 - Applications/Motd/Russian.txt | 11 - Applications/Motd/motd.lua | 55 - Applications/Pastebin/Icon.pic | Bin 204 -> 0 bytes Applications/Pastebin/Pastebin.lua | 620 - Applications/RCON/Icon.pic | Bin 311 -> 0 bytes Applications/RCON/Icon.png | 4 - Applications/RCON/RCON.lua | 135 - Applications/RemoteDesktop/RemoteDesktop.lua | 87 - Applications/Shop/Icon.pic | Bin 121 -> 0 bytes Applications/Shop/Shop.lua | 1073 - Applications/Snake/Icon.pic | Bin 135 -> 0 bytes Applications/Snake/Snake.lua | 132 - Applications/Sudoku/Sudoku.lua | 249 - Applications/Sudoku/testSudokuFile.txt | 9 - Applications/Sudoku/testSudokuFile2.txt | 9 - Applications/TimerSpam.lua | 45 - Applications/UpdateCenter/Icon.png | 4 - Applications/UpdateCenter/UpdateCenter.lua | 1 - Applications/event.lua | 10 - Applications/fileSender.lua | 76 - Applications/get.lua | 175 - Applications/getecs.lua | 2 - Applications/github.lua | 104 - Applications/ls.lua | 109 - Applications/memory.lua | 40 - Applications/meowGenerator.lua | 149 - Applications/pastebin.lua | 48 - Applications/profile | 29 - Applications/reload.lua | 48 - Applications/wget.lua | 25 - Applications/zip.lua | 15 - 44 files changed, 37829 deletions(-) delete mode 100644 Applications/CompressedPaletteGenerator/Colors.lua delete mode 100644 Applications/CompressedPaletteGenerator/Generator.lua delete mode 100644 Applications/CompressedPaletteGenerator/cyka.lua delete mode 100644 Applications/Exam/school.lua delete mode 100644 Applications/Exam/schoolQuestions.lua delete mode 100644 Applications/Home.lua delete mode 100644 Applications/Matrix/About/English.txt delete mode 100644 Applications/Matrix/About/Russian.txt delete mode 100644 Applications/Matrix/Icon.pic delete mode 100644 Applications/Matrix/Matrix.lua delete mode 100644 Applications/MineOSPacker/MineOS.pkg delete mode 100644 Applications/MineOSPacker/MineOSPacker.lua delete mode 100755 Applications/Motd/English.txt delete mode 100755 Applications/Motd/Russian.txt delete mode 100755 Applications/Motd/motd.lua delete mode 100644 Applications/Pastebin/Icon.pic delete mode 100644 Applications/Pastebin/Pastebin.lua delete mode 100644 Applications/RCON/Icon.pic delete mode 100644 Applications/RCON/Icon.png delete mode 100644 Applications/RCON/RCON.lua delete mode 100644 Applications/RemoteDesktop/RemoteDesktop.lua delete mode 100644 Applications/Shop/Icon.pic delete mode 100644 Applications/Shop/Shop.lua delete mode 100644 Applications/Snake/Icon.pic delete mode 100644 Applications/Snake/Snake.lua delete mode 100644 Applications/Sudoku/Sudoku.lua delete mode 100644 Applications/Sudoku/testSudokuFile.txt delete mode 100644 Applications/Sudoku/testSudokuFile2.txt delete mode 100644 Applications/TimerSpam.lua delete mode 100644 Applications/UpdateCenter/Icon.png delete mode 100644 Applications/UpdateCenter/UpdateCenter.lua delete mode 100644 Applications/event.lua delete mode 100644 Applications/fileSender.lua delete mode 100644 Applications/get.lua delete mode 100644 Applications/getecs.lua delete mode 100644 Applications/github.lua delete mode 100644 Applications/ls.lua delete mode 100644 Applications/memory.lua delete mode 100644 Applications/meowGenerator.lua delete mode 100644 Applications/pastebin.lua delete mode 100755 Applications/profile delete mode 100644 Applications/reload.lua delete mode 100644 Applications/wget.lua delete mode 100644 Applications/zip.lua diff --git a/Applications/CompressedPaletteGenerator/Colors.lua b/Applications/CompressedPaletteGenerator/Colors.lua deleted file mode 100644 index 28b261cc..00000000 --- a/Applications/CompressedPaletteGenerator/Colors.lua +++ /dev/null @@ -1,258 +0,0 @@ -return { - 0x000000, - 0x000040, - 0x000080, - 0x0000BF, - 0x0000FF, - 0x002400, - 0x002440, - 0x002480, - 0x0024BF, - 0x0024FF, - 0x004900, - 0x004940, - 0x004980, - 0x0049BF, - 0x0049FF, - 0x006D00, - 0x006D40, - 0x006D80, - 0x006DBF, - 0x006DFF, - 0x009200, - 0x009240, - 0x009280, - 0x0092BF, - 0x0092FF, - 0x00B600, - 0x00B640, - 0x00B680, - 0x00B6BF, - 0x00B6FF, - 0x00DB00, - 0x00DB40, - 0x00DB80, - 0x00DBBF, - 0x00DBFF, - 0x00FF00, - 0x00FF40, - 0x00FF80, - 0x00FFBF, - 0x00FFFF, - 0x0F0F0F, - 0x1E1E1E, - 0x2D2D2D, - 0x330000, - 0x330040, - 0x330080, - 0x3300BF, - 0x3300FF, - 0x332400, - 0x332440, - 0x332480, - 0x3324BF, - 0x3324FF, - 0x334900, - 0x334940, - 0x334980, - 0x3349BF, - 0x3349FF, - 0x336D00, - 0x336D40, - 0x336D80, - 0x336DBF, - 0x336DFF, - 0x339200, - 0x339240, - 0x339280, - 0x3392BF, - 0x3392FF, - 0x33B600, - 0x33B640, - 0x33B680, - 0x33B6BF, - 0x33B6FF, - 0x33DB00, - 0x33DB40, - 0x33DB80, - 0x33DBBF, - 0x33DBFF, - 0x33FF00, - 0x33FF40, - 0x33FF80, - 0x33FFBF, - 0x33FFFF, - 0x3C3C3C, - 0x4B4B4B, - 0x5A5A5A, - 0x660000, - 0x660040, - 0x660080, - 0x6600BF, - 0x6600FF, - 0x662400, - 0x662440, - 0x662480, - 0x6624BF, - 0x6624FF, - 0x664900, - 0x664940, - 0x664980, - 0x6649BF, - 0x6649FF, - 0x666D00, - 0x666D40, - 0x666D80, - 0x666DBF, - 0x666DFF, - 0x669200, - 0x669240, - 0x669280, - 0x6692BF, - 0x6692FF, - 0x66B600, - 0x66B640, - 0x66B680, - 0x66B6BF, - 0x66B6FF, - 0x66DB00, - 0x66DB40, - 0x66DB80, - 0x66DBBF, - 0x66DBFF, - 0x66FF00, - 0x66FF40, - 0x66FF80, - 0x66FFBF, - 0x66FFFF, - 0x696969, - 0x787878, - 0x878787, - 0x969696, - 0x990000, - 0x990040, - 0x990080, - 0x9900BF, - 0x9900FF, - 0x992400, - 0x992440, - 0x992480, - 0x9924BF, - 0x9924FF, - 0x994900, - 0x994940, - 0x994980, - 0x9949BF, - 0x9949FF, - 0x996D00, - 0x996D40, - 0x996D80, - 0x996DBF, - 0x996DFF, - 0x999200, - 0x999240, - 0x999280, - 0x9992BF, - 0x9992FF, - 0x99B600, - 0x99B640, - 0x99B680, - 0x99B6BF, - 0x99B6FF, - 0x99DB00, - 0x99DB40, - 0x99DB80, - 0x99DBBF, - 0x99DBFF, - 0x99FF00, - 0x99FF40, - 0x99FF80, - 0x99FFBF, - 0x99FFFF, - 0xA5A5A5, - 0xB4B4B4, - 0xC3C3C3, - 0xCC0000, - 0xCC0040, - 0xCC0080, - 0xCC00BF, - 0xCC00FF, - 0xCC2400, - 0xCC2440, - 0xCC2480, - 0xCC24BF, - 0xCC24FF, - 0xCC4900, - 0xCC4940, - 0xCC4980, - 0xCC49BF, - 0xCC49FF, - 0xCC6D00, - 0xCC6D40, - 0xCC6D80, - 0xCC6DBF, - 0xCC6DFF, - 0xCC9200, - 0xCC9240, - 0xCC9280, - 0xCC92BF, - 0xCC92FF, - 0xCCB600, - 0xCCB640, - 0xCCB680, - 0xCCB6BF, - 0xCCB6FF, - 0xCCDB00, - 0xCCDB40, - 0xCCDB80, - 0xCCDBBF, - 0xCCDBFF, - 0xCCFF00, - 0xCCFF40, - 0xCCFF80, - 0xCCFFBF, - 0xCCFFFF, - 0xD2D2D2, - 0xE1E1E1, - 0xF0F0F0, - 0xFF0000, - 0xFF0040, - 0xFF0080, - 0xFF00BF, - 0xFF00FF, - 0xFF2400, - 0xFF2440, - 0xFF2480, - 0xFF24BF, - 0xFF24FF, - 0xFF4900, - 0xFF4940, - 0xFF4980, - 0xFF49BF, - 0xFF49FF, - 0xFF6D00, - 0xFF6D40, - 0xFF6D80, - 0xFF6DBF, - 0xFF6DFF, - 0xFF9200, - 0xFF9240, - 0xFF9280, - 0xFF92BF, - 0xFF92FF, - 0xFFB600, - 0xFFB640, - 0xFFB680, - 0xFFB6BF, - 0xFFB6FF, - 0xFFDB00, - 0xFFDB40, - 0xFFDB80, - 0xFFDBBF, - 0xFFDBFF, - 0xFFFF00, - 0xFFFF40, - 0xFFFF80, - 0xFFFFBF, - 0xFFFFFF, -} diff --git a/Applications/CompressedPaletteGenerator/Generator.lua b/Applications/CompressedPaletteGenerator/Generator.lua deleted file mode 100644 index c0e317c4..00000000 --- a/Applications/CompressedPaletteGenerator/Generator.lua +++ /dev/null @@ -1,86 +0,0 @@ - - -local colors = { } -local color = 0x000000 -local gray = 0x000000 - -local xPos, yPos = 2, 2 - -local function printColor(color) - gpu.setForeground(color) - gpu.set(xPos, yPos, ecs.HEXtoString(color, 6, true)) - yPos = yPos + 1 - if yPos > 48 then yPos = 2; xPos = xPos + 10 end -end - -local function addColor(value) - table.insert(colors, color) - printColor(color) - color = color + value -end - -local function add4() - addColor(0x40) - addColor(0x40) - addColor(0x3F) - addColor(0x40) - addColor(0x40) - color = bit32.band(color, 0xffff00) -end - -local function createNext4(value) - add4() - color = color + value - yPos = yPos + 1 -end - -local function add8() - createNext4(0x2300) - createNext4(0x2400) - createNext4(0x2300) - createNext4(0x2400) - createNext4(0x2300) - createNext4(0x2400) - createNext4(0x2300) - createNext4(0x2400) -end - -local function newGray() - gray = gray + 0x0F0F0F - printColor(gray) - table.insert(colors, gray) -end - -local function add8WithGray() - add8() - for i = 1, 3 do - newGray() - end - color = bit32.band(color, 0xff0000) - color = color + 0x320000 -end - -ecs.prepareToExit() -ecs.error("Создаем таблицу") -for i = 1, 6 do - add8WithGray() -end - -for i = 1, 3 do - table.remove(colors, #colors) -end - -ecs.wait() -ecs.prepareToExit() -ecs.error("Размер массива = " .. #colors .. ", рисуем таблицу") -xPos, yPos = 2, 2 -local file = io.open("colors.lua", "w") -file:write("return {\n") -for i = 1, #colors do - printColor(colors[i]) - file:write(" [" .. ecs.HEXtoString(i - 1, 2, true) .. "] = " .. ecs.HEXtoString(colors[i], 6, true) .. ",\n") -end -file:write("}\n") -file:close() - - diff --git a/Applications/CompressedPaletteGenerator/cyka.lua b/Applications/CompressedPaletteGenerator/cyka.lua deleted file mode 100644 index eb77ebbf..00000000 --- a/Applications/CompressedPaletteGenerator/cyka.lua +++ /dev/null @@ -1,18 +0,0 @@ -{ - 0x000000, 0x000040, 0x000080, 0x0000BF, 0x0000FF, 0x002400, 0x002440, 0x002480, 0x0024BF, 0x0024FF, 0x004900, 0x004940, 0x004980, 0x0049BF, 0x0049FF, 0x006D00, - 0x006D40, 0x006D80, 0x006DBF, 0x006DFF, 0x009200, 0x009240, 0x009280, 0x0092BF, 0x0092FF, 0x00B600, 0x00B640, 0x00B680, 0x00B6BF, 0x00B6FF, 0x00DB00, 0x00DB40, - 0x00DB80, 0x00DBBF, 0x00DBFF, 0x00FF00, 0x00FF40, 0x00FF80, 0x00FFBF, 0x00FFFF, 0x0F0F0F, 0x1E1E1E, 0x2D2D2D, 0x330000, 0x330040, 0x330080, 0x3300BF, 0x3300FF, - 0x332400, 0x332440, 0x332480, 0x3324BF, 0x3324FF, 0x334900, 0x334940, 0x334980, 0x3349BF, 0x3349FF, 0x336D00, 0x336D40, 0x336D80, 0x336DBF, 0x336DFF, 0x339200, - 0x339240, 0x339280, 0x3392BF, 0x3392FF, 0x33B600, 0x33B640, 0x33B680, 0x33B6BF, 0x33B6FF, 0x33DB00, 0x33DB40, 0x33DB80, 0x33DBBF, 0x33DBFF, 0x33FF00, 0x33FF40, - 0x33FF80, 0x33FFBF, 0x33FFFF, 0x3C3C3C, 0x4B4B4B, 0x5A5A5A, 0x660000, 0x660040, 0x660080, 0x6600BF, 0x6600FF, 0x662400, 0x662440, 0x662480, 0x6624BF, 0x6624FF, - 0x664900, 0x664940, 0x664980, 0x6649BF, 0x6649FF, 0x666D00, 0x666D40, 0x666D80, 0x666DBF, 0x666DFF, 0x669200, 0x669240, 0x669280, 0x6692BF, 0x6692FF, 0x66B600, - 0x66B640, 0x66B680, 0x66B6BF, 0x66B6FF, 0x66DB00, 0x66DB40, 0x66DB80, 0x66DBBF, 0x66DBFF, 0x66FF00, 0x66FF40, 0x66FF80, 0x66FFBF, 0x66FFFF, 0x696969, 0x787878, - 0x878787, 0x969696, 0x990000, 0x990040, 0x990080, 0x9900BF, 0x9900FF, 0x992400, 0x992440, 0x992480, 0x9924BF, 0x9924FF, 0x994900, 0x994940, 0x994980, 0x9949BF, - 0x9949FF, 0x996D00, 0x996D40, 0x996D80, 0x996DBF, 0x996DFF, 0x999200, 0x999240, 0x999280, 0x9992BF, 0x9992FF, 0x99B600, 0x99B640, 0x99B680, 0x99B6BF, 0x99B6FF, - 0x99DB00, 0x99DB40, 0x99DB80, 0x99DBBF, 0x99DBFF, 0x99FF00, 0x99FF40, 0x99FF80, 0x99FFBF, 0x99FFFF, 0xA5A5A5, 0xB4B4B4, 0xC3C3C3, 0xCC0000, 0xCC0040, 0xCC0080, - 0xCC00BF, 0xCC00FF, 0xCC2400, 0xCC2440, 0xCC2480, 0xCC24BF, 0xCC24FF, 0xCC4900, 0xCC4940, 0xCC4980, 0xCC49BF, 0xCC49FF, 0xCC6D00, 0xCC6D40, 0xCC6D80, 0xCC6DBF, - 0xCC6DFF, 0xCC9200, 0xCC9240, 0xCC9280, 0xCC92BF, 0xCC92FF, 0xCCB600, 0xCCB640, 0xCCB680, 0xCCB6BF, 0xCCB6FF, 0xCCDB00, 0xCCDB40, 0xCCDB80, 0xCCDBBF, 0xCCDBFF, - 0xCCFF00, 0xCCFF40, 0xCCFF80, 0xCCFFBF, 0xCCFFFF, 0xD2D2D2, 0xE1E1E1, 0xF0F0F0, 0xFF0000, 0xFF0040, 0xFF0080, 0xFF00BF, 0xFF00FF, 0xFF2400, 0xFF2440, 0xFF2480, - 0xFF24BF, 0xFF24FF, 0xFF4900, 0xFF4940, 0xFF4980, 0xFF49BF, 0xFF49FF, 0xFF6D00, 0xFF6D40, 0xFF6D80, 0xFF6DBF, 0xFF6DFF, 0xFF9200, 0xFF9240, 0xFF9280, 0xFF92BF, - 0xFF92FF, 0xFFB600, 0xFFB640, 0xFFB680, 0xFFB6BF, 0xFFB6FF, 0xFFDB00, 0xFFDB40, 0xFFDB80, 0xFFDBBF, 0xFFDBFF, 0xFFFF00, 0xFFFF40, 0xFFFF80, 0xFFFFBF, 0xFFFFFF, -} diff --git a/Applications/Exam/school.lua b/Applications/Exam/school.lua deleted file mode 100644 index eb4b1aa6..00000000 --- a/Applications/Exam/school.lua +++ /dev/null @@ -1,163 +0,0 @@ -local ecs = require("ECSAPI") - -local component = require "component" -local event = require "event" -local ser = require "serialization" -local unicode = require "unicode" - -local gpu = component.gpu - --- КОНСТАНТЫ -- -local bgColor = 0xffffff -local fgColor = 0x000000 -local questionsFilePath = "schoolQuestions.lua" -local screenWidth, screenHeight = gpu.getResolution() - --- РАЗНОЕ -- -function swap(array, index1, index2) - array[index1], array[index2] = array[index2], array[index1] -end - -function shake(array) - local counter = #array - - while counter > 1 do - local index = math.random(counter) - - swap(array, index, counter) - counter = counter - 1 - end -end - --- Информация о кнопках -- -local buttons = {} - -local buttonStyle = { - standart = { - buttonColor = 0xBBBBBB, - textColor = 0xffffff, - }, - correct = { - buttonColor = 0x008800, - textColor = 0xffffff, - }, - incorrect = { - buttonColor = 0xFF4444, - textColor = 0xffffff, - } -} - --- Информация о кол-ве вопросов и текущем вопросе -- -local database -local currentQuestion = 1 -local currentCategory = "Информатика" -local buttonsWidth = 0 - --- Получение массива вопросов из файла -- -local function readDatabase( filename ) - local file = assert( io.open(filename, "r"), "File not found!" ) - database = ser.unserialize( file:read("*a") ) - file:close() -end - -local xOffset, yOffset, spaceBetweenButtons = 4, 1, 2 -local xPos, yPos - -local function drawMain(x) - - buttons = {} - buttonsWidth = 0 - for i = 1, #database.categories[currentCategory].exercises[currentQuestion].answers do - buttonsWidth = buttonsWidth + spaceBetweenButtons + xOffset * 2 + unicode.len(database.categories[currentCategory].exercises[currentQuestion].answers[i]) - end - buttonsWidth = buttonsWidth - - xPos, yPos = math.floor(x + screenWidth / 2 - buttonsWidth / 2 - 1), math.floor(screenHeight / 2) - 3 - - ecs.square(1, yPos, screenWidth, 8, 0xFFFFFF) - - ecs.colorText(x + math.floor(screenWidth / 2 - unicode.len(database.categories[currentCategory].exercises[currentQuestion].question) / 2 - 1), yPos, 0x262626, database.categories[currentCategory].exercises[currentQuestion].question) - yPos = yPos + 3 - - local xButtons = xPos - for i = 1, #database.categories[currentCategory].exercises[currentQuestion].answers do - local data = { ecs.drawAdaptiveButton(xButtons, yPos, xOffset, yOffset, database.categories[currentCategory].exercises[currentQuestion].answers[i], buttonStyle.standart.buttonColor, buttonStyle.standart.textColor) } - data.text = database.categories[currentCategory].exercises[currentQuestion].answers[i] - table.insert(buttons, data) - xButtons = xButtons + spaceBetweenButtons + xOffset * 2 + unicode.len(database.categories[currentCategory].exercises[currentQuestion].answers[i]) - end - - yPos = yPos - 3 -end - -local function startAnimation(speed) - for i = screenWidth - screenWidth*0.2, 1, -speed do - drawMain(i) - os.sleep(0.05) - end -end - -local function drawProgress() - local width = math.floor(screenWidth * 0.65) - local xPos = math.floor(screenWidth / 2 - width / 2) - local yPos = math.floor(screenHeight / 2 + 5) - gpu.setBackground(0xFFFFFF) - - gpu.setForeground(0xCCCCCC) - gpu.fill( xPos, yPos, width, 1, '▂' ) - - gpu.setForeground(0xBF2008) - gpu.fill( xPos, yPos, (width * (currentQuestion - 1)) / #database.categories[currentCategory].exercises, 1, '▂' ) -end - -local function endAnimation(speed) - for i = screenWidth, 1, -5 do - gpu.copy(1, yPos, screenWidth, 8, -speed, 0) - os.sleep(0.05) - end -end - -local function test() - local animationSpeed = 6 - ecs.square(1, 1, screenWidth, screenHeight, 0xFFFFFF) - - for i = 1, #database.categories[currentCategory].exercises do - currentQuestion = i - local correctAnswer = database.categories[currentCategory].exercises[i].correctAnswer - - drawProgress() - startAnimation(animationSpeed) - - local doWhile = true - while doWhile do - if exitWhile then break end - local e = {event.pull()} - if e[1] == "touch" then - for key = 1, #buttons do - if ecs.clickedAtArea(e[3], e[4], buttons[key][1], buttons[key][2], buttons[key][3], buttons[key][4]) then - if key == correctAnswer then - ecs.drawAdaptiveButton(buttons[key][1], buttons[key][2], xOffset, yOffset, buttons[key].text, buttonStyle.correct.buttonColor, buttonStyle.correct.textColor) - else - ecs.drawAdaptiveButton(buttons[key][1], buttons[key][2], xOffset, yOffset, buttons[key].text, buttonStyle.incorrect.buttonColor, buttonStyle.incorrect.textColor) - end - doWhile = false - break - end - end - end - end - endAnimation(animationSpeed) - end - - ecs.square(1, 1, screenWidth, screenHeight, 0xFFFFFF) -end - -readDatabase(questionsFilePath) -test() - - - - - - - diff --git a/Applications/Exam/schoolQuestions.lua b/Applications/Exam/schoolQuestions.lua deleted file mode 100644 index 5a9f394f..00000000 --- a/Applications/Exam/schoolQuestions.lua +++ /dev/null @@ -1,56 +0,0 @@ -{ - categories = { - ["Математика"] = { - - }, - ["Информатика"] = { - exercises = { - { - question = "Сколько истребителей на хуй?", - correctAnswer = 1, - answers = { - "12", - "Низнаю", - "Я не учил!", - "Можно пересдать?", - } - }, - { - question = "Любишь сосать хуй?", - correctAnswer = 1, - answers = { - "Да", - "Нет", - "Пидора ответ", - "Чмок", - } - }, - { - question = "Сколько будет 5 в двоичной системе счисления?", - correctAnswer = 3, - answers = { - "100", - "011", - "101", - "010", - } - }, - { - question = "Наименьшая структурная единица информации?", - correctAnswer = 2, - answers = { - "Байт", - "Бит", - "Мегабайт", - "Килобайт", - } - }, - } - } - } -} - - - - - diff --git a/Applications/Home.lua b/Applications/Home.lua deleted file mode 100644 index 1427e77e..00000000 --- a/Applications/Home.lua +++ /dev/null @@ -1,262 +0,0 @@ -local c = require("component") -local computer = require("computer") -local event = require("event") -local ecs = require("ECSAPI") -local colors = require("colors") -local sides = require("sides") -local config = require("config") -local fs = require("filesystem") -local rs = c.redstone -local gpu = c.gpu -local modem = c.modem - -modem.open(512) - ---------------------------------------------------------------------- - -local pathToWhitelist = "System/Home/whitelist.txt" -local pathToDoors = "System/Home/doors.txt" - -local whitelist -local doors - -if not fs.exists(pathToWhitelist) then - fs.makeDirectory(fs.path(pathToWhitelist)) - config.write(pathToWhitelist, "Igor_Timofeev", "owner") -end - -if not fs.exists(pathToDoors) then - config.write(pathToDoors, "3b1ea", colors.lightblue) - config.write(pathToDoors, "9d482", colors.yellow) - doors = config.readAll(pathToDoors) -end - -whitelist = config.readAll(pathToWhitelist) -doors = config.readAll(pathToDoors) - ------------------------------------------------------------------------- - -local redstoneSide = sides.bottom - -local xScale, yScale = 40, 20 -local xSize, ySize = gpu.getResolution() - -local doorTimer = 3 - -local buttons = {{false, 0x444444, colors.lightblue}, {false, 0x444444, colors.black}, {false, 0x444444, colors.brown}, {true, ecs.colors.green, colors.pink}, {true, ecs.colors.green, colors.red}, {true, ecs.colors.green, colors.orange}} - -local killWireColor = colors.blue - ---------------------------------------- - -local obj = {} -local function newObj(class, name, ...) - obj[class] = obj[class] or {} - obj[class][name] = {...} -end - -local function getScreens() - local list = c.list() - local screens = {} - for key, val in pairs(list) do - if val == "screen" then - screens[key] = {val, c.isPrimary(key)} - end - end - return screens -end - -local function clearMonitor(backColor, frontColor, text) - gpu.setBackground(backColor) - local xSize, ySize = gpu.getResolution() - gpu.fill(1, 1, xSize, ySize, " ") - gpu.setForeground(frontColor) - ecs.centerText("xy", 1, text) -end - -local screens = getScreens() -local primaryScreen = c.getPrimary("screen").address - -local function bind(address) - if address then - gpu.bind(address) - gpu.setResolution(xScale, yScale) - else - gpu.bind(primaryScreen) - gpu.setResolution(xSize, ySize) - end -end - -local function checkNick(nick) - for key, val in pairs(whitelist) do - if key == nick then return true end - end - return false -end - ---DOORS - -local function door(which, open) - - local color = 0 - local address - for key, val in pairs(doors) do - address = c.get(key, "screen") - if address == which then - color = tonumber(val) - break - end - end - - - if open then - rs.setBundledOutput(redstoneSide, color, 100) - else - rs.setBundledOutput(redstoneSide, color, 0) - end -end - -local function openAllDoors(open) - local color - for key, val in pairs(doors) do - color = tonumber(val) - if open then - rs.setBundledOutput(redstoneSide, color, 100) - else - rs.setBundledOutput(redstoneSide, color, 0) - end - end -end - -local function mini() - clearMonitor(0xffffff, 0x444444, "Приложите палец для идентификации") -end - -local function infa() - gpu.setBackground(0xffffff) - gpu.setForeground(0x444444) - - local yPos = ySize - 3 - if c.isAvailable("mfsu") then ecs.centerText("x", yPos, "Заряд МФСУ: "..c.mfsu.getStored()); yPos = yPos + 1 end - if c.isAvailable("reactor") then ecs.centerText("x", yPos, "Нагрев реактора: "..math.ceil(c.reactor.getHeat() / c.reactor.getMaxHeat() * 100).."%"); yPos = yPos + 1 end - if c.isAvailable("reactor_chamber") then ecs.centerText("x", yPos, "Нагрев реактора: "..math.ceil(c.reactor_chamber.getHeat() / c.reactor_chamber.getMaxHeat() * 100).."%"); yPos = yPos + 1 end -end - -local function main() - gpu.setBackground(0xffffff) - gpu.fill(1, 1, xSize, ySize, " ") - - local yCenter = math.floor(ySize / 2) - local yPos = yCenter - 12 - newObj("buttons", 1, ecs.drawAdaptiveButton("auto", yPos, 3, 1, "Открыть двери", buttons[1][2] or 0x444444, 0xffffff)); yPos = yPos + 4 - newObj("buttons", 2, ecs.drawAdaptiveButton("auto", yPos, 3, 1, "Фабрика материи", buttons[2][2] or 0x444444, 0xffffff)); yPos = yPos + 4 - newObj("buttons", 3, ecs.drawAdaptiveButton("auto", yPos, 3, 1, "Управление реактором", buttons[3][2] or 0x444444, 0xffffff)); yPos = yPos + 4 - newObj("buttons", 4, ecs.drawAdaptiveButton("auto", yPos, 3, 1, "Свет на втором этаже", buttons[4][2] or 0x444444, 0xffffff)); yPos = yPos + 4 - newObj("buttons", 5, ecs.drawAdaptiveButton("auto", yPos, 3, 1, "Свет на первом этаже", buttons[5][2] or 0x444444, 0xffffff)); yPos = yPos + 4 - newObj("buttons", 6, ecs.drawAdaptiveButton("auto", yPos, 3, 1, "Свет в шахте", buttons[6][2] or 0x444444, 0xffffff)); yPos = yPos + 4 - - infa() -end - -local function redstoneRecontrol() - for i = 1, #buttons do - if buttons[i][1] then - rs.setBundledOutput(redstoneSide, buttons[i][3], 100) - else - rs.setBundledOutput(redstoneSide, buttons[i][3], 0) - end - end -end - -local function killThemAll() - ecs.square(1, 1, xSize, ySize, 0xff0000) - gpu.setForeground(0xffffff) - ecs.centerText("xy", 1, "KILL THEM ALL!") - rs.setBundledOutput(redstoneSide, killWireColor, 100) - os.sleep(2) - rs.setBundledOutput(redstoneSide, killWireColor, 0) - main("Помещение очищено от всего живого.") -end - -local function switchButton(key, buttonColor) - if buttons[key][1] then - buttons[key][1] = false - buttons[key][2] = 0x444444 - else - buttons[key][1] = true - buttons[key][2] = buttonColor or ecs.colors.green - end -end - ------------------------------------------ - -main("Ничего интересного.") - -for key, val in pairs(screens) do - if not val[2] then - bind(key) - mini() - bind() - end -end - -while true do - local e = {event.pull()} - if e[1] == "touch" then - - --ЕСЛИ КЛИКНУТО НА ГЛАВНОМ МОНИКЕ - if e[2] == primaryScreen then - for key, val in pairs(obj["buttons"]) do - if ecs.clickedAtArea(e[3], e[4], obj["buttons"][key][1], obj["buttons"][key][2], obj["buttons"][key][3], obj["buttons"][key][4]) then - local color - if key == 3 then color = ecs.colors.red end - switchButton(key, color) - main("Изменен параметр кнопки "..tostring(key).." на "..tostring(buttons[key][1])) - if key == 1 then - openAllDoors(buttons[key][1]) - else - redstoneRecontrol() - end - break - end - end - - --ЕСЛИ КЛИКНУТО НА КАКОМ-ТО ЛЕВОМ МОНИКЕ - else - bind(e[2]) - - if checkNick(e[6]) then - clearMonitor(0x44ff44, 0xffffff, "С возвращением, "..e[6].."!") - door(e[2], true) - os.sleep(doorTimer) - door(e[2], false) - mini() - bind() - main(e[6].." вернулся в нашу скромную обитель!") - else - clearMonitor(0xff0000, 0xffffff, "Недостойным дороги нет.") - bind() - killThemAll() - bind(e[2]) - os.sleep(doorTimer) - mini() - bind() - main(e[6].." попытался зайти в дом. Убей его. Убей чужака!") - end - end - - infa() - elseif e[1] == "modem_message" then - if e[6] == "killThemAll!" then - killThemAll() - elseif e[6] == "openAllDoors" then - switchButton(1) - openAllDoors(buttons[1][1]) - main("Двери открыты!") - end - elseif e[1] == "key_down" then - if e[4] == 28 then - killThemAll() - end - end -end diff --git a/Applications/Matrix/About/English.txt b/Applications/Matrix/About/English.txt deleted file mode 100644 index a0b527e9..00000000 --- a/Applications/Matrix/About/English.txt +++ /dev/null @@ -1 +0,0 @@ -Демонстрационная программа, генерирующая всем известный "дождь" из символов из не менее известного фильма "Матрица". Идеально вписывается в любой интерьер. \ No newline at end of file diff --git a/Applications/Matrix/About/Russian.txt b/Applications/Matrix/About/Russian.txt deleted file mode 100644 index a0b527e9..00000000 --- a/Applications/Matrix/About/Russian.txt +++ /dev/null @@ -1 +0,0 @@ -Демонстрационная программа, генерирующая всем известный "дождь" из символов из не менее известного фильма "Матрица". Идеально вписывается в любой интерьер. \ No newline at end of file diff --git a/Applications/Matrix/Icon.pic b/Applications/Matrix/Icon.pic deleted file mode 100644 index e504b54c259f7371149bceaee6e22b9d71004f66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 293 zcmYL^I}QRd3`8?yZxV@1fPjXc27wk96p023SGf;o;DB5NvLCXAi_sg8$It6MY19iA z&TvZDcMdKCYB(meqmrU_36F_I!z$r6Dpd>1+1Vs~J4cr(kW39R;V?Q`Sc#Cr*fR++ zi-MBireqAUy#o?5SVqg%zf@yr>tT4qww5A|o0)N_(SO2dTvtF}fza@Iy{lGlJkMpd HMvMOeVS*(* diff --git a/Applications/Matrix/Matrix.lua b/Applications/Matrix/Matrix.lua deleted file mode 100644 index 8b6c2f83..00000000 --- a/Applications/Matrix/Matrix.lua +++ /dev/null @@ -1,60 +0,0 @@ - -local event = require("event") -local gpu = require("component").gpu - --------------------------------------------------------------------------------------------------------------------- - -local backgroundColor = 0x000000 -local maximumLines = 20 -local minumLineLength = 5 -local maximumLineLength = 25 - --------------------------------------------------------------------------------------------------------------------- - --- local chars = {"%", "?", "@", "#", "$", "!", "0", "/", "№", "&"} -local chars = {"ァ", "ア", "ィ", "イ", "ゥ", "ウ", "ェ", "エ", "ォ", "オ", "カ", "ガ", "キ", "ギ", "ク", "グ", "ケ", "ゲ", "コ", "ゴ", "サ", "ザ", "シ", "ジ", "ス", "ズ", "セ", "ゼ", "ソ", "ゾ", "タ", "ダ", "チ", "ヂ", "ッ", "ツ", "ヅ", "テ", "デ", "ト", "ド", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "バ", "パ", "ヒ", "ビ", "ピ", "フ", "ブ", "プ", "ヘ", "ベ", "ペ", "ホ", "ボ", "ポ", "マ", "ミ", "ム", "メ", "モ", "ャ", "ヤ", "ュ", "ユ", "ョ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ヮ", "ワ", "ヰ", "ヱ", "ヲ", "ン", "ヴ", "ヵ", "ヶ", "ヷ", "ヸ", "ヹ", "ヺ", "・", "ー", "ヽ", "ヾ", "ヿ"} -local lineColorsForeground = { 0xFFFFFF, 0xBBFFBB, 0x88FF88, 0x33FF33, 0x00FF00, 0x00EE00, 0x00DD00, 0x00CC00, 0x00BB00, 0x00AA00, 0x009900, 0x008800, 0x007700, 0x006600, 0x005500, 0x004400, 0x003300, 0x002200, 0x001100 } -local lineColorsBackground = { 0x004400, 0x004400, 0x003300, 0x003300, 0x002200, 0x001100 } -local xScreen, yScreen = gpu.getResolution() -local lines = {} - --------------------------------------------------------------------------------------------------------------------- - -gpu.setBackground(backgroundColor) -gpu.fill(1, 1, xScreen, yScreen, " ") - -while true do - while #lines < maximumLines do - table.insert(lines, { x = math.random(1, xScreen), y = 1, length = math.random(minumLineLength, maximumLineLength) }) - end - - gpu.copy(1, 1, xScreen, yScreen, 0, 1) - gpu.setBackground(backgroundColor) - gpu.fill(1, 1, xScreen, 1, " ") - - local i = 1 - while i <= #lines do - local part = math.ceil(lines[i].y * #lineColorsForeground / lines[i].length) - gpu.setBackground(lineColorsBackground[part] or 0x000000) - gpu.setForeground(lineColorsForeground[part]) - gpu.set(lines[i].x, 1, chars[math.random(1, #chars)]) - - lines[i].y = lines[i].y + 1 - if lines[i].y - lines[i].length > 0 then - table.remove(lines, i) - i = i - 1 - end - i = i + 1 - end - - local e = {event.pull(0.03)} - if (e[1] == "key_down" and e[4] == 28) or e[1] == "touch" then - gpu.setBackground(backgroundColor) - gpu.fill(1, 1, xScreen, yScreen, " ") - break - end -end - - - - diff --git a/Applications/MineOSPacker/MineOS.pkg b/Applications/MineOSPacker/MineOS.pkg deleted file mode 100644 index d1c0ad14..00000000 --- a/Applications/MineOSPacker/MineOS.pkg +++ /dev/null @@ -1,33539 +0,0 @@ -ARCHDboot/Fboot/00_base.luafunction dofile(filename) - local program, reason = loadfile(filename) - if not program then - return error(reason .. ':' .. filename, 0) - end - return program() -end - -function loadfile(filename, mode, env) - local file, reason = io.open(filename) - if not file then - return nil, reason - end - local source, reason = file:read("*a") - file:close() - if not source then - return nil, reason - end - if string.sub(source, 1, 1) == "#" then - local endline = string.find(source, "\n", 2, true) - if endline then - source = string.sub(source, endline + 1) - else - source = "" - end - end - return load(source, "=" .. filename, mode, env) -end - -function print(...) - local args = table.pack(...) - local stdout = io.stdout - stdout:setvbuf("line") - for i = 1, args.n do - local arg = tostring(args[i]) - if i > 1 then - arg = "\t" .. arg - end - stdout:write(arg) - end - stdout:write("\n") - stdout:setvbuf("no") - stdout:flush() -end -Fboot/01_process.lualocal process = require("process") - ---Initialize coroutine library-- -local _coroutine = coroutine -- real coroutine backend - -_G.coroutine = {} -package.loaded.coroutine = _G.coroutine - -for key,value in pairs(_coroutine) do - if type(value) == "function" and value ~= "running" and value ~= "create" then - _G.coroutine[key] = function(...) - local thread = _coroutine.running() - local info = process.info(thread) - -- note the gc thread does not have a process info - assert(info,"process not found for " .. tostring(thread)) - local data = info.data - local co = data.coroutine_handler - local handler = co[key] - return handler(...) - end - else - _G.coroutine[key] = value - end -end - -local init_thread = _coroutine.running() -local init_load = _G.load - -_G.load = function(ld, source, mode, env) - env = env or select(2, process.running()) - return init_load(ld, source, mode, env) -end - -local kernel_create = _coroutine.create -_coroutine.create = function(f,standAlone) - local co = kernel_create(f) - if not standAlone then - table.insert(process.findProcess().instances, co) - end - return co -end - -_coroutine.wrap = function(f) - local thread = coroutine.create(f) - return function(...) - local result_pack = table.pack(coroutine.resume(thread, ...)) - local result, reason = result_pack[1], result_pack[2] - assert(result, reason) - return select(2, table.unpack(result_pack)) - end -end - -process.list[init_thread] = { - path = "/init.lua", - command = "init", - env = _ENV, - data = - { - vars={}, - io={}, --init will populate this - coroutine_handler=setmetatable({}, {__index=_coroutine}) - }, - instances = setmetatable({}, {__mode="v"}) -} -Fboot/02_os.lualocal computer = require("computer") -local event = require("event") -local fs = require("filesystem") -local shell = require("shell") -local unicode = require("unicode") -local process = require("process") - -local function env() - return process.info().data.vars -end - -os.execute = function(command) - if not command then - return type(shell) == "table" - end - return shell.execute(command) -end - -function os.exit(code) - error({reason="terminated", code=code}, 0) -end - -function os.getenv(varname) - if varname == '#' then - return #env() - elseif varname ~= nil then - return env()[varname] - else - return env() - end -end - -function os.setenv(varname, value) - checkArg(1, varname, "string", "number") - if value == nil then - env()[varname] = nil - else - local success, val = pcall(tostring, value) - if success then - env()[varname] = val - return env()[varname] - else - return nil, val - end - end -end - -function os.remove(...) - return fs.remove(...) -end - -function os.rename(...) - return fs.rename(...) -end - -function os.sleep(timeout) - checkArg(1, timeout, "number", "nil") - local deadline = computer.uptime() + (timeout or 0) - repeat - event.pull(deadline - computer.uptime()) - until computer.uptime() >= deadline -end - -function os.tmpname() - local path = os.getenv("TMPDIR") or "/tmp" - if fs.exists(path) then - for i = 1, 10 do - local name = fs.concat(path, tostring(math.random(1, 0x7FFFFFFF))) - if not fs.exists(name) then - return name - end - end - end -end - -os.setenv("PATH", "/bin:/usr/bin:/home/bin:.") -os.setenv("TMP", "/tmp") -- Deprecated -os.setenv("TMPDIR", "/tmp") - -if computer.tmpAddress() then - fs.mount(computer.tmpAddress(), os.getenv("TMPDIR") or "/tmp") -end -Fboot/03_io.luaalocal buffer = require("buffer") -local term = require("term") - -local io_open = io.open -function io.open(path, mode) - return io_open(require("shell").resolve(path), mode) -end - -local stdinStream = {handle="stdin"} -local stdoutStream = {handle="stdout"} -local stderrStream = {handle="stderr"} -local stdinHistory = {} - -local function badFileDescriptor() - return nil, "bad file descriptor" -end - -function stdinStream:close() - return nil, "cannot close standard file" -end -stdoutStream.close = stdinStream.close -stderrStream.close = stdinStream.close - -function stdinStream:read(n, dobreak) - stdinHistory.dobreak = dobreak - local result = term.readKeyboard(stdinHistory) - return result -end - -function stdoutStream:write(str) - term.drawText(str, self.wrap ~= false) - return self -end - -function stderrStream:write(str) - local gpu = term.gpu() - local set_depth = gpu and gpu.getDepth() and gpu.getDepth() > 1 - - if set_depth then - set_depth = gpu.setForeground(0xFF0000) - end - - term.drawText(str, true) - - if set_depth then - gpu.setForeground(set_depth) - end - - return self -end - -stdinStream.seek = badFileDescriptor -stdinStream.write = badFileDescriptor -stdoutStream.read = badFileDescriptor -stdoutStream.seek = badFileDescriptor -stderrStream.read = badFileDescriptor -stderrStream.seek = badFileDescriptor - -local core_stdin = buffer.new("r", stdinStream) -local core_stdout = buffer.new("w", stdoutStream) -local core_stderr = buffer.new("w", stderrStream) - -core_stdout:setvbuf("no") -core_stderr:setvbuf("no") -core_stdin.tty = true -core_stdout.tty = true -core_stderr.tty = true - -core_stdin.close = stdinStream.close -core_stdout.close = stdinStream.close -core_stderr.close = stdinStream.close - -local fd_map = -{ - -- key name => method name - stdin = 'input', - stdout = 'output', - stderr = 'error' -} - -local io_mt = getmetatable(io) or {} -io_mt.__index = function(t, k) - if fd_map[k] then - return io[fd_map[k]]() - end -end -io_mt.__newindex = function(t, k, v) - if fd_map[k] then - io[fd_map[k]](v) - else - rawset(io, k, v) - end -end - -setmetatable(io, io_mt) - -io.stdin = core_stdin -io.stdout = core_stdout -io.stderr = core_stderr -Fboot/04_component.lua)local component = require("component") -local computer = require("computer") -local event = require("event") - -local adding = {} -local removing = {} -local primaries = {} - -------------------------------------------------------------------------------- - --- This allows writing component.modem.open(123) instead of writing --- component.getPrimary("modem").open(123), which may be nicer to read. -setmetatable(component, { - __index = function(_, key) - return component.getPrimary(key) - end, - __pairs = function(self) - local parent = false - return function(_, key) - if parent then - return next(primaries, key) - else - local k, v = next(self, key) - if not k then - parent = true - return next(primaries) - else - return k, v - end - end - end - end -}) - -function component.get(address, componentType) - checkArg(1, address, "string") - checkArg(2, componentType, "string", "nil") - for c in component.list(componentType, true) do - if c:sub(1, address:len()) == address then - return c - end - end - return nil, "no such component" -end - -function component.isAvailable(componentType) - checkArg(1, componentType, "string") - if not primaries[componentType] and not adding[componentType] then - -- This is mostly to avoid out of memory errors preventing proxy - -- creation cause confusion by trying to create the proxy again, - -- causing the oom error to be thrown again. - component.setPrimary(componentType, component.list(componentType, true)()) - end - return primaries[componentType] ~= nil -end - -function component.isPrimary(address) - local componentType = component.type(address) - if componentType then - if component.isAvailable(componentType) then - return primaries[componentType].address == address - end - end - return false -end - -function component.getPrimary(componentType) - checkArg(1, componentType, "string") - assert(component.isAvailable(componentType), - "no primary '" .. componentType .. "' available") - return primaries[componentType] -end - -function component.setPrimary(componentType, address) - checkArg(1, componentType, "string") - checkArg(2, address, "string", "nil") - if address ~= nil then - address = component.get(address, componentType) - assert(address, "no such component") - end - - local wasAvailable = primaries[componentType] - if wasAvailable and address == wasAvailable.address then - return - end - local wasAdding = adding[componentType] - if wasAdding and address == wasAdding.address then - return - end - if wasAdding then - event.cancel(wasAdding.timer) - end - primaries[componentType] = nil - adding[componentType] = nil - - local primary = address and component.proxy(address) or nil - if wasAvailable then - computer.pushSignal("component_unavailable", componentType) - end - if primary then - if wasAvailable or wasAdding then - adding[componentType] = { - address=address, - timer=event.timer(0.1, function() - adding[componentType] = nil - primaries[componentType] = primary - computer.pushSignal("component_available", componentType) - end) - } - else - primaries[componentType] = primary - computer.pushSignal("component_available", componentType) - end - end -end - -------------------------------------------------------------------------------- - -for address in component.list('screen', true) do - if #component.invoke(address,'getKeyboards') > 0 then - component.setPrimary('screen',address) - end -end - -local function onComponentAdded(_, address, componentType) - if not (primaries[componentType] or adding[componentType]) then - component.setPrimary(componentType, address) - end -end - -local function onComponentRemoved(_, address, componentType) - if primaries[componentType] and primaries[componentType].address == address or - adding[componentType] and adding[componentType].address == address - then - component.setPrimary(componentType, component.list(componentType, true)()) - end -end - -event.listen("component_added", onComponentAdded) -event.listen("component_removed", onComponentRemoved) -Fboot/10_devfs.luarequire("filesystem").mount( -setmetatable({ - isReadOnly = function()return false end -}, -{ - __index=function(tbl,key)return require("devfs")[key]end -}), "/dev") -Fboot/90_filesystem.lualocal component = require("component") -local event = require("event") -local fs = require("filesystem") -local shell = require("shell") - -local isInitialized, pendingAutoruns = false, {} - -local function onInit() - isInitialized = true - for _, run in ipairs(pendingAutoruns) do - local result, reason = pcall(run) - if not result then - local path = fs.concat(os.getenv("TMPDIR") or "/tmp", "event.log") - local log = io.open(path, "a") - if log then - log:write(reason .. "\n") - log:close() - end - end - end - pendingAutoruns = nil -end - -local function onComponentAdded(_, address, componentType) - if componentType == "filesystem" then - local proxy = component.proxy(address) - if proxy then - local name = address:sub(1, 3) - while fs.exists(fs.concat("/mnt", name)) and - name:len() < address:len() -- just to be on the safe side - do - name = address:sub(1, name:len() + 1) - end - name = fs.concat("/mnt", name) - fs.mount(proxy, name) - if fs.isAutorunEnabled() then - local function run() - local file = shell.resolve(fs.concat(name, "autorun"), "lua") or - shell.resolve(fs.concat(name, ".autorun"), "lua") - if file then - local result, reason = shell.execute(file, _ENV, proxy) - if not result then - error(reason, 0) - end - end - end - if isInitialized then - run() - else - table.insert(pendingAutoruns, run) - end - end - end - end -end - -local function onComponentRemoved(_, address, componentType) - if componentType == "filesystem" then - if fs.get(shell.getWorkingDirectory()).address == address then - shell.setWorkingDirectory("/") - end - fs.umount(address) - end -end - -event.listen("init", onInit) -event.listen("component_added", onComponentAdded) -event.listen("component_removed", onComponentRemoved) -Fboot/91_gpu.lua@local component = require("component") -local event = require("event") - -local function onComponentAvailable(_, componentType) - if (componentType == "screen" and component.isAvailable("gpu")) or - (componentType == "gpu" and component.isAvailable("screen")) - then - component.gpu.bind(component.screen.address) - local depth = 2^(component.gpu.getDepth()) - os.setenv("TERM", "term-"..depth.."color") - require("computer").pushSignal("gpu_bound", component.gpu.address, component.screen.address) - end -end - -event.listen("component_available", onComponentAvailable) -Fboot/92_keyboard.lualocal component = require("component") -local event = require("event") -local keyboard = require("keyboard") - -local function onKeyDown(_, address, char, code) - if keyboard.pressedChars[address] then - keyboard.pressedChars[address][char] = true - keyboard.pressedCodes[address][code] = true - end -end - -local function onKeyUp(_, address, char, code) - if keyboard.pressedChars[address] then - keyboard.pressedChars[address][char] = nil - keyboard.pressedCodes[address][code] = nil - end -end - -local function onComponentAdded(_, address, componentType) - if componentType == "keyboard" then - keyboard.pressedChars[address] = {} - keyboard.pressedCodes[address] = {} - end -end - -local function onComponentRemoved(_, address, componentType) - if componentType == "keyboard" then - keyboard.pressedChars[address] = nil - keyboard.pressedCodes[address] = nil - end -end - -for address in component.list("keyboard", true) do - onComponentAdded("component_added", address, "keyboard") -end - -event.listen("key_down", onKeyDown) -event.listen("key_up", onKeyUp) -event.listen("component_added", onComponentAdded) -event.listen("component_removed", onComponentRemoved) -Fboot/93_term.lua)local component = require("component") -local computer = require("computer") -local event = require("event") -local term = require("term") -local process = require("process") - --- this should be the init level process -process.info().data.window = term.internal.open() - -event.listen("gpu_bound", function(ename, gpu, screen) - gpu=component.proxy(gpu) - screen=component.proxy(screen) - term.bind(gpu, screen) - computer.pushSignal("term_available") -end) - -event.listen("component_unavailable", function(_,type) - if type == "screen" or type == "gpu" then - if term.isAvailable() then - local window = term.internal.window() - if window[type] and not component.proxy(window[type].address) then - window[type] = nil - end - end - if not term.isAvailable() then - computer.pushSignal("term_unavailable") - end - end -end) - -event.listen("screen_resized", function(_,addr,w,h) - local window = term.internal.window() - if term.isAvailable(window) and window.screen.address == addr and window.fullscreen then - window.w,window.h = w,h - end -end) -Fboot/94_shell.lualocal shell = require("shell") - -require("event").listen("init", function() - local file = io.open("/etc/hostname") - if file then - os.setenv("HOSTNAME", file:read("*l")) - os.setenv("PS1", "$HOSTNAME:$PWD# ") - file:close() - end -end) -Fboot/99_rc.lua-- Run all enabled rc scripts. -local shell = require("shell") -local rc = shell.resolve("rc", "lua") -if rc then - dofile(rc) -end -Dlib/Flib/ECSAPI.luaUP - -local advancedLua = require("advancedLua") -local component = require("component") -local term = require("term") -local unicode = require("unicode") -local event = require("event") -local fs = require("filesystem") -local shell = require("shell") -local keyboard = require("keyboard") -local computer = require("computer") -local serialization = require("serialization") - - -local gpu = component.gpu -local ecs = {} - ----------------------------------------------------------------------------------------------------- - -ecs.windowColors = { - background = 0xeeeeee, - usualText = 0x444444, - subText = 0x888888, - tab = 0xaaaaaa, - title = 0xffffff, - shadow = 0x444444, -} - -ecs.colors = { - white = 0xffffff, - orange = 0xF2B233, - magenta = 0xE57FD8, - lightBlue = 0x99B2F2, - yellow = 0xDEDE6C, - lime = 0x7FCC19, - pink = 0xF2B2CC, - gray = 0x4C4C4C, - lightGray = 0x999999, - cyan = 0x4C99B2, - purple = 0xB266E5, - blue = 0x3366CC, - brown = 0x7F664C, - green = 0x57A64E, - red = 0xCC4C4C, - black = 0x000000, - ["0"] = 0xffffff, - ["1"] = 0xF2B233, - ["2"] = 0xE57FD8, - ["3"] = 0x99B2F2, - ["4"] = 0xDEDE6C, - ["5"] = 0x7FCC19, - ["6"] = 0xF2B2CC, - ["7"] = 0x4C4C4C, - ["8"] = 0x999999, - ["9"] = 0x4C99B2, - ["a"] = 0xB266E5, - ["b"] = 0x3366CC, - ["c"] = 0x7F664C, - ["d"] = 0x57A64E, - ["e"] = 0xCC4C4C, - ["f"] = 0x000000 -} - ----------------------------------------------------------------------------------------------------- - ---Адекватный запрос к веб-серверу вместо стандартного Internet API, бросающего stderr, когда ему вздумается -function ecs.internetRequest(url) - local success, response = pcall(component.internet.request, url) - if success then - local responseData = "" - while true do - local data, responseChunk = response.read() - if data then - responseData = responseData .. data - else - if responseChunk then - return false, responseChunk - else - return true, responseData - end - end - end - else - return false, reason - end -end - ---Загрузка файла с инета -function ecs.getFileFromUrl(url, path) - local success, response = ecs.internetRequest(url) - if success then - fs.makeDirectory(fs.path(path) or "") - local file = io.open(path, "w") - file:write(response) - file:close() - else - ecs.error("Could not connect to to URL address \"" .. url .. "\"") - return - end -end - ---Отключение принудительного завершения программ -function ecs.disableInterrupting() - _G.eventInterruptBackup = package.loaded.event.shouldInterrupt - _G.eventSoftInterruptBackup = package.loaded.event.shouldSoftInterrupt - - package.loaded.event.shouldInterrupt = function () return false end - package.loaded.event.shouldSoftInterrupt = function () return false end -end - ---Включение принудительного завершения программ -function ecs.enableInterrupting() - if _G.eventInterruptBackup then - package.loaded.event.shouldInterrupt = _G.eventInterruptBackup - package.loaded.event.shouldSoftInterrupt = _G.eventSoftInterruptBackup - else - error("Cant't enable interrupting beacause of it's already enabled.") - end -end - -function ecs.getScaledResolution(scale, debug) - --Базовая коррекция масштаба, чтобы всякие умники не писали своими погаными ручонками, чего не следует - if scale > 1 then - scale = 1 - elseif scale < 0.1 then - scale = 0.1 - end - - --Просчет монитора в псевдопикселях - забей, даже объяснять не буду, работает как часы - local function calculateAspect(screens) - local abc = 12 - - if screens == 2 then - abc = 28 - elseif screens > 2 then - abc = 28 + (screens - 2) * 16 - end - - return abc - end - - --Рассчитываем пропорцию монитора в псевдопикселях - local xScreens, yScreens = component.proxy(component.gpu.getScreen()).getAspectRatio() - local xPixels, yPixels = calculateAspect(xScreens), calculateAspect(yScreens) - local proportion = xPixels / yPixels - - --Получаем максимально возможное разрешение данной видеокарты - local xMax, yMax = component.gpu.maxResolution() - - --Получаем теоретическое максимальное разрешение монитора с учетом его пропорции, но без учета лимита видеокарты - local newWidth, newHeight - if proportion >= 1 then - newWidth = xMax - newHeight = math.floor(newWidth / proportion / 2) - else - newHeight = yMax - newWidth = math.floor(newHeight * proportion * 2) - end - - --Получаем оптимальное разрешение для данного монитора с поддержкой видеокарты - local optimalNewWidth, optimalNewHeight = newWidth, newHeight - - if optimalNewWidth > xMax then - local difference = newWidth / xMax - optimalNewWidth = xMax - optimalNewHeight = math.ceil(newHeight / difference) - end - - if optimalNewHeight > yMax then - local difference = newHeight / yMax - optimalNewHeight = yMax - optimalNewWidth = math.ceil(newWidth / difference) - end - - --Корректируем идеальное разрешение по заданному масштабу - local finalNewWidth, finalNewHeight = math.floor(optimalNewWidth * scale), math.floor(optimalNewHeight * scale) - - --Выводим инфу, если нужно - if debug then - print(" ") - print("Максимальное разрешение: "..xMax.."x"..yMax) - print("Пропорция монитора: "..xPixels.."x"..yPixels) - print("Коэффициент пропорции: "..proportion) - print(" ") - print("Теоретическое разрешение: "..newWidth.."x"..newHeight) - print("Оптимизированное разрешение: "..optimalNewWidth.."x"..optimalNewHeight) - print(" ") - print("Новое разрешение: "..finalNewWidth.."x"..finalNewHeight) - print(" ") - end - - return finalNewWidth, finalNewHeight -end - ---Установка масштаба монитора -function ecs.setScale(scale, debug) - --Устанавливаем выбранное разрешение - component.gpu.setResolution(ecs.getScaledResolution(scale, debug)) -end - -function ecs.rebindGPU(address) - component.gpu.bind(address) -end - ---Получаем всю инфу об оперативку в килобайтах -function ecs.getInfoAboutRAM() - local free = math.floor(computer.freeMemory() / 1024) - local total = math.floor(computer.totalMemory() / 1024) - local used = total - free - - return free, total, used -end - ---Получить информацию о жестких дисках -function ecs.getHDDs() - local candidates = {} - for address in component.list("filesystem") do - local proxy = component.proxy(address) - if proxy.address ~= computer.tmpAddress() and proxy.getLabel() ~= "internet" then - local isFloppy, spaceTotal = false, math.floor(proxy.spaceTotal() / 1024) - if spaceTotal < 600 then isFloppy = true end - table.insert(candidates, { - ["spaceTotal"] = spaceTotal, - ["spaceUsed"] = math.floor(proxy.spaceUsed() / 1024), - ["label"] = proxy.getLabel(), - ["address"] = proxy.address, - ["isReadOnly"] = proxy.isReadOnly(), - ["isFloppy"] = isFloppy, - }) - end - end - return candidates -end - ---Форматировать диск -function ecs.formatHDD(address) - local proxy = component.proxy(address) - local list = proxy.list("") - ecs.info("auto", "auto", "", "Formatting disk...") - for _, file in pairs(list) do - if type(file) == "string" then - if not proxy.isReadOnly(file) then proxy.remove(file) end - end - end - list = nil -end - ---Установить имя жесткого диска -function ecs.setHDDLabel(address, label) - local proxy = component.proxy(address) - proxy.setLabel(label or "Untitled") -end - ---Найти монтированный путь конкретного адреса диска -function ecs.findMount(address) - for fs1, path in fs.mounts() do - if fs1.address == component.get(address) then - return path - end - end -end - -function ecs.getArraySize(array) - local size = 0 - for key in pairs(array) do - size = size + 1 - end - return size -end - ---Скопировать файлы с одного диска на другой с заменой -function ecs.duplicateFileSystem(fromAddress, toAddress) - local source, destination = ecs.findMount(fromAddress), ecs.findMount(toAddress) - ecs.info("auto", "auto", "", "Copying file system...") - shell.execute("bin/cp -rx "..source.."* "..destination) -end - ---Загрузка файла с пастебина -function ecs.getFromPastebin(paste, path) - local url = "http://pastebin.com/raw.php?i=" .. paste - ecs.getFileFromUrl(url, path) -end - ---Загрузка файла с гитхаба -function ecs.getFromGitHub(url, path) - url = "https://raw.githubusercontent.com/" .. url - ecs.getFileFromUrl(url, path) -end - ---Загрузить ОС-приложение -function ecs.getOSApplication(application) - --Если это приложение - if application.type == "Application" then - --Удаляем приложение, если оно уже существовало и создаем все нужные папочки - application.name = "/" .. application.name - fs.remove(application.name .. ".app") - fs.makeDirectory(application.name .. ".app/Resources") - - --Загружаем основной исполняемый файл и иконку - ecs.getFromGitHub(application.url, application.name .. ".app/" .. fs.name(application.name .. ".lua")) - ecs.getFromGitHub(application.icon, application.name .. ".app/Resources/Icon.pic") - - --Если есть ресурсы, то загружаем ресурсы - if application.resources then - for i = 1, #application.resources do - ecs.getFromGitHub(application.resources[i].url, application.name .. ".app/Resources/" .. application.resources[i].name) - end - end - - --Если есть файл "о программе", то грузим и его - if application.about then - ecs.getFromGitHub(application.about .. _G.OSSettings.language .. ".txt", application.name .. ".app/Resources/About/" .. _G.OSSettings.language .. ".txt") - end - - --Если имеется режим создания ярлыка, то создаем его - if application.createShortcut then - local desktopPath = "MineOS/Desktop/" - - if application.createShortcut == "desktop" then - ecs.createShortCut(desktopPath .. fs.name(application.name) .. ".lnk", application.name .. ".app") - end - end - - --Если тип = другой, чужой, а мб и свой пастебин - elseif application.type == "Pastebin" then - ecs.getFromPastebin(application.url, application.name) - - --Если просто какой-то скрипт - elseif application.type == "Script" or application.type == "Library" or application.type == "Icon" or application.type == "Wallpaper" then - ecs.getFromGitHub(application.url, application.name) - - --А если ваще какая-то абстрактная хуйня, либо ссылка на веб, то загружаем по УРЛ-ке - else - ecs.getFileFromUrl(application.url, application.name) - end -end - ---Получить список приложений, которые требуется обновить -function ecs.getAppsToUpdate(debug) - --Задаем стартовые пути - local pathToApplicationsFile = "MineOS/System/OS/Applications.txt" - local pathToSecondApplicationsFile = "MineOS/System/OS/Applications2.txt" - --Путь к файл-листу на пастебине - local paste = "3j2x4dDn" - --Выводим инфу - local oldPixels - if debug then oldPixels = ecs.info("auto", "auto", " ", "Checking for updates...") end - --Получаем свеженький файл - ecs.getFromPastebin(paste, pathToSecondApplicationsFile) - --Читаем оба файла - local file = io.open(pathToApplicationsFile, "r") - local applications = serialization.unserialize(file:read("*a")) - file:close() - --И второй - file = io.open(pathToSecondApplicationsFile, "r") - local applications2 = serialization.unserialize(file:read("*a")) - file:close() - - local countOfUpdates = 0 - - --Просматриваем свеженький файлик и анализируем, че в нем нового, все старое удаляем - local i = 1 - while true do - --Разрыв цикла - if i > #applications2 then break end - --Новая версия файла - local newVersion, oldVersion = applications2[i].version, 0 - --Получаем старую версию этого файла - for j = 1, #applications do - if applications2[i].name == applications[j].name then - oldVersion = applications[j].version or 0 - break - end - end - --Если новая версия новее, чем старая, то добавить в массив то, что нужно обновить - if newVersion > oldVersion then - applications2[i].needToUpdate = true - countOfUpdates = countOfUpdates + 1 - end - - i = i + 1 - end - --Если чет рисовалось, то стереть на хер - if oldPixels then ecs.drawOldPixels(oldPixels) end - --Возвращаем массив с тем, че нужно обновить и просто старый аппликашнс на всякий случай - return applications2, countOfUpdates -end - ---Сделать строку пригодной для отображения в ОпенКомпах ---Заменяет табсы на пробелы и виндовый возврат каретки на человеческий UNIX-овский -function ecs.stringOptimize(sto4ka, indentatonWidth) - sto4ka = string.gsub(sto4ka, "\r\n", "\n") - sto4ka = string.gsub(sto4ka, " ", string.rep(" ", indentatonWidth or 2)) - return stro4ka -end - ---ИЗ ДЕСЯТИЧНОЙ В ШЕСТНАДЦАТИРИЧНУЮ -function ecs.decToBase(IN,BASE) - local hexCode = "0123456789ABCDEFGHIJKLMNOPQRSTUVW" - OUT = "" - local ostatok = 0 - while IN>0 do - ostatok = math.fmod(IN,BASE) + 1 - IN = math.floor(IN/BASE) - OUT = string.sub(hexCode,ostatok,ostatok)..OUT - end - if #OUT == 1 then OUT = "0"..OUT end - if OUT == "" then OUT = "00" end - return OUT -end - ---Правильное конвертирование HEX-переменной в строковую -function ecs.HEXtoString(color, bitCount, withNull) - local stro4ka = string.format("%X",color) - local sStro4ka = unicode.len(stro4ka) - if sStro4ka < bitCount then - stro4ka = string.rep("0", bitCount - sStro4ka) .. stro4ka - end - sStro4ka = nil - if withNull then return "0x"..stro4ka else return stro4ka end -end - ---КЛИКНУЛИ ЛИ В ЗОНУ -function ecs.clickedAtArea(x,y,sx,sy,ex,ey) - if (x >= sx) and (x <= ex) and (y >= sy) and (y <= ey) then return true end - return false -end - ---Заливка всего экрана указанным цветом -function ecs.clearScreen(color) - if color then component.gpu.setBackground(color) end - term.clear() -end - ---Установка пикселя нужного цвета -function ecs.setPixel(x,y,color) - component.gpu.setBackground(color) - component.gpu.set(x,y," ") -end - ---Простая установка цветов в одну строку, ибо я ленивый -function ecs.setColor(background, foreground) - component.gpu.setBackground(background) - component.gpu.setForeground(foreground) -end - ---Цветной текст -function ecs.colorText(x,y,textColor,text) - component.gpu.setForeground(textColor) - component.gpu.set(x,y,text) -end - ---Цветной текст с жопкой! -function ecs.colorTextWithBack(x,y,textColor,backColor,text) - component.gpu.setForeground(textColor) - component.gpu.setBackground(backColor) - component.gpu.set(x,y,text) -end - ---Инверсия цвета -function ecs.invertColor(color) - return 0xffffff - color -end - ---Адаптивный текст, подстраивающийся под фон -function ecs.adaptiveText(x,y,text,textColor) - component.gpu.setForeground(textColor) - x = x - 1 - for i=1,unicode.len(text) do - local info = {component.gpu.get(x+i,y)} - component.gpu.setBackground(info[3]) - component.gpu.set(x+i,y,unicode.sub(text,i,i)) - end -end - ---Костыльная замена обычному string.find() ---Работает медленнее, но хотя бы поддерживает юникод -function unicode.find(str, pattern, init, plain) - if init then - if init < 0 then - init = -#unicode.sub(str,init) - elseif init > 0 then - init = #unicode.sub(str,1,init-1)+1 - end - end - - a, b = string.find(str, pattern, init, plain) - - if a then - local ap,bp = str:sub(1,a-1), str:sub(a,b) - a = unicode.len(ap)+1 - b = a + unicode.len(bp)-1 - return a,b - else - return a - end -end - ---Умный текст по аналогии с майнчатовским. Ставишь символ параграфа, указываешь хуйню - и хуякс! Работает! -function ecs.smartText(x, y, text) - local sText = unicode.len(text) - local specialSymbol = "§" - --Разбираем по кусочкам строку и получаем цвета - local massiv = {} - local iterator = 1 - local currentColor = component.gpu.getForeground() - while iterator <= sText do - local symbol = unicode.sub(text, iterator, iterator) - if symbol == specialSymbol then - currentColor = ecs.colors[unicode.sub(text, iterator + 1, iterator + 1) or "f"] - iterator = iterator + 1 - else - table.insert(massiv, {symbol, currentColor}) - end - symbol = nil - iterator = iterator + 1 - end - x = x - 1 - for i = 1, #massiv do - if currentColor ~= massiv[i][2] then currentColor = massiv[i][2]; component.gpu.setForeground(massiv[i][2]) end - component.gpu.set(x + i, y, massiv[i][1]) - end -end - ---Аналог умного текста, но использующий HEX-цвета для кодировки -function ecs.formattedText(x, y, text, limit) - --Ограничение длины строки - limit = limit or math.huge - --Стартовая позиция курсора для отрисовки - local xPos = x - --Создаем массив символов данной строки - local symbols = {} - for i = 1, unicode.len(text) do table.insert(symbols, unicode.sub(text, i, i)) end - --Перебираем все символы строки, пока не переберем все или не достигнем указанного лимита - local i = 1 - while i <= #symbols and i <= limit do - --Если находим символ параграфа, то - if symbols[i] == "§" then - --Меняем цвет текста на указанный - component.gpu.setForeground(tonumber("0x" .. symbols[i+1] .. symbols[i+2] .. symbols[i+3] .. symbols[i+4] .. symbols[i+5] .. symbols[i+6])) - --Увеличиваем лимит на 7, т.к. - limit = limit + 7 - --Сдвигаем итератор цикла на 7 - i = i + 7 - end - --Рисуем символ на нужной позиции - component.gpu.set(xPos, y, symbols[i]) - --Увеличиваем позицию курсора и итератор на 1 - xPos = xPos + 1 - i = i + 1 - end -end - ---Инвертированный текст на основе цвета фона -function ecs.invertedText(x,y,symbol) - local info = {component.gpu.get(x,y)} - ecs.adaptiveText(x,y,symbol,ecs.invertColor(info[3])) -end - ---Адаптивное округление числа -function ecs.adaptiveRound(chislo) - local celaya,drobnaya = math.modf(chislo) - if drobnaya >= 0.5 then - return (celaya + 1) - else - return celaya - end -end - ---Округление до опред. кол-ва знаков после запятой -function ecs.round(num, idp) - local mult = 10^(idp or 0) - return math.floor(num * mult + 0.5) / mult -end - ---Обычный квадрат указанного цвета -function ecs.square(x,y,width,height,color) - component.gpu.setBackground(color) - component.gpu.fill(x,y,width,height," ") -end - ---Юникодовская рамка -function ecs.border(x, y, width, height, back, fore) - local stringUp = "┌"..string.rep("─", width - 2).."┐" - local stringDown = "└"..string.rep("─", width - 2).."┘" - component.gpu.setForeground(fore) - component.gpu.setBackground(back) - component.gpu.set(x, y, stringUp) - component.gpu.set(x, y + height - 1, stringDown) - - local yPos = 1 - for i = 1, (height - 2) do - component.gpu.set(x, y + yPos, "│") - component.gpu.set(x + width - 1, y + yPos, "│") - yPos = yPos + 1 - end -end - ---Кнопка в виде текста в рамке -function ecs.drawFramedButton(x, y, width, height, text, color) - ecs.border(x, y, width, height, component.gpu.getBackground(), color) - component.gpu.fill(x + 1, y + 1, width - 2, height - 2, " ") - x = x + math.floor(width / 2 - unicode.len(text) / 2) - y = y + math.floor(width / 2 - 1) - component.gpu.set(x, y, text) -end - ---Юникодовский разделитель -function ecs.separator(x, y, width, back, fore) - ecs.colorTextWithBack(x, y, fore, back, string.rep("─", width)) -end - ---Автоматическое центрирование текста по указанной координате (x, y, xy) -function ecs.centerText(mode,coord,text) - local dlina = unicode.len(text) - local xSize,ySize = component.gpu.getResolution() - - if mode == "x" then - component.gpu.set(math.floor(xSize/2-dlina/2),coord,text) - elseif mode == "y" then - component.gpu.set(coord,math.floor(ySize/2),text) - else - component.gpu.set(math.floor(xSize/2-dlina/2),math.floor(ySize/2),text) - end -end - ---Отрисовка "изображения" по указанному массиву -function ecs.drawCustomImage(x,y,pixels) - x = x - 1 - y = y - 1 - local pixelsWidth = #pixels[1] - local pixelsHeight = #pixels - local xEnd = x + pixelsWidth - local yEnd = y + pixelsHeight - - for i=1,pixelsHeight do - for j=1,pixelsWidth do - if pixels[i][j][3] ~= "#" then - if component.gpu.getBackground() ~= pixels[i][j][1] then component.gpu.setBackground(pixels[i][j][1]) end - if component.gpu.getForeground() ~= pixels[i][j][2] then component.gpu.setForeground(pixels[i][j][2]) end - component.gpu.set(x+j,y+i,pixels[i][j][3]) - end - end - end - - return (x+1),(y+1),xEnd,yEnd -end - ---Корректировка стартовых координат. Core-функция для всех моих программ -function ecs.correctStartCoords(xStart,yStart,xWindowSize,yWindowSize) - local xSize,ySize = component.gpu.getResolution() - if xStart == "auto" then - xStart = math.floor(xSize/2 - xWindowSize/2) - end - if yStart == "auto" then - yStart = math.ceil(ySize/2 - yWindowSize/2) - end - return xStart,yStart -end - ---Запомнить область пикселей и возвратить ее в виде массива -function ecs.rememberOldPixels(x, y, x2, y2) - local newPNGMassiv = { ["backgrounds"] = {} } - local xSize, ySize = component.gpu.getResolution() - newPNGMassiv.x, newPNGMassiv.y = x, y - - --Перебираем весь массив стандартного PNG-вида по высоте - local xCounter, yCounter = 1, 1 - for j = y, y2 do - xCounter = 1 - for i = x, x2 do - - if (i > xSize or i < 0) or (j > ySize or j < 0) then - error("Can't remember pixel, because it's located behind the screen: x("..i.."), y("..j..") out of xSize("..xSize.."), ySize("..ySize..")\n") - end - - local symbol, fore, back = component.gpu.get(i, j) - - newPNGMassiv["backgrounds"][back] = newPNGMassiv["backgrounds"][back] or {} - newPNGMassiv["backgrounds"][back][fore] = newPNGMassiv["backgrounds"][back][fore] or {} - - table.insert(newPNGMassiv["backgrounds"][back][fore], {xCounter, yCounter, symbol} ) - - xCounter = xCounter + 1 - back, fore, symbol = nil, nil, nil - end - - yCounter = yCounter + 1 - end - - xSize, ySize = nil, nil - return newPNGMassiv -end - ---Нарисовать запомненные ранее пиксели из массива -function ecs.drawOldPixels(massivSudaPihay) - --Перебираем массив с фонами - for back, backValue in pairs(massivSudaPihay["backgrounds"]) do - component.gpu.setBackground(back) - for fore, foreValue in pairs(massivSudaPihay["backgrounds"][back]) do - component.gpu.setForeground(fore) - for pixel = 1, #massivSudaPihay["backgrounds"][back][fore] do - if massivSudaPihay["backgrounds"][back][fore][pixel][3] ~= transparentSymbol then - component.gpu.set(massivSudaPihay.x + massivSudaPihay["backgrounds"][back][fore][pixel][1] - 1, massivSudaPihay.y + massivSudaPihay["backgrounds"][back][fore][pixel][2] - 1, massivSudaPihay["backgrounds"][back][fore][pixel][3]) - end - end - end - end -end - ---Ограничение длины строки. Маст-хев функция. -function ecs.stringLimit(mode, text, size, noDots) - if unicode.len(text) <= size then return text end - local length = unicode.len(text) - if mode == "start" then - if noDots then - return unicode.sub(text, length - size + 1, -1) - else - return "…" .. unicode.sub(text, length - size + 2, -1) - end - else - if noDots then - return unicode.sub(text, 1, size) - else - return unicode.sub(text, 1, size - 1) .. "…" - end - end -end - ---Получить текущее реальное время компьютера, хостящего сервер майна -function ecs.getHostTime(timezone) - timezone = timezone or 2 - --Создаем файл с записанной в него парашей - local file = io.open("HostTime.tmp", "w") - file:write("") - file:close() - --Коррекция времени на основе часового пояса - local timeCorrection = timezone * 3600 - --Получаем дату изменения файла в юникс-виде - local lastModified = tonumber(string.sub(fs.lastModified("HostTime.tmp"), 1, -4)) + timeCorrection - --Удаляем файл, ибо на хуй он нам не нужен - fs.remove("HostTime.tmp") - --Конвертируем юникс-время в норм время - local year, month, day, hour, minute, second = os.date("%Y", lastModified), os.date("%m", lastModified), os.date("%d", lastModified), os.date("%H", lastModified), os.date("%M", lastModified), os.date("%S", lastModified) - --Возвращаем все - return tonumber(day), tonumber(month), tonumber(year), tonumber(hour), tonumber(minute), tonumber(second) -end - ---Получить спискок файлов из конкретной директории, костыль -function ecs.getFileList(path) - local list = fs.list(path) - local massiv = {} - for file in list do - --if string.find(file, "%/$") then file = unicode.sub(file, 1, -2) end - table.insert(massiv, file) - end - list = nil - return massiv -end - ---Получить файловое древо. Сильно нагружает систему, только для дебага! -function ecs.getFileTree(path) - local massiv = {} - local list = ecs.getFileList(path) - for key, file in pairs(list) do - if fs.isDirectory(path.."/"..file) then - table.insert(massiv, getFileTree(path.."/"..file)) - else - table.insert(massiv, file) - end - end - list = nil - - return massiv -end - ---Поиск по файловой системе -function ecs.find(path, cheBudemIskat) - --Массив, в котором будут находиться все найденные соответствия - local massivNaydennogoGovna = {} - --Костыль, но удобный - local function dofind(path, cheBudemIskat) - --Получаем список файлов в директории - local list = ecs.getFileList(path) - --Перебираем все элементы файл листа - for key, file in pairs(list) do - --Путь к файлу - local pathToFile = path..file - --Если нашло совпадение в имени файла, то выдает путь к этому файлу - if string.find(unicode.lower(file), unicode.lower(cheBudemIskat)) then - table.insert(massivNaydennogoGovna, pathToFile) - end - --Анализ, что делать дальше - if fs.isDirectory(pathToFile) then - dofind(pathToFile, cheBudemIskat) - end - --Очищаем оперативку - pathToFile = nil - end - --Очищаем оперативку - list = nil - end - --Выполняем функцию - dofind(path, cheBudemIskat) - --Возвращаем, че нашло - return massivNaydennogoGovna -end - ---Получение формата файла -function ecs.getFileFormat(path) - local name = fs.name(path) - local starting, ending = string.find(name, "(.)%.[%d%w]*$") - if starting == nil then - return nil - else - return unicode.sub(name,starting + 1, -1) - end - name, starting, ending = nil, nil, nil -end - ---Проверить, скрытый ли файл (.пидор, .хуй = true; пидор, хуй = false) -function ecs.isFileHidden(path) - local name = fs.name(path) - local starting, ending = string.find(name, "^%.(.*)$") - if starting == nil then - return false - else - return true - end - name, starting, ending = nil, nil, nil -end - ---Скрыть формат файла -function ecs.hideFileFormat(path) - local name = fs.name(path) - local fileFormat = ecs.getFileFormat(name) - if fileFormat == nil then - return name - else - return unicode.sub(name, 1, unicode.len(name) - unicode.len(fileFormat)) - end -end - ---Ожидание клика либо нажатия какой-либо клавиши -function ecs.waitForTouchOrClick() - while true do - local e = { event.pull() } - if e[1] == "key_down" or e[1] == "touch" then break end - end -end - ---То же самое, но в сокращенном варианте -function ecs.wait() - ecs.waitForTouchOrClick() -end - ---Нарисовать кнопочки закрытия окна -function ecs.drawCloses(x, y, active) - local symbol = "⮾" - ecs.colorText(x, y , (active == 1 and ecs.colors.blue) or 0xCC4C4C, symbol) - ecs.colorText(x + 2, y , (active == 2 and ecs.colors.blue) or 0xDEDE6C, symbol) - ecs.colorText(x + 4, y , (active == 3 and ecs.colors.blue) or 0x57A64E, symbol) -end - ---Нарисовать верхнюю оконную панель с выбором объектов -function ecs.drawTopBar(x, y, width, selectedObject, background, foreground, ...) - local objects = { ... } - ecs.square(x, y, width, 3, background) - local widthOfObjects = 0 - local spaceBetween = 2 - for i = 1, #objects do - widthOfObjects = widthOfObjects + unicode.len(objects[i][1]) + spaceBetween - end - local xPos = x + math.floor(width / 2 - widthOfObjects / 2) - for i = 1, #objects do - if i == selectedObject then - ecs.square(xPos, y, unicode.len(objects[i][1]) + spaceBetween, 3, ecs.colors.blue) - component.gpu.setForeground(0xffffff) - else - component.gpu.setBackground(background) - component.gpu.setForeground(foreground) - end - component.gpu.set(xPos + spaceBetween / 2, y + 2, objects[i][1]) - component.gpu.set(xPos + math.ceil(unicode.len(objects[i][1]) / 2), y + 1, objects[i][2]) - - xPos = xPos + unicode.len(objects[i][1]) + spaceBetween - end -end - ---Нарисовать топ-меню, горизонтальная полоска такая с текстами -function ecs.drawTopMenu(x, y, width, color, selectedObject, ...) - local objects = { ... } - local objectsToReturn = {} - local xPos = x + 2 - local spaceBetween = 2 - ecs.square(x, y, width, 1, color) - for i = 1, #objects do - if i == selectedObject then - ecs.square(xPos - 1, y, unicode.len(objects[i][1]) + spaceBetween, 1, ecs.colors.blue) - component.gpu.setForeground(0xffffff) - component.gpu.set(xPos, y, objects[i][1]) - component.gpu.setForeground(objects[i][2]) - component.gpu.setBackground(color) - else - if component.gpu.getForeground() ~= objects[i][2] then component.gpu.setForeground(objects[i][2]) end - component.gpu.set(xPos, y, objects[i][1]) - end - objectsToReturn[objects[i][1]] = { xPos, y, xPos + unicode.len(objects[i][1]) - 1, y, i } - xPos = xPos + unicode.len(objects[i][1]) + spaceBetween - end - return objectsToReturn -end - ---Функция отрисовки кнопки указанной ширины -function ecs.drawButton(x,y,width,height,text,backColor,textColor) - x,y = ecs.correctStartCoords(x,y,width,height) - - local textPosX = math.floor(x + width / 2 - unicode.len(text) / 2) - local textPosY = math.floor(y + height / 2) - ecs.square(x,y,width,height,backColor) - ecs.colorText(textPosX,textPosY,textColor,text) - - return x, y, (x + width - 1), (y + height - 1) -end - ---Отрисовка кнопки с указанными отступами от текста -function ecs.drawAdaptiveButton(x,y,offsetX,offsetY,text,backColor,textColor) - local length = unicode.len(text) - local width = offsetX*2 + length - local height = offsetY*2 + 1 - - x,y = ecs.correctStartCoords(x,y,width,height) - - ecs.square(x,y,width,height,backColor) - ecs.colorText(x+offsetX,y+offsetY,textColor,text) - - return x,y,(x+width-1),(y+height-1) -end - ---Отрисовка оконной "тени" -function ecs.windowShadow(x,y,width,height) - component.gpu.setBackground(ecs.windowColors.shadow) - component.gpu.fill(x+width,y+1,2,height," ") - component.gpu.fill(x+1,y+height,width,1," ") -end - ---Просто белое окошко с тенью -function ecs.blankWindow(x,y,width,height) - local oldPixels = ecs.rememberOldPixels(x,y,x+width+1,y+height) - - ecs.square(x,y,width,height,ecs.windowColors.background) - - ecs.windowShadow(x,y,width,height) - - return oldPixels -end - ---Белое окошко, но уже с титлом вверху! -function ecs.emptyWindow(x,y,width,height,title) - - local oldPixels = ecs.rememberOldPixels(x,y,x+width+1,y+height) - - --ОКНО - component.gpu.setBackground(ecs.windowColors.background) - component.gpu.fill(x,y+1,width,height-1," ") - - --ТАБ СВЕРХУ - component.gpu.setBackground(ecs.windowColors.tab) - component.gpu.fill(x,y,width,1," ") - - --ТИТЛ - component.gpu.setForeground(ecs.windowColors.title) - local textPosX = x + math.floor(width/2-unicode.len(title)/2) -1 - component.gpu.set(textPosX,y,title) - - --ТЕНЬ - ecs.windowShadow(x,y,width,height) - - return oldPixels - -end - -function ecs.getWordsArrayFromString(s) - local words = {} - for word in string.gmatch(s, "[^%s]+") do table.insert(words, word) end - return words -end - ---Моя любимая функция ошибки C: -function ecs.error(...) - local args = {...} - local text = "" - if #args > 1 then - for i = 1, #args do - --text = text .. "[" .. i .. "] = " .. tostring(args[i]) - if type(args[i]) == "string" then args[i] = "\"" .. args[i] .. "\"" end - text = text .. tostring(args[i]) - if i ~= #args then text = text .. ", " end - end - else - text = tostring(args[1]) - end - ecs.universalWindow("auto", "auto", math.ceil(component.gpu.getResolution() * 0.45), ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x880000, "Ошибка!"}, {"EmptyLine"}, {"WrappedText", 0x262626, text}, {"EmptyLine"}, {"Button", {0x880000, 0xffffff, "OK!"}}) -end - ---Очистить экран, установить комфортные цвета и поставить курсок на 1, 1 -function ecs.prepareToExit(color1, color2) - term.setCursor(1, 1) - ecs.clearScreen(color1 or 0x333333) - component.gpu.setForeground(color2 or 0xffffff) - component.gpu.set(1, 1, "") -end - ---Конвертация из юникода в символ. Вроде норм, а вроде и не норм. Но полезно. -function ecs.convertCodeToSymbol(code) - local symbol - if code ~= 0 and code ~= 13 and code ~= 8 and code ~= 9 and code ~= 200 and code ~= 208 and code ~= 203 and code ~= 205 and not keyboard.isControlDown() then - symbol = unicode.char(code) - if keyboard.isShiftPressed then symbol = unicode.upper(symbol) end - end - return symbol -end - ---Шкала прогресса - маст-хев! -function ecs.progressBar(x, y, width, height, background, foreground, percent) - local activeWidth = math.ceil(width * percent / 100) - ecs.square(x, y, width, height, background) - ecs.square(x, y, activeWidth, height, foreground) -end - ---Окошко с прогрессбаром! Давно хотел -function ecs.progressWindow(x, y, width, percent, text, returnOldPixels) - local height = 6 - local barWidth = width - 6 - - x, y = ecs.correctStartCoords(x, y, width, height) - - local oldPixels - if returnOldPixels then - oldPixels = ecs.rememberOldPixels(x, y, x + width + 1, y + height) - end - - ecs.emptyWindow(x, y, width, height, " ") - ecs.colorTextWithBack(x + math.floor(width / 2 - unicode.len(text) / 2), y + 4, 0x000000, ecs.windowColors.background, text) - ecs.progressBar(x + 3, y + 2, barWidth, 1, 0xCCCCCC, ecs.colors.blue, percent) - - return oldPixels -end - ---Функция для ввода текста в мини-поле. -function ecs.inputText(x, y, limit, cheBiloVvedeno, background, foreground, justDrawNotEvent, maskTextWith) - limit = limit or 10 - cheBiloVvedeno = cheBiloVvedeno or "" - background = background or 0xffffff - foreground = foreground or 0x000000 - - component.gpu.setBackground(background) - component.gpu.setForeground(foreground) - component.gpu.fill(x, y, limit, 1, " ") - - local text = cheBiloVvedeno - - local function draw() - term.setCursorBlink(false) - - local dlina = unicode.len(text) - local xCursor = x + dlina - if xCursor > (x + limit - 1) then xCursor = (x + limit - 1) end - - if maskTextWith then - component.gpu.set(x, y, ecs.stringLimit("start", string.rep("●", dlina), limit)) - else - component.gpu.set(x, y, ecs.stringLimit("start", text, limit)) - end - - term.setCursor(xCursor, y) - - term.setCursorBlink(true) - end - - draw() - - if justDrawNotEvent then term.setCursorBlink(false); return cheBiloVvedeno end - - while true do - local e = {event.pull()} - if e[1] == "key_down" then - if e[4] == 14 then - term.setCursorBlink(false) - text = unicode.sub(text, 1, -2) - if unicode.len(text) < limit then component.gpu.set(x + unicode.len(text), y, " ") end - draw() - elseif e[4] == 28 then - term.setCursorBlink(false) - return text - else - local symbol = ecs.convertCodeToSymbol(e[3]) - if symbol then - text = text..symbol - draw() - end - end - elseif e[1] == "touch" then - term.setCursorBlink(false) - return text - elseif e[1] == "clipboard" then - if e[3] then - text = text..e[3] - draw() - end - end - end -end - ---Спросить, заменять ли файл (если таковой уже имеется) -function ecs.askForReplaceFile(path, includeForAllButton) - local cyka = { - {"EmptyLine"}, - {"CenterText", 0x262626, "Файл \"".. fs.name(path) .. "\" уже имеется в этом месте."}, - {"CenterText", 0x262626, "Заменить его перемещаемым объектом?"}, - {"EmptyLine"} - } - if includeForAllButton then table.insert(cyka, {"Switch", 0xF2B233, 0xffffff, 0x262626, "Для всех", true}) end - table.insert(cyka, {"Button", {0x444444, 0xFFFFFF, "Заменить"}, {0x666666, 0xFFFFFF, "Отмена"}}) - - local action = ecs.universalWindow("auto", "auto", 46, ecs.windowColors.background, true, table.unpack(cyka)) - if action[includeForAllButton and 2 or 1] == "Заменить" then - return 1, action[includeForAllButton and 1] - else - return 2, action[includeForAllButton and 1] - end -end - ---Проверить имя файла на соответствие критериям -function ecs.checkName(name, path) - --Если ввели хуйню какую-то, то - if name == "" or name == " " or name == nil then - ecs.error("Неверное имя файла.") - return false - else - --Если файл с новым путем уже существует, то - if fs.exists(path .. name) then - ecs.error("Файл \"".. name .. "\" уже имеется в этом месте.") - return false - --А если все заебок, то - else - return true - end - end -end - ---Переименование файлов (для операционки) -function ecs.rename(mainPath) - --Задаем стартовую щнягу - local name = fs.name(mainPath) - path = fs.path(mainPath) - --Рисуем окошко ввода нового имени файла - local inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Переименовать"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, name}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - --Переименовываем - if ecs.checkName(inputs[1], path) then - fs.rename(mainPath, path .. inputs[1]) - end -end - ---Создать новую папку (для операционки) -function ecs.newFolder(path) - --Рисуем окошко ввода нового имени файла - local inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новая папка"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - - if ecs.checkName(inputs[1], path) then - fs.makeDirectory(path .. inputs[1]) - end -end - ---Создать новый файл (для операционки) -function ecs.newFile(path) - --Рисуем окошко ввода нового имени файла - local inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новый файл"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - - if ecs.checkName(inputs[1], path) then - local MineOSCore = require("MineOSCore") - MineOSCore.safeLaunch(MineOSCore.paths.applications .. "/MineCode IDE.app/MineCode IDE.lua", "open", path .. inputs[1]) - end -end - ---Создать новое приложение (для операционки) -function ecs.newApplication(path, startName) - --Рисуем окошко ввода нового имени файла - local inputs - if not startName then - inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новое приложение"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Введите имя"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - end - - if ecs.checkName(inputs[1] .. ".app", path) then - local name = path .. inputs[1] .. ".app/Resources/" - fs.makeDirectory(name) - fs.copy("MineOS/System/OS/Icons/SampleIcon.pic", name .. "Icon.pic") - local file = io.open(path .. inputs[1] .. ".app/" .. inputs[1] .. ".lua", "w") - file:write("local ecs = require(\"ECSAPI\")", "\n") - file:write("ecs.universalWindow(\"auto\", \"auto\", 30, 0xeeeeee, true, {\"EmptyLine\"}, {\"CenterText\", 0x262626, \"Hello world!\"}, {\"EmptyLine\"}, {\"Button\", {0x880000, 0xffffff, \"Hello!\"}})", "\n") - file:close() - end -end - ---Создать приложение на основе существующего ЛУА-файла -function ecs.newApplicationFromLuaFile(pathToLuaFile, pathWhereToCreateApplication) - local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x000000, "Новое приложение"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Имя приложения"}, {"Input", 0x262626, 0x880000, "Путь к иконке приложения"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}}) - data[1] = data[1] or "MyApplication" - data[2] = data[2] or "MineOS/System/OS/Icons/SampleIcon.pic" - if fs.exists(data[2]) then - fs.makeDirectory(pathWhereToCreateApplication .. "/" .. data[1] .. ".app/Resources") - fs.copy(pathToLuaFile, pathWhereToCreateApplication .. "/" .. data[1] .. ".app/" .. data[1] .. ".lua") - fs.copy(data[2], pathWhereToCreateApplication .. "/" .. data[1] .. ".app/Resources/Icon.pic") - - --ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x000000, "Приложение создано!"}, {"EmptyLine"}, {"Button", {ecs.colors.green, 0xffffff, "OK"}}) - else - ecs.error("Указанный файл иконки не существует.") - return - end -end - ---Простое информационное окошечко. Возвращает старые пиксели - мало ли понадобится. -function ecs.info(x, y, title, text) - x = x or "auto" - y = y or "auto" - title = title or " " - text = text or "Sample text" - - local width = unicode.len(text) + 4 - local height = 4 - x, y = ecs.correctStartCoords(x, y, width, height) - - local oldPixels = ecs.rememberOldPixels(x, y, x + width + 1, y + height) - - ecs.emptyWindow(x, y, width, height, title) - ecs.colorTextWithBack(x + 2, y + 2, ecs.windowColors.usualText, ecs.windowColors.background, text) - - return oldPixels -end - ---Вертикальный скроллбар. Маст-хев! -function ecs.srollBar(x, y, width, height, countOfAllElements, currentElement, backColor, frontColor) - local sizeOfScrollBar = math.ceil(1 / countOfAllElements * height) - local displayBarFrom = math.floor(y + height * ((currentElement - 1) / countOfAllElements)) - - ecs.square(x, y, width, height, backColor) - ecs.square(x, displayBarFrom, width, sizeOfScrollBar, frontColor) - - sizeOfScrollBar, displayBarFrom = nil, nil -end - ---Отрисовка поля с текстом. Сюда пихать массив вида {"строка1", "строка2", "строка3", ...} -function ecs.textField(x, y, width, height, lines, displayFrom, background, foreground, scrollbarBackground, scrollbarForeground) - x, y = ecs.correctStartCoords(x, y, width, height) - - background = background or 0xffffff - foreground = foreground or ecs.windowColors.usualText - - local sLines = #lines - local lineLimit = width - 3 - - --Парсим строки - local line = 1 - while lines[line] do - local sLine = unicode.len(lines[line]) - if sLine > lineLimit then - local part1, part2 = unicode.sub(lines[line], 1, lineLimit), unicode.sub(lines[line], lineLimit + 1, -1) - lines[line] = part1 - table.insert(lines, line + 1, part2) - part1, part2 = nil, nil - end - line = line + 1 - sLine = nil - end - line = nil - - ecs.square(x, y, width - 1, height, background) - ecs.srollBar(x + width - 1, y, 1, height, sLines, displayFrom, scrollbarBackground, scrollbarForeground) - - component.gpu.setBackground(background) - component.gpu.setForeground(foreground) - local yPos = y - for i = displayFrom, (displayFrom + height - 1) do - if lines[i] then - component.gpu.set(x + 1, yPos, lines[i]) - yPos = yPos + 1 - else - break - end - end - - return sLines -end - ---Получение верного имени языка. Просто для безопасности. (для операционки) -function ecs.getCorrectLangName(pathToLangs) - local language = _G.OSSettings.language .. ".lang" - if not fs.exists(pathToLangs .. "/" .. language) then - language = "English.lang" - end - return language -end - ---Чтение языкового файла (для операционки) -function ecs.readCorrectLangFile(pathToLangs) - local lang - - local language = ecs.getCorrectLangName(pathToLangs) - - lang = config.readAll(pathToLangs .. "/" .. language) - - return lang -end - --------------------------ВСЕ ДЛЯ ОСКИ------------------------------------------------------------------------------- - -function ecs.searchInArray(array, textToSearch) - local newArray = {} - for i = 1, #array do - if string.find(unicode.lower(array[i]), unicode.lower(textToSearch)) then table.insert(newArray, array[i]) end - end - return newArray -end - -function ecs.sortFiles(path, fileList, sortingMethod, showHiddenFiles) - local sortedFileList = {} - if sortingMethod == "type" or sortingMethod == 0 then - local typeList = {} - for i = 1, #fileList do - local fileFormat = ecs.getFileFormat(fileList[i]) or "Script" - if fs.isDirectory(path .. fileList[i]) and fileFormat ~= ".app" then fileFormat = "Folder" end - typeList[fileFormat] = typeList[fileFormat] or {} - table.insert(typeList[fileFormat], fileList[i]) - end - - if typeList["Folder"] then - for i = 1, #typeList["Folder"] do - table.insert(sortedFileList, typeList["Folder"][i]) - end - typeList["Folder"] = nil - end - - for fileFormat in pairs(typeList) do - for i = 1, #typeList[fileFormat] do - table.insert(sortedFileList, typeList[fileFormat][i]) - end - end - elseif sortingMethod == "name" or sortingMethod == 1 then - sortedFileList = fileList - elseif sortingMethod == "date" or sortingMethod == 2 then - for i = 1, #fileList do - fileList[i] = {fileList[i], fs.lastModified(path .. fileList[i])} - end - table.sort(fileList, function(a,b) return a[2] > b[2] end) - for i = 1, #fileList do - table.insert(sortedFileList, fileList[i][1]) - end - else - error("Unknown sorting method: " .. tostring(sortingMethod)) - end - - local i = 1 - while i <= #sortedFileList do - if not showHiddenFiles and ecs.isFileHidden(sortedFileList[i]) then - table.remove(sortedFileList, i) - else - i = i + 1 - end - end - - return sortedFileList -end - ---Сохранить файл конфигурации ОС -function ecs.saveOSSettings() - local pathToOSSettings = "MineOS/System/OS/OSSettings.cfg" - if not _G.OSSettings then error("Массив настроек ОС отсутствует в памяти!") end - fs.makeDirectory(fs.path(pathToOSSettings)) - local file = io.open(pathToOSSettings, "w") - file:write(serialization.serialize(_G.OSSettings)) - file:close() -end - ---Загрузить файл конфигурации ОС, а если его не существует, то создать -function ecs.loadOSSettings() - local pathToOSSettings = "MineOS/System/OS/OSSettings.cfg" - if fs.exists(pathToOSSettings) then - local file = io.open(pathToOSSettings, "r") - _G.OSSettings = serialization.unserialize(file:read("*a")) - file:close() - else - _G.OSSettings = { showHelpOnApplicationStart = true, language = "Russian" } - ecs.saveOSSettings() - end -end - ---Отобразить окно с содержимым файла информации о приложении -function ecs.applicationHelp(pathToApplication) - local pathToAboutFile = pathToApplication .. "/resources/About/" .. _G.OSSettings.language .. ".txt" - if _G.OSSettings and _G.OSSettings.showHelpOnApplicationStart and fs.exists(pathToAboutFile) then - local applicationName = fs.name(pathToApplication) - local file = io.open(pathToAboutFile, "r") - local text = "" - for line in file:lines() do text = text .. line .. " " end - file:close() - - local data = ecs.universalWindow("auto", "auto", 30, 0xeeeeee, true, - {"EmptyLine"}, - {"CenterText", 0x000000, "О приложении " .. applicationName}, - {"EmptyLine"}, - {"TextField", 16, 0xFFFFFF, 0x262626, 0xcccccc, 0x353535, text}, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "OK"}, {0x999999, 0xffffff, "Больше не показывать"}} - ) - if data[1] ~= "OK" then - _G.OSSettings.showHelpOnApplicationStart = false - ecs.saveOSSettings() - end - end -end - -function ecs.correctFileNameIfFileExists(path, requestedName) - local number = 1 - local fileFormat = ecs.getFileFormat(requestedName) or "" - requestedName = ecs.hideFileFormat(requestedName) - while true do - local finalFileName = requestedName .. string.rep("-copy", number) .. fileFormat - if fs.exists(path .. "/" .. finalFileName) then - number = number + 1 - else - return finalFileName - end - end -end - ---Создать ярлык для конкретной проги (для операционки) -function ecs.createShortCut(pathToShortcut, pathToFile) - local pathToPathToShortcut = fs.path(pathToShortcut) or "/" - fs.makeDirectory(pathToPathToShortcut) - if fs.exists(pathToShortcut) then - pathToShortcut = ecs.correctFileNameIfFileExists(pathToPathToShortcut, pathToShortcut) - end - - local file = io.open(pathToShortcut, "w") - file:write("return ", "\"", pathToFile, "\"") - file:close() -end - ---Получить данные о файле из ярлыка (для операционки) -function ecs.readShortcut(path) - local success, filename = pcall(loadfile(path)) - if success then - return filename - else - error("Ошибка чтения файла ярлыка. Вероятно, он создан криво, либо не существует в папке \"" .. fs.path(path) or "" .. "\"") - end -end - ---Редактирование файла (для операционки) -function ecs.editFile(path) - ecs.prepareToExit() - shell.execute("edit " .. path) -end - ---Копирование файлов и папок -function ecs.copy(from, toFolder) - fs.makeDirectory(toFolder) - - if fs.isDirectory(from) then - local currentAction, yesToAllAction - local function recursiveFolderCopy(from, to) - for file in fs.list(from) do - local finalFromName = from .. "/" .. file - local finalToName = to .. "/" .. file - - if fs.exists(finalToName) then - if not yesToAllAction then - currentAction, yesToAll = ecs.askForReplaceFile(finalToName, true) - if yesToAll == true then yesToAllAction = true end - end - else - currentAction = 1 - end - - if currentAction == 1 then - if fs.isDirectory(finalFromName) then - fs.makeDirectory(finalToName) - recursiveFolderCopy(finalFromName, finalToName) - else - fs.copy(finalFromName, finalToName) - end - end - end - end - - recursiveFolderCopy(from, toFolder .. fs.name(from)) - else - local to = toFolder .. "/" .. fs.name(from) - local action = 1 - if fs.exists(to) then action = ecs.askForReplaceFile(to) end - if action == 1 then fs.copy(from, to) end - end -end - --- Анимация затухания экрана -function ecs.fadeOut(startColor, targetColor, speed) - local xSize, ySize = component.gpu.getResolution() - while startColor >= targetColor do - component.gpu.setBackground(startColor) - component.gpu.fill(1, 1, xSize, ySize, " ") - startColor = startColor - 0x111111 - os.sleep(speed or 0) - end -end - --- Анимация загорания экрана -function ecs.fadeIn(startColor, targetColor, speed) - local xSize, ySize = component.gpu.getResolution() - while startColor <= targetColor do - component.gpu.setBackground(startColor) - component.gpu.fill(1, 1, xSize, ySize, " ") - startColor = startColor + 0x111111 - os.sleep(speed or 0) - end -end - --- Анимация выхода в олдскул-телевизионном стиле -function ecs.TV(speed, targetColor) - local xSize, ySize = component.gpu.getResolution() - local xCenter, yCenter = math.floor(xSize / 2), math.floor(ySize / 2) - component.gpu.setBackground(targetColor or 0x000000) - - for y = 1, yCenter do - component.gpu.fill(1, y - 1, xSize, 1, " ") - component.gpu.fill(1, ySize - y + 1, xSize, 1, " ") - os.sleep(speed or 0) - end - - for x = 1, xCenter - 1 do - component.gpu.fill(x, yCenter, 1, 1, " ") - component.gpu.fill(xSize - x + 1, yCenter, 1, 1, " ") - os.sleep(speed or 0) - end - os.sleep(0.3) - component.gpu.fill(1, yCenter, xSize, 1, " ") -end - - - ----------------------------------------------ОКОШЕЧКИ------------------------------------------------------------ - - ---Описание ниже, ебана. Ниже - это значит в самой жопе кода! -function ecs.universalWindow(x, y, width, background, closeWindowAfter, ...) - local objects = {...} - local countOfObjects = #objects - - local pressedButton - local pressedMultiButton - - --Задаем высотные константы для объектов - local objectsHeights = { - ["button"] = 3, - ["centertext"] = 1, - ["emptyline"] = 1, - ["input"] = 3, - ["slider"] = 3, - ["select"] = 3, - ["selector"] = 3, - ["separator"] = 1, - ["switch"] = 1, - ["color"] = 3, - } - - --Скорректировать ширину, если нужно - local function correctWidth(newWidthForAnalyse) - width = math.max(width, newWidthForAnalyse) - end - - --Корректируем ширину - for i = 1, countOfObjects do - local objectType = string.lower(objects[i][1]) - - if objectType == "centertext" then - correctWidth(unicode.len(objects[i][3]) + 2) - elseif objectType == "slider" then --!!!!!!!!!!!!!!!!!! ВОТ ТУТ НЕ ЗАБУДЬ ФИКСАНУТЬ - correctWidth(unicode.len(objects[i][7]..tostring(objects[i][5].." ")) + 2) - elseif objectType == "select" then - for j = 4, #objects[i] do - correctWidth(unicode.len(objects[i][j]) + 2) - end - --elseif objectType == "selector" then - - --elseif objectType == "separator" then - - elseif objectType == "textfield" then - correctWidth(5) - elseif objectType == "wrappedtext" then - correctWidth(6) - elseif objectType == "button" then - --Корректируем ширину - local widthOfButtons = 0 - local maxButton = 0 - for j = 2, #objects[i] do - maxButton = math.max(maxButton, unicode.len(objects[i][j][3]) + 2) - end - widthOfButtons = maxButton * #objects[i] - correctWidth(widthOfButtons) - elseif objectType == "switch" then - local dlina = unicode.len(objects[i][5]) + 2 + 10 + 4 - correctWidth(dlina) - elseif objectType == "color" then - correctWidth(unicode.len(objects[i][2]) + 6) - end - end - - --Считаем высоту этой хуйни - local height = 0 - for i = 1, countOfObjects do - local objectType = string.lower(objects[i][1]) - if objectType == "select" then - height = height + (objectsHeights[objectType] * (#objects[i] - 3)) - elseif objectType == "textfield" then - height = height + objects[i][2] - elseif objectType == "wrappedtext" then - --Заранее парсим текст перенесенный - objects[i].wrapped = string.wrap({objects[i][3]}, width - 4) - objects[i].height = #objects[i].wrapped - height = height + objects[i].height - else - height = height + objectsHeights[objectType] - end - end - - --Коорректируем стартовые координаты - x, y = ecs.correctStartCoords(x, y, width, height) - --Запоминаем инфу о том, что было нарисовано, если это необходимо - local oldPixels, oldBackground, oldForeground - if closeWindowAfter then - oldBackground = component.gpu.getBackground() - oldForeground = component.gpu.getForeground() - oldPixels = ecs.rememberOldPixels(x, y, x + width - 1, y + height - 1) - end - --Считаем все координаты объектов - objects[1].y = y - if countOfObjects > 1 then - for i = 2, countOfObjects do - local objectType = string.lower(objects[i - 1][1]) - if objectType == "select" then - objects[i].y = objects[i - 1].y + (objectsHeights[objectType] * (#objects[i - 1] - 3)) - elseif objectType == "textfield" then - objects[i].y = objects[i - 1].y + objects[i - 1][2] - elseif objectType == "wrappedtext" then - objects[i].y = objects[i - 1].y + objects[i - 1].height - else - objects[i].y = objects[i - 1].y + objectsHeights[objectType] - end - end - end - - --Объекты для тача - local obj = {} - local function newObj(class, name, ...) - obj[class] = obj[class] or {} - obj[class][name] = {...} - end - - --Отображение объекта по номеру - local function displayObject(number, active) - local objectType = string.lower(objects[number][1]) - - if objectType == "centertext" then - local xPos = x + math.floor(width / 2 - unicode.len(objects[number][3]) / 2) - component.gpu.setForeground(objects[number][2]) - component.gpu.setBackground(background) - component.gpu.set(xPos, objects[number].y, objects[number][3]) - - elseif objectType == "input" then - - if active then - --Рамочка - ecs.border(x + 1, objects[number].y, width - 2, objectsHeights.input, background, objects[number][3]) - --Тестик - objects[number][4] = ecs.inputText(x + 3, objects[number].y + 1, width - 6, "", background, objects[number][3], false, objects[number][5]) - else - --Рамочка - ecs.border(x + 1, objects[number].y, width - 2, objectsHeights.input, background, objects[number][2]) - --Текстик - component.gpu.set(x + 3, objects[number].y + 1, ecs.stringLimit("start", objects[number][4], width - 6)) - ecs.inputText(x + 3, objects[number].y + 1, width - 6, objects[number][4], background, objects[number][2], true, objects[number][5]) - end - - newObj("Inputs", number, x + 1, objects[number].y, x + width - 2, objects[number].y + 2) - - elseif objectType == "slider" then - local widthOfSlider = width - 2 - local xOfSlider = x + 1 - local yOfSlider = objects[number].y + 1 - local countOfSliderThings = objects[number][5] - objects[number][4] - local showSliderValue= objects[number][7] - - local dolya = widthOfSlider / countOfSliderThings - local position = math.floor(dolya * objects[number][6]) - --Костыль - if (xOfSlider + position) > (xOfSlider + widthOfSlider - 1) then position = widthOfSlider - 2 end - - --Две линии - ecs.separator(xOfSlider, yOfSlider, position, background, objects[number][3]) - ecs.separator(xOfSlider + position, yOfSlider, widthOfSlider - position, background, objects[number][2]) - --Слудир - ecs.square(xOfSlider + position, yOfSlider, 2, 1, objects[number][3]) - - --Текстик под слудиром - if showSliderValue then - local text = showSliderValue .. tostring(objects[number][6]) .. (objects[number][8] or "") - local textPos = (xOfSlider + widthOfSlider / 2 - unicode.len(text) / 2) - ecs.square(x, yOfSlider + 1, width, 1, background) - ecs.colorText(textPos, yOfSlider + 1, objects[number][2], text) - end - - newObj("Sliders", number, xOfSlider, yOfSlider, x + widthOfSlider, yOfSlider, dolya) - - elseif objectType == "select" then - local usualColor = objects[number][2] - local selectionColor = objects[number][3] - - objects[number].selectedData = objects[number].selectedData or 1 - - local symbol = "✔" - local yPos = objects[number].y - for i = 4, #objects[number] do - --Коробка для галочки - ecs.border(x + 1, yPos, 5, 3, background, usualColor) - --Текст - component.gpu.set(x + 7, yPos + 1, objects[number][i]) - --Галочка - if objects[number].selectedData == (i - 3) then - ecs.colorText(x + 3, yPos + 1, selectionColor, symbol) - else - component.gpu.set(x + 3, yPos + 1, " ") - end - - obj["Selects"] = obj["Selects"] or {} - obj["Selects"][number] = obj["Selects"][number] or {} - obj["Selects"][number][i - 3] = { x + 1, yPos, x + width - 2, yPos + 2 } - - yPos = yPos + objectsHeights.select - end - - elseif objectType == "selector" then - local borderColor = objects[number][2] - local arrowColor = objects[number][3] - local selectorWidth = width - 2 - objects[number].selectedElement = objects[number].selectedElement or objects[number][4] - - local topLine = "┌" .. string.rep("─", selectorWidth - 6) .. "┬───┐" - local midLine = "│" .. string.rep(" ", selectorWidth - 6) .. "│ │" - local botLine = "└" .. string.rep("─", selectorWidth - 6) .. "┴───┘" - - local yPos = objects[number].y - - local function bordak(borderColor) - component.gpu.setBackground(background) - component.gpu.setForeground(borderColor) - component.gpu.set(x + 1, objects[number].y, topLine) - component.gpu.set(x + 1, objects[number].y + 1, midLine) - component.gpu.set(x + 1, objects[number].y + 2, botLine) - component.gpu.set(x + 3, objects[number].y + 1, ecs.stringLimit("start", objects[number].selectedElement, width - 6)) - ecs.colorText(x + width - 4, objects[number].y + 1, arrowColor, "▼") - end - - bordak(borderColor) - - --Выпадающий список, самый гемор, блядь - if active then - local xPos, yPos = x + 1, objects[number].y + 3 - local spisokWidth = width - 2 - local countOfElements = #objects[number] - 3 - local spisokHeight = countOfElements + 1 - local oldPixels = ecs.rememberOldPixels( xPos, yPos, xPos + spisokWidth - 1, yPos + spisokHeight - 1) - - local coords = {} - - bordak(arrowColor) - - --Рамку рисуем поверх фоника - local topLine = "├"..string.rep("─", spisokWidth - 6).."┴───┤" - local midLine = "│"..string.rep(" ", spisokWidth - 2).."│" - local botLine = "└"..string.rep("─", selectorWidth - 2) .. "┘" - ecs.colorTextWithBack(xPos, yPos - 1, arrowColor, background, topLine) - for i = 1, spisokHeight - 1 do - component.gpu.set(xPos, yPos + i - 1, midLine) - end - component.gpu.set(xPos, yPos + spisokHeight - 1, botLine) - - --Элементы рисуем - xPos = xPos + 2 - for i = 1, countOfElements do - ecs.colorText(xPos, yPos, borderColor, ecs.stringLimit("start", objects[number][i + 3], spisokWidth - 4)) - coords[i] = {xPos - 1, yPos, xPos + spisokWidth - 4, yPos} - yPos = yPos + 1 - end - - --Обработка - local exit - while true do - if exit then break end - local e = {event.pull()} - if e[1] == "touch" then - for i = 1, #coords do - if ecs.clickedAtArea(e[3], e[4], coords[i][1], coords[i][2], coords[i][3], coords[i][4]) then - ecs.square(coords[i][1], coords[i][2], spisokWidth - 2, 1, ecs.colors.blue) - ecs.colorText(coords[i][1] + 1, coords[i][2], 0xffffff, objects[number][i + 3]) - os.sleep(0.3) - objects[number].selectedElement = objects[number][i + 3] - exit = true - break - end - end - end - end - - ecs.drawOldPixels(oldPixels) - end - - newObj("Selectors", number, x + 1, objects[number].y, x + width - 2, objects[number].y + 2) - - elseif objectType == "separator" then - ecs.separator(x, objects[number].y, width, background, objects[number][2]) - - elseif objectType == "textfield" then - newObj("TextFields", number, x + 1, objects[number].y, x + width - 2, objects[number].y + objects[number][2] - 1) - if not objects[number].strings then objects[number].strings = string.wrap({objects[number][7]}, width - 3) end - objects[number].displayFrom = objects[number].displayFrom or 1 - ecs.textField(x, objects[number].y, width, objects[number][2], objects[number].strings, objects[number].displayFrom, objects[number][3], objects[number][4], objects[number][5], objects[number][6]) - - elseif objectType == "wrappedtext" then - component.gpu.setBackground(background) - component.gpu.setForeground(objects[number][2]) - for i = 1, #objects[number].wrapped do - component.gpu.set(x + 2, objects[number].y + i - 1, objects[number].wrapped[i]) - end - - elseif objectType == "button" then - - obj["MultiButtons"] = obj["MultiButtons"] or {} - obj["MultiButtons"][number] = {} - - local widthOfButton = math.floor(width / (#objects[number] - 1)) - - local xPos, yPos = x, objects[number].y - for i = 1, #objects[number] do - if type(objects[number][i]) == "table" then - local x1, y1, x2, y2 = ecs.drawButton(xPos, yPos, widthOfButton, 3, objects[number][i][3], objects[number][i][1], objects[number][i][2]) - table.insert(obj["MultiButtons"][number], {x1, y1, x2, y2, widthOfButton}) - xPos = x2 + 1 - - if i == #objects[number] then - ecs.square(xPos, yPos, x + width - xPos, 3, objects[number][i][1]) - obj["MultiButtons"][number][i - 1][5] = obj["MultiButtons"][number][i - 1][5] + x + width - xPos - end - - x1, y1, x2, y2 = nil, nil, nil, nil - end - end - - elseif objectType == "switch" then - - local xPos, yPos = x + 2, objects[number].y - local activeColor, passiveColor, textColor, text, state = objects[number][2], objects[number][3], objects[number][4], objects[number][5], objects[number][6] - local switchWidth = 8 - ecs.colorTextWithBack(xPos, yPos, textColor, background, text) - - xPos = x + width - switchWidth - 2 - if state then - ecs.square(xPos, yPos, switchWidth, 1, activeColor) - ecs.square(xPos + switchWidth - 2, yPos, 2, 1, passiveColor) - --ecs.colorTextWithBack(xPos + 4, yPos, passiveColor, activeColor, "ON") - else - ecs.square(xPos, yPos, switchWidth, 1, passiveColor - 0x444444) - ecs.square(xPos, yPos, 2, 1, passiveColor) - --ecs.colorTextWithBack(xPos + 4, yPos, passiveColor, passiveColor - 0x444444, "OFF") - end - newObj("Switches", number, xPos, yPos, xPos + switchWidth - 1, yPos) - - elseif objectType == "color" then - local xPos, yPos = x + 1, objects[number].y - local blendedColor = require("colorlib").alphaBlend(objects[number][3], 0xFFFFFF, 180) - local w = width - 2 - - ecs.colorTextWithBack(xPos, yPos + 2, blendedColor, background, string.rep("▀", w)) - ecs.colorText(xPos, yPos, objects[number][3], string.rep("▄", w)) - ecs.square(xPos, yPos + 1, w, 1, objects[number][3]) - - ecs.colorText(xPos + 1, yPos + 1, 0xffffff - objects[number][3], objects[number][2]) - newObj("Colors", number, xPos, yPos, x + width - 2, yPos + 2) - end - end - - --Отображение всех объектов - local function displayAllObjects() - for i = 1, countOfObjects do - displayObject(i) - end - end - - --Подготовить массив возвращаемый - local function getReturn() - local massiv = {} - - for i = 1, countOfObjects do - local type = string.lower(objects[i][1]) - - if type == "button" then - table.insert(massiv, pressedButton) - elseif type == "input" then - table.insert(massiv, objects[i][4]) - elseif type == "select" then - table.insert(massiv, objects[i][objects[i].selectedData + 3]) - elseif type == "selector" then - table.insert(massiv, objects[i].selectedElement) - elseif type == "slider" then - table.insert(massiv, objects[i][6]) - elseif type == "switch" then - table.insert(massiv, objects[i][6]) - elseif type == "color" then - table.insert(massiv, objects[i][3]) - else - table.insert(massiv, nil) - end - end - - return massiv - end - - local function redrawBeforeClose() - if closeWindowAfter then - ecs.drawOldPixels(oldPixels) - component.gpu.setBackground(oldBackground) - component.gpu.setForeground(oldForeground) - end - end - - --Рисуем окно - ecs.square(x, y, width, height, background) - displayAllObjects() - - while true do - local e = {event.pull()} - if e[1] == "touch" or e[1] == "drag" then - - --Анализируем клик на кнопки - if obj["MultiButtons"] then - for key in pairs(obj["MultiButtons"]) do - for i = 1, #obj["MultiButtons"][key] do - if ecs.clickedAtArea(e[3], e[4], obj["MultiButtons"][key][i][1], obj["MultiButtons"][key][i][2], obj["MultiButtons"][key][i][3], obj["MultiButtons"][key][i][4]) then - ecs.drawButton(obj["MultiButtons"][key][i][1], obj["MultiButtons"][key][i][2], obj["MultiButtons"][key][i][5], 3, objects[key][i + 1][3], objects[key][i + 1][2], objects[key][i + 1][1]) - os.sleep(0.3) - pressedButton = objects[key][i + 1][3] - redrawBeforeClose() - return getReturn() - end - end - end - end - - --А теперь клик на инпуты! - if obj["Inputs"] then - for key in pairs(obj["Inputs"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Inputs"][key][1], obj["Inputs"][key][2], obj["Inputs"][key][3], obj["Inputs"][key][4]) then - displayObject(key, true) - displayObject(key) - break - end - end - end - - --А теперь галочковыбор! - if obj["Selects"] then - for key in pairs(obj["Selects"]) do - for i in pairs(obj["Selects"][key]) do - if ecs.clickedAtArea(e[3], e[4], obj["Selects"][key][i][1], obj["Selects"][key][i][2], obj["Selects"][key][i][3], obj["Selects"][key][i][4]) then - objects[key].selectedData = i - displayObject(key) - break - end - end - end - end - - --Хм, а вот и селектор подъехал! - if obj["Selectors"] then - for key in pairs(obj["Selectors"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Selectors"][key][1], obj["Selectors"][key][2], obj["Selectors"][key][3], obj["Selectors"][key][4]) then - displayObject(key, true) - displayObject(key) - break - end - end - end - - --Слайдеры, епта! "Потный матан", все делы - if obj["Sliders"] then - for key in pairs(obj["Sliders"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Sliders"][key][1], obj["Sliders"][key][2], obj["Sliders"][key][3], obj["Sliders"][key][4]) then - local xOfSlider, dolya = obj["Sliders"][key][1], obj["Sliders"][key][5] - local currentPixels = e[3] - xOfSlider - local currentValue = math.floor(currentPixels / dolya) - --Костыль - if e[3] == obj["Sliders"][key][3] then currentValue = objects[key][5] end - objects[key][6] = currentValue or objects[key][6] - displayObject(key) - break - end - end - end - - if obj["Switches"] then - for key in pairs(obj["Switches"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Switches"][key][1], obj["Switches"][key][2], obj["Switches"][key][3], obj["Switches"][key][4]) then - objects[key][6] = not objects[key][6] - displayObject(key) - break - end - end - end - - if obj["Colors"] then - for key in pairs(obj["Colors"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Colors"][key][1], obj["Colors"][key][2], obj["Colors"][key][3], obj["Colors"][key][4]) then - local oldColor = objects[key][3] - objects[key][3] = 0xffffff - objects[key][3] - displayObject(key) - os.sleep(0.3) - objects[key][3] = oldColor - displayObject(key) - - local paletteWidth, paletteHeight = 75, 27 - local screenWidth, screenHeight = component.gpu.getResolution() - local paletteX, paletteY = math.floor(screenWidth / 2 - paletteWidth / 2), math.floor(screenHeight / 2 - paletteHeight / 2) - local oldPixels = ecs.rememberOldPixels(paletteX, paletteY, paletteX + paletteWidth - 1, paletteY + paletteHeight - 1) - local color = require("palette").draw("auto", "auto", objects[key][3]) - ecs.drawOldPixels(oldPixels) - objects[key][3] = color or oldColor - - displayObject(key) - break - end - end - end - - elseif e[1] == "scroll" then - if obj["TextFields"] then - for key in pairs(obj["TextFields"]) do - if ecs.clickedAtArea(e[3], e[4], obj["TextFields"][key][1], obj["TextFields"][key][2], obj["TextFields"][key][3], obj["TextFields"][key][4]) then - if e[5] == 1 then - if objects[key].displayFrom > 1 then objects[key].displayFrom = objects[key].displayFrom - 1; displayObject(key) end - else - if objects[key].displayFrom < #objects[key].strings then objects[key].displayFrom = objects[key].displayFrom + 1; displayObject(key) end - end - end - end - end - elseif e[1] == "key_down" then - if e[4] == 28 then - redrawBeforeClose() - return getReturn() - end - end - end -end - ---Демонстрационное окно, показывающее всю мощь universalWindow -function ecs.demoWindow() - --Очищаем экран перед юзанием окна и ставим курсор на 1, 1 - ecs.prepareToExit() - --Рисуем окно и получаем данные после взаимодействия с ним - local data = ecs.universalWindow("auto", "auto", 36, 0xeeeeee, true, - {"EmptyLine"}, - {"CenterText", 0x880000, "Здорово, ебана!"}, - {"EmptyLine"}, - {"Input", 0x262626, 0x880000, "Сюда вводить можно"}, - {"Selector", 0x262626, 0x880000, "Выбор формата", "PNG", "JPG", "GIF", "PSD"}, - {"EmptyLine"}, - {"WrappedText", 0x262626, "Тест автоматического переноса букв в зависимости от ширины данного окна. Пока что тупо режет на куски, не особо красиво."}, - {"EmptyLine"}, - {"Select", 0x262626, 0x880000, "Я пидор", "Я не пидор"}, - {"Slider", 0x262626, 0x880000, 1, 100, 50, "Убито ", " младенцев"}, - {"EmptyLine"}, - {"Separator", 0xaaaaaa}, - {"Switch", 0xF2B233, 0xffffff, 0x262626, "✈ Авиарежим", false}, - {"EmptyLine"}, - {"Switch", 0x3366CC, 0xffffff, 0x262626, "☾ Не беспокоить", true}, - {"Separator", 0xaaaaaa}, - {"EmptyLine"}, - {"TextField", 5, 0xffffff, 0x262626, 0xcccccc, 0x3366CC, "Тест текстового информационного поля. По сути это тот же самый WrappedText, разве что эта хрень ограничена по высоте, и ее можно скроллить. Ну же, поскролль меня! Скролль меня полностью! Моя жадная пизда жаждет твой хуй!"}, - {"Color", "Цвет фона", 0xFF0000}, - {"EmptyLine"}, - {"Button", {0x57A64E, 0xffffff, "Да"}, {0xF2B233, 0xffffff, "Нет"}, {0xCC4C4C, 0xffffff, "Отмена"}} - ) - --Еще разок - ecs.prepareToExit() - --Выводим данные - print(" ") - print("Вывод данных из окна:") - for i = 1, #data do print("["..i.."] = "..tostring(data[i])) end - print(" ") -end - --- ecs.demoWindow() - ---[[ -Функция universalWindow(x, y, width, background, closeWindowAfter, ...) - - Это универсальная модульная функция для максимально удобного и быстрого отображения - необходимой вам информации. С ее помощью вводить данные с клавиатуры, осуществлять выбор - из предложенных вариантов, рисовать красивые кнопки, отрисовывать обычный текст, - отрисовывать текстовые поля с возможностью прокрутки, рисовать разделители и прочее. - Любой объект выделяется с помощью клика мыши, после чего функция приступает к работе - с этим объектом. - -Аргументы функции: - - x и y: это числа, обозначающие стартовые координаты левого верхнего угла данного окна. - Вместо цифр вы также можете написать "auto" - и программа автоматически разместит окно - по центру экрана по выбранной координате. Или по обеим координатам, если вам угодно. - - width: это ширина окна, которую вы можете задать по собственному желанию. Если некторые - объекты требуют расширения окна, то окно будет автоматически расширено до нужной ширины. - Да, вот такая вот тавтология ;) - - background: базовый цвет окна (цвет фона, кому как понятнее). - - closeWindowAfter: eсли true, то окно по завершению функции будет выгружено, а на его месте - отрисуются пиксели, которые имелись на экране до выполнения функции. Удобно, если не хочешь - париться с перерисовкой интерфейса. - - ... : многоточием тут является перечень объектов, указанных через запятую. Каждый объект - является массивом и имеет собственный формат. Ниже перечислены все возможные типы объектов. - - {"Button", {Цвет кнопки1, Цвет текста на кнопке1, Сам текст1}, {Цвет кнопки2, Цвет текста на кнопке2, Сам текст2}, ...} - - Это объект для рисования кнопок. Каждая кнопка - это массив, состоящий из трех элементов: - цвета кнопки, цвета текста на кнопке и самого текста. Кнопок может быть неограниченное количество, - однако чем их больше, тем большее требуется разрешение экрана по ширине. - - Интерактивный объект. - - {"Input", Цвет рамки и текста, Цвет при выделении, Стартовый текст [, Маскировать символом]} - - Объект для рисования полей ввода текстовой информации. Удобно для открытия или сохранения файлов, - Опциональный аргумент "Маскировать символом" полезен, если вы делаете поле для ввода пароля. - Никто не увидит ваш текст. В качестве данного аргумента передается символ, например "*". - - Интерактивный объект. - - {"Selector", Цвет рамки, Цвет при выделении, Выбор 1, Выбор 2, Выбор 3 ...} - - Внешне схож с объектом "Input", однако в этом случае вы будете выбирать один из предложенных - вариантов из выпадающего списка. По умолчанию выбран первый вариант. - - Интерактивный объект. - - {"Select", Цвет рамки, Цвет галочки, Выбор 1, Выбор 2, Выбор 3 ...} - - Объект выбора. Отличается от "Selector" тем, что здесь вы выбираете один из вариантов, отмечая - его галочкой. По умолчанию выбран первый вариант. - - Интерактивный объект. - - {"Slider", Цвет линии слайдера, Цвет пимпочки слайдера, Значения слайдера ОТ, Значения слайдера ДО, Текущее значение [, Текст-подсказка ДО] [, Текст-подсказка ПОСЛЕ]} - - Ползунок, позволяющий задавать определенное количество чего-либо в указанном интервале. Имеются два - опциональных аргумента, позволяющих четко понимать, с чем именно мы имеем дело. - - К примеру, если аргумент "Текст-подсказка ДО" будет равен "Съедено ", а аргумент "Текст-подсказка ПОСЛЕ" - будет равен " яблок", а значение слайдера будет равно 50, то на экране будет написано "Съедено 50 яблок". - - Интерактивный объект. - - {"Switch", Активный цвет, Пассивный цвет, Цвет текста, Текст, Состояние} - - Переключатель, принимающий два состояния: true или false. Текст - это всего лишь информация, некое - название данного переключателя. - - Интерактивный объект. - - {"CenterText", Цвет текста, Сам текст} - - Отображение текста указанного цвета по центру окна. Чисто для информативных целей. - - {"WrappedText", Цвет текста, Текст} - - Отображение большого количества текста с автоматическим переносом. Прото режет слова на кусочки, - перенос символический. Чисто для информативных целей. - - {"TextField", Высота, Цвет фона, Цвет текста, Цвет скроллбара, Цвет пимпочки скроллбара, Сам текст} - - Текстовое поле с возможностью прокрутки. Отличается от "WrappedText" - фиксированной высотой. Чисто для информативных целей. - - {"Separator", Цвет разделителя} - - Линия-разделитель, помогающая лучше отделять объекты друг от друга. Декоративный объект. - - {"EmptyLine"} - - Пустое пространство, помогающая лучше отделять объекты друг от друга. Декоративный объект. - - Каждый из объектов рисуется по порядку сверху вниз. Каждый объект автоматически - увеличивает высоту окна до необходимого значения. Если объектов будет указано слишком много - - т.е. если окно вылезет за пределы экрана, то программа завершится с ошибкой. - - Что возвращает функция: - - Возвратом является массив, пронумерованный от 1 до <количества объектов>. - К примеру, 1 индекс данного массива соответствует 1 указанному объекту. - Каждый индекс данного массива несет в себе какие-то данные, которые вы - внесли в объект во время работы функции. - Например, если в 1-ый объект типа "Input" вы ввели фразу "Hello world", - то первый индекс в возвращенном массиве будет равен "Hello world". - Конкретнее это будет вот так: massiv[1] = "Hello world". - - Если взаимодействие с объектом невозможно - например, как в случае - с EmptyLine, CenterText, TextField или Separator, то в возвращенном - массиве этот объект указываться не будет. - - Готовые примеры использования функции указаны ниже и закомментированы. - Выбирайте нужный и раскомментируйте. -]] - ---Функция-демонстратор, показывающая все возможные объекты в одном окне. Код окна находится выше. ---ecs.demoWindow() - ---Функция, предлагающая сохранить файл в нужном месте в нужном формате. ---ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Сохранить как"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Путь"}, {"Selector", 0x262626, 0x880000, "PNG", "JPG", "PSD"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK!"}}) - ----------------------------------------------------------------------------------------------------- - -return ecs - - -F lib/GUI.lua ------------------------------------------ Libraries ----------------------------------------- - -require("advancedLua") -local keyboard = require("keyboard") -local buffer = require("doubleBuffering") -local unicode = require("unicode") -local event = require("event") -local syntax = require("syntax") -local fs = require("filesystem") - ------------------------------------------ Core constants ----------------------------------------- - -local GUI = {} - -GUI.alignment = { - horizontal = enum( - "left", - "center", - "right" - ), - vertical = enum( - "top", - "center", - "bottom" - ) -} - -GUI.directions = enum( - "horizontal", - "vertical" -) - -GUI.colors = { - disabled = { - background = 0x888888, - text = 0xAAAAAA - }, - contextMenu = { - separator = 0xAAAAAA, - default = { - background = 0xFFFFFF, - text = 0x2D2D2D - }, - disabled = { - text = 0xAAAAAA - }, - pressed = { - background = 0x3366CC, - text = 0xFFFFFF - }, - transparency = { - background = 20, - shadow = 50 - } - } -} - -GUI.dropDownMenuElementTypes = enum( - "default", - "separator" -) - -GUI.objectTypes = enum( - "unknown", - "empty", - "panel", - "label", - "button", - "framedButton", - "image", - "windowActionButtons", - "windowActionButton", - "tabBar", - "tabBarTab", - "menu", - "menuItem", - "window", - "inputTextBox", - "textBox", - "horizontalSlider", - "switch", - "progressBar", - "chart", - "comboBox", - "scrollBar", - "codeView", - "treeView", - "colorSelector" -) - ------------------------------------------ Primitive objects ----------------------------------------- - --- Universal method to check if object was clicked by following coordinates -local function isObjectClicked(object, x, y) - if x >= object.x and y >= object.y and x <= object.x + object.width - 1 and y <= object.y + object.height - 1 and not object.disabled and not object.isHidden then return true end - return false -end - --- Limit object's text field to its' size -local function objectTextLimit(object) - local text, textLength = object.text, unicode.len(object.text) - if textLength > object.width then text = unicode.sub(text, 1, object.width); textLength = object.width end - return text, textLength -end - --- Base object to use in everything -function GUI.object(x, y, width, height) - return { - x = x, - y = y, - width = width, - height = height, - isClicked = isObjectClicked - } -end - -function GUI.point(x, y) - return { x = x, y = y } -end - ------------------------------------------ Object alignment ----------------------------------------- - --- Set children alignment in parent object -function GUI.setAlignment(object, horizontalAlignment, verticalAlignment) - object.alignment = { - horizontal = horizontalAlignment, - vertical = verticalAlignment - } - return object -end - --- Get subObject position inside of parent object -function GUI.getAlignmentCoordinates(object, subObject) - local x, y - if object.alignment.horizontal == GUI.alignment.horizontal.left then - x = object.x - elseif object.alignment.horizontal == GUI.alignment.horizontal.center then - x = math.floor(object.x + object.width / 2 - subObject.width / 2) - elseif object.alignment.horizontal == GUI.alignment.horizontal.right then - x = object.x + object.width - subObject.width - else - error("Unknown horizontal alignment: " .. tostring(object.alignment.horizontal)) - end - - if object.alignment.vertical == GUI.alignment.vertical.top then - y = object.y - elseif object.alignment.vertical == GUI.alignment.vertical.center then - y = math.floor(object.y + object.height / 2 - subObject.height / 2) - elseif object.alignment.vertical == GUI.alignment.vertical.bottom then - y = object.y + object.height - subObject.height - else - error("Unknown vertical alignment: " .. tostring(object.alignment.vertical)) - end - - return x, y -end - ------------------------------------------ Containers ----------------------------------------- - --- Go recursively through every container's object (including other containers) and return object that was clicked firstly by it's GUI-layer position -function GUI.getClickedObject(container, xEvent, yEvent) - local clickedObject, clickedIndex - for childIndex = #container.children, 1, -1 do - if not container.children[childIndex].isHidden then - container.children[childIndex].x, container.children[childIndex].y = container.children[childIndex].localPosition.x + container.x - 1, container.children[childIndex].localPosition.y + container.y - 1 - if container.children[childIndex].children and #container.children[childIndex].children > 0 then - clickedObject, clickedIndex = GUI.getClickedObject(container.children[childIndex], xEvent, yEvent) - if clickedObject then break end - elseif container.children[childIndex]:isClicked(xEvent, yEvent) then - clickedObject, clickedIndex = container.children[childIndex], childIndex - break - end - end - end - - return clickedObject, clickedIndex -end - -local function checkObjectParentExists(object) - if not object.parent then error("Object doesn't have a parent container") end -end - -local function containerObjectIndexOf(object) - checkObjectParentExists(object) - for objectIndex = 1, #object.parent.children do - if object.parent.children[objectIndex] == object then - return objectIndex - end - end -end - --- Move container's object "closer" to our eyes -local function containerObjectMoveForward(object) - local objectIndex = object:indexOf() - if objectIndex < #object.parent.children then - object.parent.children[index], object.parent.children[index + 1] = swap(object.parent.children[index], object.parent.children[index + 1]) - end -end - --- Move container's object "more far out" of our eyes -local function containerObjectMoveBackward(object) - local objectIndex = object:indexOf() - if objectIndex > 1 then - object.parent.children[index], object.parent.children[index - 1] = swap(object.parent.children[index], object.parent.children[index - 1]) - end -end - --- Move container's object to front of all objects -local function containerObjectMoveToFront(object) - local objectIndex = object:indexOf() - table.insert(object.parent.children, object) - table.remove(object.parent.children, objectIndex) -end - --- Move container's object to back of all objects -local function containerObjectMoveToBack(object) - local objectIndex = object:indexOf() - table.insert(object.parent.children, 1, object) - table.remove(object.parent.children, objectIndex + 1) -end - -local function containerGetFirstParent(object) - if object.parent then - local currentParent = object.parent - while currentParent.parent do - currentParent = currentParent.parent - end - return currentParent - else - error("Object doesn't have any parents") - end -end - --- Add any object as children to parent container with specified objectType -function GUI.addChildToContainer(container, object, objectType) - object.type = objectType or GUI.objectTypes.unknown - object.parent = container - object.indexOf = containerObjectIndexOf - object.moveToFront = containerObjectMoveToFront - object.moveToBack = containerObjectMoveToBack - object.moveForward = containerObjectMoveForward - object.moveBackward = containerObjectMoveBackward - object.getFirstParent = containerGetFirstParent - object.localPosition = {x = object.x, y = object.y} - - table.insert(container.children, object) - - return object -end - --- Add empty GUI.object to container -local function addEmptyObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.object(...), GUI.objectTypes.empty) -end - --- Add button object to container -local function addButtonObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.button(...), GUI.objectTypes.button) -end - --- Add adaptive button object to container -local function addAdaptiveButtonObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.adaptiveButton(...), GUI.objectTypes.button) -end - --- Add framedButton object to container -local function addFramedButtonObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.framedButton(...), GUI.objectTypes.button) -end - --- Add adaptive framedButton object to container -local function addAdaptiveFramedButtonObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.adaptiveFramedButton(...), GUI.objectTypes.button) -end - --- Add label object to container -local function addLabelObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.label(...), GUI.objectTypes.label) -end - --- Add panel object to container -local function addPanelObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.panel(...), GUI.objectTypes.panel) -end - --- Add windowActionButtons object to container -local function addWindowActionButtonsObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.windowActionButtons(...), GUI.objectTypes.windowActionButtons) -end - --- Add another container to container -local function addContainerToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.container(...), GUI.objectTypes.container) -end - --- Add image object to container -local function addImageObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.image(...), GUI.objectTypes.image) -end - --- Add image object to container -local function addTabBarObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.tabBar(...), GUI.objectTypes.tabBar) -end - --- Add InputTextBox object to container -local function addInputTextBoxObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.inputTextBox(...), GUI.objectTypes.inputTextBox) -end - --- Add TextBox object to container -local function addTextBoxObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.textBox(...), GUI.objectTypes.textBox) -end - --- Add Horizontal Slider object to container -local function addHorizontalSliderObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.horizontalSlider(...), GUI.objectTypes.horizontalSlider) -end - --- Add Progressbar object to container -local function addProgressBarObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.progressBar(...), GUI.objectTypes.progressBar) -end - --- Add Switch object to container -local function addSwitchObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.switch(...), GUI.objectTypes.switch) -end - --- Add Chart object to container -local function addChartObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.chart(...), GUI.objectTypes.chart) -end - --- Add ComboBox object to container -local function addComboBoxObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.comboBox(...), GUI.objectTypes.comboBox) -end - --- Add Menu object to container -local function addMenuObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.menu(...), GUI.objectTypes.menu) -end - --- Add ScrollBar object to container -local function addScrollBarObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.scrollBar(...), GUI.objectTypes.scrollBar) -end - --- Add CodeView object to container -local function addCodeViewObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.codeView(...), GUI.objectTypes.codeView) -end - --- Add TreeView object to container -local function addTreeViewObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.treeView(...), GUI.objectTypes.treeView) -end - --- Add ColorSelector object to container -local function addColorSelectorObjectToContainer(container, ...) - return GUI.addChildToContainer(container, GUI.colorSelector(...), GUI.objectTypes.colorSelector) -end - --- Recursively draw container's content including all children container's content -local function drawContainerContent(container) - for objectIndex = 1, #container.children do - if not container.children[objectIndex].isHidden then - container.children[objectIndex].x, container.children[objectIndex].y = container.children[objectIndex].localPosition.x + container.x - 1, container.children[objectIndex].localPosition.y + container.y - 1 - if container.children[objectIndex].children then - -- cyka blyad - -- drawContainerContent(container.children[objectIndex]) - -- We use :draw() method against of recursive call. The reason is possible user-defined :draw() reimplementations - container.children[objectIndex]:draw() - else - -- if container.children[objectIndex].draw then - container.children[objectIndex]:draw() - -- else - -- error("Container object with index " .. objectIndex .. " doesn't have :draw() method") - -- end - end - end - end - - return container -end - --- Delete every container's children object -local function deleteContainersContent(container) - for objectIndex = 1, #container.children do container.children[objectIndex] = nil end -end - --- Universal container to store any other objects like buttons, labels, etc -function GUI.container(x, y, width, height) - local container = GUI.object(x, y, width, height) - container.children = {} - container.draw = drawContainerContent - container.getClickedObject = GUI.getClickedObject - container.deleteChildren = deleteContainersContent - - container.addChild = GUI.addChildToContainer - container.addObject = addEmptyObjectToContainer - container.addContainer = addContainerToContainer - container.addPanel = addPanelObjectToContainer - container.addLabel = addLabelObjectToContainer - container.addButton = addButtonObjectToContainer - container.addAdaptiveButton = addAdaptiveButtonObjectToContainer - container.addFramedButton = addFramedButtonObjectToContainer - container.addAdaptiveFramedButton = addAdaptiveFramedButtonObjectToContainer - container.addWindowActionButtons = addWindowActionButtonsObjectToContainer - container.addImage = addImageObjectToContainer - container.addTabBar = addTabBarObjectToContainer - container.addTextBox = addTextBoxObjectToContainer - container.addInputTextBox = addInputTextBoxObjectToContainer - container.addHorizontalSlider = addHorizontalSliderObjectToContainer - container.addSwitch = addSwitchObjectToContainer - container.addProgressBar = addProgressBarObjectToContainer - container.addChart = addChartObjectToContainer - container.addComboBox = addComboBoxObjectToContainer - container.addMenu = addMenuObjectToContainer - container.addScrollBar = addScrollBarObjectToContainer - container.addCodeView = addCodeViewObjectToContainer - container.addTreeView = addTreeViewObjectToContainer - container.addColorSelector = addColorSelectorObjectToContainer - - return container -end - ------------------------------------------ Buttons ----------------------------------------- - -local function drawButton(object) - local text, textLength = objectTextLimit(object) - - local xText, yText = GUI.getAlignmentCoordinates(object, {width = textLength, height = 1}) - local buttonColor = object.disabled and object.colors.disabled.background or (object.pressed and object.colors.pressed.background or object.colors.default.background) - local textColor = object.disabled and object.colors.disabled.text or (object.pressed and object.colors.pressed.text or object.colors.default.text) - - if buttonColor then - if object.buttonType == GUI.objectTypes.button then - buffer.square(object.x, object.y, object.width, object.height, buttonColor, textColor, " ") - else - buffer.frame(object.x, object.y, object.width, object.height, buttonColor) - end - end - - buffer.text(xText, yText, textColor, text) - - return object -end - -local function pressButton(object) - object.pressed = true - drawButton(object) -end - -local function releaseButton(object) - object.pressed = nil - drawButton(object) -end - -local function pressAndReleaseButton(object, pressTime) - pressButton(object) - buffer.draw() - os.sleep(pressTime or 0.2) - releaseButton(object) - buffer.draw() -end - --- Создание таблицы кнопки со всеми необходимыми параметрами -local function createButtonObject(buttonType, x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) - local object = GUI.object(x, y, width, height) - object.colors = { - default = { - background = buttonColor, - text = textColor - }, - pressed = { - background = buttonPressedColor, - text = textPressedColor - }, - disabled = { - background = GUI.colors.disabled.background, - text = GUI.colors.disabled.text, - } - } - object.buttonType = buttonType - object.disabled = disabledState - object.text = text - object.press = pressButton - object.release = releaseButton - object.pressAndRelease = pressAndReleaseButton - object.draw = drawButton - object.setAlignment = GUI.setAlignment - object:setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.center) - - return object -end - --- Кнопка фиксированных размеров -function GUI.button(x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) - return createButtonObject(GUI.objectTypes.button, x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) -end - --- Кнопка, подстраивающаяся под размер текста -function GUI.adaptiveButton(x, y, xOffset, yOffset, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) - return createButtonObject(GUI.objectTypes.button, x, y, unicode.len(text) + xOffset * 2, yOffset * 2 + 1, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) -end - --- Кнопка в рамке -function GUI.framedButton(x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) - return createButtonObject(GUI.objectTypes.framedButton, x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) -end - -function GUI.adaptiveFramedButton(x, y, xOffset, yOffset, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) - return createButtonObject(GUI.objectTypes.framedButton, x, y, unicode.len(text) + xOffset * 2, yOffset * 2 + 1, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) -end - ------------------------------------------ TabBar ----------------------------------------- - -local function drawTabBar(object) - for tab = 1, #object.tabs.children do - if tab == object.selectedTab then - object.tabs.children[tab].pressed = true - else - object.tabs.children[tab].pressed = false - end - end - - object:reimplementedDraw() - return object -end - -function GUI.tabBar(x, y, width, height, spaceBetweenElements, backgroundColor, textColor, backgroundSelectedColor, textSelectedColor, ...) - local elements, object = {...}, GUI.container(x, y, width, height) - object.selectedTab = 1 - object.tabsWidth = 0; for elementIndex = 1, #elements do object.tabsWidth = object.tabsWidth + unicode.len(elements[elementIndex]) + 2 + spaceBetweenElements end; object.tabsWidth = object.tabsWidth - spaceBetweenElements - object.reimplementedDraw = object.draw - object.draw = drawTabBar - - object:addPanel(1, 1, object.width, object.height, backgroundColor) - object.tabs = object:addContainer(1, 1, object.width, object.height) - - x = math.floor(width / 2 - object.tabsWidth / 2) - for elementIndex = 1, #elements do - local tab = object.tabs:addButton(x, 1, unicode.len(elements[elementIndex]) + 2, height, backgroundColor, textColor, backgroundSelectedColor, textSelectedColor, elements[elementIndex]) - tab.type = GUI.objectTypes.tabBarTab - x = x + tab.width + spaceBetweenElements - end - - return object -end - ------------------------------------------ Panel ----------------------------------------- - -local function drawPanel(object) - buffer.square(object.x, object.y, object.width, object.height, object.colors.background, 0x000000, " ", object.colors.transparency) - return object -end - -function GUI.panel(x, y, width, height, color, transparency) - local object = GUI.object(x, y, width, height) - object.colors = {background = color, transparency = transparency} - object.draw = drawPanel - return object -end - ------------------------------------------ Label ----------------------------------------- - -local function drawLabel(object) - local text, textLength = objectTextLimit(object) - local xText, yText = GUI.getAlignmentCoordinates(object, {width = textLength, height = 1}) - buffer.text(xText, yText, object.colors.text, text) - return object -end - -function GUI.label(x, y, width, height, textColor, text) - local object = GUI.object(x, y, width, height) - object.setAlignment = GUI.setAlignment - object:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top) - object.colors = {text = textColor} - object.text = text - object.draw = drawLabel - return object -end - ------------------------------------------ Image ----------------------------------------- - -local function drawImage(object) - buffer.image(object.x, object.y, object.image) - return object -end - -function GUI.image(x, y, image) - local object = GUI.object(x, y, image.width, image.height) - object.image = image - object.draw = drawImage - return object -end - ------------------------------------------ Window action buttons ----------------------------------------- - -function GUI.windowActionButtons(x, y, fatSymbol) - local symbol = fatSymbol and "⬤" or "●" - - local container = GUI.container(x, y, 5, 1) - container.close = container:addButton(1, 1, 1, 1, nil, 0xFF4940, nil, 0x992400, symbol) - container.minimize = container:addButton(3, 1, 1, 1, nil, 0xFFB640, nil, 0x996D00, symbol) - container.maximize = container:addButton(5, 1, 1, 1, nil, 0x00B640, nil, 0x006D40, symbol) - - return container -end - ------------------------------------------ Dropdown Menu ----------------------------------------- - -local function drawDropDownMenuElement(object, itemIndex, isPressed) - local y = object.y + itemIndex * (object.spaceBetweenElements + 1) - 1 - local yText = math.floor(y) - - if object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then - local textColor = object.items[itemIndex].disabled and object.colors.disabled.text or (object.items[itemIndex].color or object.colors.default.text) - - -- Нажатие - if isPressed then - buffer.square(object.x, y - object.spaceBetweenElements, object.width, object.spaceBetweenElements * 2 + 1, object.colors.pressed.background, object.colors.pressed.text, " ") - textColor = object.colors.pressed.text - end - - -- Основной текст - buffer.text(object.x + object.sidesOffset, yText, textColor, string.limit(object.items[itemIndex].text, object.width - object.sidesOffset * 2, false)) - -- Шурткатикус - if object.items[itemIndex].shortcut then - buffer.text(object.x + object.width - unicode.len(object.items[itemIndex].shortcut) - object.sidesOffset, yText, textColor, object.items[itemIndex].shortcut) - end - else - -- Сепаратор - buffer.text(object.x, yText, object.colors.separator, string.rep("─", object.width)) - end -end - -local function drawDropDownMenu(object) - buffer.square(object.x, object.y, object.width, object.height, object.colors.default.background, object.colors.default.text, " ", object.colors.transparency) - if object.drawShadow then GUI.windowShadow(object.x, object.y, object.width, object.height, GUI.colors.contextMenu.transparency.shadow, true) end - for itemIndex = 1, #object.items do drawDropDownMenuElement(object, itemIndex, false) end -end - -local function showDropDownMenu(object) - local oldDrawLimit = buffer.getDrawLimit(); buffer.resetDrawLimit() - object.height = #object.items * (object.spaceBetweenElements + 1) + object.spaceBetweenElements - - local oldPixels = buffer.copy(object.x, object.y, object.width + 1, object.height + 1) - local function quit() - buffer.paste(object.x, object.y, oldPixels) - buffer.draw() - buffer.setDrawLimit(oldDrawLimit) - end - - drawDropDownMenu(object) - buffer.draw() - - while true do - local e = {event.pull()} - if e[1] == "touch" then - local objectFound = false - for itemIndex = 1, #object.items do - if - e[3] >= object.x and - e[3] <= object.x + object.width - 1 and - e[4] == object.y + itemIndex * (object.spaceBetweenElements + 1) - 1 - then - objectFound = true - if not object.items[itemIndex].disabled and object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then - drawDropDownMenuElement(object, itemIndex, true) - buffer.draw() - os.sleep(0.2) - quit() - if object.items[itemIndex].onTouch then object.items[itemIndex].onTouch() end - return object.items[itemIndex].text, itemIndex - end - break - end - end - - if not objectFound then quit(); return end - end - end -end - -local function addDropDownMenuItem(object, text, disabled, shortcut, color) - local item = {} - item.type = GUI.dropDownMenuElementTypes.default - item.text = text - item.disabled = disabled - item.shortcut = shortcut - item.color = color - - table.insert(object.items, item) - return item -end - -local function addDropDownMenuSeparator(object) - local item = {type = GUI.dropDownMenuElementTypes.separator} - table.insert(object.items, item) - return item -end - -function GUI.dropDownMenu(x, y, width, spaceBetweenElements, backgroundColor, textColor, backgroundPressedColor, textPressedColor, disabledColor, separatorColor, transparency, items) - local object = GUI.object(x, y, width, 1) - object.colors = { - default = { - background = backgroundColor, - text = textColor - }, - pressed = { - background = backgroundPressedColor, - text = textPressedColor - }, - disabled = { - text = disabledColor - }, - separator = separatorColor, - transparency = transparency - } - object.sidesOffset = 2 - object.spaceBetweenElements = spaceBetweenElements - object.addSeparator = addDropDownMenuSeparator - object.addItem = addDropDownMenuItem - object.items = {} - if items then - for i = 1, #items do - object:addItem(items[i]) - end - end - object.drawShadow = true - object.draw = drawDropDownMenu - object.show = showDropDownMenu - return object -end - ------------------------------------------ Context Menu ----------------------------------------- - -local function showContextMenu(object) - -- Расчет ширины окна меню - local longestItem, longestShortcut = 0, 0 - for itemIndex = 1, #object.items do - if object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then - longestItem = math.max(longestItem, unicode.len(object.items[itemIndex].text)) - if object.items[itemIndex].shortcut then longestShortcut = math.max(longestShortcut, unicode.len(object.items[itemIndex].shortcut)) end - end - end - object.width = object.sidesOffset + longestItem + (longestShortcut > 0 and 3 + longestShortcut or 0) + object.sidesOffset - object.height = #object.items * (object.spaceBetweenElements + 1) + object.spaceBetweenElements - - -- А это чтоб за края экрана не лезло - if object.y + object.height >= buffer.screen.height then object.y = buffer.screen.height - object.height end - if object.x + object.width + 1 >= buffer.screen.width then object.x = buffer.screen.width - object.width - 1 end - - return object:reimplementedShow() -end - -function GUI.contextMenu(x, y, ...) - local argumentItems = {...} - local object = GUI.dropDownMenu(x, y, 1, 0, GUI.colors.contextMenu.default.background, GUI.colors.contextMenu.default.text, GUI.colors.contextMenu.pressed.background, GUI.colors.contextMenu.pressed.text, GUI.colors.contextMenu.disabled.text, GUI.colors.contextMenu.separator, GUI.colors.contextMenu.transparency.background) - - -- Заполняем менюшку парашей - for itemIndex = 1, #argumentItems do - if argumentItems[itemIndex] == "-" then - object:addSeparator() - else - object:addItem(argumentItems[itemIndex][1], argumentItems[itemIndex][2], argumentItems[itemIndex][3], argumentItems[itemIndex][4]) - end - end - - object.reimplementedShow = object.show - object.show = showContextMenu - object.selectedElement = nil - object.spaceBetweenElements = 0 - - return object -end - ------------------------------------------ Menu ----------------------------------------- - -local function menuDraw(menu) - buffer.square(menu.x, menu.y, menu.width, 1, menu.colors.default.background, menu.colors.default.text, " ", menu.colors.transparency) - menu:reimplementedDraw() -end - -local function menuAddItem(menu, text, textColor) - local x = 2; for i = 1, #menu.children do x = x + unicode.len(menu.children[i].text) + 2; end - local item = menu:addAdaptiveButton(x, 1, 1, 0, nil, textColor or menu.colors.default.text, menu.colors.pressed.background, menu.colors.pressed.text, text) - item.type = GUI.objectTypes.menuItem - return item -end - -function GUI.menu(x, y, width, backgroundColor, textColor, backgroundPressedColor, textPressedColor, backgroundTransparency) - local menu = GUI.container(x, y, width, 1) - menu.colors = { - default = { - background = backgroundColor, - text = textColor, - }, - pressed = { - background = backgroundPressedColor, - text = textPressedColor, - }, - transparency = backgroundTransparency - } - - menu.addItem = menuAddItem - menu.reimplementedDraw = menu.draw - menu.draw = menuDraw - - return menu -end - ------------------------------------------ ProgressBar Object ----------------------------------------- - -local function drawProgressBar(object) - local activeWidth = math.floor(object.value * object.width / 100) - if object.thin then - buffer.text(object.x, object.y, object.colors.passive, string.rep("━", object.width)) - buffer.text(object.x, object.y, object.colors.active, string.rep("━", activeWidth)) - else - buffer.square(object.x, object.y, object.width, object.height, object.colors.passive) - buffer.square(object.x, object.y, activeWidth, object.height, object.colors.active) - end - - if object.showValue then - local stringValue = tostring((object.valuePrefix or "") .. object.value .. (object.valuePostfix or "")) - buffer.text(math.floor(object.x + object.width / 2 - unicode.len(stringValue) / 2), object.y + 1, object.colors.value, stringValue) - end - - return object -end - -function GUI.progressBar(x, y, width, activeColor, passiveColor, valueColor, value, thin, showValue, valuePrefix, valuePostfix) - local object = GUI.object(x, y, width, 1) - object.value = value - object.colors = {active = activeColor, passive = passiveColor, value = valueColor} - object.thin = thin - object.draw = drawProgressBar - object.showValue = showValue - object.valuePrefix = valuePrefix - object.valuePostfix = valuePostfix - return object -end - ------------------------------------------ Other GUI elements ----------------------------------------- - -function GUI.windowShadow(x, y, width, height, transparency, thin) - transparency = transparency or 50 - if thin then - buffer.square(x + width, y + 1, 1, height - 1, 0x000000, 0x000000, " ", transparency) - buffer.text(x + 1, y + height, 0x000000, string.rep("▀", width), transparency) - buffer.text(x + width, y, 0x000000, "▄", transparency) - else - buffer.square(x + width, y + 1, 2, height, 0x000000, 0x000000, " ", transparency) - buffer.square(x + 2, y + height, width - 2, 1, 0x000000, 0x000000, " ", transparency) - end -end - -------------------------------------------------- Окна ------------------------------------------------------------------- - --- Красивое окошко для отображения сообщения об ошибке. Аргумент errorWindowParameters может принимать следующие значения: --- local errorWindowParameters = { --- backgroundColor = 0x262626, --- textColor = 0xFFFFFF, --- truncate = 50, --- title = {color = 0xFF8888, text = "Ошибочка"} --- noAnimation = true, --- } -function GUI.error(text, errorWindowParameters) - --Всякие константы, бла-бла - local backgroundColor = (errorWindowParameters and errorWindowParameters.backgroundColor) or 0x1b1b1b - local errorPixMap = { - {{0xffdb40 , 0xffffff,"#"}, {0xffdb40 , 0xffffff, "#"}, {backgroundColor, 0xffdb40, "▟"}, {backgroundColor, 0xffdb40, "▙"}, {0xffdb40 , 0xffffff, "#"}, {0xffdb40 , 0xffffff, "#"}}, - {{0xffdb40 , 0xffffff,"#"}, {backgroundColor, 0xffdb40, "▟"}, {0xffdb40 , 0xffffff, " "}, {0xffdb40 , 0xffffff, " "}, {backgroundColor, 0xffdb40, "▙"}, {0xffdb40 , 0xffffff, "#"}}, - {{backgroundColor, 0xffdb40,"▟"}, {0xffdb40 , 0xffffff, "c"}, {0xffdb40 , 0xffffff, "y"}, {0xffdb40 , 0xffffff, "k"}, {0xffdb40 , 0xffffff, "a"}, {backgroundColor, 0xffdb40, "▙"}}, - } - local textColor = (errorWindowParameters and errorWindowParameters.textColor) or 0xFFFFFF - local buttonWidth = 12 - local verticalOffset = 2 - local minimumHeight = verticalOffset * 2 + #errorPixMap - local height = 0 - local widthOfText = math.floor(buffer.screen.width * 0.5) - - --Ебемся с текстом, делаем его пиздатым во всех смыслах - if type(text) ~= "table" then - text = tostring(text) - text = (errorWindowParameters and errorWindowParameters.truncate) and unicode.sub(text, 1, errorWindowParameters.truncate) or text - text = { text } - end - text = string.wrap(text, widthOfText) - - - --Ебашим высоту правильнуюe - height = verticalOffset * 2 + #text + 1 - if errorWindowParameters and errorWindowParameters.title then height = height + 2 end - if height < minimumHeight then height = minimumHeight end - - --Ебашим стартовые коорды отрисовки - local x, y = math.ceil(buffer.screen.width / 2 - widthOfText / 2), math.ceil(buffer.screen.height / 2 - height / 2) - local OKButton = {} - local oldPixels = buffer.copy(1, y, buffer.screen.width, height) - - --Отрисовочка - local function draw() - local yPos = y - --Подложка - buffer.square(1, yPos, buffer.screen.width, height, backgroundColor, 0x000000); yPos = yPos + verticalOffset - buffer.customImage(x - #errorPixMap[1] - 3, yPos, errorPixMap) - --Титл, епта! - if errorWindowParameters and errorWindowParameters.title then buffer.text(x, yPos, errorWindowParameters.title.color, errorWindowParameters.title.text); yPos = yPos + 2 end - --Текстус - for i = 1, #text do buffer.text(x, yPos, textColor, text[i]); yPos = yPos + 1 end; yPos = yPos + 1 - --Кнопачка - OKButton = GUI.button(x + widthOfText - buttonWidth, y + height - 2, buttonWidth, 1, 0x3392FF, 0xFFFFFF, 0xFFFFFF, 0x262626, "OK"):draw() - --Атрисовачка - buffer.draw() - end - - --Графонистый выход - local function quit() - OKButton:pressAndRelease(0.2) - buffer.paste(1, y, oldPixels) - buffer.draw() - end - - --Онимацыя - if not (errorWindowParameters and errorWindowParameters.noAnimation) then for i = 1, height do buffer.setDrawLimit(1, math.floor(buffer.screen.height / 2) - i, buffer.screen.width, i * 2); draw(); os.sleep(0.05) end; buffer.resetDrawLimit() end - draw() - - --Анализ говнища - while true do - local e = {event.pull()} - if e[1] == "key_down" then - if e[4] == 28 then - quit(); return - end - elseif e[1] == "touch" then - if OKButton:isClicked(e[3], e[4]) then - quit(); return - end - end - end -end - ------------------------------------------ Universal keyboard-input function ----------------------------------------- - -local function findValue(t, whatToSearch) - if type(t) ~= "table" then return end - for key, value in pairs(t) do - if type(key) == "string" and string.match(key, "^" .. whatToSearch) then - local valueType, postfix = type(value), "" - if valueType == "function" or (valueType == "table" and getmetatable(value) and getmetatable(value).__call) then - postfix = "()" - elseif valueType == "table" then - postfix = "." - end - return key .. postfix - end - end -end - -local function findTable(whereToSearch, t, whatToSearch) - local beforeFirstDot = string.match(whereToSearch, "^[^%.]+%.") - -- Если вообще есть таблица, где надо искать - if beforeFirstDot then - beforeFirstDot = unicode.sub(beforeFirstDot, 1, -2) - if t[beforeFirstDot] then - return findTable(unicode.sub(whereToSearch, unicode.len(beforeFirstDot) + 2, -1), t[beforeFirstDot], whatToSearch) - else - -- Кароч, слушай суда: вот в эту зону хуйня может зайти толька - -- тагда, кагда ты вручную ебенишь массив вида "abc.cda.blabla.test" - -- без автозаполнения, т.е. он МОЖЕТ быть неверным, однако прога все - -- равно проверяет на верность, и вот если НИ ХУЯ такого говнища типа - -- ... .blabla не существует, то интерхпретатор захуяривается СУДЫ - -- И ЧТОБ БОЛЬШЕ ВОПРОСОВ НЕ ЗАДАВАЛ!11! - end - -- Или если таблиц либо ваще нету, либо рекурсия суда вон вошла - else - return findValue(t[whereToSearch], whatToSearch) - end -end - -local function autocompleteVariables(sourceText) - local varPath = string.match(sourceText, "[a-zA-Z0-9%.%_]+$") - if varPath then - local prefix = string.sub(sourceText, 1, -unicode.len(varPath) - 1) - local whereToSearch = string.match(varPath, "[a-zA-Z0-9%.%_]+%.") - - if whereToSearch then - whereToSearch = unicode.sub(whereToSearch, 1, -2) - local findedTable = findTable(whereToSearch, _G, unicode.sub(varPath, unicode.len(whereToSearch) + 2, -1)) - return findedTable and prefix .. whereToSearch .. "." .. findedTable or sourceText - else - local findedValue = findValue(_G, varPath) - return findedValue and prefix .. findedValue or sourceText - end - else - return sourceText - end -end - -local function inputFieldDraw(inputField) - if inputField.x < 1 or inputField.y < 1 or inputField.x + inputField.width - 1 > buffer.screen.width or inputField.y > buffer.screen.height then return inputField end - if inputField.oldPixels then - buffer.paste(inputField.x, inputField.y, inputField.oldPixels) - else - inputField.oldPixels = buffer.copy(inputField.x, inputField.y, inputField.width, 1) - end - - if inputField.highlightLuaSyntax then - require("syntax").highlightString(inputField.x, inputField.y, inputField.text, 2) - else - buffer.text( - inputField.x, - inputField.y, - inputField.colors.text, - unicode.sub( - inputField.textMask and string.rep(inputField.textMask, unicode.len(inputField.text)) or inputField.text, - inputField.textCutFrom, - inputField.textCutFrom + inputField.width - 1 - ) - ) - end - - if inputField.cursorBlinkState then - buffer.text(inputField.x + inputField.cursorPosition - inputField.textCutFrom, inputField.y, inputField.cursorColor, inputField.cursorSymbol) - end - - return inputField -end - -local function inputFieldSetCursorPosition(inputField, newPosition) - if newPosition < 1 then - newPosition = 1 - elseif newPosition > unicode.len(inputField.text) + 1 then - newPosition = unicode.len(inputField.text) + 1 - end - - if newPosition > inputField.textCutFrom + inputField.width - 1 then - inputField.textCutFrom = inputField.textCutFrom + newPosition - (inputField.textCutFrom + inputField.width - 1) - elseif newPosition < inputField.textCutFrom then - inputField.textCutFrom = newPosition - end - - inputField.cursorPosition = newPosition - - return inputField -end - -local function inputFieldBeginInput(inputField) - inputField.cursorBlinkState = true; inputField:draw(); buffer.draw() - - while true do - local e = { event.pull(inputField.cursorBlinkDelay) } - if e[1] == "touch" or e[1] == "drag" then - if inputField:isClicked(e[3], e[4]) then - inputField:setCursorPosition(inputField.textCutFrom + e[3] - inputField.x) - inputField.cursorBlinkState = true; inputField:draw(); buffer.draw() - else - inputField.cursorBlinkState = false; inputField:draw(); buffer.draw() - return inputField - end - elseif e[1] == "key_down" then - if e[4] == 28 then - inputField.cursorBlinkState = false; inputField:draw(); buffer.draw() - return inputField - elseif e[4] == 15 then - if inputField.autocompleteVariables then - inputField.text = autocompleteVariables(inputField.text) - inputField:setCursorPosition(unicode.len(inputField.text) + 1) - inputField.cursorBlinkState = true; inputField:draw(); buffer.draw() - end - elseif e[4] == 203 then - inputField:setCursorPosition(inputField.cursorPosition - 1) - inputField.cursorBlinkState = true; inputField:draw(); buffer.draw() - elseif e[4] == 205 then - inputField:setCursorPosition(inputField.cursorPosition + 1) - inputField.cursorBlinkState = true; inputField:draw(); buffer.draw() - elseif e[4] == 14 then - inputField.text = unicode.sub(unicode.sub(inputField.text, 1, inputField.cursorPosition - 1), 1, -2) .. unicode.sub(inputField.text, inputField.cursorPosition, -1) - inputField:setCursorPosition(inputField.cursorPosition - 1) - inputField.cursorBlinkState = true; inputField:draw(); buffer.draw() - else - if not keyboard.isControl(e[3]) then - inputField.text = unicode.sub(inputField.text, 1, inputField.cursorPosition - 1) .. unicode.char(e[3]) .. unicode.sub(inputField.text, inputField.cursorPosition, -1) - inputField:setCursorPosition(inputField.cursorPosition + 1) - inputField.cursorBlinkState = true; inputField:draw(); buffer.draw() - end - end - elseif e[1] == "clipboard" then - inputField.text = unicode.sub(inputField.text, 1, inputField.cursorPosition - 1) .. e[3] .. unicode.sub(inputField.text, inputField.cursorPosition, -1) - inputField:setCursorPosition(inputField.cursorPosition + unicode.len(e[3])) - inputField.cursorBlinkState = true; inputField:draw(); buffer.draw() - else - inputField.cursorBlinkState = not inputField.cursorBlinkState; inputField:draw(); buffer.draw() - end - end -end - -function GUI.inputField(x, y, width, textColor, text, textMask, highlightLuaSyntax, autocompleteVariables) - local inputField = GUI.object(x, y, width, 1) - - inputField.textCutFrom = 1 - inputField.cursorPosition = 1 - inputField.cursorColor = 0x00A8FF - inputField.cursorSymbol = "┃" - inputField.cursorBlinkDelay = 0.4 - inputField.cursorBlinkState = false - - inputField.colors = {text = textColor} - inputField.text = text - inputField.textMask = textMask - inputField.highlightLuaSyntax = highlightLuaSyntax - inputField.autocompleteVariables = autocompleteVariables - - inputField.setCursorPosition = inputFieldSetCursorPosition - inputField.draw = inputFieldDraw - inputField.input = inputFieldBeginInput - - inputField:setCursorPosition(unicode.len(inputField.text) + 1) - - return inputField -end - ------------------------------------------ Input Text Box object ----------------------------------------- - -local function drawInputTextBox(inputTextBox) - local background = inputTextBox.isFocused and inputTextBox.colors.focused.background or inputTextBox.colors.default.background - local foreground = inputTextBox.isFocused and inputTextBox.colors.focused.text or inputTextBox.colors.default.text - local y = math.floor(inputTextBox.y + inputTextBox.height / 2) - local text = inputTextBox.isFocused and (inputTextBox.eraseTextOnFocus and "" or inputTextBox.text) or (inputTextBox.text ~= "" and inputTextBox.text or inputTextBox.placeholderText or "") - - if background then - buffer.square(inputTextBox.x, inputTextBox.y, inputTextBox.width, inputTextBox.height, background, foreground, " ") - end - - local inputField = GUI.inputField(inputTextBox.x + 1, y, inputTextBox.width - 2, foreground, text, inputTextBox.textMask, inputTextBox.highlightLuaSyntax, inputTextBox.autocompleteVariables) - if inputTextBox.isFocused then - inputField:input() - if inputTextBox.validator then - if inputTextBox.validator(inputField.text) then - inputTextBox.text = inputField.text - end - else - inputTextBox.text = inputField.text - end - else - local oldHighlightLuaSyntaxValue = inputField.highlightLuaSyntax - inputField.highlightLuaSyntax = false - inputField:draw() - inputField.highlightLuaSyntax = oldHighlightLuaSyntaxValue - end - - return inputTextBox -end - -local function inputTextBoxBeginInput(inputTextBox) - inputTextBox.isFocused = true - inputTextBox:draw() - inputTextBox.isFocused = false - - return inputTextBox -end - -function GUI.inputTextBox(x, y, width, height, inputTextBoxColor, textColor, inputTextBoxFocusedColor, textFocusedColor, text, placeholderText, eraseTextOnFocus, textMask, highlightLuaSyntax, autocompleteVariables) - local inputTextBox = GUI.object(x, y, width, height) - inputTextBox.colors = { - default = { - background = inputTextBoxColor, - text = textColor - }, - focused = { - background = inputTextBoxFocusedColor, - text = textFocusedColor - } - } - inputTextBox.text = text - inputTextBox.placeholderText = placeholderText - inputTextBox.draw = drawInputTextBox - inputTextBox.input = inputTextBoxBeginInput - inputTextBox.eraseTextOnFocus = eraseTextOnFocus - inputTextBox.textMask = textMask - - return inputTextBox -end - ------------------------------------------ Text Box object ----------------------------------------- - -local function drawTextBox(object) - if object.colors.background then buffer.square(object.x, object.y, object.width, object.height, object.colors.background, object.colors.text, " ", object.colors.transparency) end - local xPos, yPos = GUI.getAlignmentCoordinates(object, {width = 1, height = object.height - object.offset.vertical * 2}) - local lineLimit = object.width - object.offset.horizontal * 2 - for line = object.currentLine, object.currentLine + object.height - 1 do - if object.lines[line] then - local lineType, text, textColor = type(object.lines[line]) - if lineType == "table" then - text, textColor = string.limit(object.lines[line].text, lineLimit), object.lines[line].color - elseif lineType == "string" then - text, textColor = string.limit(object.lines[line], lineLimit), object.colors.text - else - error("Unknown TextBox line type: " .. tostring(lineType)) - end - - xPos = GUI.getAlignmentCoordinates( - { - x = object.x + object.offset.horizontal, - y = object.y + object.offset.vertical, - width = object.width - object.offset.horizontal * 2, - height = object.height - object.offset.vertical * 2, - alignment = object.alignment - }, - {width = unicode.len(text), height = object.height} - ) - buffer.text(xPos, yPos, textColor, text) - yPos = yPos + 1 - else - break - end - end - - return object -end - -local function scrollDownTextBox(object, count) - count = count or 1 - local maxCountAvailableToScroll = #object.lines - object.height - object.currentLine + 1 - count = math.min(count, maxCountAvailableToScroll) - if #object.lines >= object.height and object.currentLine < #object.lines - count then - object.currentLine = object.currentLine + count - end - return object -end - -local function scrollUpTextBox(object, count) - count = count or 1 - if object.currentLine > count and object.currentLine >= 1 then object.currentLine = object.currentLine - count end - return object -end - -local function scrollToStartTextBox(object) - object.currentLine = 1 - return object -end - -local function scrollToEndTextBox(object) - object.currentLine = #lines - return object -end - -function GUI.textBox(x, y, width, height, backgroundColor, textColor, lines, currentLine, horizontalOffset, verticalOffset) - local object = GUI.object(x, y, width, height) - object.colors = { text = textColor, background = backgroundColor } - object.setAlignment = GUI.setAlignment - object:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top) - object.lines = lines - object.currentLine = currentLine or 1 - object.draw = drawTextBox - object.scrollUp = scrollUpTextBox - object.scrollDown = scrollDownTextBox - object.scrollToStart = scrollToStartTextBox - object.scrollToEnd = scrollToEndTextBox - object.offset = {horizontal = horizontalOffset or 0, vertical = verticalOffset or 0} - - return object -end - ------------------------------------------ Horizontal Slider Object ----------------------------------------- - -local function drawHorizontalSlider(object) - -- На всякий случай делаем значение не меньше минимального и не больше максимального - object.value = math.min(math.max(object.value, object.minimumValue), object.maximumValue) - - -- Отображаем максимальное и минимальное значение, если требуется - if object.showMaximumAndMinimumValues then - local stringMaximumValue, stringMinimumValue = tostring(object.roundValues and math.floor(object.maximumValue) or math.roundToDecimalPlaces(object.maximumValue, 2)), tostring(object.roundValues and math.floor(object.maximumValue) or math.roundToDecimalPlaces(object.minimumValue, 2)) - buffer.text(object.x - unicode.len(stringMinimumValue) - 1, object.y, object.colors.value, stringMinimumValue) - buffer.text(object.x + object.width + 1, object.y, object.colors.value, stringMaximumValue) - end - - -- А еще текущее значение рисуем, если хочется нам - if object.currentValuePrefix or object.currentValuePostfix then - local stringCurrentValue = (object.currentValuePrefix or "") .. (object.roundValues and math.floor(object.value) or math.roundToDecimalPlaces(object.value, 2)) .. (object.currentValuePostfix or "") - buffer.text(math.floor(object.x + object.width / 2 - unicode.len(stringCurrentValue) / 2), object.y + 1, object.colors.value, stringCurrentValue) - end - - -- Рисуем сам слайдер - local activeWidth = math.floor(object.width - ((object.maximumValue - object.value) * object.width / (object.maximumValue - object.minimumValue))) - buffer.text(object.x, object.y, object.colors.passive, string.rep("━", object.width)) - buffer.text(object.x, object.y, object.colors.active, string.rep("━", activeWidth)) - buffer.square(object.x + activeWidth - 1, object.y, 2, 1, object.colors.pipe, 0x000000, " ") - - return object -end - -function GUI.horizontalSlider(x, y, width, activeColor, passiveColor, pipeColor, valueColor, minimumValue, maximumValue, value, showMaximumAndMinimumValues, currentValuePrefix, currentValuePostfix) - local object = GUI.object(x, y, width, 1) - object.colors = {active = activeColor, passive = passiveColor, pipe = pipeColor, value = valueColor} - object.draw = drawHorizontalSlider - object.minimumValue = minimumValue - object.maximumValue = maximumValue - object.value = value - object.showMaximumAndMinimumValues = showMaximumAndMinimumValues - object.currentValuePrefix = currentValuePrefix - object.currentValuePostfix = currentValuePostfix - object.roundValues = false - return object -end - ------------------------------------------ Switch object ----------------------------------------- - -local function drawSwitch(object) - local pipeWidth = object.height * 2 - local pipePosition, backgroundColor - if object.state then pipePosition, backgroundColor = object.x + object.width - pipeWidth, object.colors.active else pipePosition, backgroundColor = object.x, object.colors.passive end - buffer.square(object.x, object.y, object.width, object.height, backgroundColor, 0x000000, " ") - buffer.square(pipePosition, object.y, pipeWidth, object.height, object.colors.pipe, 0x000000, " ") - return object -end - -function GUI.switch(x, y, width, activeColor, passiveColor, pipeColor, state) - local object = GUI.object(x, y, width, 1) - object.colors = {active = activeColor, passive = passiveColor, pipe = pipeColor, value = valueColor} - object.draw = drawSwitch - object.state = state or false - return object -end - ------------------------------------------ Chart object ----------------------------------------- - -local function drawChart(object) - -- Ебошем пездатые оси - for i = object.y, object.y + object.height - 2 do buffer.text(object.x, i, object.colors.axis, "│") end - buffer.text(object.x + 1, object.y + object.height - 1, object.colors.axis, string.rep("─", object.width - 1)) - buffer.text(object.x, object.y + object.height - 1, object.colors.axis, "└") - - if #object.values > 1 then - local oldDrawLimit = buffer.getDrawLimit() - buffer.setDrawLimit(object.x, object.y, object.width, object.height) - - local delta, fieldWidth, fieldHeight = object.maximumValue - object.minimumValue, object.width - 2, object.height - 1 - - -- Рисуем линии значений - local roundValues = object.maximumValue > 10 - local step = 0.2 * fieldHeight - for i = step, fieldHeight, step do - local value = object.minimumValue + delta * (i / fieldHeight) - local stringValue = roundValues and tostring(math.floor(value)) or math.doubleToString(value, 1) - buffer.text(object.x + 1, math.floor(object.y + fieldHeight - i), object.colors.value, string.rep("─", object.width - unicode.len(stringValue) - 2) .. " " .. stringValue) - end - - -- Рисуем графек, йопта - local function getDotPosition(valueIndex) - return - object.x + math.round((fieldWidth * (valueIndex - 1) / (#object.values - 1))) + 1, - object.y + math.round(((fieldHeight - 1) * (object.maximumValue - object.values[valueIndex]) / delta)) - end - - local x, y = getDotPosition(1) - for valueIndex = 2, #object.values do - local xNew, yNew = getDotPosition(valueIndex) - buffer.semiPixelLine(x, y * 2, xNew, yNew * 2, object.colors.chart) - x, y = xNew, yNew - end - - buffer.setDrawLimit(oldDrawLimit) - end - - -- Дорисовываем названия осей - if object.axisNames.y then buffer.text(object.x + 1, object.y, object.colors.axis, object.axisNames.y) end - if object.axisNames.x then buffer.text(object.x + object.width - unicode.len(object.axisNames.x), object.y + object.height - 2, object.colors.axis, object.axisNames.x) end -end - -function GUI.chart(x, y, width, height, axisColor, axisValueColor, chartColor, xAxisName, yAxisName, minimumValue, maximumValue, values) - if minimumValue >= maximumValue then error("Chart's minimum value can't be >= maximum value!") end - local object = GUI.object(x, y, width, height) - object.colors = {axis = axisColor, chart = chartColor, value = axisValueColor} - object.draw = drawChart - object.values = values - object.minimumValue = minimumValue - object.maximumValue = maximumValue - object.axisNames = {x = xAxisName, y = yAxisName} - return object -end - ------------------------------------------ Combo Box Object ----------------------------------------- - -local function drawComboBox(object) - buffer.square(object.x, object.y, object.width, object.height, object.colors.default.background) - local x, y, limit, arrowSize = object.x + 1, math.floor(object.y + object.height / 2), object.width - 5, object.height - buffer.text(x, y, object.colors.default.text, string.limit(object.items[object.currentItem].text, limit, false)) - GUI.button(object.x + object.width - arrowSize * 2 + 1, object.y, arrowSize * 2 - 1, arrowSize, object.colors.arrow.background, object.colors.arrow.text, 0x0, 0x0, object.state and "▲" or "▼"):draw() -end - -local function selectComboBoxItem(object) - object.state = true - object:draw() - - local dropDownMenu = GUI.dropDownMenu(object.x, object.y + object.height, object.width, object.height == 1 and 0 or 1, object.colors.default.background, object.colors.default.text, object.colors.pressed.background, object.colors.pressed.text, GUI.colors.contextMenu.disabled.text, GUI.colors.contextMenu.separator, GUI.colors.contextMenu.transparency.background, object.items) - dropDownMenu.items = object.items - dropDownMenu.sidesOffset = 1 - local _, itemIndex = dropDownMenu:show() - - object.currentItem = itemIndex or object.currentItem - object.state = false - object:draw() - buffer.draw() -end - -function GUI.comboBox(x, y, width, height, backgroundColor, textColor, arrowBackgroundColor, arrowTextColor, items) - local object = GUI.object(x, y, width, height) - object.colors = { - default = { - background = backgroundColor, - text = textColor - }, - pressed = { - background = GUI.colors.contextMenu.pressed.background, - text = GUI.colors.contextMenu.pressed.text - }, - arrow = { - background = arrowBackgroundColor, - text = arrowTextColor - } - } - object.items = {} - object.currentItem = 1 - object.addItem = addDropDownMenuItem - object.addSeparator = addDropDownMenuSeparator - if items then - for i = 1, #items do - object:addItem(items[i]) - end - end - object.draw = drawComboBox - object.selectItem = selectComboBoxItem - object.state = false - return object -end - ------------------------------------------ Scrollbar object ----------------------------------------- - -local function scrollBarDraw(scrollBar) - local isVertical = scrollBar.height > scrollBar.width - local valuesDelta = scrollBar.maximumValue - scrollBar.minimumValue + 1 - local part = scrollBar.value / valuesDelta - - if not isVertical and scrollBar.thinHorizontalMode then - buffer.text(scrollBar.x, scrollBar.y, scrollBar.colors.background, string.rep("▄", scrollBar.width)) - else - buffer.square(scrollBar.x, scrollBar.y, scrollBar.width, scrollBar.height, scrollBar.colors.background, 0x0, " ") - end - - if isVertical then - local barSize = math.ceil(scrollBar.shownValueCount / valuesDelta * scrollBar.height) - local halfBarSize = math.floor(barSize / 2) - - scrollBar.ghostPosition.x = scrollBar.x - scrollBar.ghostPosition.y = scrollBar.y + halfBarSize - scrollBar.ghostPosition.width = scrollBar.width - scrollBar.ghostPosition.height = scrollBar.height - barSize - - buffer.square( - scrollBar.ghostPosition.x, - math.floor(scrollBar.ghostPosition.y + part * scrollBar.ghostPosition.height - halfBarSize), - scrollBar.ghostPosition.width, - barSize, - scrollBar.colors.foreground, 0x0, " " - ) - else - local barSize = math.ceil(scrollBar.shownValueCount / valuesDelta * scrollBar.width) - local halfBarSize = math.floor(barSize / 2) - - scrollBar.ghostPosition.x = scrollBar.x + halfBarSize - scrollBar.ghostPosition.y = scrollBar.y - scrollBar.ghostPosition.width = scrollBar.width - barSize - scrollBar.ghostPosition.height = scrollBar.height - - if not isVertical and scrollBar.thinHorizontalMode then - buffer.text(math.floor(scrollBar.ghostPosition.x + part * scrollBar.ghostPosition.width - halfBarSize), scrollBar.ghostPosition.y, scrollBar.colors.foreground, string.rep("▄", barSize)) - else - buffer.square( - math.floor(scrollBar.ghostPosition.x + part * scrollBar.ghostPosition.width - halfBarSize), - scrollBar.ghostPosition.y, - barSize, - scrollBar.ghostPosition.height, - scrollBar.colors.foreground, 0x0, " " - ) - end - end - - return scrollBar -end - -function GUI.scrollBar(x, y, width, height, backgroundColor, foregroundColor, minimumValue, maximumValue, value, shownValueCount, onScrollValueIncrement, thinHorizontalMode) - local scrollBar = GUI.object(x, y, width, height) - - scrollBar.maximumValue = maximumValue - scrollBar.minimumValue = minimumValue - scrollBar.value = value - scrollBar.onScrollValueIncrement = onScrollValueIncrement - scrollBar.shownValueCount = shownValueCount - scrollBar.thinHorizontalMode = thinHorizontalMode - scrollBar.colors = { - background = backgroundColor, - foreground = foregroundColor, - } - scrollBar.ghostPosition = {} - scrollBar.draw = scrollBarDraw - - return scrollBar -end - ------------------------------------------ CodeView object ----------------------------------------- - -local function codeViewDraw(codeView) - -- local toLine = codeView.fromLine + codeView.height - (codeView.scrollBars.horizontal.isHidden and 1 or 2) - local toLine = codeView.fromLine + codeView.height - 1 - - -- Line numbers bar and code area - codeView.lineNumbersWidth = unicode.len(tostring(toLine)) + 2 - codeView.codeAreaPosition = codeView.x + codeView.lineNumbersWidth - codeView.codeAreaWidth = codeView.width - codeView.lineNumbersWidth - buffer.square(codeView.x, codeView.y, codeView.lineNumbersWidth, codeView.height, syntax.colorScheme.lineNumbersBackground, syntax.colorScheme.lineNumbersText, " ") - buffer.square(codeView.codeAreaPosition, codeView.y, codeView.codeAreaWidth, codeView.height, syntax.colorScheme.background, syntax.colorScheme.text, " ") - - -- Line numbers texts - local y = codeView.y - for line = codeView.fromLine, toLine do - if codeView.lines[line] then - local text = tostring(line) - if codeView.highlights[line] then - buffer.square(codeView.x, y, codeView.lineNumbersWidth, 1, codeView.highlights[line], syntax.colorScheme.text, " ", 30) - buffer.square(codeView.codeAreaPosition, y, codeView.codeAreaWidth, 1, codeView.highlights[line], syntax.colorScheme.text, " ") - end - buffer.text(codeView.codeAreaPosition - unicode.len(text) - 1, y, syntax.colorScheme.lineNumbersText, text) - y = y + 1 - else - break - end - end - - -- Selections - local oldDrawLimit = buffer.getDrawLimit() - buffer.setDrawLimit(codeView.codeAreaPosition, codeView.y, codeView.codeAreaWidth, codeView.height) - - local function drawUpperSelection(y, selectionIndex) - buffer.square( - codeView.codeAreaPosition + codeView.selections[selectionIndex].from.symbol, - y + codeView.selections[selectionIndex].from.line - codeView.fromLine, - codeView.codeAreaWidth - codeView.selections[selectionIndex].from.symbol, - 1, - codeView.selections[selectionIndex].color or syntax.colorScheme.selection, syntax.colorScheme.text, " " - ) - end - - local function drawLowerSelection(y, selectionIndex) - buffer.square( - codeView.codeAreaPosition, - y + codeView.selections[selectionIndex].from.line - codeView.fromLine, - codeView.selections[selectionIndex].to.symbol + 1, - 1, - codeView.selections[selectionIndex].color or syntax.colorScheme.selection, syntax.colorScheme.text, " " - ) - end - - if #codeView.selections > 0 then - for selectionIndex = 1, #codeView.selections do - y = codeView.y - local dy = codeView.selections[selectionIndex].to.line - codeView.selections[selectionIndex].from.line - if dy == 0 then - buffer.square( - codeView.codeAreaPosition + codeView.selections[selectionIndex].from.symbol, - y + codeView.selections[selectionIndex].from.line - codeView.fromLine, - codeView.selections[selectionIndex].to.symbol - codeView.selections[selectionIndex].from.symbol + 1, - 1, - codeView.selections[selectionIndex].color or syntax.colorScheme.selection, syntax.colorScheme.text, " " - ) - elseif dy == 1 then - drawUpperSelection(y, selectionIndex); y = y + 1 - drawLowerSelection(y, selectionIndex) - else - drawUpperSelection(y, selectionIndex); y = y + 1 - for i = 1, dy - 1 do - buffer.square(codeView.codeAreaPosition, y + codeView.selections[selectionIndex].from.line - codeView.fromLine, codeView.codeAreaWidth, 1, codeView.selections[selectionIndex].color or syntax.colorScheme.selection, syntax.colorScheme.text, " "); y = y + 1 - end - drawLowerSelection(y, selectionIndex) - end - end - end - - -- Code strings - y = codeView.y - buffer.setDrawLimit(codeView.codeAreaPosition + 1, y, codeView.codeAreaWidth - 2, codeView.height) - for i = codeView.fromLine, toLine do - if codeView.lines[i] then - if codeView.highlightLuaSyntax then - syntax.highlightString(codeView.codeAreaPosition - codeView.fromSymbol + 2, y, codeView.lines[i], codeView.indentationWidth) - else - buffer.text(codeView.codeAreaPosition - codeView.fromSymbol + 2, y, syntax.colorScheme.text, codeView.lines[i]) - end - y = y + 1 - else - break - end - end - buffer.setDrawLimit(oldDrawLimit) - - if #codeView.lines > codeView.height then - codeView.scrollBars.vertical.isHidden = false - codeView.scrollBars.vertical.colors.background, codeView.scrollBars.vertical.colors.foreground = syntax.colorScheme.scrollBarBackground, syntax.colorScheme.scrollBarForeground - codeView.scrollBars.vertical.minimumValue, codeView.scrollBars.vertical.maximumValue, codeView.scrollBars.vertical.value, codeView.scrollBars.vertical.shownValueCount = 1, #codeView.lines, codeView.fromLine, codeView.height - codeView.scrollBars.vertical.localPosition.x = codeView.width - codeView.scrollBars.vertical.localPosition.y = 1 - codeView.scrollBars.vertical.height = codeView.height - else - codeView.scrollBars.vertical.isHidden = true - end - - if codeView.maximumLineLength > codeView.codeAreaWidth - 2 then - codeView.scrollBars.horizontal.isHidden = false - codeView.scrollBars.horizontal.colors.background, codeView.scrollBars.horizontal.colors.foreground = syntax.colorScheme.scrollBarBackground, syntax.colorScheme.scrollBarForeground - codeView.scrollBars.horizontal.minimumValue, codeView.scrollBars.horizontal.maximumValue, codeView.scrollBars.horizontal.value, codeView.scrollBars.horizontal.shownValueCount = 1, codeView.maximumLineLength, codeView.fromSymbol, codeView.codeAreaWidth - 2 - codeView.scrollBars.horizontal.localPosition.x, codeView.scrollBars.horizontal.width = codeView.lineNumbersWidth + 1, codeView.codeAreaWidth - 1 - codeView.scrollBars.horizontal.localPosition.y = codeView.height - else - codeView.scrollBars.horizontal.isHidden = true - end - - codeView:reimplementedDraw() -end - -function GUI.codeView(x, y, width, height, lines, fromSymbol, fromLine, maximumLineLength, selections, highlights, highlightLuaSyntax, indentationWidth) - local codeView = GUI.container(x, y, width, height) - - codeView.lines = lines - codeView.fromSymbol = fromSymbol - codeView.fromLine = fromLine - codeView.maximumLineLength = maximumLineLength - codeView.selections = selections or {} - codeView.highlights = highlights or {} - codeView.highlightLuaSyntax = highlightLuaSyntax - codeView.indentationWidth = indentationWidth - - codeView.scrollBars = { - vertical = codeView:addScrollBar(1, 1, 1, 1, 0x0, 0x0, 1, 1, 1, 1, 1, false), - horizontal = codeView:addScrollBar(1, 1, 1, 1, 0x0, 0x0, 1, 1, 1, 1, 1, true) - } - - codeView.reimplementedDraw = codeView.draw - codeView.draw = codeViewDraw - - return codeView -end - ------------------------------------------ Color Selector object ----------------------------------------- - -local function updateFileList(treeView, xOffset, path) - for file in fs.list(path) do - local element = {} - element.path = path .. file - element.xOffset = xOffset - element.isDirectory = fs.isDirectory(element.path) - table.insert(treeView.fileList, element) - - if treeView.directoriesToShowContent[element.path] then - updateFileList(treeView, xOffset + 2, path .. file) - end - end -end - -local function treeViewUpdateFileList(treeView) - treeView.fileList = {} - updateFileList(treeView, 1, treeView.workPath) - - return treeView -end - -local function treeViewDraw(treeView) - local y = treeView.y + 1 - local showScrollBar = #treeView.fileList > treeView.height - local textLimit = treeView.width - (showScrollBar and 2 or 1) - - if treeView.colors.default.background then - buffer.square(treeView.x, treeView.y, treeView.width, treeView.height, treeView.colors.default.background, treeView.colors.default.text, " ") - end - - for fileIndex = treeView.fromFile, #treeView.fileList do - local textColor = treeView.colors.default.text - if treeView.fileList[fileIndex].path == treeView.currentFile then - textColor = treeView.colors.selected.text - buffer.square(treeView.x, y, treeView.width, 1, treeView.colors.selected.background, textColor, " ") - end - - if treeView.fileList[fileIndex].isDirectory then - buffer.text(treeView.x + treeView.fileList[fileIndex].xOffset, y, treeView.colors.arrow, treeView.directoriesToShowContent[treeView.fileList[fileIndex].path] and "▽" or "▷") - buffer.text(treeView.x + treeView.fileList[fileIndex].xOffset + 2, y, textColor, unicode.sub("■ " .. fs.name(treeView.fileList[fileIndex].path), 1, textLimit - treeView.fileList[fileIndex].xOffset - 2)) - else - buffer.text(treeView.x + treeView.fileList[fileIndex].xOffset, y, textColor, unicode.sub(" □ " .. fs.name(treeView.fileList[fileIndex].path), 1, textLimit - treeView.fileList[fileIndex].xOffset)) - end - - y = y + 1 - if y > treeView.y + treeView.height - 2 then break end - end - - if showScrollBar then - GUI.scrollBar( - treeView.x + treeView.width - 1, - treeView.y, - 1, - treeView.height, - treeView.colors.scrollBar.background, - treeView.colors.scrollBar.foreground, - 1, - #treeView.fileList, - treeView.fromFile, - treeView.height - 2, - 1 - ):draw() - end - - return treeView -end - -function GUI.treeView(x, y, width, height, backgroundColor, textColor, selectionColor, selectionTextColor, arrowColor, scrollBarBackground, scrollBarForeground, workPath) - local treeView = GUI.container(x, y, width, height) - - treeView.colors = { - default = { - background = backgroundColor, - text = textColor, - }, - selected = { - background = selectionColor, - text = selectionTextColor, - }, - scrollBar = { - background = scrollBarBackground, - foreground = scrollBarForeground - }, - arrow = arrowColor - } - treeView.directoriesToShowContent = {} - treeView.fileList = {} - treeView.workPath = workPath - - treeView.updateFileList = treeViewUpdateFileList - treeView.draw = treeViewDraw - treeView.currentFile = nil - treeView.fromFile = 1 - - treeView:updateFileList() - - return treeView -end - ------------------------------------------ Color Selector object ----------------------------------------- - -local function colorSelectorDraw(colorSelector) - local overlayColor = colorSelector.color < 0x7FFFFF and 0xFFFFFF or 0x000000 - buffer.square(colorSelector.x, colorSelector.y, colorSelector.width, colorSelector.height, colorSelector.color, overlayColor, " ") - if colorSelector.pressed then - buffer.square(colorSelector.x, colorSelector.y, colorSelector.width, colorSelector.height, overlayColor, overlayColor, " ", 80) - end - buffer.text(colorSelector.x, colorSelector.y + colorSelector.height - 1, overlayColor, string.rep("▄", colorSelector.width), 80) - buffer.text(colorSelector.x + 1, colorSelector.y + math.floor(colorSelector.height / 2), overlayColor, string.limit(colorSelector.text, colorSelector.width - 2)) - return colorSelector -end - -function GUI.colorSelector(x, y, width, height, color, text) - local colorSelector = GUI.object(x, y, width, height) - colorSelector.color = color - colorSelector.text = text - colorSelector.draw = colorSelectorDraw - return colorSelector -end - --------------------------------------------------------------------------------------------------------------------------------- - --- buffer.start() --- buffer.clear(0xFF8888) --- buffer.draw(true) - --- local y = 2 --- for i = 1, 10 do --- GUI.colorSelector(2, y, 30, 3, math.random(0x0, 0xFFFFFF), "Типа цвет " .. i):draw() --- y = y + 4 --- end --- buffer.draw() - --- local lines = {} --- local file = io.open("/OS.lua", "r") --- local maximumLineLength = 0 --- for line in file:lines() do line = line:gsub(" ", " "); table.insert(lines, line); maximumLineLength = math.max(maximumLineLength, unicode.len(line)) end --- file:close() - --- GUI.codeView(1, 1, buffer.screen.width, buffer.screen.height, lines, 1, 110, maximumLineLength, {{from = {symbol = 14, line = 122}, to = {symbol = 20, line = 128}}}, {[131] = 0xFF4444}, true):draw() --- -- GUI.scrollBar(1, 5, 1, 20, 0x444444, 0x00DBFF, 1, 100, 50, 20, 1, true):draw() - --- buffer.draw() - --------------------------------------------------------------------------------------------------------------------------------- - -return GUI - - - - - - -Flib/MineOSCore.lua ----------------------------------------------- Libraries ------------------------------------------------------------------------ - -local component = require("component") -local computer = require("computer") -local event = require("event") -local advancedLua = require("advancedLua") -local image = require("image") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local windows = require("windows") -local ecs = require("ECSAPI") -local fs = require("filesystem") -local unicode = require("unicode") - ----------------------------------------------- Core constants ------------------------------------------------------------------------ - -local MineOSCore = {} - -MineOSCore.showApplicationIcons = true -MineOSCore.iconWidth = 12 -MineOSCore.iconHeight = 6 -MineOSCore.iconClickDelay = 0.2 - -MineOSCore.paths = {} -MineOSCore.paths.OS = "/MineOS/" -MineOSCore.paths.system = MineOSCore.paths.OS .. "System/" -MineOSCore.paths.wallpaper = MineOSCore.paths.system .. "OS/Wallpaper.lnk" -MineOSCore.paths.localizationFile = MineOSCore.paths.system .. "OS/Languages/" .. _G.OSSettings.language .. ".lang" -MineOSCore.paths.icons = MineOSCore.paths.system .. "OS/Icons/" -MineOSCore.paths.applications = MineOSCore.paths.OS .. "Applications/" -MineOSCore.paths.pictures = MineOSCore.paths.OS .. "Pictures/" -MineOSCore.paths.desktop = MineOSCore.paths.OS .. "Desktop/" -MineOSCore.paths.applicationList = MineOSCore.paths.system .. "OS/Applications.txt" -MineOSCore.paths.trash = MineOSCore.paths.OS .. "Trash/" -MineOSCore.paths.OSSettings = MineOSCore.paths.system .. "OS/OSSettings.cfg" - -MineOSCore.sortingMethods = enum( - "type", - "name", - "date" -) - -MineOSCore.localization = {} - ----------------------------------------------- Current sсript processing methods ------------------------------------------------------------------------ - -function MineOSCore.getCurrentScriptDirectory() - return MineOSCore.getFilePath(getCurrentScript()) -end - -function MineOSCore.getCurrentApplicationResourcesDirectory() - return MineOSCore.getCurrentScriptDirectory() .. "/Resources/" -end - -function MineOSCore.getLocalization(pathToLocalizationFolder) - local localizationFileName = pathToLocalizationFolder .. _G.OSSettings.language .. ".lang" - if fs.exists(localizationFileName) then - return table.fromFile(localizationFileName) - else - error("Localization file \"" .. localizationFileName .. "\" doesn't exists") - end -end - -function MineOSCore.getCurrentApplicationLocalization() - return MineOSCore.getLocalization(MineOSCore.getCurrentApplicationResourcesDirectory() .. "Localization/") -end - -function MineOSCore.getMethodExecutionTime(method) - local oldOSClock = os.clock() - method() - return os.clock() - oldOSClock -end - -function MineOSCore.getAverageMethodExecutionTime(method, countOfTries) - local averageTime = 0 - for i = 1, countOfTries do - averageTime = (averageTime + MineOSCore.getMethodExecutionTime(method)) / 2 - os.sleep(0.1) - end - return averageTime -end - ----------------------------------------------- Filesystem-related methods ------------------------------------------------------------------------ - -local function getFilenameAndFormat(path) - local fileName, format = string.match(path, "^(.+)(%.[^%/]+)%/?$") - return (fileName or path), format -end - -function MineOSCore.getFilePath(path) - return string.match(path, "^(.+%/).") or "" -end - -function MineOSCore.getFileName(path) - return string.match(path, "%/?([^%/]+)%/?$") -end - -function MineOSCore.getFileFormat(path) - local fileName, format = getFilenameAndFormat(path) - return format -end - -function MineOSCore.hideFileFormat(path) - local fileName, format = getFilenameAndFormat(path) - return fileName -end - -function MineOSCore.isFileHidden(path) - if string.match(path, "^%..+$") then return true end - return false -end - -function MineOSCore.getFileList(path) - if not fs.exists(path) then error("Failed to get file list: directory \"" .. tostring(path) .. "\" doesn't exists") end - if not fs.isDirectory(path) then error("Failed to get file list: path \"" .. tostring(path) .. "\" is not a directory") end - - local fileList = {} - for file in fs.list(path) do table.insert(fileList, file) end - return fileList -end - -function MineOSCore.sortFileList(path, fileList, sortingMethod, showHiddenFiles) - local sortedFileList = {} - - if sortingMethod == MineOSCore.sortingMethods.type then - local typeList = {} - for i = 1, #fileList do - local fileFormat = MineOSCore.getFileFormat(fileList[i]) or "Script" - if fs.isDirectory(path .. fileList[i]) and fileFormat ~= ".app" then fileFormat = "Folder" end - typeList[fileFormat] = typeList[fileFormat] or {} - table.insert(typeList[fileFormat], fileList[i]) - end - - if typeList.Folder then - for i = 1, #typeList.Folder do - table.insert(sortedFileList, typeList.Folder[i]) - end - typeList["Folder"] = nil - end - - for fileFormat in pairs(typeList) do - for i = 1, #typeList[fileFormat] do - table.insert(sortedFileList, typeList[fileFormat][i]) - end - end - elseif MineOSCore.sortingMethods.name then - sortedFileList = fileList - elseif MineOSCore.sortingMethods.date then - for i = 1, #fileList do - fileList[i] = {fileList[i], fs.lastModified(path .. fileList[i])} - end - table.sort(fileList, function(a,b) return a[2] > b[2] end) - for i = 1, #fileList do - table.insert(sortedFileList, fileList[i][1]) - end - else - error("Unknown sorting method: " .. tostring(sortingMethod)) - end - - local i = 1 - while i <= #sortedFileList do - if not showHiddenFiles and MineOSCore.isFileHidden(sortedFileList[i]) then - table.remove(sortedFileList, i) - else - i = i + 1 - end - end - - return sortedFileList -end - -function MineOSCore.limitFileName(text, limit) - if unicode.len(text) > limit then - local partSize = math.ceil(limit / 2) - text = unicode.sub(text, 1, partSize) .. "…" .. unicode.sub(text, -partSize + 1, -1) - end - return text -end - -function MineOSCore.getFolderSize(path) - local size = 0 - for file in fs.list(path) do - if fs.isDirectory(path .. file) then - size = size + MineOSCore.getFolderSize(path .. file) - else - size = size + fs.size(path .. file) - end - end - return size -end - ----------------------------------------------- MineOS Icons related methods ------------------------------------------------------------------------ - -function MineOSCore.saveOSSettings() - table.toFile(MineOSCore.paths.OSSettings, _G.OSSettings, true) -end - -function MineOSCore.loadOSSettings() - _G.OSSettings = table.fromFile(MineOSCore.paths.OSSettings) -end - -function MineOSCore.loadIcon(name, path) - if not MineOSCore.icons[name] then MineOSCore.icons[name] = image.load(path) end - return MineOSCore.icons[name] -end - ---Вся необходимая информация для иконок -function MineOSCore.loadStandartIcons() - MineOSCore.icons = {} - MineOSCore.loadIcon("folder", MineOSCore.paths.icons .. "Folder.pic") - MineOSCore.loadIcon("script", MineOSCore.paths.icons .. "Script.pic") - MineOSCore.loadIcon("text", MineOSCore.paths.icons .. "Text.pic") - MineOSCore.loadIcon("config", MineOSCore.paths.icons .. "Config.pic") - MineOSCore.loadIcon("lua", MineOSCore.paths.icons .. "Lua.pic") - MineOSCore.loadIcon("image", MineOSCore.paths.icons .. "Image.pic") - MineOSCore.loadIcon("pastebin", MineOSCore.paths.icons .. "Pastebin.pic") - MineOSCore.loadIcon("fileNotExists", MineOSCore.paths.icons .. "FileNotExists.pic") - MineOSCore.loadIcon("archive", MineOSCore.paths.icons .. "Archive.pic") - MineOSCore.loadIcon("model3D", MineOSCore.paths.icons .. "3DModel.pic") - MineOSCore.loadIcon("application", MineOSCore.paths.icons .. "Application.pic") - MineOSCore.loadIcon("trash", MineOSCore.paths.icons .. "Trash.pic") -end - -function MineOSCore.init() - MineOSCore.localization = table.fromFile(MineOSCore.paths.localizationFile) - MineOSCore.loadStandartIcons() -end - -function MineOSCore.waitForPressingAnyKey() - print(" ") - print(MineOSCore.localization.pressAnyKeyToContinue) - while true do - local eventType = event.pull() - if eventType == "key_down" or eventType == "touch" then break end - end -end - -function MineOSCore.analyzeIconFormat(iconObject) - if iconObject.isDirectory then - if iconObject.format == ".app" then - if MineOSCore.showApplicationIcons then - iconObject.iconImage.image = image.load(iconObject.path .. "/Resources/Icon.pic") - else - iconObject.iconImage.image = MineOSCore.icons.application - end - - iconObject.launch = function() - ecs.applicationHelp(iconObject.path) - MineOSCore.safeLaunch(iconObject.path .. "/" .. MineOSCore.hideFileFormat(MineOSCore.getFileName(iconObject.path)) .. ".lua") - end - else - iconObject.iconImage.image = MineOSCore.icons.folder - iconObject.launch = function() - computer.pushSignal("MineOSCore", "changeWorkpath", iconObject.path) - end - end - else - if iconObject.format == ".lnk" then - iconObject.shortcutPath = ecs.readShortcut(iconObject.path) - iconObject.shortcutFormat = MineOSCore.getFileFormat(iconObject.shortcutPath) - iconObject.shortcutIsDirectory = fs.isDirectory(iconObject.shortcutPath) - iconObject.isShortcut = true - - local shortcutIconObject = MineOSCore.analyzeIconFormat({ - path = iconObject.shortcutPath, - format = iconObject.shortcutFormat, - isDirectory = iconObject.shortcutIsDirectory, - iconImage = iconObject.iconImage - }) - - iconObject.iconImage.image = shortcutIconObject.iconImage.image - iconObject.launch = shortcutIconObject.launch - - shortcutIconObject = nil - elseif iconObject.format == ".cfg" or iconObject.format == ".config" then - iconObject.iconImage.image = MineOSCore.icons.config - iconObject.launch = function() - MineOSCore.safeLaunch(MineOSCore.paths.applications .. "/MineCode IDE.app/MineCode IDE.lua", "open", iconObject.path) - end - elseif iconObject.format == ".txt" or iconObject.format == ".rtf" then - iconObject.iconImage.image = MineOSCore.icons.text - iconObject.launch = function() - MineOSCore.safeLaunch(MineOSCore.paths.applications .. "/MineCode IDE.app/MineCode IDE.lua", "open", iconObject.path) - end - elseif iconObject.format == ".lua" then - iconObject.iconImage.image = MineOSCore.icons.lua - iconObject.launch = function() - ecs.prepareToExit() - if MineOSCore.safeLaunch(iconObject.path) then - MineOSCore.waitForPressingAnyKey() - end - end - elseif iconObject.format == ".pic" or iconObject.format == ".png" then - iconObject.iconImage.image = MineOSCore.icons.image - iconObject.launch = function() - MineOSCore.safeLaunch(MineOSCore.paths.applications .. "Photoshop.app/Photoshop.lua", "open", iconObject.path) - end - elseif iconObject.format == ".pkg" then - iconObject.iconImage.image = MineOSCore.icons.archive - iconObject.launch = function() - require("compressor").unpack(iconObject.path, MineOSCore.getFilePath(iconObject.path)) - end - elseif iconObject.format == ".3dm" then - iconObject.iconImage.image = MineOSCore.icons.model3D - iconObject.launch = function() - MineOSCore.safeLaunch(MineOSCore.paths.applications .. "3DPrint.app/3DPrint.lua", "open", iconObject.path) - end - elseif not fs.exists(iconObject.path) then - iconObject.iconImage.image = MineOSCore.icons.fileNotExists - iconObject.launch = function() - GUI.error("Application is corrupted") - end - else - iconObject.iconImage.image = MineOSCore.icons.script - iconObject.launch = function() - ecs.prepareToExit() - if MineOSCore.safeLaunch(iconObject.path) then - MineOSCore.waitForPressingAnyKey() - end - end - end - end - - return iconObject -end - -function MineOSCore.getParametersForDrawingIcons(fieldWidth, fieldHeight, xSpaceBetweenIcons, ySpaceBetweenIcons) - local xCountOfIcons, yCountOfIcons = math.floor(fieldWidth / (MineOSCore.iconWidth + xSpaceBetweenIcons)), math.floor(fieldHeight / (MineOSCore.iconHeight + ySpaceBetweenIcons)) - local totalCountOfIcons = xCountOfIcons * yCountOfIcons - return xCountOfIcons, yCountOfIcons, totalCountOfIcons -end - -function MineOSCore.createIconObject(x, y, path, textColor, showFileFormat) - local iconObject = GUI.container(x, y, MineOSCore.iconWidth, MineOSCore.iconHeight) - - iconObject.path = path - iconObject.size = fs.size(iconObject.path) - iconObject.isDirectory = fs.isDirectory(iconObject.path) - iconObject.format = MineOSCore.getFileFormat(iconObject.path) - iconObject.showFormat = showFileFormat - iconObject.isShortcut = false - iconObject.isSelected = false - - iconObject.iconImage = iconObject:addImage(3, 1, {width = 8, height = 4}) - iconObject.textLabel = iconObject:addLabel(1, MineOSCore.iconHeight, MineOSCore.iconWidth, 1, textColor, MineOSCore.getFileName(iconObject.path)):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) - - local oldDraw = iconObject.draw - iconObject.draw = function(iconObject) - if iconObject.isSelected then buffer.square(iconObject.x, iconObject.y, iconObject.width, iconObject.height, 0xFFFFFF, 0x000000, " ", 50) end - if iconObject.showFormat then - iconObject.textLabel.text = MineOSCore.limitFileName(MineOSCore.getFileName(iconObject.path), iconObject.textLabel.width) - else - iconObject.textLabel.text = MineOSCore.limitFileName(MineOSCore.hideFileFormat(MineOSCore.getFileName(iconObject.path)), iconObject.textLabel.width) - end - oldDraw(iconObject) - if iconObject.isShortcut then buffer.set(iconObject.iconImage.x + iconObject.iconImage.width - 1, iconObject.iconImage.y + iconObject.iconImage.height - 1, 0xFFFFFF, 0x000000, "<") end - end - - -- Поддержка изменяемых извне функций правого и левого кликов - iconObject.onLeftClick = MineOSCore.iconLeftClick - iconObject.onRightClick = MineOSCore.iconRightClick - - -- Обработка клика непосредственно на иконку - iconObject.iconImage.onTouch = function(eventData) - iconObject.isSelected = true - local firstParent = iconObject:getFirstParent() - firstParent:draw() - buffer.draw() - - if eventData[5] == 0 then - os.sleep(MineOSCore.iconClickDelay) - iconObject.onLeftClick(iconObject, eventData) - else - iconObject.onRightClick(iconObject, eventData) - end - - iconObject.isSelected = false - firstParent:draw() - buffer.draw() - end - - -- Онализ формата и прочего говна иконки для последующего получения изображения иконки и функции-лаунчера - MineOSCore.analyzeIconFormat(iconObject) - - return iconObject -end - -local function updateIconFieldFileList(iconField) - iconField.fileList = MineOSCore.getFileList(iconField.workpath) - iconField.fileList = MineOSCore.sortFileList(iconField.workpath, iconField.fileList, iconField.sortingMethod, iconField.showHiddenFiles) - iconField.children = {} - - local xPos, yPos, counter = 1, 1, 1 - for i = iconField.fromFile, iconField.fromFile + iconField.iconCount.total - 1 do - if not iconField.fileList[i] then break end - - iconField:addChild( - MineOSCore.createIconObject( - xPos, yPos, iconField.workpath .. iconField.fileList[i], iconField.colors.iconText, iconField.showFileFormat - ), - GUI.objectTypes.container - ) - - xPos, counter = xPos + MineOSCore.iconWidth + iconField.spaceBetweenIcons.x, counter + 1 - if counter > iconField.iconCount.width then - xPos, counter = 1, 1 - yPos = yPos + MineOSCore.iconHeight + iconField.spaceBetweenIcons.y - end - end - - return iconField -end - -function MineOSCore.createIconField(x, y, width, height, xCountOfIcons, yCountOfIcons, totalCountOfIcons, xSpaceBetweenIcons, ySpaceBetweenIcons, iconTextColor, showFileFormat, showHiddenFiles, sortingMethod, workpathworkpath) - local iconField = GUI.container(x, y, width, height) - - iconField.colors = {iconText = iconTextColor} - - iconField.iconCount = {} - iconField.spaceBetweenIcons = {x = xSpaceBetweenIcons, y = ySpaceBetweenIcons} - iconField.iconCount.width, iconField.iconCount.height, iconField.iconCount.total = xCountOfIcons, yCountOfIcons, totalCountOfIcons - - iconField.workpath = workpath - iconField.showFileFormat = showFileFormat - iconField.showHiddenFiles = showHiddenFiles - iconField.sortingMethod = sortingMethod - iconField.fileList = {} - iconField.fromFile = fromFile - - iconField.updateFileList = updateIconFieldFileList - - return iconField -end - ------------------------------------------------------------------------------------------------------------------------------------ - ---Функция парсинга Lua-сообщения об ошибке. Конвертирует из строки в массив. -function MineOSCore.parseErrorMessage(error, indentationWidth) - local parsedError = {} - - --Замена /r/n и табсов - error = string.gsub(error, "\r\n", "\n") - error = string.gsub(error, " ", string.rep(" ", indentationWidth or 4)) - - --Удаление энтеров - local searchFrom, starting, ending = 1 - for i = 1, unicode.len(error) do - starting, ending = string.find(error, "\n", searchFrom) - if starting then - table.insert(parsedError, unicode.sub(error, searchFrom, starting - 1)) - searchFrom = ending + 1 - else - break - end - end - - --На всякий случай, если сообщение об ошибке без энтеров вообще, т.е. однострочное - if #parsedError == 0 then table.insert(parsedError, error) end - - return parsedError -end - -local function drawErrorWindow(path, programVersion, errorLine, reason) - local oldDrawLimit = buffer.getDrawLimit(); buffer.resetDrawLimit() - local width, height = buffer.screen.width, math.floor(buffer.screen.height * 0.45) - local y = math.floor(buffer.screen.height / 2 - height / 2) - - -- Окошечко и всякая шняжка на нем - local window = windows.empty(1, y, width, height, width, height) - window:addPanel(1, 1, width, 3, 0x383838) - window:addLabel(1, 2, width, 1, 0xFFFFFF, MineOSCore.localization.errorWhileRunningProgram .. "\"" .. MineOSCore.getFileName(path) .. "\""):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) - local windowActionButtons = window:addWindowActionButtons(2, 2, false) - local sendToDeveloperButton = window:addAdaptiveButton(9, 1, 2, 1, 0x444444, 0xFFFFFF, 0x343434, 0xFFFFFF, MineOSCore.localization.sendFeedback) - - --Кодик на окошечке - local lines = {} - local fromLine = errorLine - math.floor((height - 3) / 2) + 1; if fromLine <= 0 then fromLine = 1 end - local toLine = fromLine + window.height - 3 - 1 - local file = io.open(path, "r") - local lineCounter = 1 - for line in file:lines() do - if lineCounter >= fromLine and lineCounter <= toLine then - lines[lineCounter] = string.gsub(line, " ", " ") - elseif lineCounter < fromLine then - lines[lineCounter] = " " - elseif lineCounter > toLine then - break - end - lineCounter = lineCounter + 1 - end - file:close() - - local codeView = window:addCodeView(1, 4, math.floor(width * 0.62), height - 3, lines, 1, fromLine, 100, {}, {[errorLine] = 0xFF4444}, true, 2) - codeView.scrollBars.horizontal.isHidden = true - - -- Текстбоксик - local stackTextBox = window:addTextBox(codeView.width + 1, 4, window.width - codeView.width, codeView.height, 0xFFFFFF, 0x000000, string.wrap(MineOSCore.parseErrorMessage(reason, 4), window.width - codeView.width - 2), 1, 1, 0) - - -- Всякие действия пиздатые - local function exit() - windowActionButtons.close:pressAndRelease() - buffer.setDrawLimit(oldDrawLimit) - window:close() - end - - windowActionButtons.close.onTouch = exit - - window.onDrawStarted = function() - buffer.clear(0x000000, 50) - end - - window.onKeyDown = function(eventData) - if eventData[4] == 28 then exit() end - end - - sendToDeveloperButton.onTouch = function() - local data = ecs.universalWindow("auto", "auto", 36, 0xeeeeee, true, - {"EmptyLine"}, - {"CenterText", 0x880000, MineOSCore.localization.sendFeedback}, - {"EmptyLine"}, - {"Input", 0x262626, 0x880000, MineOSCore.localization.yourContacts}, - {"Input", 0x262626, 0x880000, MineOSCore.localization.additionalInfo}, - {"EmptyLine"}, - {"CenterText", 0x880000, MineOSCore.localization.stackTraceback .. ":"}, - {"EmptyLine"}, - {"TextField", 5, 0xFFFFFF, 0x000000, 0xcccccc, 0x3366CC, reason}, - {"Button", {0x999999, 0xffffff, "OK"}, {0x777777, 0xffffff, MineOSCore.localization.cancel}} - ) - - if data[3] == "OK" then - if component.isAvailable("internet") then - local url = "http://igortimofeev.wallst.ru/MineOSErrorReports/Report.php?path=" .. path .. "&version=" .. string.optimizeForURLRequests(programVersion) .. "&userContacts=" .. string.optimizeForURLRequests(data[1]) .. "&userMessage=" .. string.optimizeForURLRequests(data[2]) .. "&errorMessage=" .. string.optimizeForURLRequests(reason) - local success, reason = component.internet.request(url) - if success then - success:close() - else - ecs.error(reason) - end - end - exit() - end - end - - -- Начинаем гомоеблю! - window:draw() - buffer.draw() - for i = 1, 3 do component.computer.beep(1500, 0.08) end - window:handleEvents() -end - -function MineOSCore.safeLaunch(path, ...) - local args = {...} - local oldResolutionWidth, oldResolutionHeight = buffer.screen.width, buffer.screen.height - local finalSuccess, finalPath, finalLine, finalTraceback = true - - if fs.exists(path) then - local loadSuccess, loadReason = loadfile(string.canonicalPath("/" .. path)) - - if loadSuccess then - local function launchMethod() - loadSuccess(table.unpack(args)) - end - - local function tracebackMethod(xpcallTraceback) - local traceback, info, firstMatch = tostring(xpcallTraceback) .. "\n" .. debug.traceback() - for runLevel = 0, math.huge do - info = debug.getinfo(runLevel) - if info then - if (info.what == "main" or info.what == "Lua") and info.source ~= "=machine" then - if firstMatch then - return { - path = info.source:sub(2, -1), - line = info.currentline, - traceback = traceback - } - else - firstMatch = true - end - end - else - error("Failed to get debug info for runlevel " .. runLevel) - end - end - end - - local runSuccess, runReason = xpcall(launchMethod, tracebackMethod) - if type(runReason) == "string" then - GUI.error(runReason, {title = {color = 0xFFDB40, text = "Warning"}}) - else - if not runSuccess and not string.match(runReason.traceback, "^table") and not string.find(runReason.traceback, "interrupted", 1, 15) then - finalSuccess, finalPath, finalLine, finalTraceback = false, runReason.path, runReason.line, runReason.traceback - end - end - else - finalSuccess, finalPath, finalTraceback = false, path, loadReason - local match = string.match(loadReason, ":(%d+)%:") - finalLine = tonumber(match) - if not match or not finalLine then error("Дебажь говно! " .. tostring(loadReason)) end - end - else - GUI.error("Failed to safely launch file that doesn't exists: \"" .. path .. "\"", {title = {color = 0xFFDB40, text = "Warning"}}) - end - - if not finalSuccess then - drawErrorWindow(finalPath, "1.0", finalLine, finalTraceback) - end - - component.gpu.setResolution(oldResolutionWidth, oldResolutionHeight) - buffer.start() - - return finalSuccess, finalPath, finalLine, finalTraceback -end - ------------------------------------------------------------------------------------------------------------------------------------ - -function MineOSCore.iconLeftClick(iconObject, eventData) - iconObject.launch() - computer.pushSignal("MineOSCore", "updateFileList") -end - -function MineOSCore.iconRightClick(icon, eventData) - local action - -- Разные контекстные меню - if icon.isDirectory then - if icon.format == ".app" then - action = GUI.contextMenu(eventData[3], eventData[4], - {MineOSCore.localization.contextMenuShowPackageContent}, - "-", - {MineOSCore.localization.contextMenuCut}, - {MineOSCore.localization.contextMenuCopy}, - {MineOSCore.localization.contextMenuRename}, - {MineOSCore.localization.contextMenuCreateShortcut, icon.format == ".lnk"}, - "-", - {MineOSCore.localization.contextMenuArchive}, - {MineOSCore.localization.contextMenuAddToDock}, - "-", - {MineOSCore.localization.contextMenuProperties}, - {MineOSCore.localization.contextMenuDelete} - ):show() - else - action = GUI.contextMenu(eventData[3], eventData[4], - {MineOSCore.localization.contextMenuCut}, - {MineOSCore.localization.contextMenuCopy}, - {MineOSCore.localization.contextMenuRename}, - {MineOSCore.localization.contextMenuCreateShortcut, icon.format == ".lnk"}, - "-", - {MineOSCore.localization.contextMenuArchive}, - {MineOSCore.localization.contextMenuAddToDock}, - "-", - {MineOSCore.localization.contextMenuProperties}, - {MineOSCore.localization.contextMenuDelete} - ):show() - end - else - if icon.isShortcut then - action = GUI.contextMenu(eventData[3], eventData[4], - {MineOSCore.localization.contextMenuEdit}, - {MineOSCore.localization.contextMenuShowContainingFolder}, - "-", - {MineOSCore.localization.contextMenuCut}, - {MineOSCore.localization.contextMenuCopy}, - {MineOSCore.localization.contextMenuRename}, - "-", - {MineOSCore.localization.contextMenuAddToDock}, - {MineOSCore.localization.contextMenuProperties}, - {MineOSCore.localization.contextMenuDelete} - ):show() - elseif icon.format == ".pic" then - action = GUI.contextMenu(eventData[3], eventData[4], - {MineOSCore.localization.contextMenuSetAsWallpaper}, - "-", - {MineOSCore.localization.contextMenuCut}, - {MineOSCore.localization.contextMenuCopy}, - {MineOSCore.localization.contextMenuRename}, - {MineOSCore.localization.contextMenuCreateShortcut, icon.format == ".lnk"}, - "-", - -- {MineOSCore.localization.contextMenuArchive}, - {MineOSCore.localization.contextMenuAddToDock}, - "-", - {MineOSCore.localization.contextMenuProperties}, - {MineOSCore.localization.contextMenuDelete} - ):show() - elseif icon.format == ".lua" then - action = GUI.contextMenu(eventData[3], eventData[4], - {MineOSCore.localization.contextMenuEdit}, - {MineOSCore.localization.contextMenuFlashEEPROM, (not component.isAvailable("eeprom") or icon.size > 4096)}, - "-", - {MineOSCore.localization.contextMenuCut}, - {MineOSCore.localization.contextMenuCopy}, - {MineOSCore.localization.contextMenuRename}, - {MineOSCore.localization.contextMenuCreateShortcut, icon.format == ".lnk"}, - -- "-", - -- {MineOSCore.localization.contextMenuUploadToPastebin, true}, - "-", - -- {MineOSCore.localization.contextMenuArchive}, - {MineOSCore.localization.contextMenuAddToDock}, - "-", - {MineOSCore.localization.contextMenuProperties}, - {MineOSCore.localization.contextMenuDelete} - ):show() - else - action = GUI.contextMenu(eventData[3], eventData[4], - {MineOSCore.localization.contextMenuEdit}, - "-", - {MineOSCore.localization.contextMenuCut}, - {MineOSCore.localization.contextMenuCopy}, - {MineOSCore.localization.contextMenuRename}, - {MineOSCore.localization.contextMenuCreateShortcut, icon.format == ".lnk"}, - "-", - -- {MineOSCore.localization.contextMenuArchive}, - {MineOSCore.localization.contextMenuAddToDock}, - "-", - {MineOSCore.localization.contextMenuProperties}, - {MineOSCore.localization.contextMenuDelete} - ):show() - end - end - - if action == MineOSCore.localization.contextMenuEdit then - MineOSCore.safeLaunch(MineOSCore.paths.applications .. "/MineCode IDE.app/MineCode IDE.lua", "open", icon.path) - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == "Свойства" then - MineOSCore.showPropertiesWindow(eventData[3], eventData[4], 40, icon) - elseif action == MineOSCore.localization.contextMenuShowContainingFolder then - computer.pushSignal("MineOSCore", "changeWorkpath", MineOSCore.getFilePath(icon.shortcutPath)) - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuAddToFavourites then - computer.pushSignal("finderFavouriteAdded", icon.path) - elseif action == MineOSCore.localization.contextMenuShowPackageContent then - computer.pushSignal("MineOSCore", "changeWorkpath", icon.path) - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuCopy then - _G.clipboard = icon.path - elseif action == MineOSCore.localization.contextMenuCut then - _G.clipboard = icon.path - _G.clipboardCut = true - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuDelete then - if MineOSCore.getFilePath(icon.path) == MineOSCore.paths.trash then - fs.remove(icon.path) - else - local newName = MineOSCore.paths.trash .. MineOSCore.getFileName(icon.path) - local clearName = MineOSCore.hideFileFormat(MineOSCore.getFileName(icon.path)) - local repeats = 1 - while fs.exists(newName) do - newName, repeats = MineOSCore.paths.trash .. clearName .. string.rep("-copy", repeats) .. icon.format, repeats + 1 - end - fs.rename(icon.path, newName) - end - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuRename then - ecs.rename(icon.path) - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuCreateShortcut then - ecs.createShortCut(MineOSCore.getFilePath(icon.path) .. "/" .. ecs.hideFileFormat(MineOSCore.getFileName(icon.path)) .. ".lnk", icon.path) - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuArchive then - require("compressor").pack(MineOSCore.getFilePath(icon.path) .. MineOSCore.hideFileFormat(MineOSCore.getFileName(icon.path)) .. ".pkg", icon.path) - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuSetAsWallpaper then - fs.remove(MineOSCore.paths.wallpaper) - ecs.createShortCut(MineOSCore.paths.wallpaper, icon.path) - computer.pushSignal("MineOSCore", "updateWallpaper") - elseif action == MineOSCore.localization.contextMenuFlashEEPROM then - local file = io.open(icon.path, "r") - component.eeprom.set(file:read("*a")) - file:close() - computer.beep(1500, 0.2) - elseif action == MineOSCore.localization.contextMenuCreateApplication then - ecs.newApplicationFromLuaFile(icon.path, MineOSCore.getFilePath(icon.path) or "") - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuAddToDock then - table.insert(_G.OSSettings.dockShortcuts, {path = icon.path}) - MineOSCore.saveOSSettings() - computer.pushSignal("MineOSCore", "updateFileList") - end -end - -function MineOSCore.emptyZoneClick(eventData, workspace, workpath) - local action = GUI.contextMenu(eventData[3], eventData[4], - {MineOSCore.localization.contextMenuNewFile}, - {MineOSCore.localization.contextMenuNewFolder}, - {MineOSCore.localization.contextMenuNewApplication}, - "-", - {MineOSCore.localization.contextMenuPaste, (_G.clipboard == nil)} - ):show() - - if action == MineOSCore.localization.contextMenuNewFile then - ecs.newFile(workpath) - computer.pushSignal("MineOSCore", "updateFileListAndBufferTrueRedraw") - elseif action == MineOSCore.localization.contextMenuNewFolder then - ecs.newFolder(workpath) - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuPaste then - ecs.copy(_G.clipboard, workpath) - if _G.clipboardCut then - fs.remove(_G.clipboard) - _G.clipboardCut = nil - _G.clipboard = nil - end - computer.pushSignal("MineOSCore", "updateFileList") - elseif action == MineOSCore.localization.contextMenuNewApplication then - ecs.newApplication(workpath) - computer.pushSignal("MineOSCore", "updateFileList") - end -end - -local function addKeyAndValue(window, x, y, key, value) - window:addLabel(x, y, window.width , 1, 0x333333, key .. ":"); x = x + unicode.len(key) + 2 - return window:addLabel(x, y, window.width, 1, 0x555555, value) -end - -function MineOSCore.showPropertiesWindow(x, y, width, iconObject) - local window = windows.empty(x, y, width, 1) - local backgroundPanel = window:addPanel(1, 2, window.width, 1, 0xDDDDDD) - window:addPanel(1, 1, window.width, 1, 0xEEEEEE) - window:addLabel(1, 1, window.width, 1, 0x333333, MineOSCore.localization.contextMenuProperties):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) - window:addButton(2, 1, 1, 1, nil, 0xFF4940, nil, 0x992400, "●").onTouch = function() window:close() end - - window:addImage(3, 3, iconObject.iconImage.image) - - local y = 3 - addKeyAndValue(window, 13, y, MineOSCore.localization.type, iconObject.format and iconObject.format or (iconObject.isDirectory and MineOSCore.localization.folder or MineOSCore.localization.unknown)); y = y + 1 - local fileSizeLabel = addKeyAndValue(window, 13, y, MineOSCore.localization.size, iconObject.isDirectory and MineOSCore.localization.calculatingSize or string.format("%.2f", iconObject.size / 1024) .. " KB"); y = y + 1 - addKeyAndValue(window, 13, y, MineOSCore.localization.date, os.date("%d.%m.%y, %H:%M", fs.lastModified(iconObject.path))); y = y + 1 - addKeyAndValue(window, 13, y, MineOSCore.localization.path, " ") - - local lines = string.wrap(iconObject.path, window.width - 19) - local textBox = window:addTextBox(19, y, window.width - 19, #lines, nil, 0x555555, lines, 1) - window.height = textBox.y + textBox.height - backgroundPanel.height = window.height - 1 - - if window.x + window.width > buffer.screen.width then window.x = window.x - (window.x + window.width - buffer.screen.width) end - if window.y + window.height > buffer.screen.height then window.y = window.y - (window.y + window.height - buffer.screen.height) end - - window:draw() - buffer.draw() - - if iconObject.isDirectory then - fileSizeLabel.text = string.format("%.2f", MineOSCore.getFolderSize(iconObject.path) / 1024) .. " KB" - window:draw() - buffer.draw() - end - - window:handleEvents() -end - ------------------------------------------------------------------------------------------------------------------------------------ - -MineOSCore.init() - ------------------------------------------------------------------------------------------------------------------------------------ - -return MineOSCore - - - - - -Dlib/OpenComputersGL/Flib/OpenComputersGL/Main.lua2 --------------------------------------------------------- Libraries -------------------------------------------------------- - -local vector = require("vector") -local matrix = require("matrix") -local buffer = require("doubleBuffering") -local materials = require("OpenComputersGL/Materials") -local renderer = require("OpenComputersGL/Renderer") -local OCGL = {} - --------------------------------------------------------- Constants -------------------------------------------------------- - -OCGL.axis = { - x = 1, - y = 2, - z = 3, -} - -OCGL.vertices = {} -OCGL.triangles = {} -OCGL.lines = {} -OCGL.floatingTexts = {} - --------------------------------------------------------- Vertex field methods -------------------------------------------------------- - -function OCGL.rotateVector(vector, axis, angle) - local sin, cos = math.sin(angle), math.cos(angle) - if axis == OCGL.axis.x then - vector[1], vector[2], vector[3] = vector[1], cos * vector[2] - sin * vector[3], sin * vector[2] + cos * vector[3] - elseif axis == OCGL.axis.y then - vector[1], vector[2], vector[3] = cos * vector[1] + sin * vector[3], vector[2], cos * vector[3] - sin * vector[1] - elseif axis == OCGL.axis.z then - vector[1], vector[2], vector[3] = cos * vector[1] - sin * vector[2], sin * vector[1] + cos * vector[2], vector[3] - else - error("Axis enum " .. tostring(axis) .. " doesn't exists") - end -end - -function OCGL.translate(xTranslation, yTranslation, zTranslation) - for vertexIndex = 1, #OCGL.vertices do - OCGL.vertices[vertexIndex][1], OCGL.vertices[vertexIndex][2], OCGL.vertices[vertexIndex][3] = OCGL.vertices[vertexIndex][1] + xTranslation, OCGL.vertices[vertexIndex][2] + yTranslation, OCGL.vertices[vertexIndex][3] + zTranslation - end -end - -function OCGL.rotate(axis, angle) - for vertexIndex = 1, #OCGL.vertices do - OCGL.rotateVector(OCGL.vertices[vertexIndex], axis, angle) - end -end - --------------------------------------------------------- Render queue methods -------------------------------------------------------- - -function OCGL.newIndexedTriangle(indexOfVertex1, indexOfVertex2, indexOfVertex3, material) - return { indexOfVertex1, indexOfVertex2, indexOfVertex3, material } -end - -function OCGL.newIndexedLine(indexOfVertex1, indexOfVertex2, color) - return { indexOfVertex1, indexOfVertex2, color } -end - -function OCGL.newIndexedFloatingText(indexOfVertex, color, text) - return {indexOfVertex, text, color} -end - -function OCGL.pushTriangleToRenderQueue(vector3Vertex1, vector3Vertex2, vector3Vertex3, material, meshPointer, meshTriangleIndexPointer) - table.insert(OCGL.vertices, vector3Vertex1) - table.insert(OCGL.vertices, vector3Vertex2) - table.insert(OCGL.vertices, vector3Vertex3) - table.insert(OCGL.triangles, OCGL.newIndexedTriangle(OCGL.nextVertexIndex, OCGL.nextVertexIndex + 1, OCGL.nextVertexIndex + 2, material, meshPointer, meshTriangleIndexPointer)) - OCGL.nextVertexIndex = OCGL.nextVertexIndex + 3 -end - -function OCGL.pushLineToRenderQueue(vector3Vertex1, vector3Vertex2, color) - table.insert(OCGL.vertices, vector3Vertex1) - table.insert(OCGL.vertices, vector3Vertex2) - table.insert(OCGL.lines, OCGL.newIndexedLine(OCGL.nextVertexIndex, OCGL.nextVertexIndex + 1, color)) - OCGL.nextVertexIndex = OCGL.nextVertexIndex + 2 -end - -function OCGL.pushFloatingTextToRenderQueue(vector3Vertex, color, text) - table.insert(OCGL.vertices, vector3Vertex) - table.insert(OCGL.floatingTexts, OCGL.newIndexedFloatingText(OCGL.nextVertexIndex, color, text)) - OCGL.nextVertexIndex = OCGL.nextVertexIndex + 1 -end - --------------------------------------------------------- Rendering methods -------------------------------------------------------- - -OCGL.setViewport = renderer.setViewport - -function OCGL.clearBuffer(backgroundColor) - OCGL.nextVertexIndex, OCGL.vertices, OCGL.triangles, OCGL.lines, OCGL.floatingTexts = 1, {}, {}, {}, {} - renderer.clearDepthBuffer() - buffer.clear(backgroundColor) -end - -function OCGL.createPerspectiveProjection() - local zNearDivZ - for vertexIndex = 1, #OCGL.vertices do - zNearDivZ = math.abs(renderer.viewport.projectionSurface / OCGL.vertices[vertexIndex][3]) - OCGL.vertices[vertexIndex][1] = zNearDivZ * OCGL.vertices[vertexIndex][1] - OCGL.vertices[vertexIndex][2] = zNearDivZ * OCGL.vertices[vertexIndex][2] - -- OCGL.vertices[vertexIndex][3] = zNearDivZ * OCGL.vertices[vertexIndex][3] - end -end - -function OCGL.render(renderMode) - local vector3Vertex1, vector3Vertex2, vector3Vertex3, material = {}, {}, {} - - -- for lineIndex = 1, #OCGL.lines do - -- vector3Vertex1, vector3Vertex2, material = OCGL.vertices[OCGL.lines[lineIndex][1]], OCGL.vertices[OCGL.lines[lineIndex][2]], OCGL.lines[lineIndex][3] - - -- if renderMode == renderer.renderModes.vertices then - -- renderer.renderDot(vector3Vertex1, material) - -- renderer.renderDot(vector3Vertex2, material) - -- else - -- renderer.renderLine( - -- math.floor(vector3Vertex1[1]), - -- math.floor(vector3Vertex1[2]), - -- vector3Vertex1[3], - -- math.floor(vector3Vertex2[1]), - -- math.floor(vector3Vertex2[2]), - -- vector3Vertex2[3], - -- material - -- ) - -- end - -- end - - for floatingTextIndex = 1, #OCGL.floatingTexts do - vector3Vertex1 = OCGL.vertices[OCGL.floatingTexts[floatingTextIndex][1]] - renderer.renderFloatingText( - renderer.viewport.xCenter + vector3Vertex1[1], - renderer.viewport.yCenter - vector3Vertex1[2], - vector3Vertex1[3], - OCGL.floatingTexts[floatingTextIndex][2], - OCGL.floatingTexts[floatingTextIndex][3] - ) - end - - for triangleIndex = 1, #OCGL.triangles do - material = OCGL.triangles[triangleIndex][4] - vector3Vertex1[1], vector3Vertex1[2], vector3Vertex1[3] = renderer.viewport.xCenter + OCGL.vertices[OCGL.triangles[triangleIndex][1]][1], renderer.viewport.yCenter - OCGL.vertices[OCGL.triangles[triangleIndex][1]][2], OCGL.vertices[OCGL.triangles[triangleIndex][1]][3] - vector3Vertex2[1], vector3Vertex2[2], vector3Vertex2[3] = renderer.viewport.xCenter + OCGL.vertices[OCGL.triangles[triangleIndex][2]][1], renderer.viewport.yCenter - OCGL.vertices[OCGL.triangles[triangleIndex][2]][2], OCGL.vertices[OCGL.triangles[triangleIndex][2]][3] - vector3Vertex3[1], vector3Vertex3[2], vector3Vertex3[3] = renderer.viewport.xCenter + OCGL.vertices[OCGL.triangles[triangleIndex][3]][1], renderer.viewport.yCenter - OCGL.vertices[OCGL.triangles[triangleIndex][3]][2], OCGL.vertices[OCGL.triangles[triangleIndex][3]][3] - - if - renderer.isVertexInViewRange(vector3Vertex1[1], vector3Vertex1[2], vector3Vertex1[3]) or - renderer.isVertexInViewRange(vector3Vertex2[1], vector3Vertex2[2], vector3Vertex2[3]) or - renderer.isVertexInViewRange(vector3Vertex3[1], vector3Vertex3[2], vector3Vertex3[3]) - then - if renderMode == renderer.renderModes.material then - if material.type == materials.types.solid then - renderer.renderFilledTriangle( - { - vector3Vertex1, - vector3Vertex2, - vector3Vertex3 - }, - material.color - ) - else - error("Material type " .. tostring(material.type) .. " doesn't supported for rendering triangles") - end - elseif renderMode == renderer.renderModes.wireframe then - renderer.renderLine(math.floor(vector3Vertex1[1]), math.floor(vector3Vertex1[2]), vector3Vertex1[3], math.floor(vector3Vertex2[1]), math.floor(vector3Vertex2[2]), vector3Vertex2[3], material.color or renderer.colors.wireframe) - renderer.renderLine(math.floor(vector3Vertex2[1]), math.floor(vector3Vertex2[2]), vector3Vertex2[3], math.floor(vector3Vertex3[1]), math.floor(vector3Vertex3[2]), vector3Vertex3[3], material.color or renderer.colors.wireframe) - renderer.renderLine(math.floor(vector3Vertex1[1]), math.floor(vector3Vertex1[2]), vector3Vertex1[3], math.floor(vector3Vertex3[1]), math.floor(vector3Vertex3[2]), vector3Vertex3[3], material.color or renderer.colors.wireframe) - elseif renderMode == renderer.renderModes.vertices then - renderer.renderDot(vector3Vertex1, material.color or renderer.colors.wireframe) - renderer.renderDot(vector3Vertex2, material.color or renderer.colors.wireframe) - renderer.renderDot(vector3Vertex3, material.color or renderer.colors.wireframe) - else - error("Rendermode enum " .. tostring(renderMode) .. " doesn't supported for rendering triangles") - end - end - end -end - --------------------------------------------------------- Raycasting methods -------------------------------------------------------- - -local function vectorMultiply(a, b) - return vector.newVector3(a[2] * b[3] - a[3] * b[2], a[3] * b[1] - a[1] * b[3], a[1] * b[2] - a[2] * b[1]) -end - -local function getVectorDistance(a) - return math.sqrt(a[1] ^ 2 + a[2] ^ 2 + a[3] ^ 2) -end - --- В случае попадания лучика этот метод вернет сам треугольник, а также дистанцию до его плоскости -function OCGL.triangleRaycast(vector3RayStart, vector3RayEnd) - local minimalDistance, closestTriangleIndex - for triangleIndex = 1, #OCGL.triangles do - -- Это вершины треугольника - local A, B, C = OCGL.vertices[OCGL.triangles[triangleIndex][1]], OCGL.vertices[OCGL.triangles[triangleIndex][3]], OCGL.vertices[OCGL.triangles[triangleIndex][3]] - -- ecs.error(A[1], A[2], A[3], vector3RayStart[1], vector3RayStart[2], vector3RayStart[3]) - -- Это хз че - local ABC = vectorMultiply(vector.newVector3(C[1] - A[1], C[2] - A[2], C[3] - A[3]), vector.newVector3(B[1] - A[1], B[2] - A[2], B[3] - A[3])) - -- Рассчитываем удаленность виртуальной плоскости треугольника от старта нашего луча - local D = -ABC[1] * A[1] - ABC[2] * A[2] - ABC[3] * A[3] - local firstPart = D + ABC[1] * vector3RayStart[1] + ABC[2] * vector3RayStart[2] + ABC[3] * vector3RayStart[3] - local secondPart = ABC[1] * vector3RayStart[1] - ABC[1] * vector3RayEnd[1] + ABC[2] * vector3RayStart[2] - ABC[2] * vector3RayEnd[2] + ABC[3] * vector3RayStart[3] - ABC[3] * vector3RayEnd[3] - - -- ecs.error(firstPart, secondPart) - - -- if firstPart ~= 0 or secondPart ~= 0 then ecs.error(firstPart, secondPart) end - -- Если наш лучик не параллелен той ебучей плоскости треугольника - if secondPart ~= 0 then - local distance = firstPart / secondPart - -- И если этот объект находится ближе к старту луча, нежели предыдущий - if (distance >= 0 and distance <= 1) and (not minimalDistance or distance < minimalDistance) then - - -- То считаем точку попадания луча в данную плоскость (но ни хуя не факт, что он попадет в треугольник!) - local S = vector.newVector3( - vector3RayStart[1] + (vector3RayEnd[1] - vector3RayStart[1]) * distance, - vector3RayStart[2] + (vector3RayEnd[2] - vector3RayStart[2]) * distance, - vector3RayStart[3] + (vector3RayEnd[3] - vector3RayStart[3]) * distance - ) - - -- Далее считаем сумму площадей параллелограммов, образованных тремя треугольниками, образовавшихся при попадании точки в треугольник - -- Нууу тип кароч смари: точка ебанула в центр, и треугольник распидорасило на три мелких. Ну, и три мелких могут образовать параллелограммы свои - -- И, кароч, если сумма трех площадей этих мелких уебков будет сильно отличаться от площади жирного треугольника, то луч не попал - -- Ну, а площадь считается через sqrt(x^2+y^2+z^2) для каждого йоба-вектора - - ---- *A *B - - - -- * Shotxyz - - - --- *C - - local SA = vector.newVector3(A[1] - S[1], A[2] - S[2], A[3] - S[3]) - local SB = vector.newVector3(B[1] - S[1], B[2] - S[2], B[3] - S[3]) - local SC = vector.newVector3(C[1] - S[1], C[2] - S[2], C[3] - S[3]) - - local vectorDistanceSum = getVectorDistance(vectorMultiply(SA, SB)) + getVectorDistance(vectorMultiply(SB, SC)) + getVectorDistance(vectorMultiply(SC, SA)) - local ABCDistance = getVectorDistance(ABC) - - -- Вот тут мы чекаем погрешность расчетов. Если все заебок, то кидаем этот треугольник в "проверенные"" - if math.abs(vectorDistanceSum - ABCDistance) < 1 then - closestTriangleIndex = triangleIndex - minimalDistance = distance - end - end - end - end - - -- ecs.error(closestTriangleIndex) - if OCGL.triangles[closestTriangleIndex] then - return OCGL.triangles[closestTriangleIndex][5], OCGL.triangles[closestTriangleIndex][6], minimalDistance - end -end - --------------------------------------------------------- Constants -------------------------------------------------------- - -return OCGL -F!lib/OpenComputersGL/Materials.lua6 - -local materials = {} - ------------------------------------------------------------------------------------------------------------------------- - -materials.types = { - textured = 1, - solid = 2, -} - -function materials.newSolidMaterial(color) - return { - type = materials.types.solid, - color = color - } -end - -function materials.newTexturedMaterial(texture) - return { - type = materials.types.textured, - texture = texture - } -end - ------------------------------------------------------------------------------------------------------------------------- - -return materials - -F lib/OpenComputersGL/Renderer.lua% --------------------------------------------------------- Libraries -------------------------------------------------------- - -local vector = require("vector") -local unicode = require("unicode") -local materials = require("OpenComputersGL/Materials") -local buffer = require("doubleBuffering") - -local renderer = { - depthBuffer = {}, - viewport = {}, -} - --------------------------------------------------------- Constants -------------------------------------------------------- - -renderer.colors = { - axis = { - x = 0xFF0000, - y = 0x00FF00, - z = 0x0000FF, - }, - pivotPoint = 0xFFFFFF, - wireframe = 0x00FFFF, -} - -renderer.renderModes = { - material = 1, - wireframe = 2, - vertices = 3, -} - --------------------------------------------------------- Renderer -------------------------------------------------------- - -function renderer.clearDepthBuffer() - for y = 1, renderer.viewport.height do - renderer.depthBuffer[y] = {} - for x = 1, renderer.viewport.width do - renderer.depthBuffer[y][x] = math.huge - end - end -end - -function renderer.setViewport(x1, y1, x2, y2, nearClippingSurface, farClippingSurface, projectionSurface) - renderer.viewport.x1 = x1 - renderer.viewport.y1 = y1 - renderer.viewport.x2 = x2 - renderer.viewport.y2 = y2 - renderer.viewport.nearClippingSurface = nearClippingSurface - renderer.viewport.farClippingSurface = farClippingSurface - renderer.viewport.projectionSurface = projectionSurface - renderer.viewport.width = x2 - x1 + 1 - renderer.viewport.height = y2 - y1 + 1 - renderer.viewport.xCenter = math.floor(x1 + renderer.viewport.width / 2) - renderer.viewport.yCenter = math.floor(y1 + renderer.viewport.height / 2) -end - -function renderer.setPixelUsingDepthBuffer(x, y, pixelDepthValue, pixelColor) - if - renderer.isVertexInViewRange(x, y, pixelDepthValue) - then - if pixelDepthValue < renderer.depthBuffer[y][x] then - renderer.depthBuffer[y][x] = pixelDepthValue - buffer.semiPixelRawSet(buffer.getBufferIndexByCoordinates(x, math.ceil(y / 2)), pixelColor, y % 2 == 0) - -- buffer.set(x, y, pixelColor, 0x0, " ") - end - end -end - -function renderer.isVertexInViewRange(x, y, z) - return - x >= renderer.viewport.x1 and - x <= renderer.viewport.x2 and - y >= renderer.viewport.y1 and - y <= renderer.viewport.y2 and - -- z >= renderer.viewport.projectionSurface - (renderer.viewport.projectionSurface - renderer.viewport.nearClippingSurface) * 0.6 and - z >= renderer.viewport.nearClippingSurface and - z <= renderer.viewport.farClippingSurface -end - --------------------------------------------------------- Line rendering -------------------------------------------------------- - -function renderer.renderLine(x1, y1, z1, x2, y2, z2, color) - local incycleValueFrom, incycleValueTo, outcycleValueFrom, outcycleValueTo, isReversed, incycleValueDelta, outcycleValueDelta = x1, x2, y1, y2, false, math.abs(x2 - x1), math.abs(y2 - y1) - if incycleValueDelta < outcycleValueDelta then - incycleValueFrom, incycleValueTo, outcycleValueFrom, outcycleValueTo, isReversed, incycleValueDelta, outcycleValueDelta = y1, y2, x1, x2, true, outcycleValueDelta, incycleValueDelta - end - - if outcycleValueFrom > outcycleValueTo then - outcycleValueFrom, outcycleValueTo = outcycleValueTo, outcycleValueFrom - incycleValueFrom, incycleValueTo = incycleValueTo, incycleValueFrom - z1, z2 = z2, z1 - end - - local outcycleValue, outcycleValueCounter, outcycleValueTriggerIncrement = outcycleValueFrom, 1, incycleValueDelta / outcycleValueDelta - local outcycleValueTrigger = outcycleValueTriggerIncrement - local z, zStep = z1, (z2 - z1) / incycleValueDelta - - for incycleValue = incycleValueFrom, incycleValueTo, incycleValueFrom < incycleValueTo and 1 or -1 do - if isReversed then - renderer.setPixelUsingDepthBuffer(outcycleValue, incycleValue, z, color) - else - renderer.setPixelUsingDepthBuffer(incycleValue, outcycleValue, z, color) - end - - outcycleValueCounter, z = outcycleValueCounter + 1, z + zStep - if outcycleValueCounter > outcycleValueTrigger then - outcycleValue, outcycleValueTrigger = outcycleValue + 1, outcycleValueTrigger + outcycleValueTriggerIncrement - end - end -end - -function renderer.renderDot(vector3Vertex, color) - renderer.setPixelUsingDepthBuffer(math.floor(vector3Vertex[1]), math.floor(vector3Vertex[2]), vector3Vertex[3], color) -end - --------------------------------------------------------- Triangles render -------------------------------------------------------- - -local function fillPart(x1Screen, x2Screen, z1Screen, z2Screen, y, color) - if x2Screen < x1Screen then x1Screen, x2Screen, z1Screen, z2Screen = x2Screen, x1Screen, z2Screen, z1Screen end - - local z, zStep = z1Screen, (z2Screen - z1Screen) / (x2Screen - x1Screen) - for x = math.floor(x1Screen), math.floor(x2Screen) do - renderer.setPixelUsingDepthBuffer(x, y, z, color) - -- buffer.semiPixelSet(x, y, color) - z = z + zStep - end -end - -function renderer.renderFilledTriangle(points, color) - local topID, centerID, bottomID = 1, 1, 1 - for i = 1, 3 do - points[i][2] = math.floor(points[i][2]) - if points[i][2] < points[topID][2] then topID = i end - if points[i][2] > points[bottomID][2] then bottomID = i end - end - for i = 1, 3 do if i ~= topID and i ~= bottomID then centerID = i end end - - local x1ScreenStep = (points[centerID][1] - points[topID][1]) / (points[centerID][2] - points[topID][2]) - local x2ScreenStep = (points[bottomID][1] - points[topID][1]) / (points[bottomID][2] - points[topID][2]) - local x1Screen, x2Screen = points[topID][1], points[topID][1] - - local z1ScreenStep = (points[centerID][3] - points[topID][3]) / (points[centerID][2] - points[topID][2]) - local z2ScreenStep = (points[bottomID][3] - points[topID][3]) / (points[bottomID][2] - points[topID][2]) - local z1Screen, z2Screen = points[topID][3], points[topID][3] - - -- Рисуем первый кусок треугольника от верхней точки до центральной - for y = points[topID][2], points[centerID][2] - 1 do - fillPart(x1Screen, x2Screen, z1Screen, z2Screen, y, color) - x1Screen, x2Screen, z1Screen, z2Screen = x1Screen + x1ScreenStep, x2Screen + x2ScreenStep, z1Screen + z1ScreenStep, z2Screen + z2ScreenStep - end - - -- Далее считаем, как будет изменяться X от центрельной точки до нижней - x1Screen, x1ScreenStep = points[centerID][1], (points[bottomID][1] - points[centerID][1]) / (points[bottomID][2] - points[centerID][2]) - z1Screen, z1ScreenStep = points[centerID][3], (points[bottomID][3] - points[centerID][3]) / (points[bottomID][2] - points[centerID][2]) - -- И рисуем нижний кусок треугольника от центральной точки до нижней - for y = points[centerID][2], points[bottomID][2] do - fillPart(x1Screen, x2Screen, z1Screen, z2Screen, y, color) - x1Screen, x2Screen, z1Screen, z2Screen = x1Screen + x1ScreenStep, x2Screen + x2ScreenStep, z1Screen + z1ScreenStep, z2Screen + z2ScreenStep - end -end - -function renderer.renderTexturedTriangle(vertices, texture) - -end - --------------------------------------------------------- Floating text rendering -------------------------------------------------------- - -function renderer.renderFloatingText(x, y, z, color, text) - local textLength = unicode.len(text) - x, y = math.floor(x - textLength / 2), math.floor(y) - local yInteger, yFractional = math.modf(y / 2) - local index, background - - for i = 1, textLength do - if renderer.isVertexInViewRange(x, y, z) then - if z < renderer.depthBuffer[y][x] then - if yFractional == 0 then - renderer.depthBuffer[y - 1][x] = z - renderer.depthBuffer[y][x] = z - else - renderer.depthBuffer[y][x] = z - if renderer.depthBuffer[y + 1] then - renderer.depthBuffer[y + 1][x] = z - end - end - - index = buffer.getBufferIndexByCoordinates(x, yInteger) - background = buffer.rawGet(index) - buffer.rawSet(index, background, color, unicode.sub(text, i, i)) - end - end - x = x + 1 - end -end - --------------------------------------------------------- FPS counter overlay render -------------------------------------------------------- - -local function drawSegments(x, y, segments, color) - for i = 1, #segments do - if segments[i] == 1 then - buffer.semiPixelSquare(x, y, 3, 1, color) - elseif segments[i] == 2 then - buffer.semiPixelSquare(x + 2, y, 1, 3, color) - elseif segments[i] == 3 then - buffer.semiPixelSquare(x + 2, y + 2, 1, 3, color) - elseif segments[i] == 4 then - buffer.semiPixelSquare(x, y + 4, 3, 1, color) - elseif segments[i] == 5 then - buffer.semiPixelSquare(x, y + 2, 1, 3, color) - elseif segments[i] == 6 then - buffer.semiPixelSquare(x, y, 1, 3, color) - elseif segments[i] == 7 then - buffer.semiPixelSquare(x, y + 2, 3, 1, color) - else - error("Че за говно ты сюда напихал? Переделывай!") - end - end -end - -function renderer.renderFPSCounter(x, y, renderMethod, color) - local numbers = { - ["0"] = { 1, 2, 3, 4, 5, 6 }, - ["1"] = { 2, 3 }, - ["2"] = { 1, 2, 4, 5, 7 }, - ["3"] = { 1, 2, 3, 4, 7 }, - ["4"] = { 2, 3, 6, 7 }, - ["5"] = { 1, 3, 4, 6, 7 }, - ["6"] = { 1, 3, 4, 5, 6, 7 }, - ["7"] = { 1, 2, 3 }, - ["8"] = { 1, 2, 3, 4, 5, 6, 7 }, - ["9"] = { 1, 2, 3, 4, 6, 7 }, - } - - -- clock sec - 1 frame - -- 1 sec - x frames - - local oldClock = os.clock() - renderMethod() - local fps = tostring(math.ceil(1 / (os.clock() - oldClock) / 10)) - - -- buffer.text(1, 1, 0xFFFFFF, "FPS: " .. os.clock() - oldClock) - - for i = 1, #fps do - drawSegments(x, y, numbers[fps:sub(i, i)], color) - x = x + 4 - end - - return x - 3 -end - - - ------------------------------------------------------------------------------------------------------------------------- - -return renderer - -Dlib/PolyCatEngine/Flib/PolyCatEngine/Main.luaO= --------------------------------------------------------- Libraries -------------------------------------------------------- - -local buffer = require("doubleBuffering") -local vector = require("vector") -local matrix = require("matrix") -local OCGL = require("OpenComputersGL/Main") -local renderer = require("OpenComputersGL/Renderer") -local materials = require("OpenComputersGL/Materials") -local postProcessing = require("PolyCatEngine/PostProcessing") -local polyCatEngine = {} - --------------------------------------------------------- Universal object methods -------------------------------------------------------- - -function polyCatEngine.newPivotPoint(vector3Position) - return { - position = vector3Position, - axis = { - vector.newVector3(1, 0, 0), - vector.newVector3(0, 1, 0), - vector.newVector3(0, 0, 1), - } - } -end - --------------------------------------------------------- Mesh object -------------------------------------------------------- - -local function pushMeshToRenderQueue(mesh) - local vector3Vertex1, vector3Vertex2, vector3Vertex3 - for triangleIndex = 1, #mesh.triangles do - vector3Vertex1, vector3Vertex2, vector3Vertex3 = mesh.vertices[mesh.triangles[triangleIndex][1]], mesh.vertices[mesh.triangles[triangleIndex][2]], mesh.vertices[mesh.triangles[triangleIndex][3]] - OCGL.pushTriangleToRenderQueue( - vector.newVector3(vector3Vertex1[1], vector3Vertex1[2], vector3Vertex1[3]), - vector.newVector3(vector3Vertex2[1], vector3Vertex2[2], vector3Vertex2[3]), - vector.newVector3(vector3Vertex3[1], vector3Vertex3[2], vector3Vertex3[3]), - mesh.triangles[triangleIndex][4] or mesh.material, - mesh, - triangleIndex - ) - end -end - - -function polyCatEngine.newMesh(vector3Position, vertices, triangles, material) - local mesh = {} - - -- mesh.pivotPoint = polyCatEngine.newPivotPoint(vector3Position) - mesh.vertices = vertices - for vertexIndex = 1, #mesh.vertices do - mesh.vertices[vertexIndex][1], mesh.vertices[vertexIndex][2], mesh.vertices[vertexIndex][3] = mesh.vertices[vertexIndex][1] + vector3Position[1], mesh.vertices[vertexIndex][2] + vector3Position[2], mesh.vertices[vertexIndex][3] + vector3Position[3] - end - mesh.triangles = triangles - mesh.material = material - mesh.pushToRenderQueue = pushMeshToRenderQueue - - return mesh -end - --------------------------------------------------------- Line object -------------------------------------------------------- - -local function pushLineToRenderQueue(line) - OCGL.pushLineToRenderQueue( - vector.newVector3(line.vertices[1][1], line.vertices[1][2], line.vertices[1][3]), - vector.newVector3(line.vertices[2][1], line.vertices[2][2], line.vertices[2][3]), - line.color - ) -end - -function polyCatEngine.newLine(vector3Position, vector3Vertex1, vector3Vertex2, color) - local line = {} - - -- line.pivotPoint = polyCatEngine.newPivotPoint(vector3Position) - line.vertices = { vector3Vertex1, vector3Vertex2 } - line.color = color - line.pushToRenderQueue = pushLineToRenderQueue - - return line -end - --------------------------------------------------------- Floating text object -------------------------------------------------------- - -local function pushFloatingTextToRenderQueue(floatingText) - OCGL.pushFloatingTextToRenderQueue( - vector.newVector3(floatingText.position[1], floatingText.position[2], floatingText.position[3]), - floatingText.text, - floatingText.color - ) -end - -function polyCatEngine.newFloatingText(vector3Position, color, text) - local floatingText = {} - - floatingText.position = vector3Position - floatingText.color = color - floatingText.text = text - floatingText.pushToRenderQueue = pushFloatingTextToRenderQueue - - return floatingText -end - --------------------------------------------------------- Plane object -------------------------------------------------------- - -function polyCatEngine.newPlane(vector3Position, width, height, material) - local halfWidth, halfHeight = width / 2, height / 2 - return polyCatEngine.newMesh( - vector3Position, - { - vector.newVector3(-halfWidth, 0, -halfHeight), - vector.newVector3(-halfWidth, 0, halfHeight), - vector.newVector3(halfWidth, 0, halfHeight), - vector.newVector3(halfWidth, 0, -halfHeight), - }, - { - OCGL.newIndexedTriangle(1, 2, 3), - OCGL.newIndexedTriangle(1, 4, 3) - }, - material - ) -end - --------------------------------------------------------- Cube object -------------------------------------------------------- - ---[[ - | / - | / - y z - x ----- - - FRONT LEFT BACK RIGHT TOP BOTTOM - 2######3 3######6 6######7 7######2 7######6 8######5 - ######## ######## ######## ######## ######## ######## - 1######4 4######5 5######8 8######1 2######3 1######4 -]] - -function polyCatEngine.newCube(vector3Position, size, material) - local halfSize = size / 2 - return polyCatEngine.newMesh( - vector3Position, - { - -- (1-2-3-4) - vector.newVector3(-halfSize, -halfSize, -halfSize), - vector.newVector3(-halfSize, halfSize, -halfSize), - vector.newVector3(halfSize, halfSize, -halfSize), - vector.newVector3(halfSize, -halfSize, -halfSize), - -- (5-6-7-8) - vector.newVector3(halfSize, -halfSize, halfSize), - vector.newVector3(halfSize, halfSize, halfSize), - vector.newVector3(-halfSize, halfSize, halfSize), - vector.newVector3(-halfSize, -halfSize, halfSize), - }, - { - -- Front - OCGL.newIndexedTriangle(1, 2, 3), - OCGL.newIndexedTriangle(1, 4, 3), - -- Left - OCGL.newIndexedTriangle(4, 3, 6), - OCGL.newIndexedTriangle(4, 5, 6), - -- Back - OCGL.newIndexedTriangle(5, 6, 7), - OCGL.newIndexedTriangle(5, 8, 7), - -- Right - OCGL.newIndexedTriangle(8, 7, 2), - OCGL.newIndexedTriangle(8, 1, 2), - -- Top - OCGL.newIndexedTriangle(2, 7, 6), - OCGL.newIndexedTriangle(2, 3, 6), - -- Bottom - OCGL.newIndexedTriangle(1, 8, 5), - OCGL.newIndexedTriangle(1, 4, 5), - }, - material - ) -end - --------------------------------------------------------- Grid lines -------------------------------------------------------- - -function polyCatEngine.newGridLines(vector3Position, axisRange, gridRange, gridRangeStep) - local objects = {} - -- Grid - for x = -gridRange, gridRange, gridRangeStep do - table.insert(objects, 1, polyCatEngine.newLine( - vector.newVector3(vector3Position[1] + x, vector3Position[2], vector3Position[3]), - vector.newVector3(0, 0, -gridRange), - vector.newVector3(0, 0, gridRange), - 0x444444 - )) - end - for z = -gridRange, gridRange, gridRangeStep do - table.insert(objects, 1, polyCatEngine.newLine( - vector.newVector3(vector3Position[1], vector3Position[2], vector3Position[3] + z), - vector.newVector3(-gridRange, 0, 0), - vector.newVector3(gridRange, 0, 0), - 0x444444 - )) - end - - -- Axis - table.insert(objects, polyCatEngine.newLine( - vector3Position, - vector.newVector3(-axisRange, -1, 0), - vector.newVector3(axisRange, -1, 0), - renderer.colors.axis.x - )) - table.insert(objects, polyCatEngine.newLine( - vector3Position, - vector.newVector3(0, -axisRange, 0), - vector.newVector3(0, axisRange, 0), - renderer.colors.axis.y - )) - table.insert(objects, polyCatEngine.newLine( - vector3Position, - vector.newVector3(0, -1, -axisRange), - vector.newVector3(0, -1, axisRange), - renderer.colors.axis.z - )) - - return objects -end - --------------------------------------------------------- Camera object -------------------------------------------------------- - - -local function cameraSetRotation(camera, axisXRotation, axisYRotation, axisZRotation) - camera.rotation[1], camera.rotation[2], camera.rotation[3] = axisXRotation, axisYRotation, axisZRotation - return camera -end - -local function cameraRotate(camera, axisXAdditionalRotation, axisYAdditionalRotation, axisZAdditionalRotation) - cameraSetRotation(camera, camera.rotation[1] + axisXAdditionalRotation, camera.rotation[2] + axisYAdditionalRotation, camera.rotation[3] + axisZAdditionalRotation) - return camera -end - -local function cameraLookAt(camera, xLook, yLook, zLook) - local dx, dy, dz = xLook - camera.position[1], yLook - camera.position[2], zLook - camera.position[3] - local rad180 = math.rad(180) - - local roty = math.atan(dx / dz) - if dz < 0 then roty = roty + rad180 end - - local rotx = math.atan(math.sqrt(dx ^ 2 + dz ^ 2) / dy) - math.rad(90) - if dy < 0 then rotx = rotx + rad180 end - - cameraSetRotation(camera, rotx, roty, 0) -end - -local function cameraSetPosition(camera, x, y, z) - camera.position[1], camera.position[2], camera.position[3] = x, y, z - return camera -end - -local function cameraTranslate(camera, xTranslation, yTranslation, zTranslation, xLookingAtTranslation, yLookingAtTranslation, zLookingAtTranslation) - cameraSetPosition(camera, camera.position[1] + xTranslation, camera.position[2] + yTranslation, camera.position[3] + zTranslation) - return camera -end - -local function cameraSetFOV(camera, FOV) - if FOV > 0 and FOV < math.pi then - camera.FOV = FOV - camera.projectionSurface = camera.farClippingSurface - camera.FOV / math.rad(180) * (camera.farClippingSurface - camera.nearClippingSurface) - else - error("FOV can't be < 0 or > 180 degrees") - end - -- ecs.error(camera.projectionSurface) - return camera -end - -function polyCatEngine.newCamera(vector3Position, FOV, nearClippingSurface, farClippingSurface) - local camera = {} - - camera.projectionEnabled = true - camera.position = vector3Position - camera.rotation = {} - camera.nearClippingSurface = nearClippingSurface - camera.farClippingSurface = farClippingSurface - camera.FOV = FOV - - camera.setPosition = cameraSetPosition - camera.translate = cameraTranslate - camera.rotate = cameraRotate - camera.setRotation = cameraSetRotation - camera.setFOV = cameraSetFOV - camera.lookAt = cameraLookAt - - -- Создаем точку "лука" (и матрицу поворота камеры), а также ее плоскость проекции через ФОВ - cameraSetRotation(camera, 0, 0, 0) - cameraSetFOV(camera, camera.FOV) - - return camera -end - --------------------------------------------------------- Scene object -------------------------------------------------------- - -local function sceneAddObject(scene, object) - table.insert(scene.objects, object) - - return object -end - -local function sceneAddObjects(scene, objects) - for objectIndex = 1, #objects do - table.insert(scene.objects, objects[objectIndex]) - end - - return objects -end - -local function sceneRender(scene) - OCGL.setViewport( 1, 1, buffer.screen.width, buffer.screen.height * 2, scene.camera.nearClippingSurface, scene.camera.farClippingSurface, scene.camera.projectionSurface) - OCGL.clearBuffer(scene.backgroundColor) - - for objectIndex = 1, #scene.objects do - scene.objects[objectIndex]:pushToRenderQueue() - end - - OCGL.translate(-scene.camera.position[1], -scene.camera.position[2], -scene.camera.position[3]) - OCGL.rotate(OCGL.axis.x, -scene.camera.rotation[1]) - OCGL.rotate(OCGL.axis.y, -scene.camera.rotation[2]) - OCGL.rotate(OCGL.axis.z, -scene.camera.rotation[3]) - - if scene.camera.projectionEnabled then OCGL.createPerspectiveProjection() end - OCGL.render(scene.renderMode) - - return scene -end - -function polyCatEngine.newScene(backgroundColor) - local scene = {} - - scene.renderMode = renderer.renderModes.material - scene.backgroundColor = backgroundColor - - scene.objects = {} - scene.addObject = sceneAddObject - scene.addObjects = sceneAddObjects - scene.render = sceneRender - - scene.camera = polyCatEngine.newCamera(vector.newVector3(0, 0, 0), math.rad(90), 1, 100) - - return scene -end - --- -------------------------------------------------------- Raycasting methods -------------------------------------------------------- - --- local function vectorMultiply(a, b) --- return vector.newVector3(a[2] * b[3] - a[3] * b[2], a[3] * b[1] - a[1] * b[3], a[1] * b[2] - a[2] * b[1]) --- end - --- local function getVectorDistance(a) --- return math.sqrt(a[1] ^ 2 + a[2] ^ 2 + a[3] ^ 2) --- end - --- -- В случае попадания лучика этот метод вернет сам треугольник, а также дистанцию до его плоскости --- function polyCatEngine.triangleRaycast(vector3RayStart, vector3RayEnd) --- local minimalDistance, closestTriangleIndex --- for triangleIndex = 1, #OCGL.triangles do --- -- Это вершины треугольника --- local A, B, C = OCGL.vertices[OCGL.triangles[triangleIndex][1]], OCGL.vertices[OCGL.triangles[triangleIndex][3]], OCGL.vertices[OCGL.triangles[triangleIndex][3]] --- -- Это хз че --- local ABC = vectorMultiply(vector.newVector3(C[1] - A[1], C[2] - A[2], C[3] - A[3]), vector.newVector3(B[1] - A[1], B[2] - A[2], B[3] - A[3])) --- -- Рассчитываем удаленность виртуальной плоскости треугольника от старта нашего луча --- local D = -ABC[1] * A[1] - ABC[2] * A[2] - ABC[3] * A[3] --- local firstPart = D + ABC[1] * vector3RayStart[1] + ABC[2] * vector3RayStart[2] + ABC[3] * vector3RayStart[3] --- local secondPart = ABC[1] * vector3RayStart[1] - ABC[1] * vector3RayEnd[1] + ABC[2] * vector3RayStart[2] - ABC[2] * vector3RayEnd[2] + ABC[3] * vector3RayStart[3] - ABC[3] * vector3RayEnd[3] - --- -- Если наш лучик не параллелен той ебучей плоскости треугольника --- if secondPart ~= 0 then --- local distance = firstPart / secondPart --- -- И если этот объект находится ближе к старту луча, нежели предыдущий --- if (distance >= 0 and distance <= 1) and (not minimalDistance or distance < minimalDistance) then - --- -- То считаем точку попадания луча в данную плоскость (но ни хуя не факт, что он попадет в треугольник!) --- local S = vector.newVector3( --- vector3RayStart[1] + (vector3RayEnd[1] - vector3RayStart[1]) * distance, --- vector3RayStart[2] + (vector3RayEnd[2] - vector3RayStart[2]) * distance, --- vector3RayStart[3] + (vector3RayEnd[3] - vector3RayStart[3]) * distance --- ) - --- -- Далее считаем сумму площадей параллелограммов, образованных тремя треугольниками, образовавшихся при попадании точки в треугольник --- -- Нууу тип кароч смари: точка ебанула в центр, и треугольник распидорасило на три мелких. Ну, и три мелких могут образовать параллелограммы свои --- -- И, кароч, если сумма трех площадей этих мелких уебков будет сильно отличаться от площади жирного треугольника, то луч не попал --- -- Ну, а площадь считается через sqrt(x^2+y^2+z^2) для каждого йоба-вектора - --- ---- *A *B - - --- -- * Shotxyz - - --- --- *C - --- local SA = vector.newVector3(A[1] - S[1], A[2] - S[2], A[3] - S[3]) --- local SB = vector.newVector3(B[1] - S[1], B[2] - S[2], B[3] - S[3]) --- local SC = vector.newVector3(C[1] - S[1], C[2] - S[2], C[3] - S[3]) - --- local vectorDistanceSum = getVectorDistance(vectorMultiply(SA, SB)) + getVectorDistance(vectorMultiply(SB, SC)) + getVectorDistance(vectorMultiply(SC, SA)) --- local ABCDistance = getVectorDistance(ABC) - --- -- Вот тут мы чекаем погрешность расчетов. Если все заебок, то кидаем этот треугольник в "проверенные"" --- if math.abs(vectorDistanceSum - ABCDistance) < 1 then --- closestTriangleIndex = triangleIndex --- minimalDistance = distance --- end --- end --- end --- end - --- -- ecs.error(closestTriangleIndex) --- return closestTriangleIndex, minimalDistance --- end - --- -- function polyCatEngine.sceneRaycast(scene, vector3RayStart, vector3RayEnd) --- -- local closestObjectIndex, closestTriangleIndex, minimalDistance - --- -- for objectIndex = 1, #scene.objects do --- -- if scene.objects[objectIndex].triangles then --- -- local triangleIndex, distance = polyCatEngine.meshRaycast(scene.objects[objectIndex], vector3RayStart, vector3RayEnd) --- -- if triangleIndex and (not minimalDistance or distance < minimalDistance ) then --- -- closestObjectIndex, closestTriangleIndex, minimalDistance = objectIndex, triangleIndex, distance --- -- end --- -- end --- -- end - --- -- return closestObjectIndex, closestTriangleIndex, minimalDistance --- -- end - --------------------------------------------------------- Intro -------------------------------------------------------- - -function polyCatEngine.newPolyCatMesh(vector3Position, size) - return polyCatEngine.newMesh( - vector3Position, - { - vector.newVector3(-1.0 * size, 0.8 * size, 0.3 * size), - vector.newVector3(-0.5 * size, 0.5 * size, 0.3 * size), - vector.newVector3(0.0 * size, 0.5 * size, 0.3 * size), - vector.newVector3(0.5 * size, 0.5 * size, 0.3 * size), - vector.newVector3(1.0 * size, 0.8 * size, 0.3 * size), - vector.newVector3(0.8 * size, 0.2 * size, 0.3 * size), - vector.newVector3(0.7 * size, -0.3 * size, 0.3 * size), - vector.newVector3(0.0 * size, -0.8 * size, 0.3 * size), - vector.newVector3(-0.7 * size, -0.3 * size, 0.3 * size), - vector.newVector3(-0.8 * size, 0.2 * size, 0.3 * size), - vector.newVector3(-0.2 * size, -0.1 * size, 0.0 * size), - vector.newVector3(0.2 * size, -0.1 * size, 0.0 * size), - vector.newVector3(0.0 * size, -0.3 * size, 0.0 * size) - }, - { - OCGL.newIndexedTriangle(1, 2, 10, materials.newSolidMaterial(0x555555)), - OCGL.newIndexedTriangle(2, 11, 10, materials.newSolidMaterial(0x6fe7fc)), - OCGL.newIndexedTriangle(2, 3, 11, materials.newSolidMaterial(0xDDDDDD)), - OCGL.newIndexedTriangle(3, 12, 11, materials.newSolidMaterial(0xDDDDDD)), - OCGL.newIndexedTriangle(3, 4, 12, materials.newSolidMaterial(0xDDDDDD)), - OCGL.newIndexedTriangle(4, 6, 12, materials.newSolidMaterial(0xa8f1fd)), - OCGL.newIndexedTriangle(4, 5, 6, materials.newSolidMaterial(0x808080)), - - OCGL.newIndexedTriangle(6, 7, 8, materials.newSolidMaterial(0xCCCCCC)), - OCGL.newIndexedTriangle(12, 6, 8, materials.newSolidMaterial(0xCCCCCC)), - OCGL.newIndexedTriangle(13, 12, 8, materials.newSolidMaterial(0xCCCCCC)), - - OCGL.newIndexedTriangle(11, 12, 13, materials.newSolidMaterial(0x555555)), - OCGL.newIndexedTriangle(11, 13, 8, materials.newSolidMaterial(0xBBBBBB)), - OCGL.newIndexedTriangle(10, 11, 8, materials.newSolidMaterial(0xBBBBBB)), - OCGL.newIndexedTriangle(10, 8, 9, materials.newSolidMaterial(0xBBBBBB)) - }, - materials.newSolidMaterial(0xFF0000) - ) -end - -function polyCatEngine.intro(vector3Position, size) - local GUI = require("GUI") - local scene = polyCatEngine.newScene(0xEEEEEE) - scene:addObject(polyCatEngine.newPolyCatMesh(vector3Position, size)) - scene:addObject(polyCatEngine.newFloatingText(vector.newVector3(vector3Position[1] + 2, vector3Position[2] - size, vector3Position[3] + size * 0.1), 0xBBBBBB, "Powered by PolyCat Engine™")) - - local from, to, speed = -30, 20, 4 - local transparency, transparencyStep = 0, 100 / math.abs(to - from) * speed - - scene.camera:setPosition(from, 0, -32) - while scene.camera.position[1] < to do - scene.camera:translate(speed, 0, 0) - scene.camera:lookAt(0, 0, 0) - scene:render() - local lines = { - "Copyright © 2016-2017 - Developed by ECS, Harch and Pirnogion", - "All rights reserved", - } - GUI.textBox(1, buffer.screen.height - #lines, buffer.screen.width, #lines, nil, 0xBBBBBB, lines, 1):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top):draw() - if scene.camera.position[1] < to then buffer.clear(0x0, transparency) end - buffer.draw() - - transparency = transparency + transparencyStep - -- ecs.error("POS: " .. scene.camera.position[1] .. ", " .. scene.camera.position[2] .. ", " .. scene.camera.position[3] .. ", ROT: " .. math.deg(scene.camera.rotation[1]) .. ", " .. math.deg(scene.camera.rotation[2]) .. ", " .. math.deg(scene.camera.rotation[3])) - os.sleep(0.01) - end - - os.sleep(2) - - for i = 100, 0, -20 do - scene:render() - buffer.clear(0x0, i) - buffer.draw() - end -end - --------------------------------------------------------- Zalupa -------------------------------------------------------- - -return polyCatEngine -F$lib/PolyCatEngine/PostProcessing.lua --------------------------------------------------------- Libraries -------------------------------------------------------- - -local buffer = require("doubleBuffering") -local postProcessing = {} - --------------------------------------------------------- Plane object -------------------------------------------------------- - -function postProcessing.photofilter(color, transparency) - buffer.clear(color, transparency) -end - -function postProcessing.fadePhotifilter(color, fromTransparency, toTransparency, step) - for i = fromTransparency, toTransparency, fromTransparency < toTransparency and step or -step do - postProcessing.photofilter(color, i) - end -end - --------------------------------------------------------- Zalupa -------------------------------------------------------- - -return postProcessing -F lib/SHA2.lua-- SHA-256 code in Lua 5.2; based on the pseudo-code from --- Wikipedia (http://en.wikipedia.org/wiki/SHA-2) - - -local band, rrotate, bxor, rshift, bnot = - bit32.band, bit32.rrotate, bit32.bxor, bit32.rshift, bit32.bnot - -local string, setmetatable, assert = string, setmetatable, assert - -_ENV = nil - --- Initialize table of round constants --- (first 32 bits of the fractional parts of the cube roots of the first --- 64 primes 2..311): -local k = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, -} - - --- transform a string of bytes in a string of hexadecimal digits -local function str2hexa (s) - local h = string.gsub(s, ".", function(c) - return string.format("%02x", string.byte(c)) - end) - return h -end - - --- transform number 'l' in a big-endian sequence of 'n' bytes --- (coded as a string) -local function num2s (l, n) - local s = "" - for i = 1, n do - local rem = l % 256 - s = string.char(rem) .. s - l = (l - rem) / 256 - end - return s -end - --- transform the big-endian sequence of four bytes starting at --- index 'i' in 's' into a number -local function s232num (s, i) - local n = 0 - for i = i, i + 3 do - n = n*256 + string.byte(s, i) - end - return n -end - - --- append the bit '1' to the message --- append k bits '0', where k is the minimum number >= 0 such that the --- resulting message length (in bits) is congruent to 448 (mod 512) --- append length of message (before pre-processing), in bits, as 64-bit --- big-endian integer -local function preproc (msg, len) - local extra = -(len + 1 + 8) % 64 - len = num2s(8 * len, 8) -- original len in bits, coded - msg = msg .. "\128" .. string.rep("\0", extra) .. len - assert(#msg % 64 == 0) - return msg -end - - -local function initH224 (H) - -- (second 32 bits of the fractional parts of the square roots of the - -- 9th through 16th primes 23..53) - H[1] = 0xc1059ed8 - H[2] = 0x367cd507 - H[3] = 0x3070dd17 - H[4] = 0xf70e5939 - H[5] = 0xffc00b31 - H[6] = 0x68581511 - H[7] = 0x64f98fa7 - H[8] = 0xbefa4fa4 - return H -end - - -local function initH256 (H) - -- (first 32 bits of the fractional parts of the square roots of the - -- first 8 primes 2..19): - H[1] = 0x6a09e667 - H[2] = 0xbb67ae85 - H[3] = 0x3c6ef372 - H[4] = 0xa54ff53a - H[5] = 0x510e527f - H[6] = 0x9b05688c - H[7] = 0x1f83d9ab - H[8] = 0x5be0cd19 - return H -end - - -local function digestblock (msg, i, H) - - -- break chunk into sixteen 32-bit big-endian words w[1..16] - local w = {} - for j = 1, 16 do - w[j] = s232num(msg, i + (j - 1)*4) - end - - -- Extend the sixteen 32-bit words into sixty-four 32-bit words: - for j = 17, 64 do - local v = w[j - 15] - local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3)) - v = w[j - 2] - local s1 = bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10)) - w[j] = w[j - 16] + s0 + w[j - 7] + s1 - end - - -- Initialize hash value for this chunk: - local a, b, c, d, e, f, g, h = - H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] - - -- Main loop: - for i = 1, 64 do - local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22)) - local maj = bxor(band(a, b), band(a, c), band(b, c)) - local t2 = s0 + maj - local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25)) - local ch = bxor (band(e, f), band(bnot(e), g)) - local t1 = h + s1 + ch + k[i] + w[i] - - h = g - g = f - f = e - e = d + t1 - d = c - c = b - b = a - a = t1 + t2 - end - - -- Add (mod 2^32) this chunk's hash to result so far: - H[1] = band(H[1] + a) - H[2] = band(H[2] + b) - H[3] = band(H[3] + c) - H[4] = band(H[4] + d) - H[5] = band(H[5] + e) - H[6] = band(H[6] + f) - H[7] = band(H[7] + g) - H[8] = band(H[8] + h) - -end - - -local function finalresult224 (H) - -- Produce the final hash value (big-endian): - return - str2hexa(num2s(H[1], 4)..num2s(H[2], 4)..num2s(H[3], 4)..num2s(H[4], 4).. - num2s(H[5], 4)..num2s(H[6], 4)..num2s(H[7], 4)) -end - - -local function finalresult256 (H) - -- Produce the final hash value (big-endian): - return - str2hexa(num2s(H[1], 4)..num2s(H[2], 4)..num2s(H[3], 4)..num2s(H[4], 4).. - num2s(H[5], 4)..num2s(H[6], 4)..num2s(H[7], 4)..num2s(H[8], 4)) -end - - ----------------------------------------------------------------------- -local HH = {} -- to reuse - -local function hash224 (msg) - msg = preproc(msg, #msg) - local H = initH224(HH) - - -- Process the message in successive 512-bit (64 bytes) chunks: - for i = 1, #msg, 64 do - digestblock(msg, i, H) - end - - return finalresult224(H) -end - - -local function hash256 (msg) - msg = preproc(msg, #msg) - local H = initH256(HH) - - -- Process the message in successive 512-bit (64 bytes) chunks: - for i = 1, #msg, 64 do - digestblock(msg, i, H) - end - - return finalresult256(H) -end ----------------------------------------------------------------------- --- local mt = {} - --- local function new256 () --- local o = {H = initH256({}), msg = "", len = 0} --- setmetatable(o, mt) --- return o --- end - --- mt.__index = mt - --- function mt:add (m) --- self.msg = self.msg .. m --- self.len = self.len + #m --- local t = 0 --- while #self.msg - t >= 64 do --- digestblock(self.msg, t + 1, self.H) --- t = t + 64 --- end --- self.msg = self.msg:sub(t + 1, -1) --- end - - --- function mt:close () --- self.msg = preproc(self.msg, self.len) --- self:add("") --- return finalresult256(self.H) --- end ----------------------------------------------------------------------- - -return { - hash = hash256 - -- hash256 = hash256, - -- hash224 = hash224, - -- new256 = new256, -}Flib/advancedLua.lua* ---[[ - - Advanced Lua Library v1.1 by ECS - - This library extends a lot of default Lua methods - and adds some really cool features that haven't been - implemented yet, such as fastest table serialization, - table binary searching, string wrapping, numbers rounding, etc. - -]] - -local filesystem = require("filesystem") -local unicode = require("unicode") -local bit32 = require("bit32") - --------------------------------------------------- System extensions -------------------------------------------------- - -function _G.getCurrentScript() - local info - for runLevel = 0, math.huge do - info = debug.getinfo(runLevel) - if info then - if info.what == "main" then - return info.source:sub(2, -1) - end - else - error("Failed to get debug info for runlevel " .. runLevel) - end - end -end - -function enum(...) - local args, enums = {...}, {} - for i = 1, #args do - if type(args[i]) ~= "string" then error("Function argument " .. i .. " have non-string type: " .. type(args[i])) end - enums[args[i]] = i - end - return enums -end - -function swap(a, b) - return b, a -end - --------------------------------------------------- Bit32 extensions -------------------------------------------------- - -function bit32.numberToByteArray(number) - local byteArray = {} - while number > 0 do - table.insert(byteArray, 1, bit32.band(number, 0xFF)) - number = bit32.rshift(number, 8) - end - return byteArray -end - -function bit32.byteArrayToNumber(byteArray) - local number = byteArray[1] - for i = 2, #byteArray do - number = bit32.bor(byteArray[i], bit32.lshift(number, 8)) - end - return number -end - -function bit32.bitArrayToByte(bitArray) - local number = 0 - for i = 1, #bitArray do - number = bit32.bor(bitArray[i], bit32.lshift(number, 1)) - end - return number -end - -bit32.byteArrayFromNumber = bit32.numberToByteArray -bit32.numberFromByteArray = bit32.byteArrayToNumber - --------------------------------------------------- Math extensions -------------------------------------------------- - -function math.round(num) - if num >= 0 then - return math.floor(num + 0.5) - else - return math.ceil(num - 0.5) - end -end - -function math.roundToDecimalPlaces(num, decimalPlaces) - local mult = 10 ^ (decimalPlaces or 0) - return math.floor(num * mult + 0.5) / mult -end - -function math.getDigitCount(num) - return num == 0 and 1 or math.ceil(math.log(num + 1, 10)) -end - -function math.doubleToString(num, digitCount) - return string.format("%." .. (digitCount or 1) .. "f", num) -end - -function math.shortenNumber(number, digitCount) - local shortcuts = { - "K", - "M", - "B", - "T" - } - - local index = math.floor(math.log(number, 1000)) - if number < 1000 then - return number - elseif index > #shortcuts then - index = #shortcuts - end - - return math.roundToDecimalPlaces(number / 1000 ^ index, digitCount) .. shortcuts[index] -end - --------------------------------------------------- Table extensions -------------------------------------------------- - -local function doSerialize(array, prettyLook, indentationSymbol, indentationSymbolAdder, equalsSymbol, currentRecusrionStack, recursionStackLimit) - local text, keyType, valueType, stringValue = {"{"} - table.insert(text, (prettyLook and "\n" or nil)) - - for key, value in pairs(array) do - keyType, valueType, stringValue = type(key), type(value), tostring(value) - - if keyType == "number" or keyType == "string" then - table.insert(text, (prettyLook and table.concat({indentationSymbol, indentationSymbolAdder}) or nil)) - table.insert(text, "[") - table.insert(text, (keyType == "string" and table.concat({"\"", key, "\""}) or key)) - table.insert(text, "]") - table.insert(text, equalsSymbol) - - if valueType == "number" or valueType == "boolean" or valueType == "nil" then - table.insert(text, stringValue) - elseif valueType == "string" or valueType == "function" then - table.insert(text, "\"") - table.insert(text, stringValue) - table.insert(text, "\"") - elseif valueType == "table" then - -- Ограничение стека рекурсии - if currentRecusrionStack < recursionStackLimit then - table.insert(text, table.concat(doSerialize(value, prettyLook, table.concat({indentationSymbol, indentationSymbolAdder}), indentationSymbolAdder, equalsSymbol, currentRecusrionStack + 1, recursionStackLimit))) - else - table.insert(text, "...") - end - end - - table.insert(text, ",") - table.insert(text, (prettyLook and "\n" or nil)) - end - end - - -- Удаляем запятую - if prettyLook then - if #text > 2 then - table.remove(text, #text - 1) - end - -- Вставляем заодно уж символ индентации, благо чек на притти лук идет - table.insert(text, indentationSymbol) - else - if #text > 1 then - table.remove(text, #text) - end - end - - table.insert(text, "}") - - return text -end - -function table.serialize(array, prettyLook, indentationWidth, indentUsingTabs, recursionStackLimit) - checkArg(1, array, "table") - return table.concat( - doSerialize( - array, - prettyLook, - "", - string.rep(indentUsingTabs and " " or " ", indentationWidth or 2), - prettyLook and " = " or "=", - 1, - recursionStackLimit or math.huge - ) - ) -end - -function table.unserialize(serializedString) - checkArg(1, serializedString, "string") - local success, result = pcall(load("return " .. serializedString)) - if success then return result else return nil, result end -end - -table.toString = table.serialize -table.fromString = table.unserialize - -function table.toFile(path, array, prettyLook, indentationWidth, indentUsingTabs, recursionStackLimit, appendToFile) - checkArg(1, path, "string") - checkArg(2, array, "table") - filesystem.makeDirectory(filesystem.path(path) or "") - local file = io.open(path, appendToFile and "a" or "w") - file:write(table.serialize(array, prettyLook, indentationWidth, indentUsingTabs, recursionStackLimit)) - file:close() -end - -function table.fromFile(path) - checkArg(1, path, "string") - if filesystem.exists(path) then - if filesystem.isDirectory(path) then - error("\"" .. path .. "\" is a directory") - else - local file = io.open(path, "r") - local data = table.unserialize(file:read("*a")) - file:close() - return data - end - else - error("\"" .. path .. "\" doesn't exists") - end -end - -function table.copy(tableToCopy) - local function recursiveCopy(source, destination) - for key, value in pairs(source) do - if type(value) == "table" then - destination[key] = {} - recursiveCopy(source[key], destination[key]) - else - destination[key] = value - end - end - end - - local tableThatCopied = {} - recursiveCopy(tableToCopy, tableThatCopied) - - return tableThatCopied -end - -function table.binarySearch(t, requestedValue) - local function recursiveSearch(startIndex, endIndex) - local difference = endIndex - startIndex - local centerIndex = math.floor(difference / 2 + startIndex) - - if difference > 1 then - if requestedValue >= t[centerIndex] then - return recursiveSearch(centerIndex, endIndex) - else - return recursiveSearch(startIndex, centerIndex) - end - else - if math.abs(requestedValue - t[startIndex]) > math.abs(t[endIndex] - requestedValue) then - return t[endIndex] - else - return t[startIndex] - end - end - end - - return recursiveSearch(1, #t) -end - -function table.size(t) - local size = #t - if size == 0 then for key in pairs(t) do size = size + 1 end end - return size -end - --------------------------------------------------- String extensions -------------------------------------------------- - -function string.canonicalPath(str) - return string.gsub("/" .. str, "%/+", "/") -end - -function string.optimize(str, indentationWidth) - str = string.gsub(str, "\r\n", "\n") - str = string.gsub(str, " ", string.rep(" ", indentationWidth or 2)) - return str -end - -function string.optimizeForURLRequests(code) - if code then - code = string.gsub(code, "([^%w ])", function (c) - return string.format("%%%02X", string.byte(c)) - end) - code = string.gsub(code, " ", "+") - end - return code -end - -function string.unicodeFind(str, pattern, init, plain) - if init then - if init < 0 then - init = -#unicode.sub(str,init) - elseif init > 0 then - init = #unicode.sub(str, 1, init - 1) + 1 - end - end - - a, b = string.find(str, pattern, init, plain) - - if a then - local ap, bp = str:sub(1, a - 1), str:sub(a,b) - a = unicode.len(ap) + 1 - b = a + unicode.len(bp) - 1 - return a, b - else - return a - end -end - -function string.limit(text, size, fromLeft, noDots) - local length = unicode.len(text) - if length <= size then return text end - - if fromLeft then - if noDots then - return unicode.sub(text, length - size + 1, -1) - else - return "…" .. unicode.sub(text, length - size + 2, -1) - end - else - if noDots then - return unicode.sub(text, 1, size) - else - return unicode.sub(text, 1, size - 1) .. "…" - end - end -end - -function string.wrap(strings, limit) - strings = type(strings) == "string" and {strings} or strings - - local currentString = 1 - while currentString <= #strings do - local words = {}; for word in string.gmatch(tostring(strings[currentString]), "[^%s]+") do table.insert(words, word) end - - local newStringThatFormedFromWords, oldStringThatFormedFromWords = "", "" - local word = 1 - local overflow = false - while word <= #words do - oldStringThatFormedFromWords = oldStringThatFormedFromWords .. (word > 1 and " " or "") .. words[word] - if unicode.len(oldStringThatFormedFromWords) > limit then - if unicode.len(words[word]) > limit then - local left = unicode.sub(oldStringThatFormedFromWords, 1, limit) - local right = unicode.sub(strings[currentString], unicode.len(left) + 1, -1) - overflow = true - strings[currentString] = left - if strings[currentString + 1] then - strings[currentString + 1] = right .. " " .. strings[currentString + 1] - else - strings[currentString + 1] = right - end - end - break - else - newStringThatFormedFromWords = oldStringThatFormedFromWords - end - word = word + 1 - end - - if word <= #words and not overflow then - local fuckToAdd = table.concat(words, " ", word, #words) - if strings[currentString + 1] then - strings[currentString + 1] = fuckToAdd .. " " .. strings[currentString + 1] - else - strings[currentString + 1] = fuckToAdd - end - strings[currentString] = newStringThatFormedFromWords - end - - currentString = currentString + 1 - end - - return strings -end - --------------------------------------------------- Playground -------------------------------------------------- - --- local t = { --- abc = 123, --- def = { --- cyka = "pidor", --- vagina = { --- chlen = 555, --- devil = 666, --- god = 777, --- serost = { --- tripleTable = "aefaef", --- aaa = "bbb", --- ccc = 123, --- } --- } --- }, --- ghi = "HEHE", --- emptyTable = {}, --- } - --- print(table.toString(t, true)) - ------------------------------------------------------------------------------------------------------------------- - -return {loaded = true} - - -Flib/bigLetters.lua) -local unicode = require("unicode") -local buffer = require("doubleBuffering") -local bigLetters = {} - -local pixelHeight = 5 -local lettersInterval = 2 -local unknownSymbol = "*" -local spaceWidth = 2 - -local letters = { - ["0"] = { - { 1, 1, 1 }, - { 1, 0, 1 }, - { 1, 0, 1 }, - { 1, 0, 1 }, - { 1, 1, 1 }, - }, - ["1"] = { - { 0, 1, 0 }, - { 1, 1, 0 }, - { 0, 1, 0 }, - { 0, 1, 0 }, - { 1, 1, 1 }, - }, - ["2"] = { - { 1, 1, 1 }, - { 0, 0, 1 }, - { 1, 1, 1 }, - { 1, 0, 0 }, - { 1, 1, 1 }, - }, - ["3"] = { - { 1, 1, 1 }, - { 0, 0, 1 }, - { 1, 1, 1 }, - { 0, 0, 1 }, - { 1, 1, 1 }, - }, - ["4"] = { - { 1, 0, 1 }, - { 1, 0, 1 }, - { 1, 1, 1 }, - { 0, 0, 1 }, - { 0, 0, 1 }, - }, - ["5"] = { - { 1, 1, 1 }, - { 1, 0, 0 }, - { 1, 1, 1 }, - { 0, 0, 1 }, - { 1, 1, 1 }, - }, - ["6"] = { - { 1, 1, 1 }, - { 1, 0, 0 }, - { 1, 1, 1 }, - { 1, 0, 1 }, - { 1, 1, 1 }, - }, - ["7"] = { - { 1, 1, 1 }, - { 0, 0, 1 }, - { 0, 0, 1 }, - { 0, 0, 1 }, - { 0, 0, 1 }, - }, - ["8"] = { - { 1, 1, 1 }, - { 1, 0, 1 }, - { 1, 1, 1 }, - { 1, 0, 1 }, - { 1, 1, 1 }, - }, - ["9"] = { - { 1, 1, 1 }, - { 1, 0, 1 }, - { 1, 1, 1 }, - { 0, 0, 1 }, - { 1, 1, 1 }, - }, - - - - - ["a"] = { - { 0, 1, 1, 0 }, - { 1, 0, 0, 1 }, - { 1, 1, 1, 1 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - }, - ["b"] = { - { 1, 1, 1, 0}, - { 1, 0, 0, 1}, - { 1, 1, 1, 0}, - { 1, 0, 0, 1}, - { 1, 1, 1, 1}, - }, - ["c"] = { - { 0, 1, 1, 1 }, - { 1, 0, 0, 0 }, - { 1, 0, 0, 0 }, - { 1, 0, 0, 0 }, - { 0, 1, 1, 1 }, - }, - ["d"] = { - { 1, 1, 1, 1, 0 }, - { 0, 1, 0, 0, 1 }, - { 0, 1, 0, 0, 1 }, - { 0, 1, 0, 0, 1 }, - { 1, 1, 1, 1, 0 }, - }, - ["e"] = { - { 1, 1, 1, 1 }, - { 1, 0, 0, 0 }, - { 1, 1, 1, 0 }, - { 1, 0, 0, 0 }, - { 1, 1, 1, 1 }, - }, - ["f"] = { - { 1, 1, 1, 1 }, - { 1, 0, 0, 0 }, - { 1, 1, 1, 0 }, - { 1, 0, 0, 0 }, - { 1, 0, 0, 0 }, - }, - ["g"] = { - { 0, 1, 1, 1}, - { 1, 0, 0, 0}, - { 1, 0, 1, 1}, - { 1, 0, 0, 1}, - { 0, 1, 1, 1}, - }, - ["h"] = { - { 1, 0, 0, 1}, - { 1, 0, 0, 1}, - { 1, 1, 1, 1}, - { 1, 0, 0, 1}, - { 1, 0, 0, 1}, - }, - ["i"] = { - { 1, 1, 1}, - { 0, 1, 0}, - { 0, 1, 0}, - { 0, 1, 0}, - { 1, 1, 1}, - }, - ["j"] = { - { 0, 0, 1}, - { 0, 0, 1}, - { 0, 0, 1}, - { 1, 0, 1}, - { 0, 1, 0}, - }, - ["k"] = { - { 1, 0, 0, 1}, - { 1, 0, 1, 0}, - { 1, 1, 0, 0}, - { 1, 0, 1, 0}, - { 1, 0, 0, 1}, - }, - ["l"] = { - { 1, 0, 0}, - { 1, 0, 0}, - { 1, 0, 0}, - { 1, 0, 0}, - { 1, 1, 1}, - }, - ["m"] = { - { 1, 0, 0, 0, 1 }, - { 1, 1, 0, 1, 1 }, - { 1, 0, 1, 0, 1 }, - { 1, 0, 0, 0, 1 }, - { 1, 0, 0, 0, 1 }, - }, - ["n"] = { - { 1, 0, 0, 0, 1 }, - { 1, 1, 0, 0, 1 }, - { 1, 0, 1, 0, 1 }, - { 1, 0, 0, 1, 1 }, - { 1, 0, 0, 0, 1 }, - }, - ["o"] = { - { 0, 1, 1, 0}, - { 1, 0, 0, 1}, - { 1, 0, 0, 1}, - { 1, 0, 0, 1}, - { 0, 1, 1, 0}, - }, - ["p"] = { - { 1, 1, 1, 0 }, - { 1, 0, 0, 1 }, - { 1, 1, 1, 0 }, - { 1, 0, 0, 0 }, - { 1, 0, 0, 0 }, - }, - ["q"] = { - { 0, 1, 1, 0}, - { 1, 0, 0, 1}, - { 1, 0, 0, 1}, - { 1, 0, 1, 1}, - { 0, 1, 1, 0}, - }, - ["r"] = { - { 1, 1, 1, 0}, - { 1, 0, 0, 1}, - { 1, 1, 1, 0}, - { 1, 0, 0, 1}, - { 1, 0, 0, 1}, - }, - ["s"] = { - { 0, 1, 1, 1}, - { 1, 0, 0, 0}, - { 0, 1, 1, 0}, - { 0, 0, 0, 1}, - { 1, 1, 1, 0}, - }, - ["t"] = { - { 1, 1, 1, 1, 1 }, - { 0, 0, 1, 0, 0 }, - { 0, 0, 1, 0, 0 }, - { 0, 0, 1, 0, 0 }, - { 0, 0, 1, 0, 0 }, - }, - ["u"] = { - { 1, 0, 0, 1}, - { 1, 0, 0, 1}, - { 1, 0, 0, 1}, - { 1, 0, 0, 1}, - { 0, 1, 1, 0}, - }, - ["v"] = { - { 1, 0, 0, 0, 1 }, - { 1, 0, 0, 0, 1 }, - { 1, 0, 0, 0, 1 }, - { 0, 1, 0, 1, 0 }, - { 0, 0, 1, 0, 0 }, - }, - ["w"] = { - { 1, 0, 0, 0, 1 }, - { 1, 0, 0, 0, 1 }, - { 1, 0, 1, 0, 1 }, - { 1, 0, 1, 0, 1 }, - { 0, 1, 0, 1, 0 }, - }, - ["x"] = { - { 1, 0, 0, 0, 1 }, - { 0, 1, 0, 1, 0 }, - { 0, 0, 1, 0, 0 }, - { 0, 1, 0, 1, 0 }, - { 1, 0, 0, 0, 1 }, - }, - ["y"] = { - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - { 0, 1, 1, 1 }, - { 0, 0, 0, 1 }, - { 1, 1, 1, 0 }, - }, - ["z"] = { - { 1, 1, 1, 1, 1 }, - { 0, 0, 0, 1, 0 }, - { 0, 0, 1, 0, 0 }, - { 0, 1, 0, 0, 0 }, - { 1, 1, 1, 1, 1 }, - }, - ["а"] = { - { 0, 1, 1, 0 }, - { 1, 0, 0, 1 }, - { 1, 1, 1, 1 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - }, - ["б"] = { - { 1, 1, 1, 1 }, - { 1, 0, 0, 0 }, - { 1, 1, 1, 0 }, - { 1, 0, 0, 1 }, - { 1, 1, 1, 0 }, - }, - ["в"] = { - { 1, 1, 1, 0 }, - { 1, 0, 0, 1 }, - { 1, 1, 1, 0 }, - { 1, 0, 0, 1 }, - { 1, 1, 1, 0 }, - }, - ["г"] = { - { 1, 1, 1 }, - { 1, 0, 0 }, - { 1, 0, 0 }, - { 1, 0, 0 }, - { 1, 0, 0 }, - }, - ["д"] = { - { 0, 0, 1, 1, 0 }, - { 0, 1, 0, 1, 0 }, - { 0, 1, 0, 1, 0 }, - { 0, 1, 0, 1, 0 }, - { 1, 1, 1, 1, 1 }, - }, - ["е"] = { - { 1, 1, 1, 1 }, - { 1, 0, 0, 0 }, - { 1, 1, 1, 0 }, - { 1, 0, 0, 0 }, - { 1, 1, 1, 1 }, - }, - ["ё"] = { - { 1, 0, 1, 0 }, - { 0, 0, 0, 0 }, - { 1, 1, 1, 1 }, - { 1, 0, 0, 0 }, - { 1, 1, 1, 0 }, - { 1, 0, 0, 0 }, - { 1, 1, 1, 1 }, - }, - ["ж"] = { - { 1, 0, 1, 0, 1 }, - { 0, 1, 1, 1, 0 }, - { 0, 0, 1, 0, 0 }, - { 0, 1, 1, 1, 0 }, - { 1, 0, 1, 0, 1 }, - }, - ["з"] = { - { 0, 1, 1, 1, 0 }, - { 0, 0, 0, 0, 1 }, - { 0, 0, 1, 1, 0 }, - { 0, 0, 0, 0, 1 }, - { 0, 1, 1, 1, 0 }, - }, - ["и"] = { - { 1, 0, 0, 0, 1 }, - { 1, 0, 0, 1, 1 }, - { 1, 0, 1, 0, 1 }, - { 1, 1, 0, 0, 1 }, - { 1, 0, 0, 0, 1 }, - }, - ["й"] = { - { 0, 1, 1, 1, 0 }, - { 0, 0, 0, 0, 0 }, - { 1, 0, 0, 0, 1 }, - { 1, 0, 0, 1, 1 }, - { 1, 0, 1, 0, 1 }, - { 1, 1, 0, 0, 1 }, - { 1, 0, 0, 0, 1 }, - }, - ["к"] = { - { 1, 0, 0, 1, 0 }, - { 1, 0, 1, 0, 0 }, - { 1, 1, 0, 0, 0 }, - { 1, 0, 1, 0, 0 }, - { 1, 0, 0, 1, 0 }, - }, - ["л"] = { - { 0, 0, 1, 1 }, - { 0, 1, 0, 1 }, - { 0, 1, 0, 1 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - }, - ["м"] = { - { 1, 0, 0, 0, 1 }, - { 1, 1, 0, 1, 1 }, - { 1, 0, 1, 0, 1 }, - { 1, 0, 0, 0, 1 }, - { 1, 0, 0, 0, 1 }, - }, - ["н"] = { - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - { 1, 1, 1, 1 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - }, - ["о"] = { - { 0, 1, 1, 0 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - { 0, 1, 1, 0 }, - }, - ["п"] = { - { 1, 1, 1, 1 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - }, - ["р"] = { - { 1, 1, 1, 0}, - { 1, 0, 0, 1}, - { 1, 1, 1, 0}, - { 1, 0, 0, 0}, - { 1, 0, 0, 0}, - }, - ["с"] = { - { 0, 1, 1, 1 }, - { 1, 0, 0, 0 }, - { 1, 0, 0, 0 }, - { 1, 0, 0, 0 }, - { 0, 1, 1, 1 }, - }, - ["т"] = { - { 1, 1, 1, 1, 1 }, - { 0, 0, 1, 0, 0 }, - { 0, 0, 1, 0, 0 }, - { 0, 0, 1, 0, 0 }, - { 0, 0, 1, 0, 0 }, - }, - ["у"] = { - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - { 0, 1, 1, 1 }, - { 0, 0, 0, 1 }, - { 1, 1, 1, 0 }, - }, - ["ф"] = { - { 0, 1, 1, 1, 0 }, - { 1, 0, 1, 0, 1 }, - { 0, 1, 1, 1, 0 }, - { 0, 0, 1, 0, 0 }, - { 0, 0, 1, 0, 0 }, - }, - ["х"] = { - { 1, 0, 0, 0, 1 }, - { 0, 1, 0, 1, 0 }, - { 0, 0, 1, 0, 0 }, - { 0, 1, 0, 1, 0 }, - { 1, 0, 0, 0, 1 }, - }, - ["ц"] = { - { 1, 0, 0, 1, 0 }, - { 1, 0, 0, 1, 0 }, - { 1, 0, 0, 1, 0 }, - { 1, 0, 0, 1, 0 }, - { 0, 1, 1, 1, 1 }, - }, - ["ч"] = { - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - { 0, 1, 1, 1 }, - { 0, 0, 0, 1 }, - { 0, 0, 0, 1 }, - }, - ["ш"] = { - { 1, 0, 0, 0, 1 }, - { 1, 0, 0, 0, 1 }, - { 1, 0, 1, 0, 1 }, - { 1, 0, 1, 0, 1 }, - { 1, 1, 1, 1, 1 }, - }, - ["щ"] = { - { 1, 0, 0, 0, 1, 0 }, - { 1, 0, 0, 0, 1, 0 }, - { 1, 0, 1, 0, 1, 0 }, - { 1, 0, 1, 0, 1, 0 }, - { 1, 1, 1, 1, 1, 1 }, - }, - ["ъ"] = { - { 1, 1, 0, 0, 0 }, - { 0, 1, 0, 0, 0 }, - { 0, 1, 1, 1, 0 }, - { 0, 1, 0, 0, 1 }, - { 0, 1, 1, 1, 0 }, - }, - ["ы"] = { - { 1, 0, 0, 0, 0, 1 }, - { 1, 0, 0, 0, 0, 1 }, - { 1, 1, 1, 0, 0, 1 }, - { 1, 0, 0, 1, 0, 1 }, - { 1, 1, 1, 0, 0, 1 }, - }, - ["ь"] = { - { 1, 0, 0, 0 }, - { 1, 0, 0, 0 }, - { 1, 1, 1, 0 }, - { 1, 0, 0, 1 }, - { 1, 1, 1, 0 }, - }, - ["э"] = { - { 1, 1, 1, 0 }, - { 0, 0, 0, 1 }, - { 0, 1, 1, 1 }, - { 0, 0, 0, 1 }, - { 1, 1, 1, 0 }, - }, - ["ю"] = { - { 1, 0, 0, 1, 1, 0 }, - { 1, 0, 1, 0, 0, 1 }, - { 1, 1, 1, 0, 0, 1 }, - { 1, 0, 1, 0, 0, 1 }, - { 1, 0, 0, 1, 1, 0 }, - }, - ["я"] = { - { 0, 1, 1, 1 }, - { 1, 0, 0, 1 }, - { 0, 1, 1, 1 }, - { 1, 0, 0, 1 }, - { 1, 0, 0, 1 }, - }, - - - ["-"] = { - { 0, 0, 0 }, - { 0, 0, 0 }, - { 1, 1, 1 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - }, - ["_"] = { - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 0, 0, 0 }, - { 1, 1, 1 }, - }, - ["+"] = { - { 0, 0, 0 }, - { 0, 1, 0 }, - { 1, 1, 1 }, - { 0, 1, 0 }, - { 0, 0, 0 }, - }, - - ["*"] = { - { 0, 0, 0 }, - { 1, 0, 1 }, - { 0, 1, 0 }, - { 1, 0, 1 }, - { 0, 0, 0 }, - }, - ["°"] = { - { 1 }, - { 0 }, - { 0 }, - { 0 }, - { 0 }, - }, - ["…"] = { - { 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0 }, - { 1, 0, 1, 0, 1 }, - }, -} - -function bigLetters.draw(x, y, color, symbol, drawWithSymbol) - if symbol == " " then - return spaceWidth - elseif not letters[symbol] then - symbol = unknownSymbol - end - - for j = 1, #letters[symbol] do - for i = 1, #letters[symbol][j] do - if letters[symbol][j][i] == 1 then - if not drawWithSymbol then - buffer.square(x + i * 2 - 2, y + (pixelHeight - #letters[symbol]) + j - 1, 2, 1, color, 0xFFFFFF, " ") - else - buffer.text(x + i * 2 - 2, y + (pixelHeight - #letters[symbol]) + j - 1, color, "*") - end - end - end - end - - return #letters[symbol][1] -end - -function bigLetters.drawText(x, y, color, stroka, drawWithSymbol) - checkArg(4, stroka, "string") - for i = 1, unicode.len(stroka) do - x = x + bigLetters.draw(x, y, color, unicode.sub(stroka, i, i), drawWithSymbol) * 2 + lettersInterval - end - return x -end - -function bigLetters.getTextSize(text) - local width, height = 0, 0 - local symbol, symbolWidth, symbolHeight - for i = 1, unicode.len(text) do - symbol = unicode.sub(text, i, i) - if symbol == " " then - symbolWidth = spaceWidth - symbolHeight = 5 - elseif not letters[symbol] then - symbolHeight = #letters[unknownSymbol] - symbolWidth = #letters[unknownSymbol][1] - else - symbolHeight = #letters[symbol] - symbolWidth = #letters[symbol][1] - end - - width = width + symbolWidth * 2 + lettersInterval - height = math.max(height, symbolHeight) - end - - return (width - lettersInterval), height -end - -return bigLetters - - - - - - - - - - - -F lib/bit32.lua--[[ Backwards compat for Lua 5.3; only loaded in 5.3 because package.loaded is - prepopulated with the existing global bit32 in 5.2. ]] - -local bit32 = {} - -------------------------------------------------------------------------------- - -local function fold(init, op, ...) - local result = init - local args = table.pack(...) - for i = 1, args.n do - result = op(result, args[i]) - end - return result -end - -local function trim(n) - return n & 0xFFFFFFFF -end - -local function mask(w) - return ~(0xFFFFFFFF << w) -end - -function bit32.arshift(x, disp) - return x // (2 ^ disp) -end - -function bit32.band(...) - return fold(0xFFFFFFFF, function(a, b) return a & b end, ...) -end - -function bit32.bnot(x) - return ~x -end - -function bit32.bor(...) - return fold(0, function(a, b) return a | b end, ...) -end - -function bit32.btest(...) - return bit32.band(...) ~= 0 -end - -function bit32.bxor(...) - return fold(0, function(a, b) return a ~ b end, ...) -end - -local function fieldargs(f, w) - w = w or 1 - assert(f >= 0, "field cannot be negative") - assert(w > 0, "width must be positive") - assert(f + w <= 32, "trying to access non-existent bits") - return f, w -end - -function bit32.extract(n, field, width) - local f, w = fieldargs(field, width) - return (n >> f) & mask(w) -end - -function bit32.replace(n, v, field, width) - local f, w = fieldargs(field, width) - local m = mask(w) - return (n & ~(m << f)) | ((v & m) << f) -end - -function bit32.lrotate(x, disp) - if disp == 0 then return x - elseif disp < 0 then return bit32.rrotate(x, -disp) - else return trim((x << disp) | (x >> (32 - disp))) end -end - -function bit32.lshift(x, disp) - return trim(x << disp) -end - -function bit32.rrotate(x, disp) - if disp == 0 then return x - elseif disp < 0 then return bit32.lrotate(x, -disp) - else return trim((x >> disp) | (x << (32 - disp))) end -end - -function bit32.rshift(x, disp) - return trim(x >> disp) -end - -------------------------------------------------------------------------------- - -return bit32 -Flib/buffer.lua.local computer = require("computer") -local unicode = require("unicode") - -local buffer = {} - -function buffer.new(mode, stream) - local result = { - mode = {}, - stream = stream, - bufferRead = "", - bufferWrite = "", - bufferSize = math.max(512, math.min(8 * 1024, computer.freeMemory() / 8)), - bufferMode = "full", - readTimeout = math.huge - } - mode = mode or "r" - for i = 1, unicode.len(mode) do - result.mode[unicode.sub(mode, i, i)] = true - end - local metatable = { - __index = buffer, - __metatable = "file" - } - return setmetatable(result, metatable) -end - -function buffer:close() - if self.mode.w or self.mode.a then - self:flush() - end - self.closed = true - return self.stream:close() -end - -function buffer:flush() - if #self.bufferWrite > 0 then - local tmp = self.bufferWrite - self.bufferWrite = "" - local result, reason = self.stream:write(tmp) - if result then - self.bufferWrite = "" - else - if reason then - return nil, reason - else - return nil, "bad file descriptor" - end - end - end - - return self -end - -function buffer:lines(...) - local args = table.pack(...) - return function() - local result = table.pack(self:read(table.unpack(args, 1, args.n))) - if not result[1] and result[2] then - error(result[2]) - end - return table.unpack(result, 1, result.n) - end -end - -function buffer:read(...) - if not self.mode.r then - return nil, "read mode was not enabled for this stream" - end - - local timeout = computer.uptime() + self.readTimeout - - local function readChunk() - if computer.uptime() > timeout then - error("timeout") - end - local result, reason = self.stream:read(self.bufferSize) - if result then - self.bufferRead = self.bufferRead .. result - return self - else -- error or eof - return nil, reason - end - end - - local function readBytesOrChars(n) - n = math.max(n, 0) - local len, sub - if self.mode.b then - len = rawlen - sub = string.sub - else - len = unicode.len - sub = unicode.sub - end - local buffer = "" - repeat - if len(self.bufferRead) == 0 then - local result, reason = readChunk() - if not result then - if reason then - return nil, reason - else -- eof - return #buffer > 0 and buffer or nil - end - end - end - local left = n - len(buffer) - buffer = buffer .. sub(self.bufferRead, 1, left) - self.bufferRead = sub(self.bufferRead, left + 1) - until len(buffer) == n - return buffer - end - - local function readNumber() - local len, sub - if self.mode.b then - len = rawlen - sub = string.sub - else - len = unicode.len - sub = unicode.sub - end - local buffer = "" - local first = true - local decimal = false - local last = false - local hex = false - local pat = "^[0-9]+" - local minbuf = 3 -- "+0x" (sign + hexadecimal tag) - -- this function is used to read trailing numbers (1e2, 0x1p2, etc) - local function readnum(checksign) - local _buffer = "" - local sign = "" - while true do - if len(self.bufferRead) == 0 then - local result, reason = readChunk() - if not result then - if reason then - return nil, reason - else -- eof - return #_buffer > 0 and (sign .. _buffer) or nil - end - end - end - if checksign then - local _sign = sub(self.bufferRead, 1, 1) - if _sign == "+" or _sign == "-" then - -- "eat" the sign (Rio Lua behaviour) - sign = sub(self.bufferRead, 1, 1) - self.bufferRead = sub(self.bufferRead, 2) - end - checksign = false - else - local x,y = string.find(self.bufferRead, pat) - if not x then - break - else - _buffer = _buffer .. sub(self.bufferRead, 1, y) - self.bufferRead = sub(self.bufferRead, y + 1) - end - end - end - return #_buffer > 0 and (sign .. _buffer) or nil - end - while true do - if len(self.bufferRead) == 0 or len(self.bufferRead) < minbuf then - local result, reason = readChunk() - if not result then - if reason then - return nil, reason - else -- eof - return #buffer > 0 and tonumber(buffer) or nil - end - end - end - -- these ifs are here so we run the buffer check above - if first then - local sign = sub(self.bufferRead, 1, 1) - if sign == "+" or sign == "-" then - -- "eat" the sign (Rio Lua behaviour) - buffer = buffer .. sub(self.bufferRead, 1, 1) - self.bufferRead = sub(self.bufferRead, 2) - end - local hextag = sub(self.bufferRead, 1, 2) - if hextag == "0x" or hextag == "0X" then - pat = "^[0-9A-Fa-f]+" - -- "eat" the 0x, see https://gist.github.com/SoniEx2/570a363d81b743353151 - buffer = buffer .. sub(self.bufferRead, 1, 2) - self.bufferRead = sub(self.bufferRead, 3) - hex = true - end - minbuf = 0 - first = false - elseif decimal then - local sep = sub(self.bufferRead, 1, 1) - if sep == "." then - buffer = buffer .. sep - self.bufferRead = sub(self.bufferRead, 2) - local temp = readnum(false) -- no sign - if temp then - buffer = buffer .. temp - end - end - if not tonumber(buffer) then break end - decimal = false - last = true - minbuf = 1 - elseif last then - local tag = sub(self.bufferRead, 1, 1) - if hex and (tag == "p" or tag == "P") then - local temp = sub(self.bufferRead, 1, 1) - self.bufferRead = sub(self.bufferRead, 2) - local temp2 = readnum(true) -- this eats the next sign if any - if temp2 then - buffer = buffer .. temp .. temp2 - end - elseif tag == "e" or tag == "E" then - local temp = sub(self.bufferRead, 1, 1) - self.bufferRead = sub(self.bufferRead, 2) - local temp2 = readnum(true) -- this eats the next sign if any - if temp2 then - buffer = buffer .. temp .. temp2 - end - end - break - else - local x,y = string.find(self.bufferRead, pat) - if not x then - minbuf = 1 - decimal = true - else - buffer = buffer .. sub(self.bufferRead, 1, y) - self.bufferRead = sub(self.bufferRead, y + 1) - end - end - end - return tonumber(buffer) - end - - local function readLine(chop) - local start = 1 - while true do - local buf = self.bufferRead - local i = buf:find("[\r\n]", start) - local c = i and buf:sub(i,i) - local is_cr = c == "\r" - if i and (not is_cr or i < #buf) then - local n = buf:sub(i+1,i+1) - if is_cr and n == "\n" then - c = c .. n - end - local result = buf:sub(1, i - 1) .. (chop and "" or c) - self.bufferRead = buf:sub(i + #c) - return result - else - start = #self.bufferRead - (is_cr and 1 or 0) - local result, reason = readChunk() - if not result then - if reason then - return nil, reason - else -- eof - local result = #self.bufferRead > 0 and self.bufferRead or nil - self.bufferRead = "" - return result - end - end - end - end - end - - local function readAll() - repeat - local result, reason = readChunk() - if not result and reason then - return nil, reason - end - until not result -- eof - local result = self.bufferRead - self.bufferRead = "" - return result - end - - local function read(n, format) - if type(format) == "number" then - return readBytesOrChars(format) - else - if type(format) ~= "string" or unicode.sub(format, 1, 1) ~= "*" then - error("bad argument #" .. n .. " (invalid option)") - end - format = unicode.sub(format, 2, 2) - if format == "n" then - return readNumber() - elseif format == "l" then - return readLine(true) - elseif format == "L" then - return readLine(false) - elseif format == "a" then - return readAll() - else - error("bad argument #" .. n .. " (invalid format)") - end - end - end - - if self.mode.w or self.mode.a then - self:flush() - end - - local results = {} - local formats = table.pack(...) - if formats.n == 0 then - return readLine(true) - end - for i = 1, formats.n do - local result, reason = read(i, formats[i]) - if result then - results[i] = result - elseif reason then - return nil, reason - end - end - return table.unpack(results, 1, formats.n) -end - -function buffer:seek(whence, offset) - whence = tostring(whence or "cur") - assert(whence == "set" or whence == "cur" or whence == "end", - "bad argument #1 (set, cur or end expected, got " .. whence .. ")") - offset = offset or 0 - checkArg(2, offset, "number") - assert(math.floor(offset) == offset, "bad argument #2 (not an integer)") - - if self.mode.w or self.mode.a then - self:flush() - elseif whence == "cur" then - offset = offset - #self.bufferRead - end - local result, reason = self.stream:seek(whence, offset) - if result then - self.bufferRead = "" - return result - else - return nil, reason - end -end - -function buffer:setvbuf(mode, size) - mode = mode or self.bufferMode - size = size or self.bufferSize - - assert(mode == "no" or mode == "full" or mode == "line", - "bad argument #1 (no, full or line expected, got " .. tostring(mode) .. ")") - assert(mode == "no" or type(size) == "number", - "bad argument #2 (number expected, got " .. type(size) .. ")") - - self.bufferMode = mode - self.bufferSize = size - - return self.bufferMode, self.bufferSize -end - -function buffer:getTimeout() - return self.readTimeout -end - -function buffer:setTimeout(value) - self.readTimeout = tonumber(value) -end - -function buffer:write(...) - if self.closed then - return nil, "bad file descriptor" - end - if not self.mode.w and not self.mode.a then - return nil, "write mode was not enabled for this stream" - end - local args = table.pack(...) - for i = 1, args.n do - if type(args[i]) == "number" then - args[i] = tostring(args[i]) - end - checkArg(i, args[i], "string") - end - - for i = 1, args.n do - local arg = args[i] - local result, reason - - if self.bufferMode == "full" then - if self.bufferSize - #self.bufferWrite < #arg then - result, reason = self:flush() - if not result then - return nil, reason - end - end - if #arg > self.bufferSize then - result, reason = self.stream:write(arg) - else - self.bufferWrite = self.bufferWrite .. arg - result = self - end - - elseif self.bufferMode == "line" then - local l - repeat - local idx = arg:find("\n", (l or 0) + 1, true) - if idx then - l = idx - end - until not idx - if l or #arg > self.bufferSize then - result, reason = self:flush() - if not result then - return nil, reason - end - end - if l then - result, reason = self.stream:write(arg:sub(1, l)) - if not result then - return nil, reason - end - arg = arg:sub(l + 1) - end - if #arg > self.bufferSize then - result, reason = self.stream:write(arg) - else - self.bufferWrite = self.bufferWrite .. arg - result = self - end - - else -- self.bufferMode == "no" - result, reason = self.stream:write(arg) - end - - if not result then - return nil, reason - end - end - - return self -end - -return buffer -Flib/colorlib.lualocal colorlib = {} -local serialization = require("serialization") - -local function isNan(x) - return x~=x -end - -function colorlib.HEXtoRGB(color) - return bit32.rshift(color, 16), bit32.band(bit32.rshift(color, 8), 0xFF), bit32.band(color, 0xFF) -end - -function colorlib.RGBtoHEX(rr, gg, bb) - return bit32.lshift(rr, 16) + bit32.lshift(gg, 8) + bb -end - ---HSB model -function colorlib.RGBtoHSB(rr, gg, bb) - local max = math.max(rr, math.max(gg, bb)) - local min = math.min(rr, math.min(gg, bb)) - local delta = max - min - - local h = 0 - if ( max == rr and gg >= bb) then h = 60*(gg-bb)/delta end - if ( max == rr and gg <= bb ) then h = 60*(gg-bb)/delta + 360 end - if ( max == gg ) then h = 60*(bb-rr)/delta + 120 end - if ( max == bb ) then h = 60*(rr-gg)/delta + 240 end - - local s = 0 - if ( max ~= 0 ) then s = 1 - (min / max) end - - local b = max * 100 / 255 - - if isNan(h) then h = 0 end - - return h, s * 100, b -end - -function colorlib.HSBtoRGB(h, s, v) - if h > 359 then h = 0 end - local rr, gg, bb = 0, 0, 0 - local const = 255 - - s = s/100 - v = v/100 - - local i = math.floor(h/60) - local f = h/60 - i - - local p = v*(1-s) - local q = v*(1-s*f) - local t = v*(1-(1-f)*s) - - if ( i == 0 ) then rr, gg, bb = v, t, p end - if ( i == 1 ) then rr, gg, bb = q, v, p end - if ( i == 2 ) then rr, gg, bb = p, v, t end - if ( i == 3 ) then rr, gg, bb = p, q, v end - if ( i == 4 ) then rr, gg, bb = t, p, v end - if ( i == 5 ) then rr, gg, bb = v, p, q end - - return math.floor(rr * const), math.floor(gg * const), math.floor(bb * const) -end - -function colorlib.HEXtoHSB(color) - local rr, gg, bb = colorlib.HEXtoRGB(color) - local h, s, b = colorlib.RGBtoHSB( rr, gg, bb ) - - return h, s, b -end - -function colorlib.HSBtoHEX(h, s, b) - local rr, gg, bb = colorlib.HSBtoRGB(h, s, b) - local color = colorlib.RGBtoHEX(rr, gg, bb) - - return color -end - ---Смешивание двух цветов на основе альфа-канала второго -function colorlib.alphaBlend(firstColor, secondColor, alphaChannel) - alphaChannel = alphaChannel / 255 - local invertedAlphaChannel = 1 - alphaChannel - - - local firstColorRed, firstColorGreen, firstColorBlue = colorlib.HEXtoRGB(firstColor) - local secondColorRed, secondColorGreen, secondColorBlue = colorlib.HEXtoRGB(secondColor) - - return colorlib.RGBtoHEX( - secondColorRed * invertedAlphaChannel + firstColorRed * alphaChannel, - secondColorGreen * invertedAlphaChannel + firstColorGreen * alphaChannel, - secondColorBlue * invertedAlphaChannel + firstColorBlue * alphaChannel - ) -end - ---Получение среднего цвета между перечисленными. К примеру, между черным и белым выдаст серый. -function colorlib.getAverageColor(colors) - local sColors = #colors - local averageRed, averageGreen, averageBlue = 0, 0, 0 - for i = 1, sColors do - local r, g, b = colorlib.HEXtoRGB(colors[i]) - averageRed, averageGreen, averageBlue = averageRed + r, averageGreen + g, averageBlue + b - end - return colorlib.RGBtoHEX(math.floor(averageRed / sColors), math.floor(averageGreen / sColors), math.floor(averageBlue / sColors)) -end - ------------------------------------------------------------------------------------------------------------------------ - -local palette = { - 0x000000, 0x000040, 0x000080, 0x0000BF, 0x0000FF, 0x002400, 0x002440, 0x002480, 0x0024BF, 0x0024FF, 0x004900, 0x004940, 0x004980, 0x0049BF, 0x0049FF, 0x006D00, - 0x006D40, 0x006D80, 0x006DBF, 0x006DFF, 0x009200, 0x009240, 0x009280, 0x0092BF, 0x0092FF, 0x00B600, 0x00B640, 0x00B680, 0x00B6BF, 0x00B6FF, 0x00DB00, 0x00DB40, - 0x00DB80, 0x00DBBF, 0x00DBFF, 0x00FF00, 0x00FF40, 0x00FF80, 0x00FFBF, 0x00FFFF, 0x0F0F0F, 0x1E1E1E, 0x2D2D2D, 0x330000, 0x330040, 0x330080, 0x3300BF, 0x3300FF, - 0x332400, 0x332440, 0x332480, 0x3324BF, 0x3324FF, 0x334900, 0x334940, 0x334980, 0x3349BF, 0x3349FF, 0x336D00, 0x336D40, 0x336D80, 0x336DBF, 0x336DFF, 0x339200, - 0x339240, 0x339280, 0x3392BF, 0x3392FF, 0x33B600, 0x33B640, 0x33B680, 0x33B6BF, 0x33B6FF, 0x33DB00, 0x33DB40, 0x33DB80, 0x33DBBF, 0x33DBFF, 0x33FF00, 0x33FF40, - 0x33FF80, 0x33FFBF, 0x33FFFF, 0x3C3C3C, 0x4B4B4B, 0x5A5A5A, 0x660000, 0x660040, 0x660080, 0x6600BF, 0x6600FF, 0x662400, 0x662440, 0x662480, 0x6624BF, 0x6624FF, - 0x664900, 0x664940, 0x664980, 0x6649BF, 0x6649FF, 0x666D00, 0x666D40, 0x666D80, 0x666DBF, 0x666DFF, 0x669200, 0x669240, 0x669280, 0x6692BF, 0x6692FF, 0x66B600, - 0x66B640, 0x66B680, 0x66B6BF, 0x66B6FF, 0x66DB00, 0x66DB40, 0x66DB80, 0x66DBBF, 0x66DBFF, 0x66FF00, 0x66FF40, 0x66FF80, 0x66FFBF, 0x66FFFF, 0x696969, 0x787878, - 0x878787, 0x969696, 0x990000, 0x990040, 0x990080, 0x9900BF, 0x9900FF, 0x992400, 0x992440, 0x992480, 0x9924BF, 0x9924FF, 0x994900, 0x994940, 0x994980, 0x9949BF, - 0x9949FF, 0x996D00, 0x996D40, 0x996D80, 0x996DBF, 0x996DFF, 0x999200, 0x999240, 0x999280, 0x9992BF, 0x9992FF, 0x99B600, 0x99B640, 0x99B680, 0x99B6BF, 0x99B6FF, - 0x99DB00, 0x99DB40, 0x99DB80, 0x99DBBF, 0x99DBFF, 0x99FF00, 0x99FF40, 0x99FF80, 0x99FFBF, 0x99FFFF, 0xA5A5A5, 0xB4B4B4, 0xC3C3C3, 0xCC0000, 0xCC0040, 0xCC0080, - 0xCC00BF, 0xCC00FF, 0xCC2400, 0xCC2440, 0xCC2480, 0xCC24BF, 0xCC24FF, 0xCC4900, 0xCC4940, 0xCC4980, 0xCC49BF, 0xCC49FF, 0xCC6D00, 0xCC6D40, 0xCC6D80, 0xCC6DBF, - 0xCC6DFF, 0xCC9200, 0xCC9240, 0xCC9280, 0xCC92BF, 0xCC92FF, 0xCCB600, 0xCCB640, 0xCCB680, 0xCCB6BF, 0xCCB6FF, 0xCCDB00, 0xCCDB40, 0xCCDB80, 0xCCDBBF, 0xCCDBFF, - 0xCCFF00, 0xCCFF40, 0xCCFF80, 0xCCFFBF, 0xCCFFFF, 0xD2D2D2, 0xE1E1E1, 0xF0F0F0, 0xFF0000, 0xFF0040, 0xFF0080, 0xFF00BF, 0xFF00FF, 0xFF2400, 0xFF2440, 0xFF2480, - 0xFF24BF, 0xFF24FF, 0xFF4900, 0xFF4940, 0xFF4980, 0xFF49BF, 0xFF49FF, 0xFF6D00, 0xFF6D40, 0xFF6D80, 0xFF6DBF, 0xFF6DFF, 0xFF9200, 0xFF9240, 0xFF9280, 0xFF92BF, - 0xFF92FF, 0xFFB600, 0xFFB640, 0xFFB680, 0xFFB6BF, 0xFFB6FF, 0xFFDB00, 0xFFDB40, 0xFFDB80, 0xFFDBBF, 0xFFDBFF, 0xFFFF00, 0xFFFF40, 0xFFFF80, 0xFFFFBF, 0xFFFFFF, -} - -function colorlib.convert24BitTo8Bit(hex24) - local encodedIndex = nil - local colorMatchFactor = nil - local colorMatchFactor_min = math.huge - - local red24, green24, blue24 = colorlib.HEXtoRGB(hex24) - - for colorIndex, colorPalette in ipairs(palette) do - local redPalette, greenPalette, bluePalette = colorlib.HEXtoRGB(colorPalette) - - colorMatchFactor = (redPalette-red24)^2 + (greenPalette-green24)^2 + (bluePalette-blue24)^2 - - if (colorMatchFactor < colorMatchFactor_min) then - encodedIndex = colorIndex - colorMatchFactor_min = colorMatchFactor - end - end - - return encodedIndex - 1 - -- return searchClosestColor(1, #palette, hex24) -end - -function colorlib.convert8BitTo24Bit(hex8) - return palette[hex8 + 1] -end - -function colorlib.debugColorCompression(color) - local compressedColor = colorlib.convert24BitTo8Bit(color) - local decompressedColor = colorlib.convert8BitTo24Bit(compressedColor) - print("Исходный цвет: " .. string.format("0x%06X", color)) - print("Сжатый цвет: " .. string.format("0x%02X", compressedColor)) - print("Расжатый цвет: " .. string.format("0x%06X", decompressedColor)) -end - - ------------------------------------------------------------------------------------------------------------------------ - -return colorlib - - - - - - -Flib/colors.lualocal colors = { - [0] = "white", - [1] = "orange", - [2] = "magenta", - [3] = "lightblue", - [4] = "yellow", - [5] = "lime", - [6] = "pink", - [7] = "gray", - [8] = "silver", - [9] = "cyan", - [10] = "purple", - [11] = "blue", - [12] = "brown", - [13] = "green", - [14] = "red", - [15] = "black" -} - -do - local keys = {} - for k in pairs(colors) do - table.insert(keys, k) - end - for _, k in pairs(keys) do - colors[colors[k]] = k - end -end - -return colorsFlib/compressor.luaP -local unicode = require("unicode") -local fs = require("filesystem") -local compressor = {} - ------------------------------------------------------------------------------------------------------------------- - -local function numberToByteArray(number) - local byteArray = {} - while number > 0 do - table.insert(byteArray, 1, bit32.band(number, 0xFF)) - number = bit32.rshift(number, 8) - end - return byteArray -end - -local function byteArrayToNumber(byteArray) - local number = byteArray[1] - for i = 2, #byteArray do - number = bit32.bor(byteArray[i], bit32.lshift(number, 8)) - end - return number -end - -local function info(showInfo, text) - if showInfo then - print(text) - end -end - ------------------------------------------------------------------------------------------------------------------- - -local function writePath(compressedFile, path) - -- Получаем юникод-байтики названия файла или папки - local pathBytes = {} - for i = 1, unicode.len(path) do - local charBytes = { string.byte(unicode.sub(path, i, i), 1, 6) } - for j = 1, #charBytes do - table.insert(pathBytes, charBytes[j]) - end - end - -- Записываем количество байт, необходимое для записи РАЗМЕРА байт пути - local bytesForCountOfBytesForPath = numberToByteArray(#pathBytes) - compressedFile:write(string.char(#bytesForCountOfBytesForPath)) - -- Записываем количество байт, необходимое для записи самого пути - for i = 1, #bytesForCountOfBytesForPath do - compressedFile:write(string.char(bytesForCountOfBytesForPath[i])) - end - -- Записываем байтики пути - for i = 1, #pathBytes do - compressedFile:write(string.char(pathBytes[i])) - end -end - -local function writeFileSize(compressedFile, path) - local size = fs.size(path) - local bytesForSize = numberToByteArray(size) - -- Записываем количество байт, необходимое для записи РАЗМЕРА байт размера файла - compressedFile:write(string.char(#bytesForSize)) - -- Записываем сами байты размера файла - for i = 1, #bytesForSize do - compressedFile:write(string.char(bytesForSize[i])) - end -end - -local function getFileList(path) - local fileList = {} - for file in fs.list(path) do - table.insert(fileList, path .. file) - end - return fileList -end - -local function doCompressionRecursively(compressedFile, fileList, currentPackPath, pathToCompressedFile, showInfo) - for file = 1, #fileList do - local filename = (fs.name(fileList[file]) or "") - local filePackPath = currentPackPath .. filename - - -- info(showInfo, "Local path: " .. filePackPath) - if fileList[file] == pathToCompressedFile or filename == "mnt" or filename == "dev" or filename == ".DS_Store" then - info(showInfo, "Skipping restricted path \"" .. fileList[file] .. "\"") - else - if fs.isDirectory(fileList[file]) then - info(showInfo, "Packing directory " .. fileList[file]) - - compressedFile:write("D") - writePath(compressedFile, filePackPath .. "/") - - doCompressionRecursively(compressedFile, getFileList(fileList[file]), filePackPath .. "/", pathToCompressedFile, showInfo) - else - info(showInfo, "Packing file " .. fileList[file]) - - compressedFile:write("F") - writePath(compressedFile, filePackPath) - writeFileSize(compressedFile, fileList[file]) - - local fileToCompress = io.open(fileList[file], "rb") - compressedFile:write(fileToCompress:read("*a")) - fileToCompress:close() - end - end - -- require("ECSAPI").wait() - end -end - -function compressor.pack(pathToCompressedFile, ...) - local data, showInfo = {...}, false - if type(data[#data]) == "boolean" then showInfo = data[#data]; data[#data] = nil end - - info(showInfo, "Packing data to file \"" .. pathToCompressedFile .. "\"...") - info(showInfo, " ") - fs.makeDirectory(fs.path(pathToCompressedFile)) - - -- Открываем файл со сжатым контентом - local compressedFile, reason = io.open(pathToCompressedFile, "wb") - if not compressedFile then - error("Failed to open package file for writing: " .. tostring(reason)) - end - -- Записываем сигнатурку - compressedFile:write("ARCH") - -- Пакуем данные - doCompressionRecursively(compressedFile, data, "", pathToCompressedFile, showInfo) - -- Закрываем файл со сжатым контентом - compressedFile:close() - info(showInfo, " ") - info(showInfo, "Data packing finished") -end - ------------------------------------------------------------------------------------------------------------------- - -local function readPath(compressedFile) - local countOfBytesForPathBytes = string.byte(compressedFile:read(1)) - local pathBytes = {} - for i = 1, countOfBytesForPathBytes do - table.insert(pathBytes, string.byte(compressedFile:read(1))) - end - local pathSize = byteArrayToNumber(pathBytes) - local path = compressedFile:read(pathSize) - -- info(showInfo, "Колво байт под байты пути: ", countOfBytesForPathBytes) - -- info(showInfo, "Колво байт под путь: ", pathSize) - -- info(showInfo, "Путь: ", path) - return path -end - -local function readFileSize(compressedFile) - local countOfBytesForFileSize = string.byte(compressedFile:read(1)) - local fileSizeBytes = {} - for i = 1, countOfBytesForFileSize do - table.insert(fileSizeBytes, string.byte(compressedFile:read(1))) - end - local fileSize = byteArrayToNumber(fileSizeBytes) - -- info(showInfo, "Размер файла: ", fileSize) - return fileSize -end - -function compressor.unpack(pathToCompressedFile, pathWhereToUnpack, showInfo) - info(showInfo, "Unpacking data from file \"" .. pathToCompressedFile .. "\"...") - info(showInfo, " ") - fs.makeDirectory(pathWhereToUnpack) - - local compressedFile, reason = io.open(pathToCompressedFile, "rb") - if not compressedFile then - error("Failed to open package file for reading: " .. tostring(reason)) - end - - local signature = compressedFile:read(4) - if signature == "ARCH" then - while true do - local type = compressedFile:read(1) - if type == "D" then - local path = readPath(compressedFile) - fs.makeDirectory(pathWhereToUnpack .. path) - info(showInfo, "Unpacking directory \"" .. path .. "\"") - elseif type == "F" then - local path = readPath(compressedFile) - local size = readFileSize(compressedFile) - - info(showInfo, "Unpacking file \"" .. path .. "\"") - local file, reason = io.open(pathWhereToUnpack .. path, "wb") - if file then - file:write(compressedFile:read(size)) - file:close() - else - compressedFile:read(size) - info(showInfo, "Failed to open file for writing while unpacking: " .. tostring(reason)) - end - elseif not type then - break - else - compressedFile:close() - error("Packed file is corrupted, unknown path type: " .. tostring(type)) - end - end - else - compressedFile:close() - error("Packed file is corrupted, wrong signature: " .. tostring(signature)) - end - - compressedFile:close() - info(showInfo, " ") - info(showInfo, "Unpacking data finished") -end - ------------------------------------------------------------------------------------------------------------------- - --- compressor.pack("/test1.pkg", "/MineOS/System/OS/", "/etc/", true) --- info(showInfo, " ") --- compressor.unpack("/test1.pkg", "/papkaUnpacked/", true) - ------------------------------------------------------------------------------------------------------------------- - -return compressor - - - -Flib/context.luaolocal component = require("component") -local event = require("event") -local unicode = require("unicode") -local ecs = require("ECSAPI") -local gpu = component.gpu - -local context = {} - -local separatorColor = 0xcccccc -local shortcutAutism = 3 - ----------------------------------------------------------------------------------------------------------------- - ---ОБЪЕКТЫ -local obj = {} -local function newObj(class, name, ...) - obj[class] = obj[class] or {} - obj[class][name] = {...} -end - -function context.menu(x, y, ...) - - local data = {...} - local sData = #data - - obj = {} - - --Получаем размер экрана - local xSize, ySize = gpu.getResolution() - - --Получаем самую жирную полоску текста - local biggestElement = 0 - for i = 1, sData do - if data[i] ~= "-" then - local length = unicode.len(data[i][1]) - if data[i][3] then length = length + shortcutAutism + unicode.len(data[i][3]) end - biggestElement = math.max(biggestElement, length) - length = nil - end - end - - --Задание ширины и высоты - local width = 4 + biggestElement - local height = sData - - --А это чтоб за края экрана не лезло - if y + height >= ySize then y = ySize - height end - if x + width + 1 >= xSize then x = xSize - width - 1 end - - --Рисуем окошечко и запоминаем, че было до него (сначала был Бог...!) - local oldPixels = ecs.rememberOldPixels(x, y, x + width + 1, y + height) - ecs.square(x, y, width, height, 0xffffff) - ecs.windowShadow(x, y, width, height) - gpu.setBackground(0xffffff) - - --Нарисовать конкретный элемент - local function drawElement(i, background, foreground, yPos) - - if background then ecs.square(x, yPos, width, 1, background) end - - --Получаем текстик - local text - if data[i] == "-" then - ecs.colorText(x, yPos, separatorColor, string.rep("─", width)) - else - --Нужный цвет - local color = foreground or 0x000000 - if data[i][2] then color = separatorColor end - - --Рисуем текстик - ecs.colorText(x + 2, yPos, color, data[i][1]) - - --Рисуем сокращение - if data[i][3] then gpu.set(x + width - 2 - unicode.len(data[i][3]), yPos, data[i][3]) end - - if not data[i][2] then newObj("Elements", i, x, yPos, x + width - 1, yPos) end - end - end - - --Рисуем все элементы - local counter = 0 - local yPos - for i = 1, sData do - yPos = y + counter - - drawElement(i, nil, nil, yPos) - - counter = counter + 1 - end - - --Проверка нажатия - local action - local e = {event.pull("touch")} - if obj["Elements"] then - for key, val in pairs(obj["Elements"]) do - if ecs.clickedAtArea(e[3], e[4], obj["Elements"][key][1], obj["Elements"][key][2], obj["Elements"][key][3], obj["Elements"][key][4]) then - - --ecs.error("Кол-во объектов: "..#obj["Elements"]..", кликнули на объект номер = "..tostring(key)..", #Data = "..#data..", sData ="..sData) - - drawElement(key, ecs.colors.blue, 0xffffff, e[4]) - os.sleep(0.3) - action = data[key][1] - break - end - end - end - - --Красим то, че было - ecs.drawOldPixels(oldPixels) - - --Возвращаем выбранное - return action - -end - ----------------------------------------------------------------------------------------------------------------- - --- while true do --- local e = {event.pull("touch")} --- ecs.prepareToExit() --- local action = context.menu(e[3], e[4], {"Показать содержимое"}, "-", {"Вырезать", false, "^X"}, {"Копировать", false, "^C"}, {"Вставить", true, "^V"}, "-", {"Переименовать"}, {"Создать ярлык"}, {"Добавить в архив"}, "-", {"Удалить", false, "⌫"}) --- ecs.prepareToExit() --- print("Ты выбрал = "..tostring(action)) --- end - ---local action = context.menu(6, 2, {(function() if showHiddenFiles then return "Скрывать скрытые файлы" else return "Показывать скрытые файлы" end end)()}, {(function() if showSystemFiles then return "Скрывать системные файлы" else return "Показывать системные файлы" end end)()}, "-", {(function() if showFileFormat then return "Скрывать формат файлов" else return "Показывать формат файлов" end end)()}) - - -return context - - - - - - - - -Flib/crc32lua.lua=--[[ - -LUA MODULE - - digest.crc32 - CRC-32 checksum implemented entirely in Lua. - -SYNOPSIS - - local CRC = require 'digest.crc32lua' - print(CRC.crc32 'test') --> 0xD87F7E0C or -662733300 - - assert(CRC.crc32('st', CRC.crc32('te')) == CRC.crc32 'test') - -DESCRIPTION - - This can be used to compute CRC-32 checksums on strings. - This is similar to [1-2]. - -API - - Note: in the functions below, checksums are 32-bit integers stored in - numbers. The number format currently depends on the bit - implementation--see DESIGN NOTES below. - - CRC.crc32_byte(byte [, crc]) --> rcrc - - Returns CRC-32 checksum `rcrc` of byte `byte` (number 0..255) appended to - a string with CRC-32 checksum `crc`. `crc` defaults to 0 (empty string) - if omitted. - - CRC.crc32_string(s, crc) --> bcrc - - Returns CRC-32 checksum `rcrc` of string `s` appended to - a string with CRC-32 checksum `crc`. `crc` defaults to 0 (empty string) - if omitted. - - CRC.crc32(o, crc) --> bcrc - - This invokes `crc32_byte` if `o` is a byte or `crc32_string` if `o` - is a string. - - CRC.bit - - This contains the underlying bit library used by the module. It - should be considered a read-only copy. - -DESIGN NOTES - - Currently, this module exposes the underlying bit array implementation in CRC - checksums returned. In BitOp, bit arrays are 32-bit signed integer numbers - (may be negative). In Lua 5.2 'bit32' and 'bit.numberlua', bit arrays are - 32-bit unsigned integer numbers (non-negative). This is subject to change - in the future but is currently done due to (unconfirmed) performance - implications. - - On platforms with 64-bit numbers, one way to normalize CRC - checksums to be unsigned is to do `crcvalue % 2^32`, - - The name of this module is inspired by Perl `Digest::CRC*`. - -DEPENDENCIES - - Requires one of the following bit libraries: - - BitOp "bit" -- bitop.luajit.org -- This is included in LuaJIT and also available - for Lua 5.1/5.2. This provides the fastest performance in LuaJIT. - Lua 5.2 "bit32" -- www.lua.org/manual/5.2 -- This is provided in Lua 5.2 - and is preferred in 5.2 (unless "bit" also happens to be installed). - "bit.numberlua" (>=000.003) -- https://github.com/davidm/lua-bit-numberlua - This is slowest and used as a last resort. - It is only a few times slower than "bit32" though. - -DOWNLOAD/INSTALLATION - - If using LuaRocks: - luarocks install lua-digest-crc32lua - - Otherwise, download . - Alternately, if using git: - git clone git://github.com/davidm/lua-digest-crc32lua.git - cd lua-digest-crc32lua - Optionally unpack: - ./util.mk - or unpack and install in LuaRocks: - ./util.mk install - -REFERENCES - - [1] http://www.axlradius.com/freestuff/CRC32.java - [2] http://www.gamedev.net/reference/articles/article1941.asp - [3] http://java.sun.com/j2se/1.5.0/docs/api/java/util/zip/CRC32.html - [4] http://www.dsource.org/projects/tango/docs/current/tango.io.digest.Crc32.html - [5] http://pydoc.org/1.5.2/zlib.html#-crc32 - [6] http://www.python.org/doc/2.5.2/lib/module-binascii.html - -LICENSE - - (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - (end license) - ---]] - - -local M = {_TYPE='module', _NAME='crc32lua', _VERSION='0.3.20111128'} - -local type = type -local require = require -local setmetatable = setmetatable - ---[[ - Requires the first module listed that exists, else raises like `require`. - If a non-string is encountered, it is returned. - Second return value is module name loaded (or ''). - --]] -local function requireany(...) - local errs = {} - for _,name in ipairs{...} do - if type(name) ~= 'string' then return name, '' end - local ok, mod = pcall(require, name) - if ok then return mod, name end - errs[#errs+1] = mod - end - error(table.concat(errs, '\n'), 2) -end - -local bit, name_ = requireany('bit', 'bit32', 'bit.numberlua') -local bxor = bit.bxor -local bnot = bit.bnot -local band = bit.band -local rshift = bit.rshift - --- CRC-32-IEEE 802.3 (V.42) -local POLY = 0xEDB88320 - --- Memoize function pattern (like http://lua-users.org/wiki/FuncTables ). -local function memoize(f) - local mt = {} - local t = setmetatable({}, mt) - function mt:__index(k) - local v = f(k); t[k] = v - return v - end - return t -end - --- CRC table. -local crc_table = memoize(function(i) - local crc = i - for _=1,8 do - local b = band(crc, 1) - crc = rshift(crc, 1) - if b == 1 then crc = bxor(crc, POLY) end - end - return crc -end) - - -function M.crc32_byte(byte, crc) - crc = bnot(crc or 0) - local v1 = rshift(crc, 8) - local v2 = crc_table[bxor(crc % 256, byte)] - return bnot(bxor(v1, v2)) -end -local M_crc32_byte = M.crc32_byte - - -function M.crc32_string(s, crc) - crc = crc or 0 - for i=1,#s do - crc = M_crc32_byte(s:byte(i), crc) - end - return crc -end -local M_crc32_string = M.crc32_string - - -function M.crc32(s, crc) - if type(s) == 'string' then - return M_crc32_string(s, crc) - else - return M_crc32_byte(s, crc) - end -end - - -M.bit = bit -- bit library used - - -return M - -Flib/deflatelua.lua?K--[[ - -LUA MODULE - - deflatelua - inflate (and gunzip/zlib) implemented in Lua. - -DESCRIPTION - - This is a pure Lua implementation of decompressing the DEFLATE format, - including the related zlib and gzip formats. - - Note: This library only supports decompression. - Compression is not currently implemented. - -REFERENCES - - [1] DEFLATE Compressed Data Format Specification version 1.3 - http://tools.ietf.org/html/rfc1951 - [2] GZIP file format specification version 4.3 - http://tools.ietf.org/html/rfc1952 - [3] http://en.wikipedia.org/wiki/DEFLATE - [4] pyflate, by Paul Sladen - http://www.paul.sladen.org/projects/pyflate/ - [5] Compress::Zlib::Perl - partial pure Perl implementation of - Compress::Zlib - http://search.cpan.org/~nwclark/Compress-Zlib-Perl/Perl.pm - -LICENSE - - (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - (end license) - - - - Modified by TehSomeLuigi under the above license. - ---]] - -local M = {_TYPE='module', _NAME='deflatelua', _VERSION='0.3.20111128'} - -local assert = assert -local error = error -local ipairs = ipairs -local pairs = pairs -local print = print -local require = require -local tostring = tostring -local type = type -local setmetatable = setmetatable -local io = io -local math = math -local table_sort = table.sort -local math_max = math.max -local string_char = string.char - -local DEBUG = false - - -local band, lshift, rshift -band = bit32.band -lshift = bit32.lshift -rshift = bit32.rshift - - -local function warn(s) - io.stderr:write(s, '\n') -end - - -local function debug(...) - print('DEBUG', ...) -end - - -local function runtime_error(s, level) - level = level or 1 - error(s, level+1) -end - -local function make_outstate(outbs) - local outstate = {} - outstate.outbs = outbs - outstate.window = {} - outstate.window_pos = 1 - return outstate -end - -local function output(outstate, byte) - -- debug('OUTPUT:', s) - local window_pos = outstate.window_pos - outstate.outbs(byte) - outstate.window[window_pos] = byte - outstate.window_pos = window_pos % 32768 + 1 -- 32K -end - -local function noeof(val, ctx) - return assert(val, 'unexpected end of file with context ' .. tostring(ctx)) -end - -local function hasbit(bits, bit) - return bits % (bit + bit) >= bit -end - -local function memoize(f) - local mt = {} - local t = setmetatable({}, mt) - function mt:__index(k) - local v = f(k) - t[k] = v - return v - end - return t -end - --- small optimization (lookup table for powers of 2) -local pow2 = memoize(function(n) return 2^n end) - ---local tbits = memoize( --- function(bits) --- return memoize( function(bit) return getbit(bits, bit) end ) --- end ) - - --- weak metatable marking objects as bitstream type -local is_bitstream = setmetatable({}, {__mode='k'}) - -local function bytestream_from_file(fh) - local o = {} - function o:read() - local sb = fh:read(1) - if sb then return sb:byte() end - end - return o -end - -local function bytestream_from_string(s) - local i = 1 - local o = {s=s} - function o:read() - local by - if i <= #self.s then - by = self.s:byte(i) - i = i + 1 - end - return by - end - return o -end - -M.stringToBytestream = bytestream_from_string - -local function bytestream_from_function(f) - local i = 0 - local buffer = '' - local o = {} - function o:read() - i = i + 1 - if i > #buffer then - buffer = f() - if not buffer then return end - i = 1 - end - return buffer:byte(i,i) - end - return o -end - -local function bitstream_from_bytestream(bys) - local buf_byte = 0 - local buf_nbit = 0 - local o = {} - - function o:nbits_left_in_byte() - return buf_nbit - end - - function o:read(nbits) - nbits = nbits or 1 - while buf_nbit < nbits do - local byte = bys:read() - if not byte then return end -- note: more calls also return nil - buf_byte = buf_byte + lshift(byte, buf_nbit) - buf_nbit = buf_nbit + 8 - end - local bits - if nbits == 0 then - bits = 0 - elseif nbits == 32 then - bits = buf_byte - buf_byte = 0 - else - bits = band(buf_byte, rshift(0xffffffff, 32 - nbits)) - buf_byte = rshift(buf_byte, nbits) - end - - if nbits == 16 then - -- bugfix: swap bytes - bits = rshift(band(bits, 0xFF00), 8) + lshift(band(bits, 0xFF), 8) - end - - buf_nbit = buf_nbit - nbits - return bits - end - - is_bitstream[o] = true - - return o -end - - -function M.adler32(byte, crc) - local s1 = crc % 65536 - local s2 = (crc - s1) / 65536 - s1 = (s1 + byte) % 65521 - s2 = (s2 + s1) % 65521 - return s2*65536 + s1 -end - -local function get_bitstream(o) - local bs - if is_bitstream[o] then - return o - elseif io.type(o) == 'file' then - bs = bitstream_from_bytestream(bytestream_from_file(o)) - elseif type(o) == 'string' then - bs = bitstream_from_bytestream(bytestream_from_string(o)) - elseif type(o) == 'function' then - bs = bitstream_from_bytestream(bytestream_from_function(o)) - else - runtime_error 'unrecognized type' - end - return bs -end - - -local function get_obytestream(o) - local bs - if io.type(o) == 'file' then - bs = function(sbyte) o:write(string_char(sbyte)) end - elseif type(o) == 'function' then - bs = o - elseif type(o) == 'table' then - -- assume __callable table - bs = o - else - runtime_error('unrecognized type: ' .. tostring(o)) - end - return bs -end - - -local function HuffmanTable(init, is_full) - local t = {} - if is_full then - for val,nbits in pairs(init) do - if nbits ~= 0 then - t[#t+1] = {val=val, nbits=nbits} - --debug('*',val,nbits) - end - end - else - for i=1,#init-2,2 do - local firstval, nbits, nextval = init[i], init[i+1], init[i+2] - --debug(val, nextval, nbits) - if nbits ~= 0 then - for val=firstval,nextval-1 do - t[#t+1] = {val=val, nbits=nbits} - end - end - end - end - table_sort(t, function(a,b) - return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits - end) - - -- assign codes - local code = 1 -- leading 1 marker - local nbits = 0 - for i,s in ipairs(t) do - if s.nbits ~= nbits then - code = code * pow2[s.nbits - nbits] - nbits = s.nbits - end - s.code = code - --debug('huffman code:', i, s.nbits, s.val, code, bits_tostring(code)) - code = code + 1 - end - - local minbits = math.huge - local look = {} - for i,s in ipairs(t) do - minbits = math.min(minbits, s.nbits) - look[s.code] = s.val - end - - --for _,o in ipairs(t) do - -- debug(':', o.nbits, o.val) - --end - - -- function t:lookup(bits) return look[bits] end - - local msb = function(bits, nbits) - local res = 0 - for i=1,nbits do - res = lshift(res, 1) + band(bits, 1) - bits = rshift(bits, 1) - end - return res - end - - local tfirstcode = memoize(function(bits) - return pow2[minbits] + msb(bits, minbits) - end) - - function t:read(bs) - local code = 1 -- leading 1 marker - local nbits = 0 - while 1 do - if nbits == 0 then -- small optimization (optional) - code = tfirstcode[noeof(bs:read(minbits), 1)] - nbits = nbits + minbits - else - local b = noeof(bs:read(), 2) - nbits = nbits + 1 - code = lshift(code, 1) + b -- MSB first - end - --debug('code?', code, bits_tostring(code)) - local val = look[code] - if val then - --debug('FOUND', val) - return val - end - end - end - - return t -end - - -local function parse_gzip_header(bs) - -- local FLG_FTEXT = 2^0 - local FLG_FHCRC = 2^1 - local FLG_FEXTRA = 2^2 - local FLG_FNAME = 2^3 - local FLG_FCOMMENT = 2^4 - - local id1 = bs:read(8) - local id2 = bs:read(8) - if id1 ~= 31 or id2 ~= 139 then - runtime_error 'not in gzip format' - end - local cm = bs:read(8) -- compression method - local flg = bs:read(8) -- FLaGs - local mtime = bs:read(32) -- Modification TIME - local xfl = bs:read(8) -- eXtra FLags - local os = bs:read(8) -- Operating System - - if DEBUG then - debug("CM=", cm) - debug("FLG=", flg) - debug("MTIME=", mtime) - -- debug("MTIME_str=",os.date("%Y-%m-%d %H:%M:%S",mtime)) -- non-portable - debug("XFL=", xfl) - debug("OS=", os) - end - - if not os then runtime_error 'invalid header' end - - if hasbit(flg, FLG_FEXTRA) then - local xlen = bs:read(16) - local extra = 0 - for i=1,xlen do - extra = bs:read(8) - end - if not extra then runtime_error 'invalid header' end - end - - local function parse_zstring(bs) - repeat - local by = bs:read(8) - if not by then runtime_error 'invalid header' end - until by == 0 - end - - if hasbit(flg, FLG_FNAME) then - parse_zstring(bs) - end - - if hasbit(flg, FLG_FCOMMENT) then - parse_zstring(bs) - end - - if hasbit(flg, FLG_FHCRC) then - local crc16 = bs:read(16) - if not crc16 then runtime_error 'invalid header' end - -- IMPROVE: check CRC. where is an example .gz file that - -- has this set? - if DEBUG then - debug("CRC16=", crc16) - end - end -end - -local function parse_zlib_header(bs) - local cm = bs:read(4) -- Compression Method - local cinfo = bs:read(4) -- Compression info - local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG) - local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary) - local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level) - local cmf = cinfo * 16 + cm -- CMF (Compresion Method and flags) - local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs - - if cm ~= 8 then -- not "deflate" - runtime_error("unrecognized zlib compression method: " + cm) - end - if cinfo > 7 then - runtime_error("invalid zlib window size: cinfo=" + cinfo) - end - local window_size = 2^(cinfo + 8) - - if (cmf*256 + flg) % 31 ~= 0 then - runtime_error("invalid zlib header (bad fcheck sum) - overflow by " .. ((cmf*256 + flg) % 31)) - end - - if fdict == 1 then - runtime_error("FIX:TODO - FDICT not currently implemented") - local dictid_ = bs:read(32) - end - - return window_size -end - -local function parse_huffmantables(bs) - local hlit = bs:read(5) -- # of literal/length codes - 257 - local hdist = bs:read(5) -- # of distance codes - 1 - local hclen = noeof(bs:read(4), 3) -- # of code length codes - 4 - - local ncodelen_codes = hclen + 4 - local codelen_init = {} - local codelen_vals = { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} - for i=1,ncodelen_codes do - local nbits = bs:read(3) - local val = codelen_vals[i] - codelen_init[val] = nbits - end - local codelentable = HuffmanTable(codelen_init, true) - - local function decode(ncodes) - local init = {} - local nbits - local val = 0 - while val < ncodes do - local codelen = codelentable:read(bs) - --FIX:check nil? - local nrepeat - if codelen <= 15 then - nrepeat = 1 - nbits = codelen - --debug('w', nbits) - elseif codelen == 16 then - nrepeat = 3 + noeof(bs:read(2), 4) - -- nbits unchanged - elseif codelen == 17 then - nrepeat = 3 + noeof(bs:read(3), 5) - nbits = 0 - elseif codelen == 18 then - nrepeat = 11 + noeof(bs:read(7), 6) - nbits = 0 - else - error 'ASSERT' - end - for i=1,nrepeat do - init[val] = nbits - val = val + 1 - end - end - local huffmantable = HuffmanTable(init, true) - return huffmantable - end - - local nlit_codes = hlit + 257 - local ndist_codes = hdist + 1 - - local littable = decode(nlit_codes) - local disttable = decode(ndist_codes) - - return littable, disttable -end - - -local tdecode_len_base -local tdecode_len_nextrabits -local tdecode_dist_base -local tdecode_dist_nextrabits -local function parse_compressed_item(bs, outstate, littable, disttable) - local val = littable:read(bs) - --debug(val, val < 256 and string_char(val)) - if val < 256 then -- literal - output(outstate, val) - elseif val == 256 then -- end of block - return true - else - if not tdecode_len_base then - local t = {[257]=3} - local skip = 1 - for i=258,285,4 do - for j=i,i+3 do t[j] = t[j-1] + skip end - if i ~= 258 then skip = skip * 2 end - end - t[285] = 258 - tdecode_len_base = t - --for i=257,285 do debug('T1',i,t[i]) end - end - if not tdecode_len_nextrabits then - local t = {} - for i=257,285 do - local j = math_max(i - 261, 0) - t[i] = rshift(j, 2) - end - t[285] = 0 - tdecode_len_nextrabits = t - --for i=257,285 do debug('T2',i,t[i]) end - end - local len_base = tdecode_len_base[val] - local nextrabits = tdecode_len_nextrabits[val] - local extrabits = bs:read(nextrabits) - local len = len_base + extrabits - - if not tdecode_dist_base then - local t = {[0]=1} - local skip = 1 - for i=1,29,2 do - for j=i,i+1 do t[j] = t[j-1] + skip end - if i ~= 1 then skip = skip * 2 end - end - tdecode_dist_base = t - --for i=0,29 do debug('T3',i,t[i]) end - end - if not tdecode_dist_nextrabits then - local t = {} - for i=0,29 do - local j = math_max(i - 2, 0) - t[i] = rshift(j, 1) - end - tdecode_dist_nextrabits = t - --for i=0,29 do debug('T4',i,t[i]) end - end - local dist_val = disttable:read(bs) - local dist_base = tdecode_dist_base[dist_val] - local dist_nextrabits = tdecode_dist_nextrabits[dist_val] - local dist_extrabits = bs:read(dist_nextrabits) - local dist = dist_base + dist_extrabits - - --debug('BACK', len, dist) - for i=1,len do - local pos = (outstate.window_pos - 1 - dist) % 32768 + 1 -- 32K - output(outstate, assert(outstate.window[pos], 'invalid distance')) - end - end - return false -end - - -local function parse_block(bs, outstate) - local bfinal = bs:read(1) - local btype = bs:read(2) - - local BTYPE_NO_COMPRESSION = 0 - local BTYPE_FIXED_HUFFMAN = 1 - local BTYPE_DYNAMIC_HUFFMAN = 2 - local BTYPE_RESERVED_ = 3 - - if DEBUG then - debug('bfinal=', bfinal) - debug('btype=', btype) - end - - if btype == BTYPE_NO_COMPRESSION then - bs:read(bs:nbits_left_in_byte()) - local len = bs:read(16) - local nlen_ = noeof(bs:read(16), 7) - - for i=1,len do - local by = noeof(bs:read(8), 8) - output(outstate, by) - end - elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then - local littable, disttable - if btype == BTYPE_DYNAMIC_HUFFMAN then - littable, disttable = parse_huffmantables(bs) - else - littable = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil} - disttable = HuffmanTable {0,5, 32,nil} - end - - repeat - local is_done = parse_compressed_item( - bs, outstate, littable, disttable) - until is_done - else - runtime_error 'unrecognized compression type' - end - - return bfinal ~= 0 -end - - -function M.inflate(t) - local bs = get_bitstream(t.input) - local outbs = get_obytestream(t.output) - local outstate = make_outstate(outbs) - - repeat - local is_final = parse_block(bs, outstate) - until is_final -end -local inflate = M.inflate - - -function M.gunzip(t) - local bs = get_bitstream(t.input) - local outbs = get_obytestream(t.output) - - parse_gzip_header(bs) - - local data_crc32 = 0 - - inflate{input=bs, output=outbs} - - bs:read(bs:nbits_left_in_byte()) - - local expected_crc32 = bs:read(32) - local isize = bs:read(32) -- ignored - --[[ - if DEBUG then - debug('crc32=', expected_crc32) - debug('isize=', isize) - end - ]]-- - if bs:read() then - warn 'trailing garbage ignored' - end -end - - -function M.inflate_zlib(t) - local bs = get_bitstream(t.input) - local outbs = get_obytestream(t.output) - - local window_size_ = parse_zlib_header(bs) - - local data_adler32 = 1 - - inflate{input=bs, output=outbs} - - bs:read(bs:nbits_left_in_byte()) - - - local b3 = bs:read(8) - local b2 = bs:read(8) - local b1 = bs:read(8) - local b0 = bs:read(8) - --[[ - local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0 - if DEBUG then - debug('alder32=', expected_adler32) - end - if not disable_crc then - if data_adler32 ~= expected_adler32 then - runtime_error('invalid compressed data--crc error') - end - end - ]]-- - if bs:read() then - warn 'trailing garbage ignored' - end -end - - -return M - -F lib/devfs.luaplocal fs = require("filesystem") - -local proxy = {points={},address=require("guid").next()} - -local nop = function()end - -function proxy.getLabel() - return "devfs" -end - -function proxy.setLabel(value) - error("drive does not support labeling") -end - -function proxy.spaceTotal() - return 0 -end - -function proxy.spaceUsed() - return 0 -end - -function proxy.exists(path) - return not not proxy.points[path] -end - -function proxy.size(path) - return 0 -end - -function proxy.isDirectory(path) - return false -end - -function proxy.lastModified(path) - return fs.lastModified("/dev/") -end - -function proxy.list() - local keys = {} - for k,v in pairs(proxy.points) do - table.insert(keys, k) - end - return keys -end - -function proxy.makeDirectory(path) - return false -end - -function proxy.remove(path) - if not proxy.exists(path) then return false end - proxy.points[path] = nil - return true -end - -function proxy.rename(from, to) - return false -end - -proxy.close = nop - -function proxy.open(path, mode) - checkArg(1, path, "string") - - local handle = proxy.points[path] - if not handle then return nil, "device point [" .. path .. "] does not exist" end - - local msg = "device point [" .. path .. "] cannot be opened for " - - if mode == "r" then - if not handle.read then - return nil, msg .. "read" - end - else - if not handle.write then - return nil, msg .. "write" - end - end - - return handle -end - -function proxy.read(h,...) - return h:read(...) -end - -function proxy.seek(h,...) - return h:seek(...) -end - -function proxy.write(h,...) - return h:write(...) -end - -function proxy.create(path, handle) - handle.close = handle.close or nop - proxy.points[path] = handle - return true -end - -proxy.create("null", {write = nop}) -proxy.create("random", {read = function(_,n) - local chars = {} - for i=1,n do - table.insert(chars,string.char(math.random(0,255))) - end - return table.concat(chars) -end}) - -return proxy -Flib/doubleBuffering.luaiZ -------------------------------------------------- Libraries ------------------------------------------------- - -local component = require("component") -local unicode = require("unicode") -local colorlib = require("colorlib") -local image = require("image") - -------------------------------------------------- Constants ------------------------------------------------- - - -local gpu = component.gpu -local buffer = {} - -------------------------------------------------- Core methods ------------------------------------------------- - ---Формула конвертации индекса массива изображения в абсолютные координаты пикселя изображения -function buffer.getBufferCoordinatesByIndex(index) - local integer, fractional = math.modf(index / (buffer.screen.tripleWidth)) - return math.ceil(fractional * buffer.screen.width), integer + 1 -end - ---Формула конвертации абсолютных координат пикселя изображения в индекс для массива изображения -function buffer.getBufferIndexByCoordinates(x, y) - return buffer.screen.tripleWidth * (y - 1) + x * 3 - 2 -end - --- Установить ограниченную зону рисования. Все пиксели, не попадающие в эту зону, будут игнорироваться. -function buffer.setDrawLimit(xOrPasteArray, y, width, height) - if type(xOrPasteArray) == "table" then - buffer.drawLimit.x, buffer.drawLimit.y, buffer.drawLimit.x2, buffer.drawLimit.y2, buffer.drawLimit.width, buffer.drawLimit.height = xOrPasteArray.x, xOrPasteArray.y, xOrPasteArray.x2, xOrPasteArray.y2, xOrPasteArray.width, xOrPasteArray.height - else - buffer.drawLimit.x, buffer.drawLimit.y, buffer.drawLimit.x2, buffer.drawLimit.y2, buffer.drawLimit.width, buffer.drawLimit.height = xOrPasteArray, y, xOrPasteArray + width - 1, y + height - 1, width, height - end -end - --- Удалить ограничение зоны рисования, по умолчанию она будет от 1х1 до координат размера экрана. -function buffer.resetDrawLimit() - buffer.drawLimit.x, buffer.drawLimit.y, buffer.drawLimit.x2, buffer.drawLimit.y2, buffer.drawLimit.width, buffer.drawLimit.height = 1, 1, buffer.screen.width, buffer.screen.height, buffer.screen.width, buffer.screen.height -end - --- Cкопировать ограничение зоны рисования в виде отдельного массива -function buffer.getDrawLimit() - return { x = buffer.drawLimit.x, y = buffer.drawLimit.y, x2 = buffer.drawLimit.x2, y2 = buffer.drawLimit.y2, width = buffer.drawLimit.width, height = buffer.drawLimit.height } -end - --- Создание массивов буфера и всех необходимых параметров -function buffer.flush(width, height) - buffer.screen = { - current = {}, - new = {}, - width = width, - height = height, - tripleWidth = width * 3, - } - buffer.drawLimit = {} - buffer.resetDrawLimit() - - for y = 1, buffer.screen.height do - for x = 1, buffer.screen.width do - table.insert(buffer.screen.current, 0x010101) - table.insert(buffer.screen.current, 0xFEFEFE) - table.insert(buffer.screen.current, " ") - - table.insert(buffer.screen.new, 0x010101) - table.insert(buffer.screen.new, 0xFEFEFE) - table.insert(buffer.screen.new, " ") - end - end -end - --- Инициализация буфера со всеми необходимыми параметрами, вызывается автоматически -function buffer.start() - buffer.flush(gpu.getResolution()) -end - --- Изменение разрешения экрана и пересоздание массивов буфера -function buffer.changeResolution(width, height) - gpu.setResolution(width, height) - buffer.flush(width, height) -end - -------------------------------------------------- Методы отрисовки ----------------------------------------------------------------- - -function buffer.rawSet(index, background, foreground, symbol) - buffer.screen.new[index], buffer.screen.new[index + 1], buffer.screen.new[index + 2] = background, foreground, symbol -end - -function buffer.rawGet(index) - return buffer.screen.new[index], buffer.screen.new[index + 1], buffer.screen.new[index + 2] -end - --- Получить информацию о пикселе из буфера -function buffer.get(x, y) - local index = buffer.getBufferIndexByCoordinates(x, y) - if x >= 1 and y >= 1 and x <= buffer.screen.width and y <= buffer.screen.height then - return buffer.rawGet(index) - else - return 0x000000, 0x000000, " " - end -end - --- Установить пиксель в буфере -function buffer.set(x, y, background, foreground, symbol) - local index = buffer.getBufferIndexByCoordinates(x, y) - if x >= buffer.drawLimit.x and y >= buffer.drawLimit.y and x <= buffer.drawLimit.x2 and y <= buffer.drawLimit.y2 then - buffer.rawSet(index, background, foreground or 0x0, symbol or " ") - end -end - ---Нарисовать квадрат -function buffer.square(x, y, width, height, background, foreground, symbol, transparency) - if transparency then - if transparency == 0 then - transparency = nil - else - transparency = transparency * 2.55 - end - end - if not foreground then foreground = 0x000000 end - if not symbol then symbol = " " end - - local index, indexStepForward, indexPlus1 = buffer.getBufferIndexByCoordinates(x, y), (buffer.screen.width - width) * 3 - for j = y, (y + height - 1) do - for i = x, (x + width - 1) do - if i >= buffer.drawLimit.x and j >= buffer.drawLimit.y and i <= buffer.drawLimit.x2 and j <= buffer.drawLimit.y2 then - indexPlus1 = index + 1 - if transparency then - buffer.screen.new[index] = colorlib.alphaBlend(buffer.screen.new[index], background, transparency) - buffer.screen.new[indexPlus1] = colorlib.alphaBlend(buffer.screen.new[indexPlus1], background, transparency) - else - buffer.screen.new[index] = background - buffer.screen.new[indexPlus1] = foreground - buffer.screen.new[index + 2] = symbol - end - end - index = index + 3 - end - index = index + indexStepForward - end -end -buffer.rectangle = buffer.square - ---Очистка экрана, по сути более короткая запись buffer.square -function buffer.clear(color, transparency) - buffer.square(1, 1, buffer.screen.width, buffer.screen.height, color or 0x262626, 0x000000, " ", transparency) -end - ---Скопировать область изображения и вернуть ее в виде массива -function buffer.copy(x, y, width, height) - local copyArray = { - width = width, - height = height, - } - - if x < 1 or y < 1 or x + width - 1 > buffer.screen.width or y + height - 1 > buffer.screen.height then - error("Copy field is out of screen range") - end - - local index - for j = y, (y + height - 1) do - for i = x, (x + width - 1) do - index = buffer.getBufferIndexByCoordinates(i, j) - table.insert(copyArray, buffer.screen.new[index]) - table.insert(copyArray, buffer.screen.new[index + 1]) - table.insert(copyArray, buffer.screen.new[index + 2]) - end - end - - return copyArray -end - ---Вставить скопированную ранее область изображения -function buffer.paste(x, y, copyArray) - local index, arrayIndex - if not copyArray or #copyArray == 0 then error("Массив области экрана пуст.") end - - for j = y, (y + copyArray.height - 1) do - for i = x, (x + copyArray.width - 1) do - if i >= buffer.drawLimit.x and j >= buffer.drawLimit.y and i <= buffer.drawLimit.x2 and j <= buffer.drawLimit.y2 then - --Рассчитываем индекс массива основного изображения - index = buffer.getBufferIndexByCoordinates(i, j) - --Копипаст формулы, аккуратнее! - --Рассчитываем индекс массива вставочного изображения - arrayIndex = (copyArray.width * (j - y) + (i - x + 1)) * 3 - 2 - --Вставляем данные - buffer.screen.new[index] = copyArray[arrayIndex] - buffer.screen.new[index + 1] = copyArray[arrayIndex + 1] - buffer.screen.new[index + 2] = copyArray[arrayIndex + 2] - end - end - end -end - ---Нарисовать линию, алгоритм спизжен с вики -function buffer.line(x1, y1, x2, y2, background, foreground, symbol) - local deltaX = math.abs(x2 - x1) - local deltaY = math.abs(y2 - y1) - local signX = (x1 < x2) and 1 or -1 - local signY = (y1 < y2) and 1 or -1 - - local errorCyka = deltaX - deltaY - local errorCyka2 - - buffer.set(x2, y2, background, foreground, symbol) - - while(x1 ~= x2 or y1 ~= y2) do - buffer.set(x1, y1, background, foreground, symbol) - - errorCyka2 = errorCyka * 2 - - if (errorCyka2 > -deltaY) then - errorCyka = errorCyka - deltaY - x1 = x1 + signX - end - - if (errorCyka2 < deltaX) then - errorCyka = errorCyka + deltaX - y1 = y1 + signY - end - end -end - --- Отрисовка текста, подстраивающегося под текущий фон -function buffer.text(x, y, color, text, transparency) - if transparency then - if transparency == 0 then - transparency = nil - else - transparency = transparency * 2.55 - end - end - - local index, sText = buffer.getBufferIndexByCoordinates(x, y), unicode.len(text) - for i = 1, sText do - if x >= buffer.drawLimit.x and y >= buffer.drawLimit.y and x <= buffer.drawLimit.x2 and y <= buffer.drawLimit.y2 then - buffer.screen.new[index + 1] = not transparency and color or colorlib.alphaBlend(buffer.screen.new[index], color, transparency) - buffer.screen.new[index + 2] = unicode.sub(text, i, i) - end - index = index + 3 - x = x + 1 - end -end - --- Отрисовка изображения -function buffer.image(x, y, picture) - local xPos, xEnd, bufferIndexStepOnReachOfImageWidth = x, x + picture.width - 1, (buffer.screen.width - picture.width) * 3 - local bufferIndex = buffer.getBufferIndexByCoordinates(x, y) - local imageIndexPlus2, imageIndexPlus3 - - for imageIndex = 1, #picture, 4 do - if xPos >= buffer.drawLimit.x and y >= buffer.drawLimit.y and xPos <= buffer.drawLimit.x2 and y <= buffer.drawLimit.y2 then - imageIndexPlus2, imageIndexPlus3 = imageIndex + 2, imageIndex + 3 - -- Ебля с прозрачностью - if picture[imageIndexPlus2] == 0x00 then - buffer.screen.new[bufferIndex] = picture[imageIndex] - buffer.screen.new[bufferIndex + 1] = picture[imageIndex + 1] - buffer.screen.new[bufferIndex + 2] = picture[imageIndexPlus3] - elseif picture[imageIndexPlus2] > 0x00 and picture[imageIndexPlus2] < 0xFF then - buffer.screen.new[bufferIndex] = colorlib.alphaBlend(buffer.screen.new[bufferIndex], picture[imageIndex], picture[imageIndexPlus2]) - buffer.screen.new[bufferIndex + 1] = picture[imageIndex + 1] - buffer.screen.new[bufferIndex + 2] = picture[imageIndexPlus3] - elseif picture[imageIndexPlus2] == 0xFF and picture[imageIndexPlus3] ~= " " then - buffer.screen.new[bufferIndex + 1] = picture[imageIndex + 1] - buffer.screen.new[bufferIndex + 2] = picture[imageIndexPlus3] - end - end - - --Корректируем координаты и индексы - xPos = xPos + 1 - bufferIndex = bufferIndex + 3 - if xPos > xEnd then xPos, y, bufferIndex = x, y + 1, bufferIndex + bufferIndexStepOnReachOfImageWidth end - end -end - --- Кнопка фиксированных размеров -function buffer.button(x, y, width, height, background, foreground, text) - local textLength = unicode.len(text) - if textLength > width - 2 then text = unicode.sub(text, 1, width - 2) end - - local textPosX = math.floor(x + width / 2 - textLength / 2) - local textPosY = math.floor(y + height / 2) - buffer.square(x, y, width, height, background, foreground, " ") - buffer.text(textPosX, textPosY, foreground, text) - - return x, y, (x + width - 1), (y + height - 1) -end - --- Кнопка, подстраивающаяся под длину текста -function buffer.adaptiveButton(x, y, xOffset, yOffset, background, foreground, text) - local width = xOffset * 2 + unicode.len(text) - local height = yOffset * 2 + 1 - - buffer.square(x, y, width, height, background, 0xFFFFFF, " ") - buffer.text(x + xOffset, y + yOffset, foreground, text) - - return x, y, (x + width - 1), (y + height - 1) -end - --- Вертикальный скролл-бар -function buffer.scrollBar(x, y, width, height, countOfAllElements, currentElement, backColor, frontColor) - local sizeOfScrollBar = math.ceil(height / countOfAllElements) - local displayBarFrom = math.floor(y + height * ((currentElement - 1) / countOfAllElements)) - - buffer.square(x, y, width, height, backColor, 0xFFFFFF, " ") - buffer.square(x, displayBarFrom, width, sizeOfScrollBar, frontColor, 0xFFFFFF, " ") - - sizeOfScrollBar, displayBarFrom = nil, nil -end - -function buffer.horizontalScrollBar(x, y, width, countOfAllElements, currentElement, background, foreground) - local pipeSize = math.ceil(width / countOfAllElements) - local displayBarFrom = math.floor(x + width * ((currentElement - 1) / countOfAllElements)) - - buffer.text(x, y, background, string.rep("▄", width)) - buffer.text(displayBarFrom, y, foreground, string.rep("▄", pipeSize)) -end - --- Отрисовка любого изображения в виде трехмерного массива. Неоптимизированно, зато просто. -function buffer.customImage(x, y, pixels) - x = x - 1 - y = y - 1 - - for i=1, #pixels do - for j=1, #pixels[1] do - if pixels[i][j][3] ~= "#" then - buffer.set(x + j, y + i, pixels[i][j][1], pixels[i][j][2], pixels[i][j][3]) - end - end - end - - return (x + 1), (y + 1), (x + #pixels[1]), (y + #pixels) -end - ---Нарисовать топ-меню, горизонтальная полоска такая с текстами -function buffer.menu(x, y, width, color, selectedObject, ...) - local objects = { ... } - local objectsToReturn = {} - local xPos = x + 2 - local spaceBetween = 2 - buffer.square(x, y, width, 1, color, 0xFFFFFF, " ") - for i = 1, #objects do - if i == selectedObject then - buffer.square(xPos - 1, y, unicode.len(objects[i][1]) + spaceBetween, 1, 0x3366CC, 0xFFFFFF, " ") - buffer.text(xPos, y, 0xFFFFFF, objects[i][1]) - else - buffer.text(xPos, y, objects[i][2], objects[i][1]) - end - objectsToReturn[objects[i][1]] = { xPos, y, xPos + unicode.len(objects[i][1]) - 1, y, i } - xPos = xPos + unicode.len(objects[i][1]) + spaceBetween - end - return objectsToReturn -end - --- Прамоугольная рамочка -function buffer.frame(x, y, width, height, color) - local stringUp, stringDown, x2 = "┌" .. string.rep("─", width - 2) .. "┐", "└" .. string.rep("─", width - 2) .. "┘", x + width - 1 - buffer.text(x, y, color, stringUp); y = y + 1 - for i = 1, (height - 2) do - buffer.text(x, y, color, "│") - buffer.text(x2, y, color, "│") - y = y + 1 - end - buffer.text(x, y, color, stringDown) -end - --- Кнопка в виде текста в рамке -function buffer.framedButton(x, y, width, height, backColor, buttonColor, text) - buffer.square(x, y, width, height, backColor, buttonColor, " ") - buffer.frame(x, y, width, height, buttonColor) - - x = math.floor(x + width / 2 - unicode.len(text) / 2) - y = math.floor(y + height / 2) - - buffer.text(x, y, buttonColor, text) -end - -------------------------------------------- Semipixel methods ------------------------------------------------------------------------ - -function buffer.semiPixelRawSet(index, color, yPercentTwoEqualsZero) - local upperPixel, lowerPixel, bothPixel, indexPlus1, indexPlus2 = "▀", "▄", " ", index + 1, index + 2 - local background, foreground, symbol = buffer.screen.new[index], buffer.screen.new[indexPlus1], buffer.screen.new[indexPlus2] - - if yPercentTwoEqualsZero then - if symbol == upperPixel then - if color == foreground then - buffer.screen.new[index], buffer.screen.new[indexPlus1], buffer.screen.new[indexPlus2] = color, foreground, bothPixel - else - buffer.screen.new[index], buffer.screen.new[indexPlus1], buffer.screen.new[indexPlus2] = color, foreground, symbol - end - elseif symbol == bothPixel then - if color ~= background then - buffer.screen.new[index], buffer.screen.new[indexPlus1], buffer.screen.new[indexPlus2] = background, color, lowerPixel - end - else - buffer.screen.new[index], buffer.screen.new[indexPlus1], buffer.screen.new[indexPlus2] = background, color, lowerPixel - end - else - if symbol == lowerPixel then - if color == foreground then - buffer.screen.new[index], buffer.screen.new[indexPlus1], buffer.screen.new[indexPlus2] = color, foreground, bothPixel - else - buffer.screen.new[index], buffer.screen.new[indexPlus1], buffer.screen.new[indexPlus2] = color, foreground, symbol - end - elseif symbol == bothPixel then - if color ~= background then - buffer.screen.new[index], buffer.screen.new[indexPlus1], buffer.screen.new[indexPlus2] = background, color, upperPixel - end - else - buffer.screen.new[index], buffer.screen.new[indexPlus1], buffer.screen.new[indexPlus2] = background, color, upperPixel - end - end -end - -function buffer.semiPixelSet(x, y, color) - local yFixed = math.ceil(y / 2) - if x >= buffer.drawLimit.x and yFixed >= buffer.drawLimit.y and x <= buffer.drawLimit.x2 and yFixed <= buffer.drawLimit.y2 then - buffer.semiPixelRawSet(buffer.getBufferIndexByCoordinates(x, yFixed), color, y % 2 == 0) - end -end - -function buffer.semiPixelSquare(x, y, width, height, color) - -- for j = y, y + height - 1 do for i = x, x + width - 1 do buffer.semiPixelSet(i, j, color) end end - local index, indexStepForward, indexStepBackward, jPercentTwoEqualsZero, jFixed = buffer.getBufferIndexByCoordinates(x, math.ceil(y / 2)), (buffer.screen.width - width) * 3, width * 3 - for j = y, y + height - 1 do - jPercentTwoEqualsZero = j % 2 == 0 - - for i = x, x + width - 1 do - jFixed = math.ceil(j / 2) - -- if x >= buffer.drawLimit.x and jFixed >= buffer.drawLimit.y and x <= buffer.drawLimit.x2 and jFixed <= buffer.drawLimit.y2 then - buffer.semiPixelRawSet(index, color, jPercentTwoEqualsZero) - -- end - index = index + 3 - end - - if jPercentTwoEqualsZero then - index = index + indexStepForward - else - index = index - indexStepBackward - end - end -end - -function buffer.semiPixelLine(x1, y1, x2, y2, color) - local incycleValueFrom, incycleValueTo, outcycleValueFrom, outcycleValueTo, isReversed, incycleValueDelta, outcycleValueDelta = x1, x2, y1, y2, false, math.abs(x2 - x1), math.abs(y2 - y1) - if incycleValueDelta < outcycleValueDelta then - incycleValueFrom, incycleValueTo, outcycleValueFrom, outcycleValueTo, isReversed, incycleValueDelta, outcycleValueDelta = y1, y2, x1, x2, true, outcycleValueDelta, incycleValueDelta - end - - if outcycleValueFrom > outcycleValueTo then - outcycleValueFrom, outcycleValueTo = swap(outcycleValueFrom, outcycleValueTo) - incycleValueFrom, incycleValueTo = swap(incycleValueFrom, incycleValueTo) - end - - local outcycleValue, outcycleValueCounter, outcycleValueTriggerIncrement = outcycleValueFrom, 1, incycleValueDelta / outcycleValueDelta - local outcycleValueTrigger = outcycleValueTriggerIncrement - for incycleValue = incycleValueFrom, incycleValueTo, incycleValueFrom < incycleValueTo and 1 or -1 do - if isReversed then - buffer.semiPixelSet(outcycleValue, incycleValue, color) - else - buffer.semiPixelSet(incycleValue, outcycleValue, color) - end - - outcycleValueCounter = outcycleValueCounter + 1 - if outcycleValueCounter > outcycleValueTrigger then - outcycleValue, outcycleValueTrigger = outcycleValue + 1, outcycleValueTrigger + outcycleValueTriggerIncrement - end - end -end - ------------------------------------------ Bezier curve ----------------------------------------- - -local function getPointTimedPosition(firstPoint, secondPoint, time) - return { - x = firstPoint.x + (secondPoint.x - firstPoint.x) * time, - y = firstPoint.y + (secondPoint.y - firstPoint.y) * time - } -end - -local function getConnectionPoints(points, time) - local connectionPoints = {} - for point = 1, #points - 1 do - table.insert(connectionPoints, getPointTimedPosition(points[point], points[point + 1], time)) - end - return connectionPoints -end - -local function getMainPointPosition(points, time) - if #points > 1 then - return getMainPointPosition(getConnectionPoints(points, time), time) - else - return points[1] - end -end - -function buffer.bezierCurve(points, color, precision) - local linePoints = {} - for time = 0, 1, precision or 0.01 do - table.insert(linePoints, getMainPointPosition(points, time)) - end - - for point = 1, #linePoints - 1 do - buffer.semiPixelLine(math.floor(linePoints[point].x), math.floor(linePoints[point].y), math.floor(linePoints[point + 1].x), math.floor(linePoints[point + 1].y), color) - end -end - -------------------------------------------- Просчет изменений и отрисовка ------------------------------------------------------------------------ - ---Функция рассчитывает изменения и применяет их, возвращая то, что было изменено -function buffer.calculateDifference(index) - local somethingIsChanged = false - - --Если цвет фона на новом экране отличается от цвета фона на текущем, то - if buffer.screen.new[index] ~= buffer.screen.current[index] then - --Присваиваем цвету фона на текущем экране значение цвета фона на новом экране - buffer.screen.current[index] = buffer.screen.new[index] - --Говорим системе, что что-то изменилось - somethingIsChanged = true - end - - index = index + 1 - - --Аналогично для цвета текста - if buffer.screen.new[index] ~= buffer.screen.current[index] then - buffer.screen.current[index] = buffer.screen.new[index] - somethingIsChanged = true - end - - index = index + 1 - - --И для символа - if buffer.screen.new[index] ~= buffer.screen.current[index] then - buffer.screen.current[index] = buffer.screen.new[index] - somethingIsChanged = true - end - - return somethingIsChanged -end - --- Функция группировки изменений и их отрисовки на экран -function buffer.draw(force) - -- Всякое дерьмо, необходимое для расчетов - local index, indexStepOnEveryLine, somethingIsChanged, indexPlus1, indexPlus2, sameCharArray, x, xCharCheck, indexCharCheck, currentBackground, currentForeground = buffer.getBufferIndexByCoordinates(buffer.drawLimit.x, buffer.drawLimit.y), (buffer.screen.width - buffer.drawLimit.width) * 3 - -- Массив третьего буфера, содержащий в себе измененные пиксели - buffer.screen.changes = {} - - for y = buffer.drawLimit.y, buffer.drawLimit.y2 do - x = buffer.drawLimit.x - while x <= buffer.drawLimit.x2 do - --Чутка оптимизируем расчеты - indexPlus1, indexPlus2 = index + 1, index + 2 - --Получаем изменения и применяем их - somethingIsChanged = buffer.calculateDifference(index) - --Если хоть что-то изменилось, то начинаем работу - if somethingIsChanged or force then - --Оптимизация by Krutoy, создаем массив, в который заносим чарсы. Работает быстрее, чем конкатенейт строк - sameCharArray = { buffer.screen.current[indexPlus2] } - --Загоняем в наш чарс-массив одинаковые пиксели справа, если таковые имеются - xCharCheck, indexCharCheck = x + 1, index + 3 - while xCharCheck <= buffer.drawLimit.x2 do - indexCharCheckPlus2 = indexCharCheck + 2 - if - buffer.screen.current[index] == buffer.screen.new[indexCharCheck] - and - ( - buffer.screen.new[indexCharCheckPlus2] == " " - or - buffer.screen.current[indexPlus1] == buffer.screen.new[indexCharCheck + 1] - ) - then - buffer.calculateDifference(indexCharCheck) - table.insert(sameCharArray, buffer.screen.current[indexCharCheckPlus2]) - else - break - end - - indexCharCheck = indexCharCheck + 3 - xCharCheck = xCharCheck + 1 - end - - --Заполняем третий буфер полученными данными - buffer.screen.changes[buffer.screen.current[index]] = buffer.screen.changes[buffer.screen.current[index]] or {} - buffer.screen.changes[buffer.screen.current[index]][buffer.screen.current[indexPlus1]] = buffer.screen.changes[buffer.screen.current[index]][buffer.screen.current[indexPlus1]] or {} - - table.insert(buffer.screen.changes[buffer.screen.current[index]][buffer.screen.current[indexPlus1]], x) - table.insert(buffer.screen.changes[buffer.screen.current[index]][buffer.screen.current[indexPlus1]], y) - table.insert(buffer.screen.changes[buffer.screen.current[index]][buffer.screen.current[indexPlus1]], table.concat(sameCharArray)) - - --Смещаемся по иксу вправо - index = index + #sameCharArray * 3 - 3 - x = x + #sameCharArray - 1 - end - - index = index + 3 - x = x + 1 - end - - index = index + indexStepOnEveryLine - end - - --Сбрасываем переменные на невозможное значение цвета, чтобы не багнуло - currentBackground, currentForeground = nil, nil - - --Перебираем все цвета текста и фона, выполняя гпу-операции - for background in pairs(buffer.screen.changes) do - gpu.setBackground(background) - for foreground in pairs(buffer.screen.changes[background]) do - if currentForeground ~= foreground then gpu.setForeground(foreground); currentForeground = foreground end - for i = 1, #buffer.screen.changes[background][foreground], 3 do - gpu.set(buffer.screen.changes[background][foreground][i], buffer.screen.changes[background][foreground][i + 1], buffer.screen.changes[background][foreground][i + 2]) - end - end - end - - --Очищаем память, ибо на кой хер нам хранить третий буфер - buffer.screen.changes = nil -end - ------------------------------------------------------------------------------------------------------- - -buffer.start() - --- buffer.clear(0xFF8888) --- buffer.bezierCurve({ --- -- { x = 32, y = 2}, --- -- { x = 2, y = 2}, --- -- { x = 2, y = 98}, --- -- { x = 98, y = 98}, --- { x = 10, y = 80 }, --- { x = 2, y = 4 }, --- { x = 110, y = 4 }, --- { x = 130, y = 70 }, --- { x = 150, y = 10 }, --- }, 0x0, 0.005) --- buffer.draw() - --- ecs.prepareToExit() --- buffer.clear(0xFF8888) - --- -- buffer.square(2, 2, 10, 5, 0xFFFFFF, 0x000000, " ") --- -- buffer.square(5, 4, 10, 5, 0x000000, 0x000000, " ") --- -- buffer.square(20, 4, 10, 5, 0xAAAAAA, 0x000000, " ") - --- buffer.semiPixelSquare(3, 3, 30, 30, 0x880088) - --- buffer.draw() - ------------------------------------------------------------------------------------------------------- - -return buffer - - - - - - - - - - - - - -F lib/event.lualocal computer = require("computer") -local keyboard = require("keyboard") - -local event, listeners, timers = {}, {}, {} -local lastInterrupt = -math.huge - -local function call(callback, ...) - local result, message = pcall(callback, ...) - if not result and type(event.onError) == "function" then - pcall(event.onError, message) - return - end - return message -end - -local function dispatch(signal, ...) - if listeners[signal] then - local function callbacks() - local list = {} - for index, listener in ipairs(listeners[signal]) do - list[index] = listener - end - return list - end - for _, callback in ipairs(callbacks()) do - if call(callback, signal, ...) == false then - event.ignore(signal, callback) -- alternative method of removing a listener - end - end - end -end - -local function tick() - local function elapsed() - local list = {} - for id, timer in pairs(timers) do - if timer.after <= computer.uptime() then - table.insert(list, timer.callback) - timer.times = timer.times - 1 - if timer.times <= 0 then - timers[id] = nil - else - timer.after = computer.uptime() + timer.interval - end - end - end - return list - end - for _, callback in ipairs(elapsed()) do - call(callback) - end -end - -local function createPlainFilter(name, ...) - local filter = table.pack(...) - if name == nil and filter.n == 0 then - return nil - end - - return function(...) - local signal = table.pack(...) - if name and not (type(signal[1]) == "string" and signal[1]:match(name)) then - return false - end - for i = 1, filter.n do - if filter[i] ~= nil and filter[i] ~= signal[i + 1] then - return false - end - end - return true - end -end - -local function createMultipleFilter(...) - local filter = table.pack(...) - if filter.n == 0 then - return nil - end - - return function(...) - local signal = table.pack(...) - if type(signal[1]) ~= "string" then - return false - end - for i = 1, filter.n do - if filter[i] ~= nil and signal[1]:match(filter[i]) then - return true - end - end - return false - end -end -------------------------------------------------------------------------------- - -function event.cancel(timerId) - checkArg(1, timerId, "number") - if timers[timerId] then - timers[timerId] = nil - return true - end - return false -end - -function event.ignore(name, callback) - checkArg(1, name, "string") - checkArg(2, callback, "function") - if listeners[name] then - for i = 1, #listeners[name] do - if listeners[name][i] == callback then - table.remove(listeners[name], i) - if #listeners[name] == 0 then - listeners[name] = nil - end - return true - end - end - end - return false -end - -function event.listen(name, callback) - checkArg(1, name, "string") - checkArg(2, callback, "function") - if listeners[name] then - for i = 1, #listeners[name] do - if listeners[name][i] == callback then - return false - end - end - else - listeners[name] = {} - end - table.insert(listeners[name], callback) - return true -end - -function event.onError(message) - local log = io.open("/tmp/event.log", "a") - if log then - log:write(message .. "\n") - log:close() - end -end - -function event.pull(...) - local args = table.pack(...) - if type(args[1]) == "string" then - return event.pullFiltered(createPlainFilter(...)) - else - checkArg(1, args[1], "number", "nil") - checkArg(2, args[2], "string", "nil") - return event.pullFiltered(args[1], createPlainFilter(select(2, ...))) - end -end - -function event.pullMultiple(...) - local seconds - local args - if type(...) == "number" then - seconds = ... - args = table.pack(select(2,...)) - for i=1,args.n do - checkArg(i+1, args[i], "string", "nil") - end - else - args = table.pack(...) - for i=1,args.n do - checkArg(i, args[i], "string", "nil") - end - end - return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n))) - -end - -function event.pullFiltered(...) - local args = table.pack(...) - local seconds, filter - - if type(args[1]) == "function" then - filter = args[1] - else - checkArg(1, args[1], "number", "nil") - checkArg(2, args[2], "function", "nil") - seconds = args[1] - filter = args[2] - end - - local deadline = seconds and (computer.uptime() + seconds) or math.huge - repeat - local closest = deadline - for _, timer in pairs(timers) do - closest = math.min(closest, timer.after) - end - local signal = table.pack(computer.pullSignal(closest - computer.uptime())) - if signal.n > 0 then - dispatch(table.unpack(signal, 1, signal.n)) - end - tick() - if event.shouldInterrupt() then - lastInterrupt = computer.uptime() - error("interrupted", 0) - end - if event.shouldSoftInterrupt() and (filter == nil or filter("interrupted", computer.uptime() - lastInterrupt)) then - local awaited = computer.uptime() - lastInterrupt - lastInterrupt = computer.uptime() - return "interrupted", awaited - end - if signal.n > 0 then - if not (seconds or filter) or filter == nil or filter(table.unpack(signal, 1, signal.n)) then - return table.unpack(signal, 1, signal.n) - end - end - until computer.uptime() >= deadline -end - -function event.shouldInterrupt() - return computer.uptime() - lastInterrupt > 1 and - keyboard.isControlDown() and - keyboard.isAltDown() and - keyboard.isKeyDown(keyboard.keys.c) -end - -function event.shouldSoftInterrupt() - return computer.uptime() - lastInterrupt > 1 and - keyboard.isControlDown() and - keyboard.isKeyDown(keyboard.keys.c) -end - -function event.timer(interval, callback, times) - checkArg(1, interval, "number") - checkArg(2, callback, "function") - checkArg(3, times, "number", "nil") - local id - repeat - id = math.floor(math.random(1, 0x7FFFFFFF)) - until not timers[id] - timers[id] = { - interval = interval, - after = computer.uptime() + interval, - callback = callback, - times = times or 1 - } - return id -end --- users may expect to find event.push to exist -event.push = computer.pushSignal -------------------------------------------------------------------------------- - -return event -Flib/filesystem.lua3?local component = require("component") -local unicode = require("unicode") - -local filesystem, fileStream = {}, {} -local isAutorunEnabled = nil -local mtab = {name="", children={}, links={}} - -local function segments(path) - path = path:gsub("\\", "/") - repeat local n; path, n = path:gsub("//", "/") until n == 0 - local parts = {} - for part in path:gmatch("[^/]+") do - table.insert(parts, part) - end - local i = 1 - while i <= #parts do - if parts[i] == "." then - table.remove(parts, i) - elseif parts[i] == ".." then - table.remove(parts, i) - i = i - 1 - if i > 0 then - table.remove(parts, i) - else - i = 1 - end - else - i = i + 1 - end - end - return parts -end - -local function saveConfig() - local root = filesystem.get("/") - if root and not root.isReadOnly() then - filesystem.makeDirectory("/etc") - local f = io.open("/etc/filesystem.cfg", "w") - if f then - f:write("autorun="..tostring(isAutorunEnabled)) - f:close() - end - end -end - -local function findNode(path, create, depth) - checkArg(1, path, "string") - depth = depth or 0 - if depth > 100 then - error("link cycle detected") - end - local parts = segments(path) - local node = mtab - while #parts > 0 do - local part = parts[1] - if not node.children[part] then - if node.links[part] then - return findNode(filesystem.concat(node.links[part], table.concat(parts, "/", 2)), create, depth + 1) - else - if create then - node.children[part] = {name=part, parent=node, children={}, links={}} - else - local vnode, vrest = node, table.concat(parts, "/") - local rest = vrest - while node and not node.fs do - rest = filesystem.concat(node.name, rest) - node = node.parent - end - return node, rest, vnode, vrest - end - end - end - node = node.children[part] - table.remove(parts, 1) - end - local vnode, vrest = node, nil - local rest = nil - while node and not node.fs do - rest = rest and filesystem.concat(node.name, rest) or node.name - node = node.parent - end - return node, rest, vnode, vrest -end - -local function removeEmptyNodes(node) - while node and node.parent and not node.fs and not next(node.children) and not next(node.links) do - node.parent.children[node.name] = nil - node = node.parent - end -end - -------------------------------------------------------------------------------- - -function filesystem.isAutorunEnabled() - if isAutorunEnabled == nil then - local env = {} - local config = loadfile("/etc/filesystem.cfg", nil, env) - if config then - pcall(config) - isAutorunEnabled = not not env.autorun - else - isAutorunEnabled = true - end - saveConfig() - end - return isAutorunEnabled -end - -function filesystem.setAutorunEnabled(value) - checkArg(1, value, "boolean") - isAutorunEnabled = value - saveConfig() -end - -function filesystem.segments(path) - return segments(path) -end - -function filesystem.canonical(path) - local result = table.concat(segments(path), "/") - if unicode.sub(path, 1, 1) == "/" then - return "/" .. result - else - return result - end -end - -function filesystem.concat(pathA, pathB, ...) - checkArg(1, pathA, "string") - local function concat(n, a, b, ...) - if not b then - return a - end - checkArg(n, b, "string") - return concat(n + 1, a .. "/" .. b, ...) - end - return filesystem.canonical(concat(2, pathA, pathB, ...)) -end - -function filesystem.get(path) - local node, rest = findNode(path) - if node.fs then - local proxy = node.fs - path = "" - while node and node.parent do - path = filesystem.concat(node.name, path) - node = node.parent - end - path = filesystem.canonical(path) - if path ~= "/" then - path = "/" .. path - end - return proxy, path - end - return nil, "no such file system" -end - -function filesystem.isLink(path) - local node, rest, vnode, vrest = findNode(filesystem.path(path)) - if not vrest and vnode.links[filesystem.name(path)] ~= nil then - return true, vnode.links[filesystem.name(path)] - end - return false -end - -function filesystem.link(target, linkpath) - checkArg(1, target, "string") - checkArg(2, linkpath, "string") - - if filesystem.exists(linkpath) then - return nil, "file already exists" - end - - local node, rest, vnode, vrest = findNode(filesystem.path(linkpath), true) - vnode.links[filesystem.name(linkpath)] = target - return true -end - -function filesystem.mount(fs, path) - checkArg(1, fs, "string", "table") - if type(fs) == "string" then - fs = filesystem.proxy(fs) - end - assert(type(fs) == "table", "bad argument #1 (file system proxy or address expected)") - checkArg(2, path, "string") - - if path ~= "/" and filesystem.exists(path) then - return nil, "file already exists" - end - - local node, rest, vnode, vrest = findNode(path, true) - if vnode.fs then - return nil, "another filesystem is already mounted here" - end - vnode.fs = fs - return true -end - -function filesystem.mounts() - local function path(node) - local result = "/" - while node and node.parent do - for name, child in pairs(node.parent.children) do - if child == node then - result = "/" .. name .. result - break - end - end - node = node.parent - end - return result - end - local queue = {mtab} - return function() - while #queue > 0 do - local node = table.remove(queue) - for _, child in pairs(node.children) do - table.insert(queue, child) - end - if node.fs then - return node.fs, path(node) - end - end - end -end - -function filesystem.path(path) - local parts = segments(path) - local result = table.concat(parts, "/", 1, #parts - 1) .. "/" - if unicode.sub(path, 1, 1) == "/" and unicode.sub(result, 1, 1) ~= "/" then - return "/" .. result - else - return result - end -end - -function filesystem.name(path) - local parts = segments(path) - return parts[#parts] -end - -function filesystem.proxy(filter) - checkArg(1, filter, "string") - local address - for c in component.list("filesystem", true) do - if component.invoke(c, "getLabel") == filter then - address = c - break - end - if c:sub(1, filter:len()) == filter then - address = c - break - end - end - if not address then - return nil, "no such file system" - end - return component.proxy(address) -end - -function filesystem.umount(fsOrPath) - checkArg(1, fsOrPath, "string", "table") - if type(fsOrPath) == "string" then - local node, rest, vnode, vrest = findNode(fsOrPath) - if not vrest and vnode.fs then - vnode.fs = nil - removeEmptyNodes(vnode) - return true - end - end - local address = type(fsOrPath) == "table" and fsOrPath.address or fsOrPath - local result = false - for proxy, path in filesystem.mounts() do - local addr = type(proxy) == "table" and proxy.address or proxy - if string.sub(addr, 1, address:len()) == address then - local node, rest, vnode, vrest = findNode(path) - vnode.fs = nil - removeEmptyNodes(vnode) - result = true - end - end - return result -end - -function filesystem.exists(path) - local node, rest, vnode, vrest = findNode(path) - if not vrest or vnode.links[vrest] then -- virtual directory or symbolic link - return true - end - if node and node.fs then - return node.fs.exists(rest) - end - return false -end - -function filesystem.size(path) - local node, rest, vnode, vrest = findNode(path) - if not vnode.fs and (not vrest or vnode.links[vrest]) then - return 0 -- virtual directory or symlink - end - if node.fs and rest then - return node.fs.size(rest) - end - return 0 -- no such file or directory -end - -function filesystem.isDirectory(path) - local node, rest, vnode, vrest = findNode(path) - if not vnode.fs and not vrest then - return true -- virtual directory - end - if node.fs then - return not rest or node.fs.isDirectory(rest) - end - return false -end - -function filesystem.lastModified(path) - local node, rest, vnode, vrest = findNode(path) - if not vnode.fs and not vrest then - return 0 -- virtual directory - end - if node.fs and rest then - return node.fs.lastModified(rest) - end - return 0 -- no such file or directory -end - -function filesystem.list(path) - local node, rest, vnode, vrest = findNode(path) - if not vnode.fs and vrest and not (node and node.fs) then - return nil, "no such file or directory" - end - local result, reason - if node and node.fs then - result, reason = node.fs.list(rest or "") - end - result = result or {} - if not vrest then - for k in pairs(vnode.children) do - table.insert(result, k .. "/") - end - for k in pairs(vnode.links) do - table.insert(result, k) - end - end - table.sort(result) - local i, f = 1, nil - while i <= #result do - if result[i] == f then - table.remove(result, i) - else - f = result[i] - i = i + 1 - end - end - local i = 0 - return function() - i = i + 1 - return result[i] - end -end - -function filesystem.makeDirectory(path) - if filesystem.exists(path) then - return nil, "file or directory with that name already exists" - end - local node, rest = findNode(path) - if node.fs and rest then - return node.fs.makeDirectory(rest) - end - if node.fs then - return nil, "virtual directory with that name already exists" - end - return nil, "cannot create a directory in a virtual directory" -end - -function filesystem.remove(path) - local function removeVirtual() - local node, rest, vnode, vrest = findNode(filesystem.path(path)) - local name = filesystem.name(path) - if vnode.children[name] then - vnode.children[name] = nil - removeEmptyNodes(vnode) - return true - elseif vnode.links[name] then - vnode.links[name] = nil - removeEmptyNodes(vnode) - return true - end - return false - end - local function removePhysical() - node, rest = findNode(path) - if node.fs and rest then - return node.fs.remove(rest) - end - return false - end - local success = removeVirtual() - success = removePhysical() or success -- Always run. - if success then return true - else return nil, "no such file or directory" - end -end - -function filesystem.rename(oldPath, newPath) - if filesystem.isLink(oldPath) then - local node, rest, vnode, vrest = findNode(filesystem.path(oldPath)) - local target = vnode.links[filesystem.name(oldPath)] - local result, reason = filesystem.link(target, newPath) - if result then - filesystem.remove(oldPath) - end - return result, reason - else - local oldNode, oldRest = findNode(oldPath) - local newNode, newRest = findNode(newPath) - if oldNode.fs and oldRest and newNode.fs and newRest then - if oldNode.fs.address == newNode.fs.address then - return oldNode.fs.rename(oldRest, newRest) - else - local result, reason = filesystem.copy(oldPath, newPath) - if result then - return filesystem.remove(oldPath) - else - return nil, reason - end - end - end - return nil, "trying to read from or write to virtual directory" - end -end - -function filesystem.copy(fromPath, toPath) - if filesystem.isDirectory(fromPath) then - return nil, "cannot copy folders" - end - local input, reason = io.open(fromPath, "rb") - if not input then - return nil, reason - end - local output, reason = io.open(toPath, "wb") - if not output then - input:close() - return nil, reason - end - repeat - local buffer, reason = input:read(1024) - if not buffer and reason then - return nil, reason - elseif buffer then - local result, reason = output:write(buffer) - if not result then - input:close() - output:close() - return nil, reason - end - end - until not buffer - input:close() - output:close() - return true -end - -function fileStream:close() - if self.handle then - self.fs.close(self.handle) - self.handle = nil - end -end - -function fileStream:read(n) - if not self.handle then - return nil, "file is closed" - end - return self.fs.read(self.handle, n) -end - -function fileStream:seek(whence, offset) - if not self.handle then - return nil, "file is closed" - end - return self.fs.seek(self.handle, whence, offset) -end - -function fileStream:write(str) - if not self.handle then - return nil, "file is closed" - end - return self.fs.write(self.handle, str) -end - -function filesystem.open(path, mode) - checkArg(1, path, "string") - mode = tostring(mode or "r") - checkArg(2, mode, "string") - assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode], - "bad argument #2 (r[b], w[b] or a[b] expected, got " .. mode .. ")") - - local node, rest = findNode(path) - if not node.fs or not rest then - return nil, "file not found" - end - - local handle, reason = node.fs.open(rest, mode) - if not handle then - return nil, reason - end - - local stream = {fs = node.fs, handle = handle} - - local metatable = {__index = fileStream, - __metatable = "filestream"} - return setmetatable(stream, metatable) -end - -------------------------------------------------------------------------------- - -return filesystem -F lib/guid.lualocal guid = {} - -function guid.toHex(n) - if type(n) ~= 'number' then - return nil, string.format("toHex only converts numbers to strings, %s is not a string, but a %s", tostring(n), type(n)) - end - if n == 0 then - return '0' - end - - local hexchars = "0123456789abcdef" - local result = "" - local prefix = "" -- maybe later allow for arg to request 0x prefix - if n < 0 then - prefix = "-" - n = -n - end - - while n > 0 do - local next = math.floor(n % 16) + 1 -- lua has 1 based array indices - n = math.floor(n / 16) - result = hexchars:sub(next, next) .. result - end - - return prefix .. result -end - -function guid.next() - -- e.g. 3c44c8a9-0613-46a2-ad33-97b6ba2e9d9a - -- 8-4-4-4-12 - local sets = {8, 4, 4, 12} - local result = "" - - local i - for _,set in ipairs(sets) do - if result:len() > 0 then - result = result .. "-" - end - for i = 1,set do - result = result .. guid.toHex(math.random(0, 15)) - end - end - - return result -end - -return guid -F lib/image.luaۤ ----------------------------------------- OpenComputers Image Format (OCIF) ----------------------------------------------------------- - ---[[ - - Автор: Pornogion - VK: https://vk.com/id88323331 - Соавтор: IT - VK: https://vk.com/id7799889 - - Основные функции: - - image.load(string путь): table изображение - Загружает существующую картинку в формате .pic и возвращает ее - в качестве массива (таблицы). - - image.draw(int x, int y, table изображение) - Рисует на экране загруженную ранее картинку по указанным координатам. - - image.save(string путь, table изображение [, int метод кодирования]) - Сохраняет указанную картинку по указанному пути в формате .pic, - по умолчанию используя метод кодирования 3. Рекомендуется - использовать именно его. - - Функции для работы с изображением: - - image.transform(table картинка, int масштаб по ширине, int масштаб по высоте): table картинка - Изменяет размер картинки по методу интерполяции по соседним пикселям. - - image.expand(table картинка, string направление, int количество пикселей[, int цвет фона, int цвет текста, int прозрачность, char символ]): table картинка - Расширяет указанную картинку в указанном направлении (fromRight, fromLeft, fromTop, fromBottom), - создавая при этом пустые белые пиксели. Если указаны опциональные аргументы, то вместо пустых - пикселей могут быть вполне конкретные значения. - - image.crop(table картинка, string направление, int количество пикселей): table картинка - Обрезает указанную картинку в указанном направлении (fromRight, fromLeft, fromTop, fromBottom), - удаляя лишние пиксели. - - image.rotate(table картинка, int угол): table картинка - Поворачивает указанную картинку на указанный угол. Угол может иметь - значение 90, 180 и 270 градусов. - - image.flipVertical(table картинка): table картинка - Отражает указанную картинку по вертикали. - - image.flipHorizontal(table картинка): table картинка - Отражает указанную картинку по горизонтали. - - Функции для работы с цветом: - - image.hueSaturationBrightness(table картинка, int тон, int насыщенность, int яркость): table картинка - Корректирует цветовой тон, насыщенность и яркость указанной картинки. - Значения аргументов могут быть отрицательными для уменьшения параметра - и положительными для его увеличения. Если значение, к примеру, насыщенности - менять не требуется, просто указывайте 0. - - Для удобства вы можете использовать следующие сокращения: - image.hue(table картинка, int тон): table картинка - image.saturation(table картинка, int насыщенность): table картинка - image.brightness(table картинка, int яркость): table картинка - image.blackAndWhite(table картинка): table картинка - - image.colorBalance(table картинка, int красный, int зеленый, int синий): table картинка - Корректирует цветовые каналы изображения указанной картинки. Аргументы цветовых - каналов могут принимать как отрицательные значения для уменьшения интенсивности канала, - так и положительные для увеличения. - - image.invert(table картинка): table картинка - Инвертирует цвета в указанной картинке. - - image.photoFilter(table картинка, int цвет, int прозрачность): table картинка - Накладывает на указанное изображение фотофильтр с указанной прозрачностью. - Прозрачность может быть от 0 до 255. - - image.replaceColor(table картинка, int заменяемыйЦвет, int цветДляЗамены): table картинка - Заменяет в указанном изображении один конкретный цвет на другой. -]] - ---------------------------------------- Подгрузка библиотек -------------------------------------------------------------- - -local component = require("component") -local unicode = require("unicode") -local fs = require("filesystem") -local colorlib = require("colorlib") -local bit32 = require("bit32") - -local image = {} - --------------------------------------------- Переменные ------------------------------------------------------------------- - ---Константы программы -local constants = { - OCIFSignature = "OCIF", - OCIF2Elements = { - alphaStart = "A", - symbolStart = "S", - backgroundStart = "B", - foregroundStart = "F", - }, - elementCount = 4, - byteSize = 8, - nullChar = 0, - rawImageLoadStep = 19, - compressedFileFormat = ".pic", - pngFileFormat = ".png", -} - ----------------------------------------- Локальные функции ------------------------------------------------------------------- - ---Формула конвертации индекса массива изображения в абсолютные координаты пикселя изображения -local function convertIndexToCoords(index, width) - --Приводим индекс к корректному виду (1 = 1, 4 = 2, 7 = 3, 10 = 4, 13 = 5, ...) - index = (index + constants.elementCount - 1) / constants.elementCount - --Получаем остаток от деления индекса на ширину изображения - local ostatok = index % width - --Если остаток равен 0, то х равен ширине изображения, а если нет, то х равен остатку - local x = (ostatok == 0) and width or ostatok - --А теперь как два пальца получаем координату по Y - local y = math.ceil(index / width) - --Очищаем остаток из оперативки - ostatok = nil - --Возвращаем координаты - return x, y -end - ---Формула конвертации абсолютных координат пикселя изображения в индекс для массива изображения -local function convertCoordsToIndex(x, y, width) - return (width * (y - 1) + x) * constants.elementCount - constants.elementCount + 1 -end - ---Костыльное получение размера массива, ибо автор луа не позволяет ---подсчитывать ненумерические индексы через #massiv ---мда, мда ---... ---мда -local function getArraySize(array) - local size = 0 - for key in pairs(array) do - size = size + 1 - end - return size -end - ---Получить количество байт, которое можно извлечь из указанного числа -local function getCountOfBytes(number) - if number == 0 or number == 1 then return 1 end - return math.ceil(math.log(number, 256)) -end - ---Распидорасить число на составляющие байты -local function extractBytesFromNumber(number, countOfBytesToExtract) - local bytes = {} - local byteCutter = 0xff - for i = 1, countOfBytesToExtract do - table.insert(bytes, 1, bit32.rshift(bit32.band(number, byteCutter), (i-1)*8)) - byteCutter = bit32.lshift(byteCutter, 8) - end - return table.unpack(bytes) -end - ---Склеить байты и создать из них число -local function mergeBytesToNumber(...) - local bytes = {...} - local finalNumber = bytes[1] - for i = 2, #bytes do - finalNumber = bit32.bor(bit32.lshift(finalNumber, 8), bytes[i]) - end - return finalNumber -end - --- Сконвертировать все переданные байты в строку -local function convertBytesToString(...) - local bytes = {...} - for i = 1, #bytes do - bytes[i] = string.char(bytes[i]) - end - return table.concat(bytes) -end - ---Выделить бит-терминатор в первом байте UTF-8 символа: 1100 0010 --> 0010 0000 -local function selectTerminateBit_l() - local prevByte = nil - local prevTerminateBit = nil - - return function( byte ) - local x, terminateBit = nil - if ( prevByte == byte ) then - return prevTerminateBit - end - - x = bit32.band( bit32.bnot(byte), 0x000000FF ) - x = bit32.bor( x, bit32.rshift(x, 1) ) - x = bit32.bor( x, bit32.rshift(x, 2) ) - x = bit32.bor( x, bit32.rshift(x, 4) ) - x = bit32.bor( x, bit32.rshift(x, 8) ) - x = bit32.bor( x, bit32.rshift(x, 16) ) - - terminateBit = x - bit32.rshift(x, 1) - - prevByte = byte - prevTerminateBit = terminateBit - - return terminateBit - end -end -local selectTerminateBit = selectTerminateBit_l() - ---Прочитать n байтов из файла, возвращает прочитанные байты как число, если не удалось прочитать, то возвращает 0 -local function readBytes(file, count) - local readedBytes = file:read(count) - return mergeBytesToNumber(string.byte(readedBytes, 1, count)) -end - ---Подготавливает цвета и символ для записи в файл сжатого формата -local function encodePixel(background, foreground, alpha, char) - --Расхерачиваем жирные цвета в компактные цвета - local ascii_background1, ascii_background2, ascii_background3 = colorlib.HEXtoRGB(background) - local ascii_foreground1, ascii_foreground2, ascii_foreground3 = colorlib.HEXtoRGB(foreground) - --Расхерачиваем жирный код юникод-символа в несколько миленьких ascii-кодов - local ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 = string.byte( char, 1, 6 ) - ascii_char1 = ascii_char1 or constants.nullChar - --Возвращаем все расхераченное - return ascii_background1, ascii_background2, ascii_background3, ascii_foreground1, ascii_foreground2, ascii_foreground3, alpha, ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 -end - ---Декодирование UTF-8 символа -local function decodeChar(file) - local first_byte = readBytes(file, 1) - local charcode_array = {first_byte} - local len = 1 - - local middle = selectTerminateBit(first_byte) - if ( middle == 32 ) then - len = 2 - elseif ( middle == 16 ) then - len = 3 - elseif ( middle == 8 ) then - len = 4 - elseif ( middle == 4 ) then - len = 5 - elseif ( middle == 2 ) then - len = 6 - end - - for i = 1, len-1 do - table.insert( charcode_array, readBytes(file, 1) ) - end - - return string.char( table.unpack( charcode_array ) ) -end - ---Правильное конвертирование HEX-переменной в строковую -local function HEXtoSTRING(color, bitCount, withNull) - local stro4ka = string.format("%X",color) - local sStro4ka = unicode.len(stro4ka) - - if sStro4ka < bitCount then - stro4ka = string.rep("0", bitCount - sStro4ka) .. stro4ka - end - - sStro4ka = nil - - if withNull then return "0x"..stro4ka else return stro4ka end -end - ---Получение формата файла -local function getFileFormat(path) - local name = fs.name(path) - local starting, ending = string.find(name, "(.)%.[%d%w]*$") - if starting == nil then - return nil - else - return unicode.sub(name, starting + 1, -1) - end - name, starting, ending = nil, nil, nil -end - ---Прочесть сигнатуру файла и сравнить ее с константой -local function readSignature(file) - local readedSignature = file:read(4) - if readedSignature ~= constants.OCIFSignature then - file:close() - error("Can't load file: wrong OCIF format signature (\""..readedSignature .. "\" ~= \"" ..constants.OCIFSignature .. "\")") - end -end - ---Записать сигнатуру в файл -local function writeSignature(file) - file:write(constants.OCIFSignature) -end - ---Сжать все цвета в изображении в 8-битную палитру -local function convertImageColorsTo8Bit(picture) - for i = 1, #picture, 4 do - picture[i] = colorlib.convert24BitTo8Bit(picture[i]) - picture[i + 1] = colorlib.convert24BitTo8Bit(picture[i + 1]) - if i % 505 == 0 then os.sleep(0) end - end - return picture -end - ---Расжать все цвета в изображении в 24-битную палитру -local function convertImageColorsTo24Bit(picture) - for i = 1, #picture, 4 do - picture[i] = colorlib.convert8BitTo24Bit(picture[i]) - picture[i + 1] = colorlib.convert8BitTo24Bit(picture[i + 1]) - if i % 505 == 0 then os.sleep(0) end - end - return picture -end - ------------------------------- Все, что касается формата OCIF1 ------------------------------------------------------------ - --- Запись в файл сжатого OCIF-формата изображения -local function saveOCIF1(file, picture) - local encodedPixel - file:write( string.char( picture.width ) ) - file:write( string.char( picture.height ) ) - - for i = 1, picture.width * picture.height * constants.elementCount, constants.elementCount do - encodedPixel = - { - encodePixel(picture[i], picture[i + 1], picture[i + 2], picture[i + 3]) - } - for j = 1, #encodedPixel do - file:write( string.char( encodedPixel[j] ) ) - end - end - - file:close() -end - ---Чтение из файла сжатого OCIF-формата изображения, возвращает массив типа 2 (подробнее о типах см. конец файла) -local function loadOCIF1(file) - local picture = {} - - --Читаем ширину и высоту файла - picture.width = readBytes(file, 1) - picture.height = readBytes(file, 1) - - for i = 1, picture.width * picture.height do - --Читаем бекграунд - table.insert(picture, readBytes(file, 3)) - --Читаем форграунд - table.insert(picture, readBytes(file, 3)) - --Читаем альфу - table.insert(picture, readBytes(file, 1)) - --Читаем символ - table.insert(picture, decodeChar( file )) - end - - file:close() - - return picture -end - ------------------------------------------- Все, что касается формата OCIF2 ------------------------------------------------ - -local function saveOCIF2(file, picture, compressColors) - --Записываем ширину изображения - file:write(string.char(picture.width)) - file:write(string.char(picture.height)) - - --Группируем картинку - local grouppedPucture = image.convertToGroupedImage(picture) - - --Перебираем все альфы - for alpha in pairs(grouppedPucture) do - --Получаем размер массива, содержащего символы - local arraySize = getArraySize(grouppedPucture[alpha]) - local countOfBytesForArraySize = getCountOfBytes(arraySize) - --Записываем в файл символ АльфаСтарта, размер массива альфы и само значение альфы - file:write( - constants.OCIF2Elements.alphaStart, - string.char(countOfBytesForArraySize), - convertBytesToString(extractBytesFromNumber(arraySize, countOfBytesForArraySize)), - string.char(alpha) - ) - - for symbol in pairs(grouppedPucture[alpha]) do - --Записываем заголовок - file:write(constants.OCIF2Elements.symbolStart) - --Записываем количество всех цветов текста и символ - if compressColors then - file:write( - string.char(getArraySize(grouppedPucture[alpha][symbol])), - convertBytesToString(string.byte(symbol, 1, 6)) - ) - else - file:write( - convertBytesToString(extractBytesFromNumber(getArraySize(grouppedPucture[alpha][symbol]), 3)), - convertBytesToString(string.byte(symbol, 1, 6)) - ) - end - - for foreground in pairs(grouppedPucture[alpha][symbol]) do - --Записываем заголовок - file:write(constants.OCIF2Elements.foregroundStart) - --Записываем количество цветов фона и цвет текста - if compressColors then - file:write( - string.char(getArraySize(grouppedPucture[alpha][symbol][foreground])), - string.char(foreground) - ) - else - file:write( - convertBytesToString(extractBytesFromNumber(getArraySize(grouppedPucture[alpha][symbol][foreground]), 3)), - convertBytesToString(extractBytesFromNumber(foreground, 3)) - ) - end - - for background in pairs(grouppedPucture[alpha][symbol][foreground]) do - --Записываем заголовок и размер массива координат - file:write( - constants.OCIF2Elements.backgroundStart, - convertBytesToString(extractBytesFromNumber(getArraySize(grouppedPucture[alpha][symbol][foreground][background]), 2)) - ) - --Записываем цвет фона - if compressColors then - file:write(string.char(background)) - else - file:write(convertBytesToString(extractBytesFromNumber(background, 3))) - end - - --Перебираем координаты - for y in pairs(grouppedPucture[alpha][symbol][foreground][background]) do - --Записываем заголовок координат, размер массива y и само значение y - file:write( - "Y", - string.char(getArraySize(grouppedPucture[alpha][symbol][foreground][background][y])), - string.char(y) - ) - --Записываем ИКСЫЫЫ - --Ы - for i = 1, #grouppedPucture[alpha][symbol][foreground][background][y] do - file:write(string.char(grouppedPucture[alpha][symbol][foreground][background][y][i])) - end - end - end - end - end - end - - file:close() -end - -local function loadOCIF2(file, decompressColors, useOCIF4) - local picture = {} - - --Читаем размер изображения - local readedWidth = string.byte(file:read(1)) - local readedHeight = string.byte(file:read(1)) - picture.width = readedWidth - picture.height = readedHeight - - local header, alpha, symbol, foreground, background, y, alphaSize, symbolSize, foregroundSize, backgroundSize, ySize = "" - while true do - header = file:read(1) - if not header then break end - -- print("----------------------") - -- print("Заголовок: " .. header) - - if header == "A" then - local countOfBytesForArraySize = string.byte(file:read(1)) - alphaSize = string.byte(file:read(countOfBytesForArraySize)) - alpha = string.byte(file:read(1)) - -- print("Количество байт под размер массива символов: " .. countOfBytesForArraySize) - -- print("Размер массива символов: " .. alphaSize) - -- print("Альфа: " .. alpha) - - elseif header == "S" then - if decompressColors then - symbolSize = string.byte(file:read(1)) - else - symbolSize = mergeBytesToNumber(string.byte(file:read(3), 1, 3)) - end - symbol = decodeChar(file) - -- print("Размер массива цвета текста: " .. symbolSize) - -- print("Символ: \"" .. symbol .. "\"") - - elseif header == "F" then - if decompressColors then - foregroundSize = string.byte(file:read(1)) - foreground = colorlib.convert8BitTo24Bit(string.byte(file:read(1))) - else - foregroundSize = mergeBytesToNumber(string.byte(file:read(3), 1, 3)) - foreground = mergeBytesToNumber(string.byte(file:read(3), 1, 3)) - end - -- print("Размер массива цвета фона: " .. foregroundSize) - -- print("Цвет текста: " .. foreground) - - elseif header == "B" then - backgroundSize = mergeBytesToNumber(string.byte(file:read(2), 1, 2)) - if decompressColors then - background = colorlib.convert8BitTo24Bit(string.byte(file:read(1))) - else - background = mergeBytesToNumber(string.byte(file:read(3), 1, 3)) - end - -- print("Размер массива координат: " .. backgroundSize) - -- print("Цвет фона: " .. background) - - --Поддержка загрузки формата OCIF3 - if not useOCIF4 then - --Читаем координаты - for i = 1, backgroundSize, 2 do - local x = string.byte(file:read(1)) - local y = string.byte(file:read(1)) - local index = convertCoordsToIndex(x, y, readedWidth) - -- print("Координата: " .. x .. "x" .. y .. ", индекс: "..index) - - picture[index] = background - picture[index + 1] = foreground - picture[index + 2] = alpha - picture[index + 3] = symbol - end - end - - --Новый формат OCIF4 - elseif header == "Y" and useOCIF4 then - ySize = string.byte(file:read(1)) - y = string.byte(file:read(1)) - -- print("Размер массива Y: " .. ySize) - -- print("Текущий Y: " .. y) - - for i = 1, ySize do - local x = string.byte(file:read(1)) - local index = convertCoordsToIndex(x, y, readedWidth) - -- print("Координата: " .. x .. "x" .. y .. ", индекс: "..index) - - picture[index] = background - picture[index + 1] = foreground - picture[index + 2] = alpha - picture[index + 3] = symbol - end - else - error("Error while reading OCIF format: unknown Header type (" .. header .. ")") - end - - end - - file:close() - - return picture -end - ------------------------------- Все, что касается формата RAW ------------------------------------------------------------ - ---Сохранение в файл сырого формата изображения типа 2 (подробнее о типах см. конец файла) -local function saveRaw(file, picture) - - file:write("\n") - - local xPos, yPos = 1, 1 - for i = 1, picture.width * picture.height * constants.elementCount, constants.elementCount do - file:write( HEXtoSTRING(picture[i], 6), " ", HEXtoSTRING(picture[i + 1], 6), " ", HEXtoSTRING(picture[i + 2], 2), " ", picture[i + 3], " ") - - xPos = xPos + 1 - if xPos > picture.width then - xPos = 1 - yPos = yPos + 1 - file:write("\n") - end - end - - file:close() -end - ---Загрузка из файла сырого формата изображения типа 2 (подробнее о типах см. конец файла) -local function loadRaw(file) - --Читаем один байт "прост так" - file:read(1) - - local picture = {} - local background, foreground, alpha, symbol, sLine - local lineCounter = 0 - - for line in file:lines() do - sLine = unicode.len(line) - for i = 1, sLine, constants.rawImageLoadStep do - background = "0x" .. unicode.sub(line, i, i + 5) - foreground = "0x" .. unicode.sub(line, i + 7, i + 12) - alpha = "0x" .. unicode.sub(line, i + 14, i + 15) - symbol = unicode.sub(line, i + 17, i + 17) - - table.insert(picture, tonumber(background)) - table.insert(picture, tonumber(foreground)) - table.insert(picture, tonumber(alpha)) - table.insert(picture, symbol) - end - lineCounter = lineCounter + 1 - end - - picture.width = sLine / constants.rawImageLoadStep - picture.height = lineCounter - - file:close() - return picture -end - ------------------------------------ Все, что касается реального PNG-формата ------------------------------------------------------------ - -function image.loadPng(path) - if not _G.libPNGImage then _G.libPNGImage = require("libPNGImage") end - - local success, pngImageOrErrorMessage = pcall(libPNGImage.newFromFile, path) - - if not success then - io.stderr:write(" * PNGView: PNG Loading Error *\n") - io.stderr:write("While attempting to load '" .. path .. "' as PNG, libPNGImage erred:\n") - io.stderr:write(pngImageOrErrorMessage) - return - end - - local picture = {} - picture.width, picture.height = pngImageOrErrorMessage:getSize() - - local r, g, b, a, hex - for j = 0, picture.height - 1 do - for i = 0, picture.width - 1 do - r, g, b, a = pngImageOrErrorMessage:getPixel(i, j) - - if r and g and b and a and a > 0 then - hex = colorlib.RGBtoHEX(r, g, b) - table.insert(picture, hex) - table.insert(picture, 0x000000) - table.insert(picture, 0x00) - table.insert(picture, " ") - end - - end - end - - return picture -end - ------------------------------------ Вспомогательные функции программы ------------------------------------------------------------ - ---Оптимизировать и сгруппировать по цветам картинку типа 2 (подробнее о типах см. конец файла) -function image.convertToGroupedImage(picture) - --Создаем массив оптимизированной картинки - local optimizedPicture = {} - --Задаем константы - local xPos, yPos, background, foreground, alpha, symbol = 1, 1, nil, nil, nil, nil - --Перебираем все элементы массива - for i = 1, picture.width * picture.height * constants.elementCount, constants.elementCount do - --Получаем символ из неоптимизированного массива - background, foreground, alpha, symbol = picture[i], picture[i + 1], picture[i + 2], picture[i + 3] - --Группируем картинку по цветам - optimizedPicture[alpha] = optimizedPicture[alpha] or {} - optimizedPicture[alpha][symbol] = optimizedPicture[alpha][symbol] or {} - optimizedPicture[alpha][symbol][foreground] = optimizedPicture[alpha][symbol][foreground] or {} - optimizedPicture[alpha][symbol][foreground][background] = optimizedPicture[alpha][symbol][foreground][background] or {} - optimizedPicture[alpha][symbol][foreground][background][yPos] = optimizedPicture[alpha][symbol][foreground][background][yPos] or {} - - table.insert(optimizedPicture[alpha][symbol][foreground][background][yPos], xPos) - --Если xPos достигает width изображения, то сбросить на 1, иначе xPos++ - xPos = (xPos == picture.width) and 1 or xPos + 1 - --Если xPos равняется 1, то yPos++, а если нет, то похуй - yPos = (xPos == 1) and yPos + 1 or yPos - end - --Возвращаем оптимизированный массив - return optimizedPicture -end - ---Нарисовать по указанным координатам картинку указанной ширины и высоты для теста -function image.create(width, height, background, foreground, alpha, symbol, random) - background, foreground, alpha, symbol = background or 0x0, foreground or 0x0, alpha or 0x0, symbol or " " - local picture, symbolArray = {width = width, height = height}, {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "Й", "К", "Л", "И", "Н", "О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ы", "Ь", "Э", "Ю", "Я"} - for i = 1, picture.width * picture.height do - if random then - background = math.random(0x000000, 0xffffff) - foreground = math.random(0x000000, 0xffffff) - symbol = symbolArray[math.random(1, #symbolArray)] - end - table.insert(picture, background) - table.insert(picture, foreground) - table.insert(picture, alpha) - table.insert(picture, symbol) - end - return picture -end - --- Функция оптимизации цвета текста и символов у картинки, уменьшает число GPU-операций при отрисовке из буфера --- Вызывается только при сохранении файла, так что на быстродействии не сказывается, --- а в целом штука очень и очень полезная. Фиксит криворукость художников. -function image.optimize(picture) - local i1, i2, i3 = 0, 0, 0 - for i = 1, #picture, constants.elementCount do - --Уменьшаем нагрузку на ЦОПЕ - i1, i2, i3 = i + 1, i + 2, i + 3 - --Если цвет фона равен цвету текста, и используется псевдографические полупиксели - if picture[i] == picture[i1] and (picture[i3] == "▄" or picture[i3] == "▀") then - picture[i3] = " " - end - --Если символ равен пролбелу, т.е. цвет текста не учитывается - if picture[i3] == " " then - picture[i1] = 0x000000 - end - end - - return picture -end - ---Получить пиксель из изображения по указанным координатам -function image.get(picture, x, y) - if x >= 1 and y >= 1 and x <= picture.width and y <= picture.height then - local index = convertCoordsToIndex(x, y, picture.width) - return picture[index], picture[index + 1], picture[index + 2], picture[index + 3] - else - return nil - end -end - ---Установить пиксель в изображении по указанным координатам -function image.set(picture, x, y, background, foreground, alpha, symbol, debug) - if x >= 1 and y >= 1 and x <= picture.width and y <= picture.height then - local index = convertCoordsToIndex(x, y, picture.width) - picture[index] = background or 0xFF00FF - picture[index + 1] = foreground or 0xFF00FF - picture[index + 2] = alpha or 0x00 - picture[index + 3] = symbol or " " - return picture - else - error("Can't set pixel because it's located out of image coordinates: x = " .. x .. ", y = " .. y) - end -end - ------------------------------------------- Функция снятия скриншота с экрана ------------------------------------------------ - ---Сделать скриншот экрана и сохранить его по указанному пути -function image.screenshot(path) - local picture = {} - local foreground, background, symbol - picture.width, picture.height = component.gpu.getResolution() - - for j = 1, picture.height do - for i = 1, picture.width do - symbol, foreground, background = component.gpu.get(i, j) - table.insert(picture, background) - table.insert(picture, foreground) - table.insert(picture, 0x00) - table.insert(picture, symbol) - end - end - - image.save(path, picture) -end - ------------------------------------------- Методы трансформирования изображения ------------------------------------------------ - ---Вставка ряда пикселей -function image.insertRow(picture, y, rowArray) - local index = convertCoordsToIndex(1, y, picture.width) - for i = 1, #rowArray, 4 do - table.insert(picture, index, rowArray[i + 3]) - table.insert(picture, index, rowArray[i + 2]) - table.insert(picture, index, rowArray[i + 1]) - table.insert(picture, index, rowArray[i]) - index = index + 4 - end - picture.height = picture.height + 1 - return picture -end - -function image.insertColumn(picture, x, columnArray) - local index = convertCoordsToIndex(x, 1, picture.width) - for i = 1, #columnArray, 4 do - table.insert(picture, index, columnArray[i + 3]) - table.insert(picture, index, columnArray[i + 2]) - table.insert(picture, index, columnArray[i + 1]) - table.insert(picture, index, columnArray[i]) - index = index + picture.width * 4 + 4 - end - picture.width = picture.width + 1 - return picture -end - ---Удаление ряда пикселей -function image.removeRow(picture, y) - local index = convertCoordsToIndex(1, y, picture.width) - for i = 1, picture.width * 4 do table.remove(picture, index) end - picture.height = picture.height - 1 - return picture -end - ---Удаление колонки пикселей -function image.removeColumn(picture, x) - local index = convertCoordsToIndex(x, 1, picture.width) - for i = 1, picture.height do - for j = 1, 4 do table.remove(picture, index) end - index = index + (picture.width) * 4 - 4 - end - picture.width = picture.width - 1 - return picture -end - ---Получение ряда пикселей -function image.getRow(picture, y) - local row, background, foreground, alpha, symbol = {width = picture.width, height = 1} - for x = 1, picture.width do - background, foreground, alpha, symbol = image.get(picture, x, y) - table.insert(row, background) - table.insert(row, foreground) - table.insert(row, alpha) - table.insert(row, symbol) - end - return row -end - ---Получение колонки пикселей -function image.getColumn(picture, x) - local column, background, foreground, alpha, symbol = {width = 1, height = picture.height} - for y = 1, picture.height do - background, foreground, alpha, symbol = image.get(picture, x, y) - table.insert(column, background) - table.insert(column, foreground) - table.insert(column, alpha) - table.insert(column, symbol) - end - return column -end - ---Создание копии массива изображения -function image.duplicate(picture) - local newPicture = {width = picture.width, height = picture.height} - for i = 1, #picture do newPicture[i] = picture[i] end - return newPicture -end - ---Аналог свободного трансформирования из фотошопа -function image.transform(picture, newWidth, newHeight) - local newPicture = image.duplicate(picture) - local widthScale, heightScale = newWidth / picture.width, newHeight / picture.height - local deltaWidth, deltaHeight = math.abs(newWidth - picture.width), math.abs(newHeight - picture.height) - local widthIteration, heightIteration = widthScale > 1 and newWidth / deltaWidth or picture.width / deltaWidth, heightScale > 1 and newHeight / deltaHeight or picture.height / deltaHeight - - -- ecs.error(widthIteration, heightIteration, deltaWidth, picture.width, newWidth) - - --Сжимаем шакалов по ширине - if widthScale > 1 then - local x = 1 - while x <= newPicture.width do - if math.floor(x % widthIteration) == 0 then newPicture = image.insertColumn(newPicture, x, image.getColumn(newPicture, x - 1)) end - x = x + 1 - end - elseif widthScale < 1 then - local x = 1 - while x <= newPicture.width do - if math.floor(x % widthIteration) == 0 then newPicture = image.removeColumn(newPicture, x) end - x = x + 1 - end - end - - --И по высоте - if heightScale > 1 then - local y = 1 - while y <= newPicture.height do - if math.floor(y % heightIteration) == 0 then newPicture = image.insertRow(newPicture, y, image.getRow(newPicture, y - 1)) end - y = y + 1 - end - elseif heightScale < 1 then - local y = 1 - while y <= newPicture.height do - if math.floor(y % heightIteration) == 0 then newPicture = image.removeRow(newPicture, y) end - y = y + 1 - end - end - - return newPicture -end - -function image.expand(picture, mode, countOfPixels, background, foreground, alpha, symbol) - local column = {}; for i = 1, picture.height do table.insert(column, background or 0xFFFFFF); table.insert(column, foreground or 0xFFFFFF); table.insert(column, alpha or 0x00); table.insert(column, symbol or " ") end - local row = {}; for i = 1, picture.height do table.insert(row, background or 0xFFFFFF); table.insert(row, foreground or 0xFFFFFF); table.insert(row, alpha or 0x00); table.insert(row, symbol or " ") end - - if mode == "fromRight" then - for i = 1, countOfPixels do picture = image.insertColumn(picture, picture.width + 1, column) end - elseif mode == "fromLeft" then - for i = 1, countOfPixels do picture = image.insertColumn(picture, 1, column) end - elseif mode == "fromTop" then - for i = 1, countOfPixels do picture = image.insertRow(picture, 1, row) end - elseif mode == "fromBottom" then - for i = 1, countOfPixels do picture = image.insertRow(picture, picture.height + 1, row) end - else - error("Wrong image expanding mode: only 'fromRight', 'fromLeft', 'fromTop' and 'fromBottom' are supported.") - end - - return picture -end - -function image.crop(picture, mode, countOfPixels) - if mode == "fromRight" then - for i = 1, countOfPixels do picture = image.removeColumn(picture, picture.width) end - elseif mode == "fromLeft" then - for i = 1, countOfPixels do picture = image.removeColumn(picture, 1) end - elseif mode == "fromTop" then - for i = 1, countOfPixels do picture = image.removeRow(picture, 1) end - elseif mode == "fromBottom" then - for i = 1, countOfPixels do picture = image.removeRow(picture, picture.height) end - else - error("Wrong image cropping mode: only 'fromRight', 'fromLeft', 'fromTop' and 'fromBottom' are supported.") - end - - return picture -end - -function image.flipVertical(picture) - local newPicture = {}; newPicture.width = picture.width; newPicture.height = picture.height - for j = picture.height, 1, -1 do - for i = 1, picture.width do - local index = convertCoordsToIndex(i, j, picture.width) - table.insert(newPicture, picture[index]); table.insert(newPicture, picture[index + 1]); table.insert(newPicture, picture[index + 2]); table.insert(newPicture, picture[index + 3]) - picture[index], picture[index + 1], picture[index + 2], picture[index + 3] = nil, nil, nil, nil - end - end - return newPicture -end - -function image.flipHorizontal(picture) - local newPicture = {}; newPicture.width = picture.width; newPicture.height = picture.height - for j = 1, picture.height do - for i = picture.width, 1, -1 do - local index = convertCoordsToIndex(i, j, picture.width) - table.insert(newPicture, picture[index]); table.insert(newPicture, picture[index + 1]); table.insert(newPicture, picture[index + 2]); table.insert(newPicture, picture[index + 3]) - picture[index], picture[index + 1], picture[index + 2], picture[index + 3] = nil, nil, nil, nil - end - end - return newPicture -end - -function image.rotate(picture, angle) - local function rotateBy90(picture) - local newPicture = {}; newPicture.width = picture.height; newPicture.height = picture.width - for i = 1, picture.width do - for j = picture.height, 1, -1 do - local index = convertCoordsToIndex(i, j, picture.width) - table.insert(newPicture, picture[index]); table.insert(newPicture, picture[index + 1]); table.insert(newPicture, picture[index + 2]); table.insert(newPicture, picture[index + 3]) - picture[index], picture[index + 1], picture[index + 2], picture[index + 3] = nil, nil, nil, nil - end - end - return newPicture - end - - local function rotateBy180(picture) - local newPicture = {}; newPicture.width = picture.width; newPicture.height = picture.height - for j = picture.height, 1, -1 do - for i = picture.width, 1, -1 do - local index = convertCoordsToIndex(i, j, picture.width) - table.insert(newPicture, picture[index]); table.insert(newPicture, picture[index + 1]); table.insert(newPicture, picture[index + 2]); table.insert(newPicture, picture[index + 3]) - picture[index], picture[index + 1], picture[index + 2], picture[index + 3] = nil, nil, nil, nil - end - end - return newPicture - end - - local function rotateBy270(picture) - local newPicture = {}; newPicture.width = picture.height; newPicture.height = picture.width - for i = picture.width, 1, -1 do - for j = 1, picture.height do - local index = convertCoordsToIndex(i, j, picture.width) - table.insert(newPicture, picture[index]); table.insert(newPicture, picture[index + 1]); table.insert(newPicture, picture[index + 2]); table.insert(newPicture, picture[index + 3]) - picture[index], picture[index + 1], picture[index + 2], picture[index + 3] = nil, nil, nil, nil - end - end - return newPicture - end - - if angle == 90 then - return rotateBy90(picture) - elseif angle == 180 then - return rotateBy180(picture) - elseif angle == 270 then - return rotateBy270(picture) - else - error("Can't rotate image: angle must be 90, 180 or 270 degrees.") - end -end - ------------------------------------------- Функции для работы с цветом ----------------------------------------------- - -function image.hueSaturationBrightness(picture, hue, saturation, brightness) - local function calculateBrightnessChanges(color) - local h, s, b = colorlib.HEXtoHSB(color) - b = b + brightness; if b < 0 then b = 0 elseif b > 100 then b = 100 end - s = s + saturation; if s < 0 then s = 0 elseif s > 100 then s = 100 end - h = h + hue; if h < 0 then h = 0 elseif h > 360 then h = 360 end - return colorlib.HSBtoHEX(h, s, b) - end - - for i = 1, #picture, 4 do - picture[i] = calculateBrightnessChanges(picture[i]) - picture[i + 1] = calculateBrightnessChanges(picture[i + 1]) - end - - return picture -end - -function image.hue(picture, hue) - return image.hueSaturationBrightness(picture, hue, 0, 0) -end - -function image.saturation(picture, saturation) - return image.hueSaturationBrightness(picture, 0, saturation, 0) -end - -function image.brightness(picture, brightness) - return image.hueSaturationBrightness(picture, 0, 0, brightness) -end - -function image.blackAndWhite(picture) - return image.hueSaturationBrightness(picture, 0, -100, 0) -end - -function image.colorBalance(picture, r, g, b) - local function calculateRGBChanges(color) - local rr, gg, bb = colorlib.HEXtoRGB(color) - rr = rr + r; gg = gg + g; bb = bb + b - if rr < 0 then rr = 0 elseif rr > 255 then rr = 255 end - if gg < 0 then gg = 0 elseif gg > 255 then gg = 255 end - if bb < 0 then bb = 0 elseif bb > 255 then bb = 255 end - return colorlib.RGBtoHEX(rr, gg, bb) - end - - for i = 1, #picture, 4 do - picture[i] = calculateRGBChanges(picture[i]) - picture[i + 1] = calculateRGBChanges(picture[i + 1]) - end - - return picture -end - -function image.invert(picture) - for i = 1, #picture, 4 do - picture[i] = 0xffffff - picture[i] - picture[i + 1] = 0xffffff - picture[i + 1] - end - return picture -end - -function image.photoFilter(picture, color, transparency) - if transparency < 0 then transparency = 0 elseif transparency > 255 then transparency = 255 end - for i = 1, #picture, 4 do - picture[i] = colorlib.alphaBlend(picture[i], color, transparency) - picture[i + 1] = colorlib.alphaBlend(picture[i + 1], color, transparency) - end - return picture -end - -function image.replaceColor(picture, fromColor, toColor) - for i = 1, #picture, 4 do - if picture[i] == fromColor then picture[i] = toColor end - end - return picture -end - ---Функция размытия по Гауссу -function image.gaussianBlur(picture, radius, force) - --Функция для генерации матрицы размытия - local function createConvolutionMatrix(maximumValue, matrixSize) - local delta = maximumValue / matrixSize - local matrix = {} - for y = 1, matrixSize do - for x = 1, matrixSize do - local value = ((x - 1) * delta + (y - 1) * delta) / 2 - matrix[y] = matrix[y] or {} - matrix[y][x] = value - end - end - return matrix - end - - --Функция для распределения стартового цвета на указанный пиксель на основе указанного значения матрицы - local function spreadPixelToSpecifiedCoordinates(picture, xCoordinate, yCoordinate, matrixValue, startBackground, startForeground, startAlpha, startSymbol) - local matrixBackground, matrixForeground, matrixAlpha, matrixSymbol = image.get(picture, xCoordinate, yCoordinate) - - if matrixBackground and matrixForeground then - local newBackground = colorlib.alphaBlend(startBackground, matrixBackground, matrixValue) - --Пизданись оно все в жопу, ебанина - --Короч, смари. Если символ равен пробелу, то мы полюбэ не учитываем цвет текста, верно? - --Но в будущих итерациях это цвет будет учтен, поэтому возникали ссаные баги графические - --Поэтому даже для ебучего пробела мы присваиваем значение цвета текста, равному НОВОМУ цвету фона - --Т.е. вроде бы как они и равны, но потом охуенно все будет, угу - local newForeground = matrixSymbol == " " and newBackground or colorlib.alphaBlend(startForeground, matrixForeground, matrixValue) - - image.set(picture, xCoordinate, yCoordinate, newBackground, newForeground, 0x00, matrixSymbol) - end - end - - --Функция, распределяющая указанный пиксель по соседним пикселям на основе матрицы - local function spreadColorToOtherPixels(picture, xStart, yStart, matrix) - --Получаем стартовые данные о пикселе - local startBackground, startForeground, startAlpha, startSymbol = image.get(picture, xStart, yStart) - local xCoordinate, yCoordinate - --Перебираем матрицу - for yMatrix = 1, #matrix do - for xMatrix = 1, #matrix[yMatrix] do - --Игнорируем стартовый пиксель, на кой хер его размывать-то? - if not (xMatrix == 1 and yMatrix == 1) then - --Получаем координаты новых пикселей в изображении - --И в обратном направлении матрицы - xCoordinate, yCoordinate = xStart - xMatrix + 1, yStart - yMatrix + 1 - spreadPixelToSpecifiedCoordinates(picture, xCoordinate, yCoordinate, matrix[yMatrix][xMatrix], startBackground, startForeground, startAlpha, startSymbol) - --Для начала в правильную сторону матрицы - xCoordinate, yCoordinate = xStart + xMatrix - 1, yStart + yMatrix - 1 - spreadPixelToSpecifiedCoordinates(picture, xCoordinate, yCoordinate, matrix[yMatrix][xMatrix], startBackground, startForeground, startAlpha, startSymbol) - end - end - end - end - - --Генерируем матрицу - local matrix = createConvolutionMatrix(force or 0x55, radius) - --Распределяем все пиксели по изображению - for y = 1, picture.height do - for x = 1, picture.width do - spreadColorToOtherPixels(picture, x, y, matrix) - end - end - return picture -end - ------------------------------------------ Строковая обработка изображений ------------------------------------------------------------------- - ---Преобразовать изображение в строковую интерпретацию, которая может быть вставлена в код ---Удобно, если не хочется возиться с файловой системой -function image.toString(picture) - local stringedPicture = {} - picture = convertImageColorsTo8Bit(picture) - table.insert(stringedPicture, string.format("%02X", picture.width)) - table.insert(stringedPicture, string.format("%02X", picture.height)) - for i = 1, #picture, 4 do - table.insert(stringedPicture, string.format("%02X", picture[i])) - table.insert(stringedPicture, string.format("%02X", picture[i + 1])) - table.insert(stringedPicture, string.format("%02X", picture[i + 2])) - table.insert(stringedPicture, picture[i + 3]) - end - picture = convertImageColorsTo24Bit(picture) - return table.concat(stringedPicture) -end - ---Получить изображение из строковой интерпретации, созданной ранее -function image.fromString(stringedPicture) - local picture = {} - local subIndex = 1 - picture.width = tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1)); subIndex = subIndex + 2 - picture.height = tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1)); subIndex = subIndex + 2 - - for pixel = 1, picture.width * picture.height do - table.insert(picture, tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1))); subIndex = subIndex + 2 - table.insert(picture, tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1))); subIndex = subIndex + 2 - table.insert(picture, tonumber("0x" .. unicode.sub(stringedPicture, subIndex, subIndex + 1))); subIndex = subIndex + 2 - table.insert(picture, unicode.sub(stringedPicture, subIndex, subIndex)); subIndex = subIndex + 1 - end - picture = convertImageColorsTo24Bit(picture) - return picture -end - ------------------------------------------ Основные функции программы ------------------------------------------------------------------- - ---Сохранить изображение любого поддерживаемого формата -function image.save(path, picture, encodingMethod) - encodingMethod = encodingMethod or 4 - --Создать папку под файл, если ее нет - fs.makeDirectory(fs.path(path)) - --Получаем формат указанного файла - local fileFormat = getFileFormat(path) - - --Проверяем соответствие формата файла - if fileFormat == constants.compressedFileFormat then - --Оптимизируем картинку - picture = image.optimize(picture) - --Открываем файл - local file = io.open(path, "w") - --Записываем сигнатуру - writeSignature(file) - --Разбираемся с кодировкой - if encodingMethod == 0 or string.lower(encodingMethod) == "raw" then - file:write(string.char(encodingMethod)) - saveRaw(file, picture) - elseif encodingMethod == 1 or string.lower(encodingMethod) == "ocif1" then - file:write(string.char(encodingMethod)) - saveOCIF1(file, picture) - elseif encodingMethod == 2 or string.lower(encodingMethod) == "ocif2" then - file:write(string.char(encodingMethod)) - saveOCIF2(file, picture) - elseif encodingMethod == 3 or string.lower(encodingMethod) == "ocif3" then - error("Saving in encoding method 3 is deprecated and no longer supported. Use method 4 instead of it.") - elseif encodingMethod == 4 or string.lower(encodingMethod) == "ocif4" then - file:write(string.char(encodingMethod)) - picture = convertImageColorsTo8Bit(picture) - saveOCIF2(file, picture, true) - picture = convertImageColorsTo24Bit(picture) - elseif encodingMethod == 6 then - file:close() - file = io.open(path, "w") - file:write(image.toString(picture)) - file:close() - else - file:close() - error("Unsupported encoding method.\n") - end - else - file:close() - error("Unsupported file format.\n") - end -end - ---Загрузить изображение любого поддерживаемого формата -function image.load(path) - --Кинуть ошибку, если такого файла не существует - if not fs.exists(path) then error("File \""..path.."\" does not exists.\n") end - --Получаем формат указанного файла - local fileFormat = getFileFormat(path) - --Проверяем соответствие формата файла - if fileFormat == constants.compressedFileFormat then - local file = io.open(path, "rb") - --Читаем сигнатуру файла - readSignature(file) - --Читаем метод обработки изображения - local encodingMethod = string.byte(file:read(1)) - --Читаем файлы в зависимости от метода - --print("Загружаю файл типа " .. encodingMethod) - if encodingMethod == 0 then - return image.optimize(loadRaw(file)) - elseif encodingMethod == 1 then - return image.optimize(loadOCIF1(file)) - elseif encodingMethod == 2 then - return image.optimize(loadOCIF2(file)) - elseif encodingMethod == 3 then - return image.optimize(loadOCIF2(file, true)) - elseif encodingMethod == 4 then - return image.optimize(loadOCIF2(file, true, true)) - else - file:close() - error("Unsupported encoding method: " .. encodingMethod .. "\n") - end - --Поддержка ПНГ-формата - elseif fileFormat == constants.pngFileFormat then - return image.loadPng(path) - else - error("Unsupported file format: " .. fileFormat .. "\n") - end -end - ---Отрисовка изображения типа 3 (подробнее о типах см. конец файла) -function image.draw(x, y, picture) - --Конвертируем в групповое изображение - picture = image.convertToGroupedImage(picture) - --Все как обычно - x, y = x - 1, y - 1 - - local xPos, yPos, currentBackground - for alpha in pairs(picture) do - for symbol in pairs(picture[alpha]) do - for foreground in pairs(picture[alpha][symbol]) do - if component.gpu.getForeground ~= foreground then component.gpu.setForeground(foreground) end - for background in pairs(picture[alpha][symbol][foreground]) do - if component.gpu.getBackground ~= background then component.gpu.setBackground(background) end - currentBackground = background - - for yArray in pairs(picture[alpha][symbol][foreground][background]) do - for xArray = 1, #picture[alpha][symbol][foreground][background][yArray] do - xPos, yPos = x + picture[alpha][symbol][foreground][background][yArray][xArray], y + yArray - - --Если альфа имеется, но она не совсем прозрачна - if (alpha > 0x00 and alpha < 0xFF) or (alpha == 0xFF and symbol ~= " ")then - _, _, currentBackground = component.gpu.get(xPos, yPos) - currentBackground = colorlib.alphaBlend(currentBackground, background, alpha) - component.gpu.setBackground(currentBackground) - - component.gpu.set(xPos, yPos, symbol) - - elseif alpha == 0x00 then - if currentBackground ~= background then - currentBackground = background - component.gpu.setBackground(currentBackground) - end - - component.gpu.set(xPos, yPos, symbol) - end - --ecs.wait() - end - end - end - end - end - end -end - -local function createSaveAndLoadFiles() - ecs.prepareToExit() - ecs.error("Создаю/загружаю изображение") - -- local cyka = image.load("MineOS/System/OS/Icons/Love.pic") - local cyka = image.createImage(4, 4) - ecs.error("Рисую загруженное изображение") - image.draw(2, 2, cyka) - ecs.error("Сохраняю его в 4 форматах") - image.save("0.pic", cyka, 0) - image.save("1.pic", cyka, 1) - image.save("4.pic", cyka, 4) - ecs.prepareToExit() - ecs.error("Загружаю все 4 формата и рисую их") - local cyka0 = image.load("0.pic") - image.draw(2, 2, cyka0) - local cyka1 = image.load("1.pic") - image.draw(10, 2, cyka1) - local cyka4 = image.load("4.pic") - image.draw(34, 2, cyka4) -end - ------------------------------------------------------------------------------------------------------------------------- - --- local picture = image.load("MineOS/System/OS/Icons/Love.pic") --- buffer.clear(0xFF8888) --- buffer.draw(true) - --- buffer.image(1, 1, picture) --- buffer.draw() - --- local newPicture = transform(picture, 0.5, 2) - --- local columnArray = {}; for i = 1, picture.height do table.insert(columnArray, 0xFFFFFF); table.insert(columnArray, 0x000000); table.insert(columnArray, 0x00); table.insert(columnArray, " ") end --- local rowArray = {}; for i = 1, picture.width do table.insert(rowArray, 0xFFFFFF); table.insert(rowArray, 0x000000); table.insert(rowArray, 0x00); table.insert(rowArray, " ") end --- local rowArray = image.getRow(picture, 2) --- picture = image.insertColumn(picture, 1, columnArray) --- picture = image.insertRow(picture, 3, rowArray) --- picture = image.removeColumn(picture, 1) - --- buffer.image(1, 19, picture) --- buffer.image(1, 19, newPicture) --- buffer.draw() - ------------------------------------------------------------------------------------------------------------------------- - -return image - - - - - - - - - - - -Flib/internet.lua -tlocal buffer = require("buffer") -local component = require("component") -local event = require("event") - -local internet = {} - -------------------------------------------------------------------------------- - -function internet.request(url, data, headers) - checkArg(1, url, "string") - checkArg(2, data, "string", "table", "nil") - checkArg(3, headers, "table", "nil") - - local inet = component.internet - if not inet then - error("no primary internet card found", 2) - end - - local post - if type(data) == "string" then - post = data - elseif type(data) == "table" then - for k, v in pairs(data) do - post = post and (post .. "&") or "" - post = post .. tostring(k) .. "=" .. tostring(v) - end - end - - local request, reason = inet.request(url, post, headers) - if not request then - error(reason, 2) - end - - return function() - while true do - local data, reason = request.read() - if not data then - request.close() - if reason then - error(reason, 2) - else - return nil -- eof - end - elseif #data > 0 then - return data - end - -- else: no data, block - os.sleep(0) - end - end -end - -------------------------------------------------------------------------------- - -local socketStream = {} - -function socketStream:close() - if self.socket then - self.socket.close() - self.socket = nil - end -end - -function socketStream:seek() - return nil, "bad file descriptor" -end - -function socketStream:read(n) - if not self.socket then - return nil, "connection is closed" - end - return self.socket.read(n) -end - -function socketStream:write(value) - if not self.socket then - return nil, "connection is closed" - end - while #value > 0 do - local written, reason = self.socket.write(value) - if not written then - return nil, reason - end - value = string.sub(value, written + 1) - end - return true -end - -function internet.socket(address, port) - checkArg(1, address, "string") - checkArg(2, port, "number", "nil") - if port then - address = address .. ":" .. port - end - - local inet = component.internet - local socket, reason = inet.connect(address) - if not socket then - return nil, reason - end - - local stream = {inet = inet, socket = socket} - local metatable = {__index = socketStream, - __metatable = "socketstream"} - return setmetatable(stream, metatable) -end - -function internet.open(address, port) - local stream, reason = internet.socket(address, port) - if not stream then - return nil, reason - end - return buffer.new("rwb", stream) -end - -------------------------------------------------------------------------------- - -return internetF -lib/io.lua klocal io = {} - -------------------------------------------------------------------------------- - -function io.close(file) - return (file or io.output()):close() -end - -function io.flush() - return io.output():flush() -end - -function io.lines(filename, ...) - if filename then - local file, reason = io.open(filename) - if not file then - error(reason, 2) - end - local args = table.pack(...) - return function() - local result = table.pack(file:read(table.unpack(args, 1, args.n))) - if not result[1] then - if result[2] then - error(result[2], 2) - else -- eof - file:close() - return nil - end - end - return table.unpack(result, 1, result.n) - end - else - return io.input():lines() - end -end - -function io.open(path, mode) - -- These requires are not on top because this is a bootstrapped file. - local stream, result = require("filesystem").open(path, mode) - if stream then - return require("buffer").new(mode, stream) - else - return nil, result - end -end - -function io.stream(fd,file,mode) - checkArg(1,fd,'number') - assert(fd>=0,'fd must be >= 0. 0 is input, 1 is stdout, 2 is stderr') - if file then - if type(file) == "string" then - local result, reason = io.open(file, mode) - if not result then - error(reason, 2) - end - file = result - elseif not io.type(file) then - error("bad argument #1 (string or file expected, got " .. type(file) .. ")", 2) - end - require("process").info().data.io[fd] = file - end - return require("process").info().data.io[fd] -end - -function io.input(file) - return io.stream(0, file, 'r') -end - -function io.output(file) - return io.stream(1, file,'w') -end - -function io.error(file) - return io.stream(2, file,'w') -end - -function io.popen(prog, mode, env) - return require('pipes').popen(prog, mode, env) -end - -function io.read(...) - return io.input():read(...) -end - -function io.tmpfile() - local name = os.tmpname() - if name then - return io.open(name, "a") - end -end - -function io.type(object) - if type(object) == "table" then - if getmetatable(object) == "file" then - if object.stream.handle then - return "file" - else - return "closed file" - end - end - end - return nil -end - -function io.write(...) - return io.output():write(...) -end - -------------------------------------------------------------------------------- - -return io -F lib/json.lua-- -*- coding: utf-8 -*- --- --- Simple JSON encoding and decoding in pure Lua. --- --- Copyright 2010-2014 Jeffrey Friedl --- http://regex.info/blog/ --- --- Latest version: http://regex.info/blog/lua/json --- --- This code is released under a Creative Commons CC-BY "Attribution" License: --- http://creativecommons.org/licenses/by/3.0/deed.en_US --- --- It can be used for any purpose so long as the copyright notice above, --- the web-page links above, and the 'AUTHOR_NOTE' string below are --- maintained. Enjoy. --- -local VERSION = 20141223.14 -- version history at end of file -local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20141223.14 ]-" - --- --- The 'AUTHOR_NOTE' variable exists so that information about the source --- of the package is maintained even in compiled versions. It's also --- included in OBJDEF below mostly to quiet warnings about unused variables. --- -local OBJDEF = { - VERSION = VERSION, - AUTHOR_NOTE = AUTHOR_NOTE, -} - - --- --- Simple JSON encoding and decoding in pure Lua. --- http://www.json.org/ --- --- --- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines --- --- local lua_value = JSON:decode(raw_json_text) --- --- local raw_json_text = JSON:encode(lua_table_or_value) --- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability --- --- --- --- DECODING (from a JSON string to a Lua table) --- --- --- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines --- --- local lua_value = JSON:decode(raw_json_text) --- --- If the JSON text is for an object or an array, e.g. --- { "what": "books", "count": 3 } --- or --- [ "Larry", "Curly", "Moe" ] --- --- the result is a Lua table, e.g. --- { what = "books", count = 3 } --- or --- { "Larry", "Curly", "Moe" } --- --- --- The encode and decode routines accept an optional second argument, --- "etc", which is not used during encoding or decoding, but upon error --- is passed along to error handlers. It can be of any type (including nil). --- --- --- --- ERROR HANDLING --- --- With most errors during decoding, this code calls --- --- JSON:onDecodeError(message, text, location, etc) --- --- with a message about the error, and if known, the JSON text being --- parsed and the byte count where the problem was discovered. You can --- replace the default JSON:onDecodeError() with your own function. --- --- The default onDecodeError() merely augments the message with data --- about the text and the location if known (and if a second 'etc' --- argument had been provided to decode(), its value is tacked onto the --- message as well), and then calls JSON.assert(), which itself defaults --- to Lua's built-in assert(), and can also be overridden. --- --- For example, in an Adobe Lightroom plugin, you might use something like --- --- function JSON:onDecodeError(message, text, location, etc) --- LrErrors.throwUserError("Internal Error: invalid JSON data") --- end --- --- or even just --- --- function JSON.assert(message) --- LrErrors.throwUserError("Internal Error: " .. message) --- end --- --- If JSON:decode() is passed a nil, this is called instead: --- --- JSON:onDecodeOfNilError(message, nil, nil, etc) --- --- and if JSON:decode() is passed HTML instead of JSON, this is called: --- --- JSON:onDecodeOfHTMLError(message, text, nil, etc) --- --- The use of the fourth 'etc' argument allows stronger coordination --- between decoding and error reporting, especially when you provide your --- own error-handling routines. Continuing with the the Adobe Lightroom --- plugin example: --- --- function JSON:onDecodeError(message, text, location, etc) --- local note = "Internal Error: invalid JSON data" --- if type(etc) = 'table' and etc.photo then --- note = note .. " while processing for " .. etc.photo:getFormattedMetadata('fileName') --- end --- LrErrors.throwUserError(note) --- end --- --- : --- : --- --- for i, photo in ipairs(photosToProcess) do --- : --- : --- local data = JSON:decode(someJsonText, { photo = photo }) --- : --- : --- end --- --- --- --- --- --- DECODING AND STRICT TYPES --- --- Because both JSON objects and JSON arrays are converted to Lua tables, --- it's not normally possible to tell which original JSON type a --- particular Lua table was derived from, or guarantee decode-encode --- round-trip equivalency. --- --- However, if you enable strictTypes, e.g. --- --- JSON = assert(loadfile "JSON.lua")() --load the routines --- JSON.strictTypes = true --- --- then the Lua table resulting from the decoding of a JSON object or --- JSON array is marked via Lua metatable, so that when re-encoded with --- JSON:encode() it ends up as the appropriate JSON type. --- --- (This is not the default because other routines may not work well with --- tables that have a metatable set, for example, Lightroom API calls.) --- --- --- ENCODING (from a lua table to a JSON string) --- --- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines --- --- local raw_json_text = JSON:encode(lua_table_or_value) --- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability --- local custom_pretty = JSON:encode(lua_table_or_value, etc, { pretty = true, indent = "| ", align_keys = false }) --- --- On error during encoding, this code calls: --- --- JSON:onEncodeError(message, etc) --- --- which you can override in your local JSON object. --- --- The 'etc' in the error call is the second argument to encode() --- and encode_pretty(), or nil if it wasn't provided. --- --- --- PRETTY-PRINTING --- --- An optional third argument, a table of options, allows a bit of --- configuration about how the encoding takes place: --- --- pretty = JSON:encode(val, etc, { --- pretty = true, -- if false, no other options matter --- indent = " ", -- this provides for a three-space indent per nesting level --- align_keys = false, -- see below --- }) --- --- encode() and encode_pretty() are identical except that encode_pretty() --- provides a default options table if none given in the call: --- --- { pretty = true, align_keys = false, indent = " " } --- --- For example, if --- --- JSON:encode(data) --- --- produces: --- --- {"city":"Kyoto","climate":{"avg_temp":16,"humidity":"high","snowfall":"minimal"},"country":"Japan","wards":11} --- --- then --- --- JSON:encode_pretty(data) --- --- produces: --- --- { --- "city": "Kyoto", --- "climate": { --- "avg_temp": 16, --- "humidity": "high", --- "snowfall": "minimal" --- }, --- "country": "Japan", --- "wards": 11 --- } --- --- The following three lines return identical results: --- JSON:encode_pretty(data) --- JSON:encode_pretty(data, nil, { pretty = true, align_keys = false, indent = " " }) --- JSON:encode (data, nil, { pretty = true, align_keys = false, indent = " " }) --- --- An example of setting your own indent string: --- --- JSON:encode_pretty(data, nil, { pretty = true, indent = "| " }) --- --- produces: --- --- { --- | "city": "Kyoto", --- | "climate": { --- | | "avg_temp": 16, --- | | "humidity": "high", --- | | "snowfall": "minimal" --- | }, --- | "country": "Japan", --- | "wards": 11 --- } --- --- An example of setting align_keys to true: --- --- JSON:encode_pretty(data, nil, { pretty = true, indent = " ", align_keys = true }) --- --- produces: --- --- { --- "city": "Kyoto", --- "climate": { --- "avg_temp": 16, --- "humidity": "high", --- "snowfall": "minimal" --- }, --- "country": "Japan", --- "wards": 11 --- } --- --- which I must admit is kinda ugly, sorry. This was the default for --- encode_pretty() prior to version 20141223.14. --- --- --- AMBIGUOUS SITUATIONS DURING THE ENCODING --- --- During the encode, if a Lua table being encoded contains both string --- and numeric keys, it fits neither JSON's idea of an object, nor its --- idea of an array. To get around this, when any string key exists (or --- when non-positive numeric keys exist), numeric keys are converted to --- strings. --- --- For example, --- JSON:encode({ "one", "two", "three", SOMESTRING = "some string" })) --- produces the JSON object --- {"1":"one","2":"two","3":"three","SOMESTRING":"some string"} --- --- To prohibit this conversion and instead make it an error condition, set --- JSON.noKeyConversion = true --- - - - - --- --- SUMMARY OF METHODS YOU CAN OVERRIDE IN YOUR LOCAL LUA JSON OBJECT --- --- assert --- onDecodeError --- onDecodeOfNilError --- onDecodeOfHTMLError --- onEncodeError --- --- If you want to create a separate Lua JSON object with its own error handlers, --- you can reload JSON.lua or use the :new() method. --- ---------------------------------------------------------------------------- - -local default_pretty_indent = " " -local default_pretty_options = { pretty = true, align_keys = false, indent = default_pretty_indent } - -local isArray = { __tostring = function() return "JSON array" end } isArray.__index = isArray -local isObject = { __tostring = function() return "JSON object" end } isObject.__index = isObject - - -function OBJDEF:newArray(tbl) - return setmetatable(tbl or {}, isArray) -end - -function OBJDEF:newObject(tbl) - return setmetatable(tbl or {}, isObject) -end - -local function unicode_codepoint_as_utf8(codepoint) - -- - -- codepoint is a number - -- - if codepoint <= 127 then - return string.char(codepoint) - - elseif codepoint <= 2047 then - -- - -- 110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8 - -- - local highpart = math.floor(codepoint / 0x40) - local lowpart = codepoint - (0x40 * highpart) - return string.char(0xC0 + highpart, - 0x80 + lowpart) - - elseif codepoint <= 65535 then - -- - -- 1110yyyy 10yyyyxx 10xxxxxx - -- - local highpart = math.floor(codepoint / 0x1000) - local remainder = codepoint - 0x1000 * highpart - local midpart = math.floor(remainder / 0x40) - local lowpart = remainder - 0x40 * midpart - - highpart = 0xE0 + highpart - midpart = 0x80 + midpart - lowpart = 0x80 + lowpart - - -- - -- Check for an invalid character (thanks Andy R. at Adobe). - -- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070 - -- - if ( highpart == 0xE0 and midpart < 0xA0 ) or - ( highpart == 0xED and midpart > 0x9F ) or - ( highpart == 0xF0 and midpart < 0x90 ) or - ( highpart == 0xF4 and midpart > 0x8F ) - then - return "?" - else - return string.char(highpart, - midpart, - lowpart) - end - - else - -- - -- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx - -- - local highpart = math.floor(codepoint / 0x40000) - local remainder = codepoint - 0x40000 * highpart - local midA = math.floor(remainder / 0x1000) - remainder = remainder - 0x1000 * midA - local midB = math.floor(remainder / 0x40) - local lowpart = remainder - 0x40 * midB - - return string.char(0xF0 + highpart, - 0x80 + midA, - 0x80 + midB, - 0x80 + lowpart) - end -end - -function OBJDEF:onDecodeError(message, text, location, etc) - if text then - if location then - message = string.format("%s at char %d of: %s", message, location, text) - else - message = string.format("%s: %s", message, text) - end - end - - if etc ~= nil then - message = message .. " (" .. OBJDEF:encode(etc) .. ")" - end - - if self.assert then - self.assert(false, message) - else - assert(false, message) - end -end - -OBJDEF.onDecodeOfNilError = OBJDEF.onDecodeError -OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError - -function OBJDEF:onEncodeError(message, etc) - if etc ~= nil then - message = message .. " (" .. OBJDEF:encode(etc) .. ")" - end - - if self.assert then - self.assert(false, message) - else - assert(false, message) - end -end - -local function grok_number(self, text, start, etc) - -- - -- Grab the integer part - -- - local integer_part = text:match('^-?[1-9]%d*', start) - or text:match("^-?0", start) - - if not integer_part then - self:onDecodeError("expected number", text, start, etc) - end - - local i = start + integer_part:len() - - -- - -- Grab an optional decimal part - -- - local decimal_part = text:match('^%.%d+', i) or "" - - i = i + decimal_part:len() - - -- - -- Grab an optional exponential part - -- - local exponent_part = text:match('^[eE][-+]?%d+', i) or "" - - i = i + exponent_part:len() - - local full_number_text = integer_part .. decimal_part .. exponent_part - local as_number = tonumber(full_number_text) - - if not as_number then - self:onDecodeError("bad number", text, start, etc) - end - - return as_number, i -end - - -local function grok_string(self, text, start, etc) - - if text:sub(start,start) ~= '"' then - self:onDecodeError("expected string's opening quote", text, start, etc) - end - - local i = start + 1 -- +1 to bypass the initial quote - local text_len = text:len() - local VALUE = "" - while i <= text_len do - local c = text:sub(i,i) - if c == '"' then - return VALUE, i + 1 - end - if c ~= '\\' then - VALUE = VALUE .. c - i = i + 1 - elseif text:match('^\\b', i) then - VALUE = VALUE .. "\b" - i = i + 2 - elseif text:match('^\\f', i) then - VALUE = VALUE .. "\f" - i = i + 2 - elseif text:match('^\\n', i) then - VALUE = VALUE .. "\n" - i = i + 2 - elseif text:match('^\\r', i) then - VALUE = VALUE .. "\r" - i = i + 2 - elseif text:match('^\\t', i) then - VALUE = VALUE .. "\t" - i = i + 2 - else - local hex = text:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) - if hex then - i = i + 6 -- bypass what we just read - - -- We have a Unicode codepoint. It could be standalone, or if in the proper range and - -- followed by another in a specific range, it'll be a two-code surrogate pair. - local codepoint = tonumber(hex, 16) - if codepoint >= 0xD800 and codepoint <= 0xDBFF then - -- it's a hi surrogate... see whether we have a following low - local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) - if lo_surrogate then - i = i + 6 -- bypass the low surrogate we just read - codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16) - else - -- not a proper low, so we'll just leave the first codepoint as is and spit it out. - end - end - VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint) - - else - - -- just pass through what's escaped - VALUE = VALUE .. text:match('^\\(.)', i) - i = i + 2 - end - end - end - - self:onDecodeError("unclosed string", text, start, etc) -end - -local function skip_whitespace(text, start) - - local _, match_end = text:find("^[ \n\r\t]+", start) -- [http://www.ietf.org/rfc/rfc4627.txt] Section 2 - if match_end then - return match_end + 1 - else - return start - end -end - -local grok_one -- assigned later - -local function grok_object(self, text, start, etc) - if text:sub(start,start) ~= '{' then - self:onDecodeError("expected '{'", text, start, etc) - end - - local i = skip_whitespace(text, start + 1) -- +1 to skip the '{' - - local VALUE = self.strictTypes and self:newObject { } or { } - - if text:sub(i,i) == '}' then - return VALUE, i + 1 - end - local text_len = text:len() - while i <= text_len do - local key, new_i = grok_string(self, text, i, etc) - - i = skip_whitespace(text, new_i) - - if text:sub(i, i) ~= ':' then - self:onDecodeError("expected colon", text, i, etc) - end - - i = skip_whitespace(text, i + 1) - - local new_val, new_i = grok_one(self, text, i) - - VALUE[key] = new_val - - -- - -- Expect now either '}' to end things, or a ',' to allow us to continue. - -- - i = skip_whitespace(text, new_i) - - local c = text:sub(i,i) - - if c == '}' then - return VALUE, i + 1 - end - - if text:sub(i, i) ~= ',' then - self:onDecodeError("expected comma or '}'", text, i, etc) - end - - i = skip_whitespace(text, i + 1) - end - - self:onDecodeError("unclosed '{'", text, start, etc) -end - -local function grok_array(self, text, start, etc) - if text:sub(start,start) ~= '[' then - self:onDecodeError("expected '['", text, start, etc) - end - - local i = skip_whitespace(text, start + 1) -- +1 to skip the '[' - local VALUE = self.strictTypes and self:newArray { } or { } - if text:sub(i,i) == ']' then - return VALUE, i + 1 - end - - local VALUE_INDEX = 1 - - local text_len = text:len() - while i <= text_len do - local val, new_i = grok_one(self, text, i) - - -- can't table.insert(VALUE, val) here because it's a no-op if val is nil - VALUE[VALUE_INDEX] = val - VALUE_INDEX = VALUE_INDEX + 1 - - i = skip_whitespace(text, new_i) - - -- - -- Expect now either ']' to end things, or a ',' to allow us to continue. - -- - local c = text:sub(i,i) - if c == ']' then - return VALUE, i + 1 - end - if text:sub(i, i) ~= ',' then - self:onDecodeError("expected comma or '['", text, i, etc) - end - i = skip_whitespace(text, i + 1) - end - self:onDecodeError("unclosed '['", text, start, etc) -end - - -grok_one = function(self, text, start, etc) - -- Skip any whitespace - start = skip_whitespace(text, start) - - if start > text:len() then - self:onDecodeError("unexpected end of string", text, nil, etc) - end - - if text:find('^"', start) then - return grok_string(self, text, start, etc) - - elseif text:find('^[-0123456789 ]', start) then - return grok_number(self, text, start, etc) - - elseif text:find('^%{', start) then - return grok_object(self, text, start, etc) - - elseif text:find('^%[', start) then - return grok_array(self, text, start, etc) - - elseif text:find('^true', start) then - return true, start + 4 - - elseif text:find('^false', start) then - return false, start + 5 - - elseif text:find('^null', start) then - return nil, start + 4 - - else - self:onDecodeError("can't parse JSON", text, start, etc) - end -end - -function OBJDEF:decode(text, etc) - if type(self) ~= 'table' or self.__index ~= OBJDEF then - OBJDEF:onDecodeError("JSON:decode must be called in method format", nil, nil, etc) - end - - if text == nil then - self:onDecodeOfNilError(string.format("nil passed to JSON:decode()"), nil, nil, etc) - elseif type(text) ~= 'string' then - self:onDecodeError(string.format("expected string argument to JSON:decode(), got %s", type(text)), nil, nil, etc) - end - - if text:match('^%s*$') then - return nil - end - - if text:match('^%s*<') then - -- Can't be JSON... we'll assume it's HTML - self:onDecodeOfHTMLError(string.format("html passed to JSON:decode()"), text, nil, etc) - end - - -- - -- Ensure that it's not UTF-32 or UTF-16. - -- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3), - -- but this package can't handle them. - -- - if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then - self:onDecodeError("JSON package groks only UTF-8, sorry", text, nil, etc) - end - - local success, value = pcall(grok_one, self, text, 1, etc) - - if success then - return value - else - -- if JSON:onDecodeError() didn't abort out of the pcall, we'll have received the error message here as "value", so pass it along as an assert. - if self.assert then - self.assert(false, value) - else - assert(false, value) - end - -- and if we're still here, return a nil and throw the error message on as a second arg - return nil, value - end -end - -local function backslash_replacement_function(c) - if c == "\n" then - return "\\n" - elseif c == "\r" then - return "\\r" - elseif c == "\t" then - return "\\t" - elseif c == "\b" then - return "\\b" - elseif c == "\f" then - return "\\f" - elseif c == '"' then - return '\\"' - elseif c == '\\' then - return '\\\\' - else - return string.format("\\u%04x", c:byte()) - end -end - -local chars_to_be_escaped_in_JSON_string - = '[' - .. '"' -- class sub-pattern to match a double quote - .. '%\\' -- class sub-pattern to match a backslash - .. '%z' -- class sub-pattern to match a null - .. '\001' .. '-' .. '\031' -- class sub-pattern to match control characters - .. ']' - -local function json_string_literal(value) - local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function) - return '"' .. newval .. '"' -end - -local function object_or_array(self, T, etc) - -- - -- We need to inspect all the keys... if there are any strings, we'll convert to a JSON - -- object. If there are only numbers, it's a JSON array. - -- - -- If we'll be converting to a JSON object, we'll want to sort the keys so that the - -- end result is deterministic. - -- - local string_keys = { } - local number_keys = { } - local number_keys_must_be_strings = false - local maximum_number_key - - for key in pairs(T) do - if type(key) == 'string' then - table.insert(string_keys, key) - elseif type(key) == 'number' then - table.insert(number_keys, key) - if key <= 0 or key >= math.huge then - number_keys_must_be_strings = true - elseif not maximum_number_key or key > maximum_number_key then - maximum_number_key = key - end - else - self:onEncodeError("can't encode table with a key of type " .. type(key), etc) - end - end - - if #string_keys == 0 and not number_keys_must_be_strings then - -- - -- An empty table, or a numeric-only array - -- - if #number_keys > 0 then - return nil, maximum_number_key -- an array - elseif tostring(T) == "JSON array" then - return nil - elseif tostring(T) == "JSON object" then - return { } - else - -- have to guess, so we'll pick array, since empty arrays are likely more common than empty objects - return nil - end - end - - table.sort(string_keys) - - local map - if #number_keys > 0 then - -- - -- If we're here then we have either mixed string/number keys, or numbers inappropriate for a JSON array - -- It's not ideal, but we'll turn the numbers into strings so that we can at least create a JSON object. - -- - - if self.noKeyConversion then - self:onEncodeError("a table with both numeric and string keys could be an object or array; aborting", etc) - end - - -- - -- Have to make a shallow copy of the source table so we can remap the numeric keys to be strings - -- - map = { } - for key, val in pairs(T) do - map[key] = val - end - - table.sort(number_keys) - - -- - -- Throw numeric keys in there as strings - -- - for _, number_key in ipairs(number_keys) do - local string_key = tostring(number_key) - if map[string_key] == nil then - table.insert(string_keys , string_key) - map[string_key] = T[number_key] - else - self:onEncodeError("conflict converting table with mixed-type keys into a JSON object: key " .. number_key .. " exists both as a string and a number.", etc) - end - end - end - - return string_keys, nil, map -end - --- --- Encode --- --- 'options' is nil, or a table with possible keys: --- pretty -- if true, return a pretty-printed version --- indent -- a string (usually of spaces) used to indent each nested level --- align_keys -- if true, align all the keys when formatting a table --- -local encode_value -- must predeclare because it calls itself -function encode_value(self, value, parents, etc, options, indent) - - if value == nil then - return 'null' - - elseif type(value) == 'string' then - return json_string_literal(value) - - elseif type(value) == 'number' then - if value ~= value then - -- - -- NaN (Not a Number). - -- JSON has no NaN, so we have to fudge the best we can. This should really be a package option. - -- - return "null" - elseif value >= math.huge then - -- - -- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should - -- really be a package option. Note: at least with some implementations, positive infinity - -- is both ">= math.huge" and "<= -math.huge", which makes no sense but that's how it is. - -- Negative infinity is properly "<= -math.huge". So, we must be sure to check the ">=" - -- case first. - -- - return "1e+9999" - elseif value <= -math.huge then - -- - -- Negative infinity. - -- JSON has no INF, so we have to fudge the best we can. This should really be a package option. - -- - return "-1e+9999" - else - return tostring(value) - end - - elseif type(value) == 'boolean' then - return tostring(value) - - elseif type(value) ~= 'table' then - self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) - - else - -- - -- A table to be converted to either a JSON object or array. - -- - local T = value - - if type(options) ~= 'table' then - options = {} - end - if type(indent) ~= 'string' then - indent = "" - end - - if parents[T] then - self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) - else - parents[T] = true - end - - local result_value - - local object_keys, maximum_number_key, map = object_or_array(self, T, etc) - if maximum_number_key then - -- - -- An array... - -- - local ITEMS = { } - for i = 1, maximum_number_key do - table.insert(ITEMS, encode_value(self, T[i], parents, etc, options, indent)) - end - - if options.pretty then - result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]" - else - result_value = "[" .. table.concat(ITEMS, ",") .. "]" - end - - elseif object_keys then - -- - -- An object - -- - local TT = map or T - - if options.pretty then - - local KEYS = { } - local max_key_length = 0 - for _, key in ipairs(object_keys) do - local encoded = encode_value(self, tostring(key), parents, etc, options, indent) - if options.align_keys then - max_key_length = math.max(max_key_length, #encoded) - end - table.insert(KEYS, encoded) - end - local key_indent = indent .. tostring(options.indent or "") - local subtable_indent = key_indent .. string.rep(" ", max_key_length) .. (options.align_keys and " " or "") - local FORMAT = "%s%" .. string.format("%d", max_key_length) .. "s: %s" - - local COMBINED_PARTS = { } - for i, key in ipairs(object_keys) do - local encoded_val = encode_value(self, TT[key], parents, etc, options, subtable_indent) - table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val)) - end - result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}" - - else - - local PARTS = { } - for _, key in ipairs(object_keys) do - local encoded_val = encode_value(self, TT[key], parents, etc, options, indent) - local encoded_key = encode_value(self, tostring(key), parents, etc, options, indent) - table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val)) - end - result_value = "{" .. table.concat(PARTS, ",") .. "}" - - end - else - -- - -- An empty array/object... we'll treat it as an array, though it should really be an option - -- - result_value = "[]" - end - - parents[T] = false - return result_value - end -end - - -function OBJDEF:encode(value, etc, options) - if type(self) ~= 'table' or self.__index ~= OBJDEF then - OBJDEF:onEncodeError("JSON:encode must be called in method format", etc) - end - return encode_value(self, value, {}, etc, options or nil) -end - -function OBJDEF:encode_pretty(value, etc, options) - if type(self) ~= 'table' or self.__index ~= OBJDEF then - OBJDEF:onEncodeError("JSON:encode_pretty must be called in method format", etc) - end - return encode_value(self, value, {}, etc, options or default_pretty_options) -end - -function OBJDEF.__tostring() - return "JSON encode/decode package" -end - -OBJDEF.__index = OBJDEF - -function OBJDEF:new(args) - local new = { } - - if args then - for key, val in pairs(args) do - new[key] = val - end - end - - return setmetatable(new, OBJDEF) -end - -return OBJDEF:new() - --- --- Version history: --- --- 20141223.14 The encode_pretty() routine produced fine results for small datasets, but isn't really --- appropriate for anything large, so with help from Alex Aulbach I've made the encode routines --- more flexible, and changed the default encode_pretty() to be more generally useful. --- --- Added a third 'options' argument to the encode() and encode_pretty() routines, to control --- how the encoding takes place. --- --- Updated docs to add assert() call to the loadfile() line, just as good practice so that --- if there is a problem loading JSON.lua, the appropriate error message will percolate up. --- --- 20140920.13 Put back (in a way that doesn't cause warnings about unused variables) the author string, --- so that the source of the package, and its version number, are visible in compiled copies. --- --- 20140911.12 Minor lua cleanup. --- Fixed internal reference to 'JSON.noKeyConversion' to reference 'self' instead of 'JSON'. --- (Thanks to SmugMug's David Parry for these.) --- --- 20140418.11 JSON nulls embedded within an array were being ignored, such that --- ["1",null,null,null,null,null,"seven"], --- would return --- {1,"seven"} --- It's now fixed to properly return --- {1, nil, nil, nil, nil, nil, "seven"} --- Thanks to "haddock" for catching the error. --- --- 20140116.10 The user's JSON.assert() wasn't always being used. Thanks to "blue" for the heads up. --- --- 20131118.9 Update for Lua 5.3... it seems that tostring(2/1) produces "2.0" instead of "2", --- and this caused some problems. --- --- 20131031.8 Unified the code for encode() and encode_pretty(); they had been stupidly separate, --- and had of course diverged (encode_pretty didn't get the fixes that encode got, so --- sometimes produced incorrect results; thanks to Mattie for the heads up). --- --- Handle encoding tables with non-positive numeric keys (unlikely, but possible). --- --- If a table has both numeric and string keys, or its numeric keys are inappropriate --- (such as being non-positive or infinite), the numeric keys are turned into --- string keys appropriate for a JSON object. So, as before, --- JSON:encode({ "one", "two", "three" }) --- produces the array --- ["one","two","three"] --- but now something with mixed key types like --- JSON:encode({ "one", "two", "three", SOMESTRING = "some string" })) --- instead of throwing an error produces an object: --- {"1":"one","2":"two","3":"three","SOMESTRING":"some string"} --- --- To maintain the prior throw-an-error semantics, set --- JSON.noKeyConversion = true --- --- 20131004.7 Release under a Creative Commons CC-BY license, which I should have done from day one, sorry. --- --- 20130120.6 Comment update: added a link to the specific page on my blog where this code can --- be found, so that folks who come across the code outside of my blog can find updates --- more easily. --- --- 20111207.5 Added support for the 'etc' arguments, for better error reporting. --- --- 20110731.4 More feedback from David Kolf on how to make the tests for Nan/Infinity system independent. --- --- 20110730.3 Incorporated feedback from David Kolf at http://lua-users.org/wiki/JsonModules: --- --- * When encoding lua for JSON, Sparse numeric arrays are now handled by --- spitting out full arrays, such that --- JSON:encode({"one", "two", [10] = "ten"}) --- returns --- ["one","two",null,null,null,null,null,null,null,"ten"] --- --- In 20100810.2 and earlier, only up to the first non-null value would have been retained. --- --- * When encoding lua for JSON, numeric value NaN gets spit out as null, and infinity as "1+e9999". --- Version 20100810.2 and earlier created invalid JSON in both cases. --- --- * Unicode surrogate pairs are now detected when decoding JSON. --- --- 20100810.2 added some checking to ensure that an invalid Unicode character couldn't leak in to the UTF-8 encoding --- --- 20100731.1 initial public release --- -Flib/keyboard.lua local component = require("component") - -local keyboard = {pressedChars = {}, pressedCodes = {}} - --- these key definitions are only a subset of all the defined keys --- __index loads all key data from /lib/tools/keyboard_full.lua (only once) --- new key metadata should be added here if required for boot -keyboard.keys = { - c = 0x2E, - d = 0x20, - q = 0x10, - back = 0x0E, -- backspace - delete = 0xD3, - down = 0xD0, - enter = 0x1C, - home = 0xC7, - lcontrol = 0x1D, - left = 0xCB, - lmenu = 0x38, -- left Alt - lshift = 0x2A, - pageDown = 0xD1, - rcontrol = 0x9D, - right = 0xCD, - rmenu = 0xB8, -- right Alt - rshift = 0x36, - space = 0x39, - tab = 0x0F, - up = 0xC8, - ["end"] = 0xCF, -} - --- Create inverse mapping for name lookup. -setmetatable(keyboard.keys, -{ - __index = function(tbl, k) - getmetatable(keyboard.keys).__index = nil -- to be safe - loadfile("/lib/tools/keyboard_full.lua","t",setmetatable({keyboard=keyboard},{__index=_G}))() - return tbl[k] - end -}) - -------------------------------------------------------------------------------- - -local function getKeyboardAddress(address) - if address then - return address - else - local primary = component.isAvailable("keyboard") and component.getPrimary("keyboard") - if primary then - return primary.address - end - end -end - -local function getPressedCodes(address) - address = getKeyboardAddress(address) - return address and keyboard.pressedCodes[address] or false -end - -local function getPressedChars(address) - address = getKeyboardAddress(address) - return address and keyboard.pressedChars[address] or false -end - -function keyboard.isAltDown(address) - checkArg(1, address, "string", "nil") - local pressedCodes = getPressedCodes(address) - return pressedCodes and (pressedCodes[keyboard.keys.lmenu] or pressedCodes[keyboard.keys.rmenu]) ~= nil -end - -function keyboard.isControl(char) - return type(char) == "number" and (char < 0x20 or (char >= 0x7F and char <= 0x9F)) -end - -function keyboard.isControlDown(address) - checkArg(1, address, "string", "nil") - local pressedCodes = getPressedCodes(address) - return pressedCodes and (pressedCodes[keyboard.keys.lcontrol] or pressedCodes[keyboard.keys.rcontrol]) ~= nil -end - -function keyboard.isKeyDown(charOrCode, address) - checkArg(1, charOrCode, "string", "number") - checkArg(2, address, "string", "nil") - if type(charOrCode) == "string" then - local pressedChars = getPressedChars(address) - return pressedChars and pressedChars[utf8 and utf8.codepoint(charOrCode) or charOrCode:byte()] - elseif type(charOrCode) == "number" then - local pressedCodes = getPressedCodes(address) - return pressedCodes and pressedCodes[charOrCode] - end -end - -function keyboard.isShiftDown(address) - checkArg(1, address, "string", "nil") - local pressedCodes = getPressedCodes(address) - return pressedCodes and (pressedCodes[keyboard.keys.lshift] or pressedCodes[keyboard.keys.rshift]) ~= nil -end - -------------------------------------------------------------------------------- - -return keyboard -Flib/libPNGImage.lua=0) expected") - end - if (not height) or (height < 1) or (math.floor(height) ~= height) then - error("Invalid param #2 (height) to PNGImage.newFromScratch - integer (>=0) expected") - end - - local bkg = {0, 0, 0, 0} -- Transparency - - if type(bkcol) == "table" then - if #bkcol == 3 then - bkg = {bkcol[1], bkcol[2], bkcol[3], 255} -- Opaque colour - elseif #bkcol == 4 then - bkg = {bkcol[1], bkcol[2], bkcol[3], bkcol[4]} -- Defined - else - error("Invalid format for param #3 (bkcol) to PNGImage.newFromScratch: nil or table expected, but the table must be of format {r, g, b} or {r, g, b, a} -- Invalid table") - end - elseif bkcol ~= nil then - error("Invalid format for param #3 (bkcol) to PNGImage.newFromScratch: nil or table expected, but the table must be of format {r, g, b} or {r, g, b, a} -- Parameter is not nil or table") - end - - bkg[1] = tonumber(bkg[1]) - if bkg[1] == nil then - error("PNGImage.newFromScratch: bkg[R] is not numeric") - end - bkg[2] = tonumber(bkg[2]) - if bkg[2] == nil then - error("PNGImage.newFromScratch: bkg[G] is not numeric") - end - bkg[3] = tonumber(bkg[3]) - if bkg[3] == nil then - error("PNGImage.newFromScratch: bkg[B] is not numeric") - end - bkg[4] = tonumber(bkg[4]) - if bkg[4] == nil then - error("PNGImage.newFromScratch: bkg[A] is not numeric") - end - - pngi.ihdr = { - width = width, - height = height, - bit_depth = 8, - color_type = 6, - compression_method = 0, - filter_method = 0, - interlace_method = 0 - } - - -- do this for every pixel.. i.e string.rep(str, w*h) - pngi.data = string.byte(bkg[1], bkg[2], bkg[3], bkg[4]):rep(width * height) - - return pngi -end - -function PNGImage.newFromFileHandle(fh) - local pngi = __newPNGImage() - local expecting = "\137\080\078\071\013\010\026\010" - if fh:read(8) ~= expecting then -- check the 8-byte PNG header exists - error("Not a PNG file") - end - - local ihdr - - local outss = outssmt.OutStringStream() - - while true do - local len = __read_msb_uint32(fh) - local stype = fh:read(4) - - if stype == 'IHDR' then - ihdr, msg = __parse_IHDR(fh, len) - elseif stype == 'IDAT' then - local res, msg = __parse_IDAT(fh, len, ihdr.compression_method, outss) - else - fh:read(len) -- dummy read - end - - local crc = __read_msb_uint32(fh) - - -- print("chunk:", "type=", stype, "len=", len, "crc=", crc) - - if stype == 'IEND' then - break - end - end - - fh:close() - - - if ihdr.filter_method ~= 0 then - error("Unsupported Filter Method: " .. ihdr.filter_method) - end - - if ihdr.interlace_method ~= 0 then - error("Unsupported Interlace Method (Interlacing is currently unsupported): " .. ihdr.interlace_method) - end - - if ihdr.color_type ~= PNGImage.ColourTypes.TruecolourAlpha and ihdr.color_type ~= PNGImage.ColourTypes.Truecolour then - error("Currently, only Truecolour and Truecolour+Alpha images are supported.") - end - - if ihdr.bit_depth ~= 8 then - error("Currently, only images with a bit depth of 8 are supported.") - end - - --[[ - local oh = io.open('before-decode.dat', 'wb') - oh:write(outss.str) - oh:close() - ]]-- - - -- now parse the IDAT chunks - local out2 = __parse_IDAT_effective_bytes(outss, ihdr) - - if ihdr.color_type == PNGImage.ColourTypes.Truecolour then - -- add an alpha layer so it effectively becomes RGBA, not RGB - local inp = out2.str - out2 = outssmt.OutStringStream() - - for i=1, ihdr.width*ihdr.height do - local b = ((i - 1)*3) + 1 - out2(inp:byte(b)) -- R - out2(inp:byte(b + 1)) -- G - out2(inp:byte(b + 2)) -- B - out2(255) -- A - end - end - - pngi.ihdr = ihdr - pngi.data = out2.str - - --[[ - local oh = io.open('effective.dat', 'wb') - oh:write(out2.str) - oh:close() - ]]-- - - return pngi -end - - --- Warning: Co-ordinates are Zero-based but strings are 1-based -function PNGImage:getByteOffsetForPixel(x, y) - return (((y * self.ihdr.width) + x) * 4) + 1 -end - -function PNGImage:getPixel(x, y) - local off = self:getByteOffsetForPixel(x, y) - return self.data:byte(off, off + 3) -end - -function PNGImage:setPixel(x, y, col) - local off = self:getByteOffsetForPixel(x, y) - self.data = table.concat({self.data:sub(1, off - 1), string.char(col[1], col[2], col[3], col[4]), self.data:sub(off + 4)}) -end - -function PNGImage:lineXAB(ax, y, bx, col) - for x=ax, bx do - self:setPixel(x, y, col) - end -end - -function PNGImage:lineYAB(x, ay, by, col) - for y=ay, by do - self:setPixel(x, y, col) - end -end - -function PNGImage:lineRectangleAB(ax, ay, bx, by, col) - self:lineXAB(ax, ay, bx, col) - self:lineXAB(ax, by, bx, col) - self:lineYAB(ax, ay, by, col) - self:lineYAB(bx, ay, by, col) -end - -function PNGImage:fillRectangleAB(ax, ay, bx, by, col) - for x=ax, bx do - for y=ay, by do - self:setPixel(x, y, col) - end - end -end - -function PNGImage:saveToFile(fn) - local fh = io.open(fn, 'wb') - if not fh then - error("Could not open for writing: " .. fn) - end - self:saveToFileHandle(fh) - fh:close() -end - -function PNGImage:getSize() - return self.ihdr.width, self.ihdr.height -end - -function PNGImage:generateRawIDATData(outbuf) - for y = 0, self.ihdr.height - 1 do - outbuf(0) -- filter type is 0 (Filt(x) = Orig(x)) - for x = 0, self.ihdr.width - 1 do - local r, g, b, a = self:getPixel(x, y) - outbuf(r) - outbuf(g) - outbuf(b) - outbuf(a) - end - end -end - -local ZLIB_LITERAL_LIMIT = 65535 - -local function __raw_to_literalZLIB(inbuf) - local outstr = string.char(8, 29) -- zlib headers - - while inbuf.str:len() > 0 do - if inbuf.str:len() > ZLIB_LITERAL_LIMIT then - outstr = outstr .. string.char(0) -- LITERAL[00] FINAL[0] - else - outstr = outstr .. string.char(1) -- LITERAL[00] FINAL[1] - end - local min = math.min(ZLIB_LITERAL_LIMIT, inbuf.str:len()) - outstr = table.concat({outstr, __pack_msb_uint16(min), __pack_msb_uint16(bit.bnot(min)), inbuf.str:sub(1, min)}) - inbuf.str = inbuf.str:sub(min + 1) - end - - local adler32 = 1 - - for i = 1, outstr:len() do - adler32 = DeflateLua.adler32(outstr:byte(i), adler32) - end - - outstr = table.concat({outstr, string.char(__sep_msb_uint32(adler32))}) - - return outstr -end - -function PNGImage:saveToFileHandle(fh) - -- basic PNG 'encoder' - -- most likely temporary - local expecting = "\137\080\078\071\013\010\026\010" - fh:write(expecting) - - local outbuf = outssmt.OutStringStream() - - __wbuf_msb_uint32(outbuf, self.ihdr.width) - __wbuf_msb_uint32(outbuf, self.ihdr.height) - - outbuf(8) -- bit depth - outbuf(6) -- colour type - outbuf(0) -- compression method - outbuf(0) -- filter method - outbuf(0) -- interlace method - - __write_msb_uint32(fh, outbuf.str:len()) -- length field - fh:write("IHDR") -- name of chunk - fh:write(outbuf.str) -- chunk data - __write_msb_uint32(fh, CRC32Lua.crc32_string("IHDR" .. outbuf.str)) -- chunk data CRC32 - outbuf.str = "" -- reset buffer - - -- now onto the IDAT - - self:generateRawIDATData(outbuf) - - local zlibstr = __raw_to_literalZLIB(outbuf) - - local tmpstr = "" - while zlibstr:len() > 0 do - local min = math.min(ZLIB_LITERAL_LIMIT, zlibstr:len()) - tmpstr = zlibstr:sub(1, min) - zlibstr = zlibstr:sub(min + 1) - - __write_msb_uint32(fh, tmpstr:len()) -- length field - fh:write("IDAT") -- name of chunk - fh:write(tmpstr) -- chunk data - __write_msb_uint32(fh, CRC32Lua.crc32_string("IDAT" .. tmpstr)) -- chunk data CRC32 - tmpstr = "" -- reset - end - - __write_msb_uint32(fh, 0) -- length field - fh:write("IEND") -- name of chunk - -- fh:write(tmpstr) -- chunk data - __write_msb_uint32(fh, CRC32Lua.crc32_string("IEND")) -- chunk data CRC32 -end - - - -return PNGImage -Flib/matrix.lua -local matrixLibrary = {} - ----------------------------------------------------- Matrix tables creation ---------------------------------------------------------------- - -function matrixLibrary.newIdentityMatrix(size) - local matrix = {} - - for y = 1, size do - matrix[y] = {} - for x = 1, size do - matrix[y][x] = (x == y) and 1 or 0 - end - end - - return matrix -end - -function matrixLibrary.newCofactorMatrix(matrix) - local cofactorMatrix = {} - for y = 1, #matrix do - cofactorMatrix[y] = {} - for x = 1, #matrix[y] do - cofactorMatrix[y][x] = matrixLibrary.getCofactor(matrix, y, x) - end - end - - return cofactorMatrix -end - -function matrixLibrary.newAdjugateMatrix(matrix) - return matrixLibrary.transpose(matrixLibrary.newCofactorMatrix(matrix)) -end - -function matrixLibrary.newFilledMatrix(width, height, value) - local matrix = {} - - for y = 1, height do - matrix[y] = {} - for x = 1, width do - matrix[y][x] = value - end - end - - return matrix -end - -function matrixLibrary.copy(matrix) - local newMatrix = {} - - for y = 1, #matrix do - newMatrix[y] = {} - for x = 1, #matrix[y] do - newMatrix[y][x] = matrix[y][x] - end - end - - return newMatrix -end - -function matrixLibrary.print(matrix) - print("Matrix size: " .. #matrix .. "x" .. #matrix[1]) - for y = 1, #matrix do - for x = 1, #matrix[y] do - io.write(tostring((not matrix[y]) and "nil" or matrix[y][x])) - io.write(' ') - end - print("") - end - - return matrix -end - ----------------------------------------------------- Matrix arithmetic operations ---------------------------------------------------------------- - -function matrixLibrary.multiply(matrix, data) - local dataType = type(data) - if dataType == "table" then - if (#matrix[1] ~= #data) then error("Couldn't multiply matrixes AxB: A[columns] ~= B[rows]") end - - local result = {} - for i = 1, #matrix do - result[i] = {} - for j = 1, #data[1] do - local resultElement = 0 - for k = 1, #matrix[1] do - resultElement = resultElement + (matrix[i][k] * data[k][j]) - end - result[i][j] = resultElement - end - end - - return result - elseif dataType == "number" then - for y = 1, #matrix do - for x = 1, #matrix[y] do - matrix[y][x] = matrix[y][x] * data - end - end - - return matrix - else - error("Unsupported operation data type: " .. tostring(dataType)) - end -end - -function matrixLibrary.divide(matrix, data) - local dataType = type(data) - if dataType == "table" then - error("Matrix by matrix division doesn't supported yet") - elseif dataType == "number" then - for y = 1, #matrix do - for x = 1, #matrix[y] do - matrix[y][x] = matrix[y][x] / data - end - end - - return matrix - else - error("Unsupported operation data type: " .. tostring(dataType)) - end -end - ----------------------------------------------------- Matrix resizing methods ---------------------------------------------------------------- - -function matrixLibrary.addRow(matrix, row) - if (#matrix[1] ~= #row) then error("Insertion row size doesn't match matrix row size: " .. #row .. " vs " .. #matrix[1]) end - - table.insert(matrix, row) - - return matrix -end - -function matrixLibrary.addColumn(matrix, column) - if (#matrix ~= #column ) then error("Insertion column size doesn't match matrix column size: " .. #column .. " vs " .. #matrix) end - - for y = 1, #column do - table.insert(matrix[y], column[y]) - end - - return matrix -end - -function matrixLibrary.removeRow(matrix, row) - if row > #matrix then error("Can't remove row that is bigger then matrix height") end - - table.remove(matrix, row) - - return matrix -end - -function matrixLibrary.removeColumn(matrix, column) - if column > #matrix[1] then error("Can't remove column that is bigger then matrix width") end - - for y = 1, #matrix do - table.remove(matrix[y], column) - end - - return matrix -end - ----------------------------------------------------- Matrix advanced manipulation methods ---------------------------------------------------------------- - -function matrixLibrary.transpose(matrix) - local transposedMatrix = {} - for x = 1, #matrix[1] do - transposedMatrix[x] = {} - for y = 1, #matrix do - transposedMatrix[x][y] = matrix[y][x] - end - end - return transposedMatrix -end - -function matrixLibrary.getMinor(matrix, row, column) - return matrixLibrary.getDeterminant(matrixLibrary.removeColumn(matrixLibrary.removeRow(matrixLibrary.copy(matrix), row), column)) -end - -function matrixLibrary.getCofactor(matrix, row, column) - return (-1) ^ (row + column) * matrixLibrary.getMinor(matrix, row, column) -end - -function matrixLibrary.getDeterminant(matrix) - local matrixSize = #matrix - if matrixSize ~= #matrix[1] then error("Can't find determinant for matrix, row count != column count: " .. #matrix .. "x" .. #matrix[1]) end - - if matrixSize == 1 then - return matrix[1][1] - elseif matrixSize == 2 then - return matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1] - else - local determinant = 0 - for j = 1, matrixSize do - determinant = determinant + matrixLibrary.getCofactor(matrix, 1, j) * matrix[1][j] - end - - return determinant - end -end - -function matrixLibrary.invert(matrix) - local determinant = matrixLibrary.getDeterminant(matrix) - if determinant == 0 then error("Can't invert matrix with determinant equals 0") end - - return matrixLibrary.divide(matrixLibrary.newAdjugateMatrix(matrix), determinant) -end - ------------------------------------------------------------------------------------------------------------------------- - --- local m = { --- {2, 5, 4}, --- {-5, 5, 6}, --- {1, 3, 7}, --- } --- matrixLibrary.print(m) --- m = matrixLibrary.invert(m) --- matrixLibrary.print(m) - ------------------------------------------------------------------------------------------------------------------------- - -return matrixLibrary - -Flib/modemConnection.luaGi -local event = require("event") -local computer = require("computer") -local component = require("component") -local term = require("term") -local serialization = require("serialization") -local unicode = require("unicode") -local ecs, image -local modem = component.modem -local gpu = component.gpu -local modemConnection = {} - ----------------------------------------------------------------------------------------------------------------------------------- - -modemConnection.port = 322 -modemConnection.waitForConnectionAcceptingDelay = 10 -modemConnection.receiveMessagesFromRobots = true -modemConnection.receiveMessagesFromTablets = true -modemConnection.receiveMessagesFromComputers = true - -local infoMessages = { - userTriesToConnectNoGUI = "Пользователь %s желает установить с вами соединение. Разрешить? Y/N", - noModem = "Этой библиотеке требуется сетевая карта для работы", -} - ----------------------------------------------------------------------------------------------------------------------------------- - -local xSize, ySize = gpu.getResolution() -local computerIcon, robotIcon, tabletIcon -if not component.isAvailable("robot") then - image = require("image") - ecs = require("ECSAPI") - computerIcon = image.load("MineOS/System/OS/Icons/Computer.pic") - robotIcon = image.load("MineOS/System/OS/Icons/Robot.pic") - tabletIcon = image.load("MineOS/System/OS/Icons/Tablet.pic") -end - ----------------------------------------------------------------------------------------------------------------------------------- - -local obj = {} -local function newObj(class, name, ...) - obj[class] = obj[class] or {} - obj[class][name] = {...} -end - -local function sendAcceptingMessage(address) - modem.send(address, modemConnection.port, "connectionAccepted", modemConnection.dataToSend) -end - -local function sendDecliningMessage(address) - modem.send(address, modemConnection.port, "connectionDeclined", modemConnection.dataToSend) -end - -local function tryToConnect(address) - modem.send(address, modemConnection.port, "iWantToConnect", modemConnection.dataToSend) -end - -local function acceptingOrDecliningDialog(address, accepted) - local text1, text2 - -- if accepted == true then - -- text1 = "Установлено соединение с пользователем" - -- text2 = "\"" .. ecs.stringLimit("end", address, 18) .. "\"" - -- else - if accepted == false then - text1 = "Пользователь \"" .. ecs.stringLimit("end", address, 12) .. "\" отказался" - text2 = "установить с вами соединение." - elseif accepted == nil then - text1 = "Пользователь \"" .. ecs.stringLimit("end", address, 12) .. "\" не ответил" - text2 = "на ваш запрос" - end - - ecs.universalWindow("auto", "auto", 36, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "WirelessConnection"}, - {"EmptyLine"}, - {"CenterText", 0xffffff, text1 }, - {"CenterText", 0xffffff, text2 }, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "OK"}} - ) -end - -local function askForAcceptConnection(address) - if not component.isAvailable("robot") then - local data = ecs.universalWindow("auto", "auto", 36, 0x262626, true, - {"EmptyLine"}, - {"CenterText", ecs.colors.orange, "WirelessConnection"}, - {"EmptyLine"}, - {"CenterText", 0xffffff, "Пользователь \"" .. ecs.stringLimit("end", address, 12) .. "\" желает" }, - {"CenterText", 0xffffff, "установить с вами беспроводное соединение." }, - {"EmptyLine"}, - {"CenterText", 0xffffff, "Разрешить подключение?" }, - {"EmptyLine"}, - {"Button", {ecs.colors.orange, 0x262626, "Да"}, {0x999999, 0xffffff, "Нет"}} - ) - - if data[1] == "Да" then - modemConnection.remoteAddress = address - sendAcceptingMessage(address) - computer.pushSignal("connectionEstabilishedExitFromGUI") - else - sendDecliningMessage(address) - end - else - term.clear() - term.setCursor(1, 1) - print("Пользователь \"" .. string.sub(address, 1, 12) .. "\" желает установить с вами беспроводное соединение. Разрешить подключение? Y/N") - local answer = term.read() - if string.sub(string.lower(answer), 1, 1) == "y" then - modemConnection.remoteAddress = address - sendAcceptingMessage(address) - print(" ") - print("Соединение установлено.") - print(" ") - -- computer.pushSignal("key_down", component.getPrimary("keyboard").address, 13, 28, "ECS") - else - sendDecliningMessage(address) - term.clear() - term.setCursor(1, 1) - end - end -end - -local function infoAboutClient(userData) - - local arguments = { - "auto", "auto", 36, 0x262626, true, {"EmptyLine"}, {"CenterText", ecs.colors.orange, "Информация о пользователе"}, {"EmptyLine"}, - } - - if userData.isRobot then - table.insert(arguments, {"CenterText", 0xffffff, "Тип: робот"}) - elseif userData.isTablet then - table.insert(arguments, {"CenterText", 0xffffff, "Тип: планшет"}) - elseif not userData.isTablet and not userData.isRobot then - table.insert(arguments, {"CenterText", 0xffffff, "Тип: компьютер"}) - end - - table.insert(arguments, {"CenterText", 0xffffff, "Имя: " .. userData.name}) - table.insert(arguments, {"CenterText", 0xffffff, "Адрес: " .. ecs.stringLimit("end", userData.address, 12)}) - table.insert(arguments, {"CenterText", 0xffffff, "Память: " .. userData.ram .. " KB"}) - - if userData.isTablet or userData.isRobot and userData.hasUpgrades then - table.insert(arguments, {"EmptyLine"}) - table.insert(arguments, {"CenterText", ecs.colors.orange, "Улучшения"}) - table.insert(arguments, {"EmptyLine"}) - if userData.inventoryController then - table.insert(arguments, {"CenterText", 0xffffff, "Контроллер инвентаря"}) - end - if userData.tankController then - table.insert(arguments, {"CenterText", 0xffffff, "Контроллер бака"}) - end - if userData.crafting then - table.insert(arguments, {"CenterText", 0xffffff, "Крафтинг"}) - end - if userData.redstone then - table.insert(arguments, {"CenterText", 0xffffff, "Редстоун-плата"}) - end - if userData.navigation then - table.insert(arguments, {"CenterText", 0xffffff, "Навигация"}) - end - if userData.piston then - table.insert(arguments, {"CenterText", 0xffffff, "Поршень"}) - end - if userData.geolyzer then - table.insert(arguments, {"CenterText", 0xffffff, "Геоанализатор"}) - end - end - - table.insert(arguments, {"EmptyLine"}) - table.insert(arguments, {"Button", {ecs.colors.orange, 0x262626, "OK"}}) - - - ecs.universalWindow(table.unpack(arguments)) -end - -local function modemMessageHandler(_, localAddress, remoteAddress, port, distance, ...) - local messages = {...} - if #messages > 0 then - if port == modemConnection.port then - if messages[1] == "iWantToConnect" and not modemConnection.remoteAddress then - askForAcceptConnection(remoteAddress) - elseif messages[1] == "iAmHereAddMePlease" and not modemConnection.remoteAddress then - if not modemConnection.availableUsers[remoteAddress] then - local userData = serialization.unserialize(messages[2]) - if - (not userData.isRobot and not userData.isTablet and modemConnection.receiveMessagesFromComputers) - or - (userData.isRobot and modemConnection.receiveMessagesFromRobots) - or - (userData.isTablet and modemConnection.receiveMessagesFromTablets) - then - modemConnection.availableUsers[userData.address] = userData - modem.send(remoteAddress, modemConnection.port, "iAmHereAddMePlease", modemConnection.dataToSend) - computer.pushSignal("userlistChanged") - end - end - elseif messages[1] == "iAmDisconnecting" then - if modemConnection.availableUsers[remoteAddress] then - modemConnection.availableUsers[remoteAddress] = nil - computer.pushSignal("userlistChanged") - end - end - end - end -end - -local function createSendingArray() - modemConnection.dataToSend = {} - modemConnection.dataToSend.address = modemConnection.localAddress - modemConnection.dataToSend.name = component.filesystem.getLabel() - modemConnection.dataToSend.ram = math.floor(computer.totalMemory() / 1024) - - if component.isAvailable("robot") then - modemConnection.dataToSend.isRobot = true - if component.isAvailable("inventory_controller") then - modemConnection.dataToSend.inventoryController = true; modemConnection.dataToSend.hasUpgrades = true - end - - if component.isAvailable("tank_controller") then - modemConnection.dataToSend.tankController = true; modemConnection.dataToSend.hasUpgrades = true - end - - if component.isAvailable("crafting") then - modemConnection.dataToSend.crafting = true; modemConnection.dataToSend.hasUpgrades = true - end - - if component.isAvailable("redstone") then - modemConnection.dataToSend.redstone = true; modemConnection.dataToSend.hasUpgrades = true - end - end - - if component.isAvailable("tablet") then - modemConnection.dataToSend.isTablet = true - if component.isAvailable("navigation") then - modemConnection.dataToSend.navigation = true; modemConnection.dataToSend.hasUpgrades = true - end - - if component.isAvailable("piston") then - modemConnection.dataToSend.piston = true; modemConnection.dataToSend.hasUpgrades = true - end - end - - if component.isAvailable("geolyzer") then - modemConnection.dataToSend.geolyzer = true; modemConnection.dataToSend.hasUpgrades = true - end - - modemConnection.dataToSend = serialization.serialize(modemConnection.dataToSend) -end - ---Нарисовать окружность, алгоритм спизжен с вики -local function circle(xCenter, yCenter, radius, color) - gpu.setBackground(color) - local function insertPoints(x, y) - gpu.set(xCenter + x * 2, yCenter + y, " ") - gpu.set(xCenter + x * 2, yCenter - y, " ") - gpu.set(xCenter - x * 2, yCenter + y, " ") - gpu.set(xCenter - x * 2, yCenter - y, " ") - - gpu.set(xCenter + x * 2 + 1, yCenter + y, " ") - gpu.set(xCenter + x * 2 + 1, yCenter - y, " ") - gpu.set(xCenter - x * 2 + 1, yCenter + y, " ") - gpu.set(xCenter - x * 2 + 1, yCenter - y, " ") - end - - local x = 0 - local y = radius - local delta = 3 - 2 * radius; - while (x < y) do - insertPoints(x, y); - insertPoints(y, x); - if (delta < 0) then - delta = delta + (4 * x + 6) - else - delta = delta + (4 * (x - y) + 10) - y = y - 1 - end - x = x + 1 - end - - if x == y then insertPoints(x, y) end -end - -local function drawCircles(xCircle, yCircle, minumumRadius, maximumRadius, step, currentRadius) - for radius = minumumRadius, maximumRadius, step do - if radius == currentRadius then - circle(xCircle, yCircle, radius, 0x888888) - else - circle(xCircle, yCircle, radius, 0xDDDDDD) - end - end -end - -local function drawIconAndAddress(x, y, background, foreground, userData) - if userData.isRobot then - image.draw(x + 3, y, robotIcon) - elseif userData.isTablet then - image.draw(x + 3, y, tabletIcon) - else - image.draw(x + 3, y, computerIcon) - end - - ecs.colorTextWithBack(x, y + 5, foreground, background, ecs.stringLimit("end", userData.address, 14)) - - return x, y, x + 13, y + 5 -end - -local function drawHorizontalIcons() - local height = 8 - local y = math.floor(ySize / 2 - height / 2) - local background = 0x66A8FF - ecs.square(1, y, xSize, height, background) - - local iconWidth = 14 - local spaceBetween = 2 - local totalWidth = ecs.getArraySize(modemConnection.availableUsers) * (iconWidth + spaceBetween) - spaceBetween - local x = math.floor(xSize / 2 - totalWidth / 2) + 1 - - obj.Users = {} - - local counter = 0 - local limit = math.floor(xSize / (iconWidth + spaceBetween)) - y = y + 1 - for address in pairs(modemConnection.availableUsers) do - if counter < limit then - newObj("Users", address, drawIconAndAddress(x, y, background, 0xFFFFFF, modemConnection.availableUsers[address])) - end - x = x + iconWidth + spaceBetween - counter = counter + 1 - end -end - -local function drawSelectedIcon(x, y, background, foreground, userData) - local selectionWidth = 16 - local skokaOtnat = (selectionWidth - 14) / 2 - local oldPixels = ecs.rememberOldPixels(x - skokaOtnat, y, x + selectionWidth - 2, y + 13) - ecs.square(x - skokaOtnat, y, selectionWidth, 8, background) - drawIconAndAddress(x, y + 1, background, foreground, userData) - obj.CykaKnopkaInfo = { ecs.drawButton(x - skokaOtnat, y + 8, selectionWidth, 3, "Информация", 0xff6699, 0xFFFFFF) } - obj.CykaKnopkaConnect = { ecs.drawButton(x - skokaOtnat, y + 11, selectionWidth, 3, "Подключиться", 0xff3333, 0xFFFFFF) } - obj.CykaKnopkaConnect.address = userData.address - return oldPixels -end - -local function connectionGUI() - ecs.square(1, 1, xSize, ySize, 0xEEEEEE) - - local xCircle, yCircle = math.floor(xSize / 2), ySize - 3 - local minumumRadius, maximumRadius = 7, xCircle * 0.8 - local step = 4 - local currentRadius = minumumRadius - local unserializedDataToSend = serialization.unserialize(modemConnection.dataToSend) - - drawIconAndAddress(xCircle - 6, ySize - 6, 0xEEEEEE, 0x262626, unserializedDataToSend) - - while true do - if ecs.getArraySize(modemConnection.availableUsers) > 0 then - currentRadius = 0 - drawCircles(xCircle, yCircle, minumumRadius, maximumRadius, step, currentRadius) - - drawHorizontalIcons() - - local oldPixels, needToUpdate - while true do - if not oldPixels and needToUpdate then - if ecs.getArraySize(modemConnection.availableUsers) <= 0 then - ecs.square(1, 1, xSize, ySize, 0xEEEEEE) - drawIconAndAddress(xCircle - 6, ySize - 6, 0xEEEEEE, 0x262626, unserializedDataToSend) - currentRadius = minumumRadius - break - else - drawHorizontalIcons() - needToUpdate = false - end - end - - local e = { event.pull() } - if e[1] == "touch" then - - if obj.CykaKnopkaInfo and obj.CykaKnopkaConnect then - if ecs.clickedAtArea(e[3], e[4], obj.CykaKnopkaInfo[1], obj.CykaKnopkaInfo[2], obj.CykaKnopkaInfo[3], obj.CykaKnopkaInfo[4]) then - ecs.drawButton(obj.CykaKnopkaInfo[1], obj.CykaKnopkaInfo[2], 16, 3, "Информация", 0x262626, 0xFFFFFF) - os.sleep(0.2) - if oldPixels then ecs.drawOldPixels(oldPixels); oldPixels = nil end - infoAboutClient(modemConnection.availableUsers[obj.CykaKnopkaConnect.address]) - - elseif ecs.clickedAtArea(e[3], e[4], obj.CykaKnopkaConnect[1], obj.CykaKnopkaConnect[2], obj.CykaKnopkaConnect[3], obj.CykaKnopkaConnect[4]) then - ecs.drawButton(obj.CykaKnopkaConnect[1], obj.CykaKnopkaConnect[2], 16, 3, "Подключиться", 0x262626, 0xFFFFFF) - os.sleep(0.2) - if oldPixels then ecs.drawOldPixels(oldPixels); oldPixels = nil end - - local oldInfoPixels = ecs.info("auto", "auto", "", "Ожидание ответа от пользователя...") - tryToConnect(obj.CykaKnopkaConnect.address) - - local function filter(name, _, remoteAddress) - if name == "modem_message" and remoteAddress == obj.CykaKnopkaConnect.address then - return true - end - end - - local e2 = { event.pullFiltered(modemConnection.waitForConnectionAcceptingDelay, filter) } - ecs.drawOldPixels(oldInfoPixels) - - if e2[6] == "connectionAccepted" then - modemConnection.remoteAddress = e2[3] - -- acceptingOrDecliningDialog(obj.CykaKnopkaConnect.address, true) - computer.pushSignal("connectionEstabilishedExitFromGUI") - elseif e2[6] == "connectionDeclined" then - acceptingOrDecliningDialog(obj.CykaKnopkaConnect.address, false) - else - acceptingOrDecliningDialog(obj.CykaKnopkaConnect.address, nil) - end - end - - obj.CykaKnopkaInfo, obj.CykaKnopkaConnect = nil, nil - end - - if oldPixels then ecs.drawOldPixels(oldPixels); oldPixels = nil end - - for address in pairs(obj.Users) do - if ecs.clickedAtArea(e[3], e[4], obj.Users[address][1], obj.Users[address][2], obj.Users[address][3], obj.Users[address][4]) then - oldPixels = drawSelectedIcon(obj.Users[address][1], obj.Users[address][2] - 1, 0xCCCCFF, 0x262626, modemConnection.availableUsers[address]) - break - end - end - elseif e[1] == "userlistChanged" then - needToUpdate = true - elseif e[1] == "connectionEstabilishedExitFromGUI" then - ecs.prepareToExit() - modemConnection.disconnect() - return - end - end - else - drawCircles(xCircle, yCircle, minumumRadius, maximumRadius, step, currentRadius) - currentRadius = currentRadius + step - if currentRadius > (maximumRadius + step) then currentRadius = minumumRadius end - os.sleep(0) - end - end -end - ----------------------------------------------------------------------------------------------------------------------------------- - -function modemConnection.stopReceivingData() - event.ignore("modem_message", modemMessageHandler) -end - -function modemConnection.startReceivingData() - modemConnection.stopReceivingData() - modemConnection.remoteAddress = nil - event.listen("modem_message", modemMessageHandler) -end - -function modemConnection.disconnect() - modem.broadcast(modemConnection.port, "iAmDisconnecting") -end - -function modemConnection.sendPersonalData() - modem.broadcast(modemConnection.port, "iAmHereAddMePlease", modemConnection.dataToSend) -end - -function modemConnection.changePort(newPort) - modem.close(modemConnection.port) - modem.open(newPort) - modemConnection.port = newPort - modemConnection.remoteAddress = nil - modemConnection.availableUsers = {} - modemConnection.localAddress = component.getPrimary("modem").address - createSendingArray() -end - -function modemConnection.search() - modemConnection.availableUsers = {} - modemConnection.remoteAddress = nil - modemConnection.disconnect() - modemConnection.sendPersonalData() - connectionGUI() -end - -function modemConnection.init() - if component.isAvailable("modem") then - modemConnection.changePort(modemConnection.port) - else - ecs.error(infoMessages.noModem) - return - end -end - ----------------------------------------------------------------------------------------------------------------------------------- - -modemConnection.init() - ----------------------------------------------------------------------------------------------------------------------------------- - -return modemConnection - -F lib/note.lua j--Provides all music notes in range of computer.beep in MIDI and frequency form ---Author: Vexatos -local computer = require("computer") - -local note = {} ---The table that maps note names to their respective MIDI codes -local notes = {} ---The reversed table "notes" -local reverseNotes = {} - -do - --All the base notes - local tempNotes = { - "c", - "c#", - "d", - "d#", - "e", - "f", - "f#", - "g", - "g#", - "a", - "a#", - "b" - } - --The table containing all the standard notes and # semitones in correct order, temporarily - local sNotes = {} - --The table containing all the b semitones - local bNotes = {} - - --Registers all possible notes in order - do - table.insert(sNotes,"a0") - table.insert(sNotes,"a#0") - table.insert(bNotes,"bb0") - table.insert(sNotes,"b0") - for i = 1,6 do - for _,v in ipairs(tempNotes) do - table.insert(sNotes,v..tostring(i)) - if #v == 1 and v ~= "c" and v ~= "f" then - table.insert(bNotes,v.."b"..tostring(i)) - end - end - end - end - for i=21,95 do - notes[sNotes[i-20]]=tostring(i) - end - - --Reversing the whole table in reverseNotes, used for note.get - do - for k,v in pairs(notes) do - reverseNotes[tonumber(v)]=k - end - end - - --This is registered after reverseNotes to avoid conflicts - for k,v in ipairs(bNotes) do - notes[v]=tostring(notes[string.gsub(v,"(.)b(.)","%1%2")]-1) - end -end - ---Converts string or frequency into MIDI code -function note.midi(n) - if type(n) == "string" then - n = string.lower(n) - if tonumber(notes[n])~=nil then - return tonumber(notes[n]) - else - error("Wrong input "..tostring(n).." given to note.midi, needs to be [semitone sign], e.g. A#0 or Gb4") - end - elseif type(n) == "number" then - return math.floor((12*math.log(n/440,2))+69) - else - error("Wrong input "..tostring(n).." given to note.midi, needs to be a number or a string") - end -end - ---Converts String or MIDI code into frequency -function note.freq(n) - if type(n) == "string" then - n = string.lower(n) - if tonumber(notes[n])~=nil then - return math.pow(2,(tonumber(notes[n])-69)/12)*440 - else - error("Wrong input "..tostring(n).." given to note.freq, needs to be [semitone sign], e.g. A#0 or Gb4",2) - end - elseif type(n) == "number" then - return math.pow(2,(n-69)/12)*440 - else - error("Wrong input "..tostring(n).." given to note.freq, needs to be a number or a string",2) - end -end - ---Converts a MIDI value back into a string -function note.name(n) - n = tonumber(n) - if reverseNotes[n] then - return string.upper(string.match(reverseNotes[n],"^(.)"))..string.gsub(reverseNotes[n],"^.(.*)","%1") - else - error("Attempt to get a note for a non-exsisting MIDI code",2) - end -end - ---Converts Note block ticks (0-24) to MIDI code (34-58) and vice-versa -function note.ticks(n) - if type(n) == "number" then - if n>=0 and n<=24 then - return n+34 - elseif n>=34 and n<=58 then - return n-34 - else - error("Wrong input "..tostring(n).." given to note.ticks, needs to be a number [0-24 or 34-58]",2) - end - else - error("Wrong input "..tostring(n).." given to note.ticks, needs to be a number",2) - end -end - ---Plays a tone, input is either the note as a string or the MIDI code as well as the duration of the tone -function note.play(tone,duration) - computer.beep(note.freq(tone),duration) -end - -return note -Flib/package.luaWlocal package = {} - -package.path = "/lib/?.lua;/usr/lib/?.lua;/home/lib/?.lua;./?.lua;/lib/?/init.lua;/usr/lib/?/init.lua;/home/lib/?/init.lua;./?/init.lua" - -local loading = {} - -local loaded = { - ["_G"] = _G, - ["bit32"] = bit32, - ["coroutine"] = coroutine, - ["math"] = math, - ["os"] = os, - ["package"] = package, - ["string"] = string, - ["table"] = table -} -package.loaded = loaded - -local preload = {} -package.preload = preload - -local delayed = {} -package.delayed = delayed - -package.searchers = {} - -function package.searchpath(name, path, sep, rep) - checkArg(1, name, "string") - checkArg(2, path, "string") - sep = sep or '.' - rep = rep or '/' - sep, rep = '%' .. sep, rep - name = string.gsub(name, sep, rep) - local fs = require("filesystem") - local errorFiles = {} - for subPath in string.gmatch(path, "([^;]+)") do - subPath = string.gsub(subPath, "?", name) - if subPath:sub(1, 1) ~= "/" and os.getenv then - subPath = fs.concat(os.getenv("PWD") or "/", subPath) - end - if fs.exists(subPath) then - local file = io.open(subPath, "r") - if file then - file:close() - return subPath - end - end - table.insert(errorFiles, "\tno file '" .. subPath .. "'") - end - return nil, table.concat(errorFiles, "\n") -end - -local function preloadSearcher(module) - if preload[module] ~= nil then - return preload[module] - else - return "\tno field package.preload['" .. module .. "']" - end -end - -local delay_data = {} -local delay_tools = setmetatable({},{__mode="v"}) - -package.delay_data = delay_data - -function delay_data.__index(tbl,key) - local lookup = delay_tools.lookup or loadfile("/lib/tools/delayLookup.lua") - delay_tools.lookup = lookup - return lookup(delay_data, tbl, key) -end -delay_data.__pairs = delay_data.__index -- nil key acts like pairs - -function delaySearcher(module) - if not delayed[module] then - return "\tno field package.delayed['" .. module .. "']" - end - local filepath, reason = package.searchpath(module, package.path) - if not filepath then - return reason - end - local parser = delay_tools.parser or loadfile("/lib/tools/delayParse.lua") - delay_tools.parser = parser - local loader, reason = parser(filepath,delay_data) - return loader, reason -end - -local function pathSearcher(module) - local filepath, reason = package.searchpath(module, package.path) - if filepath then - local loader, reason = loadfile(filepath, "bt", _G) - if loader then - return loader, filepath - else - return reason - end - else - return reason - end -end - -table.insert(package.searchers, preloadSearcher) -table.insert(package.searchers, delaySearcher) -table.insert(package.searchers, pathSearcher) - -function require(module) - checkArg(1, module, "string") - if loaded[module] ~= nil then - return loaded[module] - elseif not loading[module] then - loading[module] = true - local loader, value, errorMsg = nil, nil, {"module '" .. module .. "' not found:"} - for i = 1, #package.searchers do - -- the pcall is mostly for out of memory errors - local ok, f, extra = pcall(package.searchers[i], module) - if not ok then - table.insert(errorMsg, "\t" .. f) - elseif f and type(f) ~= "string" then - loader = f - value = extra - break - elseif f then - table.insert(errorMsg, f) - end - end - if loader then - local success, result = pcall(loader, module, value) - loading[module] = false - if not success then - error(result, 2) - end - if result then - loaded[module] = result - elseif not loaded[module] then - loaded[module] = true - end - return loaded[module] - else - loading[module] = false - error(table.concat(errorMsg, "\n"), 2) - end - else - error("already loading: " .. module .. debug.traceback(), 2) - end -end - -------------------------------------------------------------------------------- - -return package -Flib/palette.lua- --- _G.windows, _G.GUI, package.loaded.windows, package.loaded.GUI = nil, nil, nil, nil - -local advancedLua = require("advancedLua") -local component = require("component") -local fs = require("filesystem") -local colorlib = require("colorlib") -local image = require("image") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local windows = require("windows") - --------------------------------------------------------------------------------------------------------------- - -local palette = {} -local window -local currentColor, favourites -local xBigCrest, yBigCrest, yMiniCrest -local favouritesContainer, bigRainbow, miniRainbow, currentColorPanel -local pathToFavouritesConfig = "/MineOS/System/Palette/Favourites.cfg" -local inputs - --------------------------------------------------------------------------------------------------------------- - -local function switchColorFromHex(hex) - currentColor = {hsb = {}, rgb = {}, hex = hex} - currentColor.rgb.red, currentColor.rgb.green, currentColor.rgb.blue = colorlib.HEXtoRGB(hex) - currentColor.hsb.hue, currentColor.hsb.saturation, currentColor.hsb.brightness = colorlib.RGBtoHSB(currentColor.rgb.red, currentColor.rgb.green, currentColor.rgb.blue) -end - -local function switchColorFromHsb(hue, saturation, brightness) - currentColor = {hsb = {hue = hue, saturation = saturation, brightness = brightness}, rgb = {}, hex = nil} - currentColor.rgb.red, currentColor.rgb.green, currentColor.rgb.blue = colorlib.HSBtoRGB(hue, saturation, brightness) - currentColor.hex = colorlib.RGBtoHEX(currentColor.rgb.red, currentColor.rgb.green, currentColor.rgb.blue) -end - -local function switchColorFromRgb(red, green, blue) - currentColor = {hsb = {}, rgb = {red = red, green = green, blue = blue}, hex = nil} - currentColor.hsb.hue, currentColor.hsb.saturation, currentColor.hsb.brightness = colorlib.RGBtoHSB(red, green, blue) - currentColor.hex = colorlib.RGBtoHEX(red, green, blue) -end - --------------------------------------------------------------------------------------------------------------- - -local function randomizeFavourites() - favourites = {}; for i = 1, 6 do favourites[i] = math.random(0x000000, 0xFFFFFF) end -end - -local function saveFavoutites() - table.toFile(pathToFavouritesConfig, favourites) -end - -local function loadFavourites() - if fs.exists(pathToFavouritesConfig) then - favourites = table.fromFile(pathToFavouritesConfig) - else - randomizeFavourites() - saveFavoutites() - end -end - --------------------------------------------------------------------------------------------------------------- - -local function changeInputsValueToCurrentColor() - inputs[1].object.text = tostring(currentColor.rgb.red) - inputs[2].object.text = tostring(currentColor.rgb.green) - inputs[3].object.text = tostring(currentColor.rgb.blue) - inputs[4].object.text = tostring(math.floor(currentColor.hsb.hue)) - inputs[5].object.text = tostring(math.floor(currentColor.hsb.saturation)) - inputs[6].object.text = tostring(math.floor(currentColor.hsb.brightness)) - inputs[7].object.text = string.format("%06X", currentColor.hex) -end - --------------------------------------------------------------------------------------------------------------- - -local function refreshBigRainbow(width, height) - local saturationStep, brightnessStep, saturation, brightness = 100 / width, 100 / (height * 2), 0, 100 - for j = 1, height do - for i = 1, width do - local background = colorlib.HSBtoHEX(currentColor.hsb.hue, saturation, brightness) - local foreground = colorlib.HSBtoHEX(currentColor.hsb.hue, saturation, brightness - brightnessStep) - image.set(bigRainbow.image, i, j, background, foreground, 0x0, "▄") - saturation = saturation + saturationStep - end - saturation = 0; brightness = brightness - brightnessStep - brightnessStep - end -end - -local function refreshMiniRainbow(width, height) - local hueStep, hue = 360 / (height * 2), 0 - for j = 1, height do - for i = 1, width do - local background = colorlib.HSBtoHEX(hue, 100, 100) - local foreground = colorlib.HSBtoHEX(hue + hueStep, 100, 100) - image.set(miniRainbow.image, i, j, background, foreground, 0x0, "▄") - end - hue = hue + hueStep + hueStep - end -end - -local function refreshRainbows() - refreshBigRainbow(50, 25) - refreshMiniRainbow(3, 25) -end - -local function betterVisiblePixel(x, y, symbol) - local background, foreground = buffer.get(x, y) - if background > 0x888888 then foreground = 0x000000 else foreground = 0xFFFFFF end - buffer.set(x, y, background, foreground, symbol) -end - -local function drawBigCrest() - local drawLimit = buffer.getDrawLimit(); buffer.setDrawLimit(window.x, window.y, bigRainbow.width + 2, bigRainbow.height) - betterVisiblePixel(xBigCrest - 2, yBigCrest, "─") - betterVisiblePixel(xBigCrest - 1, yBigCrest, "─") - betterVisiblePixel(xBigCrest + 1, yBigCrest, "─") - betterVisiblePixel(xBigCrest + 2, yBigCrest, "─") - betterVisiblePixel(xBigCrest, yBigCrest - 1, "│") - betterVisiblePixel(xBigCrest, yBigCrest + 1, "│") - buffer.setDrawLimit(drawLimit) -end - -local function drawMiniCrest() - buffer.text(miniRainbow.x - 1, yMiniCrest, 0x000000, ">") - buffer.text(miniRainbow.x + miniRainbow.width, yMiniCrest, 0x000000, "<") -end - -local function drawCrests() - drawBigCrest() - drawMiniCrest() -end - -local function drawAll() - currentColorPanel.colors.background = currentColor.hex - changeInputsValueToCurrentColor() - window:draw() - drawCrests() - buffer.draw() -end - --------------------------------------------------------------------------------------------------------------- - -local function createCrestsCoordinates() - local xBigCrestModifyer = (bigRainbow.width - 1) * currentColor.hsb.saturation / 100 - local yBigCrestModifyer = (bigRainbow.height - 1) - (bigRainbow.height - 1) * currentColor.hsb.brightness / 100 - local yMiniCrestModifyer = (miniRainbow.height - 1) - (miniRainbow.height - 1) * currentColor.hsb.hue / 360 - - xBigCrest, yBigCrest, yMiniCrest = math.floor(window.x + xBigCrestModifyer), math.floor(window.y + yBigCrestModifyer), math.floor(window.y + yMiniCrestModifyer) -end - -local function createInputs(x, y) - local function onAnyInputFinished() refreshRainbows(); createCrestsCoordinates(); drawAll() end - local function onHexInputFinished(object) switchColorFromHex(tonumber("0x" .. inputs[7].object.text)); onAnyInputFinished() end - local function onRgbInputFinished(object) switchColorFromRgb(tonumber(inputs[1].object.text), tonumber(inputs[2].object.text), tonumber(inputs[3].object.text)); onAnyInputFinished() end - local function onHsbInputFinished(object) switchColorFromHsb(tonumber(inputs[4].object.text), tonumber(inputs[5].object.text), tonumber(inputs[6].object.text)); onAnyInputFinished() end - - local function rgbValidaror(text) local num = tonumber(text) if num and num >= 0 and num <= 255 then return true end end - local function hValidator(text) local num = tonumber(text) if num and num >= 0 and num <= 359 then return true end end - local function sbValidator(text) local num = tonumber(text) if num and num >= 0 and num <= 100 then return true end end - local function hexValidator(text) if string.match(text, "^[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$") then return true end end - - inputs = { - { shortcut = "R:", arrayName = "red", validator = rgbValidaror, onInputFinished = onRgbInputFinished }, - { shortcut = "G:", arrayName = "green", validator = rgbValidaror, onInputFinished = onRgbInputFinished }, - { shortcut = "B:", arrayName = "blue", validator = rgbValidaror, onInputFinished = onRgbInputFinished }, - { shortcut = "H:", arrayName = "hue", validator = hValidator, onInputFinished = onHsbInputFinished }, - { shortcut = "S:", arrayName = "saturation", validator = sbValidator, onInputFinished = onHsbInputFinished }, - { shortcut = "L:", arrayName = "brightness", validator = sbValidator, onInputFinished = onHsbInputFinished }, - { shortcut = "0x", arrayName = "red", validator = hexValidator, onInputFinished = onHexInputFinished } - } - - for i = 1, #inputs do - window:addLabel(x, y, 2, 1, 0x000000, inputs[i].shortcut) - inputs[i].object = window:addInputTextBox(x + 3, y, 9, 1, 0xFFFFFF, 0x444444, 0xFFFFFF, 0x000000, "", "", true) - inputs[i].object.validator = inputs[i].validator - inputs[i].object.onInputFinished = inputs[i].onInputFinished - y = y + 2 - end - - return y -end - -local function createFavourites() - for i = 1, #favourites do - local button = favouritesContainer:addButton(i * 2 - 1, 1, 2, 1, favourites[i], 0x0, 0x0, 0x0, " ") - button.onTouch = function() - switchColorFromHex(button.colors.default.background) - refreshRainbows() - createCrestsCoordinates() - drawAll() - end - end -end - -local function createWindow(x, y) - window = windows.empty(x, y, 71, 25, 71, 25) - - x, y = 1, 1 - window:addPanel(x, y, window.width, window.height, 0xEEEEEE) - - bigRainbow = window:addImage(x, y, image.create(50, 25)) - bigRainbow.onTouch = function(eventData) - xBigCrest, yBigCrest = eventData[3], eventData[4] - local _, _, background = component.gpu.get(eventData[3], eventData[4]) - switchColorFromHex(background) - drawAll() - end - bigRainbow.onDrag = bigRainbow.onTouch - - x = x + bigRainbow.width + 2 - - miniRainbow = window:addImage(x, y, image.create(3, 25)) - miniRainbow.onTouch = function(eventData) - yMiniCrest = eventData[4] - switchColorFromHsb((eventData[4] - miniRainbow.y) * 360 / miniRainbow.height, currentColor.hsb.saturation, currentColor.hsb.brightness) - refreshRainbows() - drawAll() - end - miniRainbow.onDrag = miniRainbow.onTouch - x, y = x + 5, y + 1 - - currentColorPanel = window:addPanel(x, y, 12, 3, currentColor.hex) - y = y + 4 - - window.okButton = window:addButton(x, y, 12, 1, 0x444444, 0xFFFFFF, 0x88FF88, 0xFFFFFF, "OK") - window.okButton.onTouch = function() - window:returnData(currentColor.hex) - end - y = y + 2 - - window:addButton(x, y, 12, 1, 0xFFFFFF, 0x444444, 0x88FF88, 0xFFFFFF, "Cancel").onTouch = function() - window:close() - end - y = y + 2 - - y = createInputs(x, y) - - favouritesContainer = window:addContainer(x, y, 12, 1) - createFavourites() - y = y + 1 - - window:addButton(x, y, 12, 1, 0xFFFFFF, 0x444444, 0x88FF88, 0xFFFFFF, "+").onTouch = function() - local favouriteExists = false; for i = 1, #favourites do if favourites[i] == currentColor.hex then favouriteExists = true; break end end - if not favouriteExists then - table.insert(favourites, 1, currentColor.hex); table.remove(favourites, #favourites) - for i = 1, #favourites do favouritesContainer.children[i].colors.default.background = favourites[i]; favouritesContainer.children[i].colors.pressed.background = 0x0 end - saveFavoutites() - drawAll() - end - end - - window.onDrawFinished = function() - drawCrests() - buffer.draw() - end - - window.onKeyDown = function(eventData) - if eventData[4] == 28 then - window.okButton:press() - drawAll() - window:returnData(currentColor.hex) - end - end -end - --------------------------------------------------------------------------------------------------------------- - -function palette.show(x, y, startColor) - buffer.start() - loadFavourites() - switchColorFromHex(startColor or 0x00B6FF) - createWindow(x or "auto", y or "auto") - createCrestsCoordinates() - refreshRainbows() - - window.drawShadow = true - drawAll() - window.drawShadow = false - - return window:handleEvents() -end - --- Поддержим олдфагов! -palette.draw = palette.show - --------------------------------------------------------------------------------------------------------------- - --- buffer.start() --- buffer.draw(true) --- require("ECSAPI").error(palette.show("auto", "auto", 0xFF5555)) - --------------------------------------------------------------------------------------------------------------- - -return palette -F lib/pipes.lua%jlocal tx = require("transforms") -local shell = require("shell") -local sh = require("sh") -local process = require("process") - -local plib = {} - -plib.internal = {} - -local pipeStream = {} -local function bfd() return nil, "bad file descriptor" end -function pipeStream.new(pm) - local stream = {pm=pm} - local metatable = {__index = pipeStream} - return setmetatable(stream, metatable) -end -function pipeStream:resume() - local yield_args = table.pack(self.pm.pco.resume_all()) - if not yield_args[1] then - self.pm.dead = true - - if not yield_args[1] and yield_args[2] then - io.stderr:write(tostring(yield_args[2]) .. "\n") - end - end - return table.unpack(yield_args) -end -function pipeStream:close() - if self.pm.closed then -- already closed - return - end - - self.pm.closed = true - - -- if our pco stack is empty, we've already run fully - if self.pm.pco.top() == nil then - return - end - - -- if a thread aborted we have set dead true - if self.pm.dead then - return - end - - -- run prog until dead - local co = self.pm.pco.previous_handler - local pco_root = self.pm.threads[1] - if co.status(pco_root) == "dead" then - -- I would have liked the pco stack to unwind itself for dead coroutines - -- maybe I haven't handled aborts corrects - return - end - - return self:resume(true) -end -function pipeStream:read(n) - local pm = self.pm - - if pm.closed then - return bfd() - end - - if pm:buffer() == '' and not pm.dead then - local result = table.pack(self:resume()) - if not result[1] then - -- resume can fail if p1 crashes - self:close() - return nil, "pipe closed unexpectedly" - elseif result.n > 1 and not result[2] then - return result[2], result[3] - end - end - - local result = pm:buffer(n) - if result == '' and pm.dead and n > 0 then - return nil -- eof - end - - return result -end -function pipeStream:seek(whence, offset) - return bfd() -end -function pipeStream:write(v) - local pm = self.pm - if pm.closed or pm.dead then - -- if prog is dead, ignore all writes - if pm.pco.previous_handler.status(pm.threads[pm.self_id]) ~= "dead" then - error("attempt to use a closed stream") - end - return bfd() - end - - pm:buffer(pm:buffer() .. v) - - -- allow handler to push write event - local result = table.pack(self:resume()) - if not result[1] then - -- resume can fail if p1 crashes - pm.dead = true - self:close() - return nil, "pipe closed unexpectedly" - end - - return self -end - -function plib.internal.redirectRead(pm) - local reader = {pm=pm} - function reader:read(n) - local pm = self.pm - local pco = pm.pco - -- if we have any buffer, return it first - - if pm:buffer() == '' and not pm.closed and not pm.dead then - pco.yield_all() - end - - if pm.closed or pm.dead then - return nil - end - - return pm:buffer(n) - end - - return reader -end - -function plib.internal.create(fp) - local _co = process.info().data.coroutine_handler - - local pco = setmetatable( - { - stack = {}, - next = nil, - create = _co.create, - wrap = _co.wrap, - previous_handler = _co - }, {__index=_co}) - - function pco.top() - return pco.stack[#pco.stack] - end - function pco.yield(...) - -- pop last - pco.set_unwind(pco.running()) - return _co.yield(...) - end - function pco.index_of(thread) - for i,t in ipairs(pco.stack) do - if t == thread then - return i - end - end - end - function pco.yield_all(...) - local current = pco.running() - local existing_index = pco.index_of(current) - assert(existing_index, "cannot yield inactive stack") - pco.next = current - return _co.yield(...) - end - function pco.set_unwind(from) - pco.next = nil - if from then - local index = pco.index_of(from) - if index then - pco.stack = tx.sub(pco.stack, 1, index-1) - pco.next = pco.stack[index-1] - end - end - end - function pco.resume_all(...) - local base = pco.stack[1] - local top = pco.top() - if type(base) ~= "thread" or _co.status(base) ~= "suspended" or - type(top) ~= "thread" or _co.status(top) ~= "suspended" then - return false - end - - local status, result = pcall(function(...) - local _result = table.pack(pco.resume(top, ...)) - return _result - end,...) - - if not status then - return nil, result - end - - return table.unpack(result) - end - function pco.resume(thread, ...) - checkArg(1, thread, "thread") - local status = pco.status(thread) - if status ~= "suspended" then - local msg = string.format("cannot resume %s coroutine", - status == "dead" and "dead" or "non-suspended") - return false, msg - end - - local current_index = pco.index_of(pco.running()) - local existing_index = pco.index_of(thread) - - if not existing_index then - assert(current_index, "pco coroutines cannot resume threads outside the stack") - pco.stack = tx.concat(tx.sub(pco.stack, 1, current_index), {thread}) - end - - if current_index then - -- current should be waiting for yield - pco.next = thread - return true, _co.yield(...) -- pass args to resume next - else - -- the stack is not running - pco.next = nil - local yield_args = table.pack(_co.resume(thread, ...)) - if #pco.stack > 0 then - -- thread may have crashed (crash unwinds as well) - -- or we don't have next lined up (unwind) - if not pco.next or not yield_args[1] then - -- unwind from current index, not top - pco.set_unwind(thread) - end - - -- if next is current thread, yield_all is active - -- in such a case, yield out first, then resume where we left off - if pco.next and pco.next ~= thread then - local next = pco.next - pco.next = nil - return pco.resume(next, table.unpack(yield_args,2,yield_args.n)) - end - end - - return table.unpack(yield_args) - end - end - function pco.status(thread) - checkArg(1, thread, "thread") - - local current_index = pco.index_of(pco.running()) - local existing_index = pco.index_of(thread) - - if current_index and existing_index and existing_index < current_index then - local current = pco.stack[current_index] - if current and _co.status(current) == "running" then - return "normal" - end - end - - return _co.status(thread) - end - - if fp then - pco.stack = {process.load(fp,nil,nil--[[init]],"pco root")} - process.info(pco.stack[1]).data.coroutine_handler = pco - end - - return pco -end - -local pipeManager = {} -function pipeManager.reader(pm,...) - while pm.pco.status(pm.threads[pm.prog_id]) ~= "dead" do - pm.pco.yield_all() - - -- kick back to main thread, true to kick back one further - if pm.closed then break end - - -- if we are a reader pipe, we leave the buffer alone and yield to previous - if pm.pco.status(pm.threads[pm.prog_id]) ~= "dead" then - pm.pco.yield() - end - end - pm.dead = true -end - -function pipeManager:buffer(value) - -- if value but no stream, buffer for buffer - - local s = self and self.pipe and self.pipe.stream - if not s then - if type(value) == "string" or self.prewrite then - self.prewrite = self.prewrite or {} - s = self.prewrite -- s.buffer will be self.prewrite.buffer - else - return '' - end - elseif self.prewrite then -- we stored, previously, a prewrite buffer - s.buffer = self.prewrite.buffer .. s.buffer - self.prewrite = nil - end - - if type(value) == "string" then - s.buffer = value - return value - elseif type(value) ~= "number" then - return s.buffer -- don't truncate - end - - local result = string.sub(s.buffer, 1, value) - s.buffer = string.sub(s.buffer, value + 1) - return result -end - -function pipeManager.new(prog, mode, env) - mode = mode or "r" - if mode ~= "r" and mode ~= "w" then - return nil, "bad argument #2: invalid mode " .. tostring(mode) .. " must be r or w" - end - - local shellPath = os.getenv("SHELL") or "/bin/sh" - local shellPath, reason = shell.resolve(shellPath, "lua") - if not shellPath then - return nil, reason - end - - local pm = setmetatable( - {dead=false,closed=false,prog=prog,mode=mode,env=env}, - {__index=pipeManager} - ) - pm.prog_id = pm.mode == "r" and 1 or 2 - pm.self_id = pm.mode == "r" and 2 or 1 - pm.handler = pm.mode == "r" and - function()return pipeManager.reader(pm)end or - function()pm.dead=true end - - pm.commands = {} - pm.commands[pm.prog_id] = {shellPath, {}} - pm.commands[pm.self_id] = {pm.handler, {}} - - pm.root = function() - local startup_args = {} - - local reason - pm.threads, reason = sh.internal.createThreads(pm.commands, {}, pm.env) - - if not pm.threads then - pm.dead = true - return false, reason - end - - pm.pipe = process.info(pm.threads[1]).data.io[1] - process.info(pm.threads[pm.prog_id]).data.args = {pm.env,pm.prog} - - -- if we are the writer, we need args to resume prog - if pm.mode == "w" then - pm.pipe.stream.redirect[0] = plib.internal.redirectRead(pm) - end - - return sh.internal.runThreads(pm.threads) - end - - return pm -end - -function plib.popen(prog, mode, env) - checkArg(1, prog, "string") - checkArg(2, mode, "string", "nil") - checkArg(3, env, "table", "nil") - - local pm, reason = pipeManager.new(prog, mode, env) - - if not pm then - return false, reason - end - - pm.pco=plib.internal.create(pm.root) - - local pfd = require("buffer").new(mode, pipeStream.new(pm)) - pfd:setvbuf("no", nil) -- 2nd are to read chunk size - - -- popen processes start on create (which is LAME :P) - pfd.stream:resume() - - return pfd -end - -return plib -Flib/process.luaJlocal process = {} - -------------------------------------------------------------------------------- - ---Initialize coroutine library-- -process.list = setmetatable({}, {__mode="k"}) - -function process.findProcess(co) - co = co or coroutine.running() - for main, p in pairs(process.list) do - if main == co then - return p - end - for _, instance in pairs(p.instances) do - if instance == co then - return p - end - end - end -end - -------------------------------------------------------------------------------- - -function process.load(path, env, init, name) - checkArg(1, path, "string", "function") - checkArg(2, env, "table", "nil") - checkArg(3, init, "function", "nil") - checkArg(4, name, "string", "nil") - - assert(type(path) == "string" or env == nil, "process cannot load function environemnts") - - local p = process.findProcess() - if p then - env = env or p.env - end - env = setmetatable({}, {__index=env or _G}) - - local code = nil - if type(path) == 'string' then - local f, reason = io.open(path) - if not f then - return nil, reason - end - local reason - if f:read(2) == "#!" then - local command = f:read() - if require("text").trim(command) == "" then - reason = "no exec command" - else - code = function() - local result = table.pack(require("shell").execute(command, env, path)) - if not result[1] then - error(result[2], 0) - else - return table.unpack(result, 1, result.n) - end - end - end - else - code, reason = loadfile(path, "t", env) - end - f:close() - if not code then - return nil, reason - end - else -- path is code - code = path - end - - local thread = nil - thread = coroutine.create(function(...) - if init then - init() - end - -- pcall code so that we can remove it from the process list on exit - local result = - { - xpcall(code, function(msg) - if type(msg) == 'table' then return msg end - local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1') - return string.format('%s:\n%s', msg or '', stack) - end, ...) - } - process.internal.close(thread) - if not result[1] then - -- msg can be a custom error object - local msg = result[2] - if type(msg) == 'table' then - if msg.reason~="terminated" then error(msg.reason,2) end - result={0,msg.code} - else - error(msg,2) - end - end - return select(2,table.unpack(result)) - end,true) - process.list[thread] = { - path = path, - command = name, - env = env, - data = setmetatable( - { - handles = {}, - io = setmetatable({}, {__index=p and p.data and p.data.io or nil}), - coroutine_handler = setmetatable({}, {__index=p and p.data and p.data.coroutine_handler or nil}), - }, {__index=p and p.data or nil}), - parent = p, - instances = setmetatable({}, {__mode="v"}) - } - return thread -end - -function process.running(level) -- kept for backwards compat, prefer process.info - local info = process.info(level) - if info then - return info.path, info.env, info.command - end -end - -function process.info(levelOrThread) - local p - if type(levelOrThread) == "thread" then - p = process.findProcess(levelOrThread) - else - local level = levelOrThread or 1 - p = process.findProcess() - while level > 1 and p do - p = p.parent - level = level - 1 - end - end - if p then - return {path=p.path, env=p.env, command=p.command, data=p.data} - end -end - ---table of undocumented api subject to change and intended for internal use -process.internal = {} ---this is a future stub for a more complete method to kill a process -function process.internal.close(thread) - checkArg(1,thread,"thread") - local pdata = process.info(thread).data - for k,v in pairs(pdata.handles) do - v:close() - end - process.list[thread] = nil -end - -return process -Flib/rayEngine.lua9 -local component = require("component") -local computer = require("computer") -local advancedLua = require("advancedLua") -local colorlib = require("colorlib") -local image = require("image") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local event = require("event") - ----------------------------------------------------- Константы ------------------------------------------------------------------ - -local rayEngine = {} - -rayEngine.debugInformationEnabled = true -rayEngine.minimapEnabled = true -rayEngine.compassEnabled = false -rayEngine.watchEnabled = false -rayEngine.drawFieldOfViewOnMinimap = false -rayEngine.chatShowTime = 4 -rayEngine.chatHistory = {} - ----------------------------------------------- Расчетные функции ------------------------------------------------------------------ - --- Позиция горизонта, относительно которой рисуется мир -function rayEngine.calculateHorizonPosition() - rayEngine.horizonPosition = math.floor(buffer.screen.height / 2) -end - --- Размер панели чата и лимита его истории -function rayEngine.calculateChatSize() - rayEngine.chatPanelWidth, rayEngine.chatPanelHeight = math.floor(buffer.screen.width * 0.4), math.floor(buffer.screen.height * 0.4) - rayEngine.chatHistoryLimit = rayEngine.chatPanelHeight -end - --- Шаг, с которым будет изменяться угол рейкаста -function rayEngine.calculateRaycastStep() - rayEngine.raycastStep = rayEngine.player.fieldOfView / buffer.screen.width -end - --- Позиция оружия на экране и всех его вспомогательных текстур -function rayEngine.calculateWeaponPosition() - rayEngine.currentWeapon.xWeapon = buffer.screen.width - rayEngine.currentWeapon.weaponTexture.width + 1 - rayEngine.currentWeapon.yWeapon = buffer.screen.height - rayEngine.currentWeapon.weaponTexture.height + 1 - rayEngine.currentWeapon.xFire = rayEngine.currentWeapon.xWeapon + rayEngine.weapons[rayEngine.currentWeapon.ID].firePosition.x - rayEngine.currentWeapon.yFire = rayEngine.currentWeapon.yWeapon + rayEngine.weapons[rayEngine.currentWeapon.ID].firePosition.y - rayEngine.currentWeapon.xCrosshair = math.floor(buffer.screen.width / 2 - rayEngine.currentWeapon.crosshairTexture.width / 2) - rayEngine.currentWeapon.yCrosshair = math.floor(buffer.screen.height / 2 - rayEngine.currentWeapon.crosshairTexture.height / 2) -end - --- Грубо говоря, это расстояние от камеры до виртуального экрана, на котором рисуется весь наш мир, влияет на размер блоков -function rayEngine.calculateDistanceToProjectionPlane() - rayEngine.distanceToProjectionPlane = (buffer.screen.width / 2) / math.tan(math.rad((rayEngine.player.fieldOfView / 2))) -end - --- Быстрый перерасчет всего, что нужно -function rayEngine.calculateAllParameters() - rayEngine.calculateHorizonPosition() - rayEngine.calculateChatSize() - rayEngine.calculateRaycastStep() - rayEngine.calculateDistanceToProjectionPlane() - if rayEngine.currentWeapon then rayEngine.calculateWeaponPosition() end -end - ----------------------------------------------- Вспомогательные функции ------------------------------------------------------------------ - -local function constrainAngle(value) - if ( value < 0 ) then - value = value + 360 - elseif ( value > 360 ) then - value = value - 360 - end - return value -end - -local function getSkyColorByTime() - return rayEngine.world.colors.sky[rayEngine.world.dayNightCycle.currentTime > 0 and math.ceil(rayEngine.world.dayNightCycle.currentTime / rayEngine.world.dayNightCycle.length * #rayEngine.world.colors.sky) or 1] -end - -local function getBrightnessByTime() - return rayEngine.properties.shadingTransparencyMap[rayEngine.world.dayNightCycle.currentTime > 0 and math.ceil(rayEngine.world.dayNightCycle.currentTime / rayEngine.world.dayNightCycle.length * #rayEngine.properties.shadingTransparencyMap) or 1] -end - -local function getTileColor(basecolor, distance) - local limitedDistance = math.floor(distance * rayEngine.properties.shadingCascades / rayEngine.properties.shadingDistance) - local transparency = rayEngine.currentShadingTransparencyMapValue - math.floor(limitedDistance * 255 / rayEngine.properties.shadingCascades) - transparency = (transparency >= rayEngine.properties.shadingTransparencyMap[1] and transparency <= 255) and transparency or rayEngine.properties.shadingTransparencyMap[1] - return colorlib.alphaBlend(basecolor, 0x000000, transparency) -end - -function rayEngine.refreshTimeDependentColors() - rayEngine.world.colors.sky.current = getSkyColorByTime() - rayEngine.currentShadingTransparencyMapValue = getBrightnessByTime() - rayEngine.world.colors.groundByTime = colorlib.alphaBlend(rayEngine.world.colors.ground, 0x000000, rayEngine.currentShadingTransparencyMapValue) -end - -local function doDayNightCycle() - if rayEngine.world.dayNightCycle.enabled then - local computerUptime = computer.uptime() - if (computerUptime - rayEngine.world.dayNightCycle.lastComputerUptime) >= rayEngine.world.dayNightCycle.speed then - rayEngine.world.dayNightCycle.currentTime = rayEngine.world.dayNightCycle.currentTime + rayEngine.world.dayNightCycle.speed - if rayEngine.world.dayNightCycle.currentTime > rayEngine.world.dayNightCycle.length then rayEngine.world.dayNightCycle.currentTime = 0 end - rayEngine.world.dayNightCycle.lastComputerUptime = computerUptime - - rayEngine.refreshTimeDependentColors() - end - end -end - -local function convertWorldCoordsToMapCoords(x, y) - return math.round(x / rayEngine.properties.tileWidth), math.round(y / rayEngine.properties.tileWidth) -end - -local function getBlockCoordsByLook(distance) - local radRotation = math.rad(rayEngine.player.rotation) - return convertWorldCoordsToMapCoords(rayEngine.player.position.x + distance * math.sin(radRotation) * rayEngine.properties.tileWidth, rayEngine.player.position.y + distance * math.cos(radRotation) * rayEngine.properties.tileWidth) -end - ----------------------------------------------------- Работа с файлами ------------------------------------------------------------------ - --- Загрузка параметров движка -function rayEngine.loadEngineProperties(pathToRayEnginePropertiesFile) - rayEngine.properties = table.fromFile(pathToRayEnginePropertiesFile) -end - --- Загрузка конифгурации оружия -function rayEngine.loadWeapons(pathToWeaponsFolder) - rayEngine.weaponsFolder = pathToWeaponsFolder - rayEngine.weapons = table.fromFile(rayEngine.weaponsFolder .. "Weapons.cfg") - rayEngine.changeWeapon(1) -end - --- Загрузка конкретного мира -function rayEngine.loadWorld(pathToWorldFolder) - rayEngine.world = table.fromFile(pathToWorldFolder .. "/World.cfg") - rayEngine.map = table.fromFile(pathToWorldFolder .. "/Map.cfg") - rayEngine.player = table.fromFile(pathToWorldFolder .. "/Player.cfg") - rayEngine.blocks = table.fromFile(pathToWorldFolder .. "/Blocks.cfg") - -- Дополняем карту ее размерами - rayEngine.map.width = #rayEngine.map[1] - rayEngine.map.height = #rayEngine.map - -- Ебашим правильную позицию игрока, основанную на этой ХУЙНЕ, которую ГЛЕБ так ЛЮБИТ - rayEngine.player.position.x = rayEngine.properties.tileWidth * rayEngine.player.position.x - rayEngine.properties.tileWidth / 2 - rayEngine.player.position.y = rayEngine.properties.tileWidth * rayEngine.player.position.y - rayEngine.properties.tileWidth / 2 - -- Рассчитываем цвета, зависимые от времени - небо, землю, стены - rayEngine.refreshTimeDependentColors() - -- Обнуляем текущее время, если превышен лимит, а то мало ли какой пидорас начнет править конфиги мира - rayEngine.world.dayNightCycle.currentTime = rayEngine.world.dayNightCycle.currentTime > rayEngine.world.dayNightCycle.length and 0 or rayEngine.world.dayNightCycle.currentTime - -- Осуществляем базовое получение аптайма пекарни - rayEngine.world.dayNightCycle.lastComputerUptime = computer.uptime() - -- Рассчитываем необходимые параметры движка - rayEngine.calculateAllParameters() - - -- rayEngine.wallsTexture = image.load("/heart.pic") - -- rayEngine.wallsTexture = image.transform(rayEngine.wallsTexture, rayEngine.properties.tileWidth, rayEngine.properties.tileWidth / 2) -end - ----------------------------------------------------- Функции, связанные с игроком ------------------------------------------------------------------ - -function rayEngine.changeWeapon(weaponID) - if rayEngine.weapons[weaponID] then - rayEngine.currentWeapon = { - ID = weaponID, - damage = rayEngine.weapons[weaponID].damage, - weaponTexture = image.load(rayEngine.weaponsFolder .. rayEngine.weapons[weaponID].weaponTexture), - fireTexture = image.load(rayEngine.weaponsFolder .. rayEngine.weapons[weaponID].fireTexture), - crosshairTexture = image.load(rayEngine.weaponsFolder .. rayEngine.weapons[weaponID].crosshairTexture) - } - rayEngine.calculateWeaponPosition() - else - rayEngine.currentWeapon = nil - end -end - -function rayEngine.move(distanceForward, distanceRight) - local forwardRotation = math.rad(rayEngine.player.rotation) - local rightRotation = math.rad(rayEngine.player.rotation + 90) - local xNew = rayEngine.player.position.x + distanceForward * math.sin(forwardRotation) + distanceRight * math.sin(rightRotation) - local yNew = rayEngine.player.position.y + distanceForward * math.cos(forwardRotation) + distanceRight * math.cos(rightRotation) - - local xWorld, yWorld = convertWorldCoordsToMapCoords(xNew, yNew) - if rayEngine.map[yWorld][xWorld] == nil then - rayEngine.player.position.x, rayEngine.player.position.y = xNew, yNew - end -end - -function rayEngine.rotate(angle) - rayEngine.player.rotation = constrainAngle(rayEngine.player.rotation + angle) -end - -function rayEngine.turnRight() - rayEngine.rotate(rayEngine.player.rotationSpeed) -end - -function rayEngine.turnLeft() - rayEngine.rotate(-rayEngine.player.rotationSpeed) -end - -function rayEngine.moveForward() - rayEngine.move(rayEngine.player.moveSpeed, 0) -end - -function rayEngine.moveBackward() - rayEngine.move(-rayEngine.player.moveSpeed, 0) -end - -function rayEngine.moveLeft() - rayEngine.move(0, -rayEngine.player.moveSpeed) -end - -function rayEngine.moveRight() - rayEngine.move(0, rayEngine.player.moveSpeed) -end - -function rayEngine.jump() - if not rayEngine.player.jumpTimer then - local function onJumpFinished() - rayEngine.horizonPosition = rayEngine.horizonPosition - rayEngine.player.jumpHeight; - rayEngine.horizonPosition = rayEngine.horizonPosition - rayEngine.player.jumpHeight; - rayEngine.player.jumpTimer = nil - end - - rayEngine.player.jumpTimer = event.timer(1, onJumpFinished) - rayEngine.horizonPosition = rayEngine.horizonPosition + rayEngine.player.jumpHeight - rayEngine.horizonPosition = rayEngine.horizonPosition + rayEngine.player.jumpHeight - end -end - -function rayEngine.crouch() - rayEngine.player.isCrouched = not rayEngine.player.isCrouched - local heightAdder = rayEngine.player.isCrouched and -rayEngine.player.crouchHeight or rayEngine.player.crouchHeight - rayEngine.horizonPosition = rayEngine.horizonPosition + heightAdder - rayEngine.horizonPosition = rayEngine.horizonPosition + heightAdder -end - -function rayEngine.destroy(distance) - local xBlock, yBlock = getBlockCoordsByLook(distance) - if rayEngine.map[yBlock] and rayEngine.map[yBlock][xBlock] and rayEngine.blocks[rayEngine.map[yBlock][xBlock]] and rayEngine.blocks[rayEngine.map[yBlock][xBlock]].canBeDestroyed then rayEngine.map[yBlock][xBlock] = nil end -end - -function rayEngine.place(distance, blockColor) - local xBlock, yBlock = getBlockCoordsByLook(distance) - if rayEngine.map[yBlock] and rayEngine.map[yBlock][xBlock] == nil then rayEngine.map[yBlock][xBlock] = blockColor end -end - ----------------------------------------------------- Функции интерфейса ------------------------------------------------------------------ - -function rayEngine.drawDebugInformation(x, y, width, transparency, ...) - local lines = {...} - buffer.square(x, y, width, #lines, 0x000000, 0x000000, " ", transparency); x = x + 1 - for line = 1, #lines do buffer.text(x, y, 0xEEEEEE, lines[line]); y = y + 1 end -end - -local function drawFieldOfViewAngle(x, y, distance, color) - local fieldOfViewHalf = rayEngine.player.fieldOfView / 2 - local firstAngle, secondAngle = math.rad(-(rayEngine.player.rotation - fieldOfViewHalf)), math.rad(-(rayEngine.player.rotation + fieldOfViewHalf)) - local xFirst, yFirst = math.floor(x + math.sin(firstAngle) * distance), math.floor(y + math.cos(firstAngle) * distance) - local xSecond, ySecond = math.floor(x + math.sin(secondAngle) * distance), math.floor(y + math.cos(secondAngle) * distance) - buffer.semiPixelLine(x, y, xFirst, yFirst, color) - buffer.semiPixelLine(x, y, xSecond, ySecond, color) -end - -function rayEngine.drawMap(x, y, width, height, transparency) - local xHalf, yHalf = math.floor(width / 2), math.floor(height / 2) - local xMap, yMap = convertWorldCoordsToMapCoords(rayEngine.player.position.x, rayEngine.player.position.y) - - buffer.square(x, y, width, yHalf, 0x000000, 0x000000, " ", transparency) - - local xPos, yPos = x, y * 2 - 1 - for i = yMap - yHalf + 1, yMap + yHalf do - for j = xMap + xHalf + 1, xMap - xHalf + 2, -1 do - if rayEngine.map[i] and rayEngine.map[i][j] then - buffer.semiPixelSet(xPos, yPos, rayEngine.blocks[rayEngine.map[i][j]].color) - end - xPos = xPos + 1 - end - xPos = x; yPos = yPos + 1 - end - - local xPlayer, yPlayer = x + xHalf, y + yHalf - --Поле зрения - if rayEngine.drawFieldOfViewOnMinimap then drawFieldOfViewAngle(xPlayer, yPlayer, 5, 0xCCFFBF) end - --Игрок - buffer.semiPixelSet(xPlayer, yPlayer, 0x66FF40) -end - -function rayEngine.intro() - local logo = image.fromString("17060000FF 0000FF 0000FF 0000FF 007EFF▄007EFF▄007EFF▄007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▄007EFF▄007EFF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFF▄007EFF▀007EFF▀0000FF 0000FF 0000FF 0000FF 0053FF▄0053FF▀0053FF▀0053FF▀0053FF▄0000FF 0000FF 0000FF 0000FF 007EFF▀007EFF▀007EFF▄0000FF 0000FF 0000FF 007EFF▀007EFF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 530000 0000FF 0078FF▀0000FF 537800▀0078FF▀0078FF▀0078FF▀0078FF▀0078FF▀0078FF▀7E7800▀0078FF▀0000FF 0078FF▀0000FF 0000FF 007EFF▀007EFF▀007EFF▄007EFF▄007EFF▄0000FF 0000FF 0053FF▀0053FF▀0053FF▀0000FF 0000FF 007EFF▄007EFF▄007EFF▄007EFF▀007EFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀007EFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFFP007EFFo007EFFw007EFFe007EFFr007EFFe007EFFd0000FF 007EFFb007EFFy0000FF 007EFFR007EFFa007EFFy007EFFE007EFFn007EFFg007EFFi007EFFn007EFFe007EFF™0000FF 0000FF ") - local x, y = math.floor(buffer.screen.width / 2 - logo.width / 2), math.floor(buffer.screen.height / 2 - logo.height / 2) - local function draw(transparency) - buffer.clear(0xF0F0F0); - buffer.image(x, y, logo) - buffer.square(1, 1, buffer.screen.width, buffer.screen.height, 0x000000, 0x000000, " ", transparency) - buffer.draw() - os.sleep(0) - end - for i = 0, 100, 20 do draw(i) end - os.sleep(1.5) - for i = 100, 0, -20 do draw(i) end -end - -function rayEngine.compass(x, y) - if not rayEngine.compassImage then rayEngine.compassImage = image.fromString("1C190000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 553600▄373100▄543600▄373600▄543600▄373100▄540000 375400▄673700▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0055FF▄675400▄553700▄375500▄550000 375500▄540000 375400▄540000 373600▄310000 675500▄677E00▄375300▄365400▄373600▄540000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 555400▄553700▄540000 543700▄540000 375300▄533100▄310B00▄310000 310000 360000 543100▄375300▄553100▄533600▄543200▄313600▄372A00▄373100▄0054FF▄0054FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 540000 543200▄540000 545300▄540600▄063100▄310000 315400▄365400▄373100▄313600▄530000 0000FF 0000FF 535400▄535400▄540000 365300▄533100▄065300▄310000 530600▄0054FF▄0000FF 0000FF 0000FF 0000FF 0000FF 530000 365300▄543600▄310600▄312A00▄2A5300▄365300▄543600▄540000 365300▄315300▄530000 0000FF 0000FF 535400▄540000 540000 313600▄363100▄540000 313600▄315300▄062A00▄543100▄0000FF 0000FF 0000FF 0000FF 315300▄533600▄312A00▄2A3100▄315300▄533600▄315400▄540000 535400▄540000 530000 533100▄0000FF 0000FF 540000 540000 315400▄540000 543100▄530000 543100▄313600▄2A0000 2A2900▄540000 0000FF 0000FF 0000FF 533100▄315400▄530000 062A00▄533100▄315300▄535400▄533100▄540000 315400▄543100▄530000 0000FF 0000FF 540000 535400▄315300▄535400▄363100▄535400▄530000 530000 312A00▄2A5300▄0054FF▀0000FF 0000FF 0000FF 312A00▄530000 553600▄2A5500▄2A0000 312A00▄533600▄545300▄315400▄535400▄540000 540000 0053FF▄0053FF▄540000 530000 535400▄535400▄545300▄530000 363100▄312A00▄313600▄545300▄0000FF 0000FF 0000FF 0000FF 530000 535400▄540000 545300▄555400▄315500▄315400▄533100▄543100▄530000 533100▄530000 535400▄535500▄533100▄535400▄315300▄533100▄533600▄315300▄530000 542A00▄312800▄0029FF▀0000FF 0000FF 0000FF 0000FF 530000 545500▄540000 555300▄535400▄530000 542A00▄545300▄365400▄530000 543600▄2A5400▄547E00▄550000 545300▄533600▄540000 530000 530000 542A00▄2A0000▄0029FF▀0000FF 0000FF 0000FF 0000FF 0000FF 530000 535400▄540000 540000 540000 545300▄530000 540000 2A5500▄545300▄292A00▄290000 292A00▄290000 295400▄292A00▄292A00▄290000 542A00▄543600▄285400▄0054FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 530000 533100▄540000 530000 535400▄0053FF▀0053FF▀542800▄542800▄532800▄2A2900▄542900▄532900▄542900▄542900▄532900▄542800▄2A2900▄2A2800▄532900▄552800▄542800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 2A5300▄530000 535400▄540000 530000 532A00▀2A5300▄2A5500▄0029FF▀0028FF▀0028FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0028FF▀0028FF▀0028FF▀295300▄535500▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 535400▄545300▄530000 545300▄530000 2A0000 2A5300▄545300▄532A00▄542900▄532900▄2A2900▄2A2900▄2A2800▄2A2800▄2A2800▄2A2900▄2A2900▄532900▄532900▄2A2900▄542A00▄545300▄0054FF▄0054FF▄0000FF 0000FF 0000FF 545300▄530000 530000 545300▄2A5300▄532A00▄542900▄290000 295400▄297F00▄548100▄548100▄558100▄558100▄558100▄558100▄538100▄2A8100▄007E00▄295400▄2A2900▄295400▄2A2900▄290000 542A00▄557E00▄0000FF 0000FF 530000 2A0000 545300▄530000 532900▄285400▄2A8000▄7F8100▄810000 810000 810000 810000 810000 810000 812A00N810000 810000 810000 810000 810000 808100▄548100▄545500▄295300▄282900▄542900▄0000FF 0000FF 2A0000 2A0000 545300▄2A2900▄298000▄810000 810000 815500381550018155005810000 810000 810000 810000 810000 810000 810000 810000 81550048155005810000 810000 810000 558100▄2A5300▄282900▄0029FF▄0000FF 532A00▄2A0000 545300▄547E00▄810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 7F8000▄290000 292800▄0000FF 2A0000 2A0000 2A5300▄7E5300▄810000 812A00W810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 812A00E810000 807E00▄2A2900▄292800▄0000FF 2A0000 2A2900▄2A0000 2A0000 552A00▄810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 810000 815500▄542900▄280000 0028FF▀530000 2A0000 2A0000 290000 2A2900▄545300▄552A00▄815500▄810000 815500281550028155005810000 810000 810000 810000 810000 810000 810000 815500181550038155005817F00▄552900▄292800▄280000 282A00▄0000FF 530000 2A0000 540000 2A5300▄282A00▄290000 532900▄542A00▄542800▄552900▄7F5300▄815500▄817F00▄817F00▄810000 812A00S810000 817F00▄815500▄815500▄555400▄532900▄292800▄280000 282A00▄282A00▄530000 0000FF 532A00▄2A0000 530000 530000 2A5300▄290000 282900▄002900▄280000 280000 280000 290000 292A00▄2A2900▄542900▄532900▄2A0000 295300▄282900▄280000 280000 280000 282900▄295300▄295300▄2A5300▄552A00▄0000FF 530000 295300▄535400▄540000 535400▄540000 535400▄2A5500▄282900▄280000▄280000 280000▄290000▄2A2800▄2A2900▄552A00▄7E0000▄2A0000▄280000▄280000 280000▄280000▄002AFF▀002AFF▀002AFF▀002AFF▀0000FF 0000FF 532900▄532800▄0029FF▀0029FF▀0029FF▀0029FF▀0029FF▀0029FF▀0000FF 2A9800▄285500▄547E00▄7E5400▄7F5300▄7E2900▄7E2900▄552A00▄542A00▄2A5300▄282A00▄2A7E00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF ") end - buffer.image(x, y, rayEngine.compassImage) - - x, y = x + 15, y + 17 - local distance = 3.4 - local northAngle = -rayEngine.player.rotation - local xScaleFactor = 2.2 - local southPoint, northPoint = {}, {} - local northAngleRad = math.rad(northAngle) - northPoint.x, northPoint.y = math.round(x + math.sin(northAngleRad) * distance * xScaleFactor), math.round(y - math.cos(northAngleRad) * distance) - northAngleRad = math.rad(northAngle + 180) - southPoint.x, southPoint.y = math.round(x + math.sin(northAngleRad) * distance * xScaleFactor), math.round(y - math.cos(northAngleRad) * distance) - - y = y * 2 - buffer.semiPixelLine(x, y, northPoint.x, northPoint.y * 2, 0xFF5555) - buffer.semiPixelLine(x, y, southPoint.x, southPoint.y * 2, 0xFFFFFF) - buffer.semiPixelSet(x, y, 0x000000) -end - -function rayEngine.watch(x, y) - if not rayEngine.watchImage then rayEngine.watchImage = image.fromString("20190000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0053FF▄552900▄673100▄7E2A00▄7E3100▄7E2A00▄7E3100▄672A00▄7E2A00▄672900▄7F3100▄7E2A00▄0053FF▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 313600▄290000 290600▄062900▄290000 062900▄290000 062900▄290000 290600▄062900▄2A2900▄2A0600▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 313600▄012900▄2A2900▄290000 012900▄290000 290000 012A00▄290000 290000 312900▄293100▄310600▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 310000 292800▄062A00▄290000 290100▄062900▄290000 290000 2C2900▄290600▄293100▄2A2900▄292800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0054FF▄557F00▄012800▄290000 290000 292800▄290000 012900▄292800▄290600▄290000 292800▄290000 012800▄807E00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF AB8100▄7F8000▄283100▄280000 015400▄318100▄005300▄065500▄315500▄282A00▄296700▄015500▄290000 002900▄677E00▄81AA00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 807F00▄7F8000▄677E00▄812900▄672800▄542800▄2A0000▄2A0000▄815400▄ACAA00▄555400▄283100▄2A0000 312900▄672800▄7F5300▄7F0000 7F0000 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 007EFF▄817E00▄7E2900▄2A0000▄805300▄54AA00▄003100▄2A0000 310600▄532900▄312800▄532900▄360100▄552900▄672800▄7E2A00▄315500▄678000▄555300▄540000▄805400▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0081FF▄802900▄280100▄280000 285400▄310600▄532900▄290000 290000 290000 290000 29D700129D7002290000 290000 290000 282900▄062900▄2A2900▄550600▄556700▄285500▄542800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0080FF▄557E00▄28AA00▄005500▄552900▄290000 290000 290000 29D700129D7001290000 290000 290000 290000 290000 290000 290000 29D7001290000 290000 290000 290000 672900▄318000▄297F00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 543100▄002800▄553100▄7E2800▄290000 29D700129D7000290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7002290000 290000 7E2900▄7E6700▄282900▄808100▄0000FF 0000FF 0000FF 0000FF 805500▄280000▄290000 310600▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 555400▄535400▄533100▄D58000▄0080FF▄0000FF 552900▀550000 54AB00▄558100▄290000 290000 29D7009290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7003290000 315400▄548100▄067E00▄557F00▄806700▄ACAB00▄0055FF▀545500▄AB3100▄815400▄290100▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 545300▄AB5500▄815300▄558000▄558000▄0000FF 0000FF 538100▄002800▄290600▄290000 290000 290000 29D7008290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7004290000 290000 530000 000000 286700▄0080FF▀0000FF 0000FF 0000FF 0000FF 292A00▄000100▄530000 285400▄290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 290000 296700▄063100▄280000 818000▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 550000 810000▄532800▄015300▄280000 292800▄290000 29D7007290000 290000 290000 290000 290000 290000 290000 290000 290000 29D7005290000 290100▄292A00▄065300▄7F0000▄802900▄81C900▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 549900▄002900▄292800▄312800▄290600▄283100▄290600▄292800▄290000 290000 290000 29D7006290000 290000 292800▄292800▄290000 285300▄2A0600▄362800▄280000 285500▄0081FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 7EAA00▄317E00▄005300▄547E00▄7F2900▄290000▄292A00▄063100▄283100▄295500▄286200▄285500▄012A00▄292A00▄310600▄540000▄807E00▄005500▄015500▄530000 0081FF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 7E8100▄7E8000▄557F00▄317E00▄2A3600▄283100▄005400▄2A5300▄817E00▄AA7E00▄545300▄005300▄005400▄283100▄537E00▄548100▄7F0000 7E8000▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 819800▄807F00▄280000 002900▄312900▄7E2800▄292800▄532800▄552900▄280000 550100▄542800▄282900▄000100▄7E0000 AA9800▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 555300▄012800▄290000 062900▄290000 062900▄290000 062900▄290000 290000 290000 290600▄280000 007EFF▀0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 313600▄062900▄312900▄290000 012900▄293100▄290000 062900▄312900▄290600▄312900▄2A0000 2A2900▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 315400▄290600▄310000 062900▄290000 293100▄062900▄290000 293100▄290000 293100▄062900▄312A00▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0054FF▀285500▄285500▄015500▄285500▄285500▄015500▄295500▄015500▄285500▄015500▄285500▄065500▄0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF 0000FF ") end - - buffer.image(x, y, rayEngine.watchImage) - x, y = x + 15, y + 12 - - local realTimeInSeconds = rayEngine.world.dayNightCycle.currentTime * 86400 / rayEngine.world.dayNightCycle.length - local hours = realTimeInSeconds / 3600 - local _, minutes = math.modf(hours) - local hourAngle = math.rad(hours * 360 / 12) - local minuteAngle = math.rad(minutes * 360) - local hourArrowLength, minuteArrowLength = 2.8, 4.5 - local xMinute, yMinute = math.round(x + math.sin(minuteAngle) * minuteArrowLength * 2), math.round(y - math.cos(minuteAngle) * minuteArrowLength) - local xHour, yHour = math.round(x + math.sin(hourAngle) * hourArrowLength * 2), math.round(y - math.cos(hourAngle) * hourArrowLength) - - y = y * 2 - buffer.semiPixelLine(x, y, xMinute, yMinute * 2, 0xEEEEEE) - buffer.semiPixelLine(x, y, xHour, yHour * 2, 0xEEEEEE) -end - -local function addItemToChatHistory(text, color) - text = string.wrap({text}, rayEngine.chatPanelWidth - 2) - table.insert(rayEngine.chatHistory, {color = color, text = text}) - if #rayEngine.chatHistory > rayEngine.chatHistoryLimit then table.remove(rayEngine.chatHistory, 1) end -end - -function rayEngine.chat(transparency) - local x, y = 1, buffer.screen.height - rayEngine.chatPanelHeight - 3 - buffer.square(x, y, rayEngine.chatPanelWidth, rayEngine.chatPanelHeight, 0x000000, 0xFFFFFF, " ", transparency or 50) - buffer.setDrawLimit(x, y, rayEngine.chatPanelWidth, rayEngine.chatPanelHeight) - local yMessage = y + rayEngine.chatPanelHeight - 1 - x = x + 1 - - for message = #rayEngine.chatHistory, 1, -1 do - for line = #rayEngine.chatHistory[message].text, 1, -1 do - buffer.text(x, yMessage, rayEngine.chatHistory[message].color or 0xFFFFFF, rayEngine.chatHistory[message].text[line]) - yMessage = yMessage - 1 - if yMessage < y then buffer.resetDrawLimit(); return end - end - end - - buffer.resetDrawLimit() -end - -function rayEngine.commandLine(transparency) - transparency = transparency or 50 - local inputPanelHeight = 3 - local x, y = 1, buffer.screen.height - inputPanelHeight + 1 - --Врубаем чат и рисуем все, включая его - rayEngine.chatEnabled = true - rayEngine.update() - --Рисуем панель ввода - buffer.square(x, y, buffer.screen.width, inputPanelHeight, 0x000000, 0xFFFFFF, " ", transparency) - - --Ввод данных - local text = GUI.input(x + 2, y + 1, buffer.screen.width - 4, 0xFFFFFF, "") - local words = {}; for word in string.gmatch(text, "[^%s]+") do table.insert(words, unicode.lower(word)) end - if #words > 0 then - if unicode.sub(words[1], 1, 1) == "/" then - words[1] = unicode.sub(words[1], 2, -1) - if words[1] == "time" then - if words[2] == "set" and words[3] and tonumber(words[3]) then - local newTime = tonumber(words[3]) - if newTime < 0 or newTime > rayEngine.world.dayNightCycle.length then - addItemToChatHistory("Время не может быть отрицательным и превышать длину суток (" .. rayEngine.world.dayNightCycle.length .. " секю)", 0xFF8888) - else - rayEngine.world.dayNightCycle.currentTime = math.floor(newTime) - addItemToChatHistory("Время успешно изменено на: " .. newTime, 0xFFDB40) - end - elseif words[2] == "get" then - addItemToChatHistory("Текущее время: " .. rayEngine.world.dayNightCycle.currentTime, 0xFFDB40) - addItemToChatHistory("Длина суток: " .. rayEngine.world.dayNightCycle.length, 0xFFDB40) - elseif words[2] == "lock" then - rayEngine.world.dayNightCycle.enabled = not rayEngine.world.dayNightCycle.enabled - addItemToChatHistory("Состояние цикла дня и ночи: " .. tostring(rayEngine.world.dayNightCycle.enabled), 0xFFDB40) - end - elseif words[1] == "setrenderquality" and tonumber(words[2]) then - rayEngine.properties.raycastQuality = tonumber(words[2]) - addItemToChatHistory("Качество рендера изменено на: " .. tonumber(words[2]), 0xFFDB40) - elseif words[1] == "setdrawdistance" and tonumber(words[2]) then - rayEngine.properties.drawDistance = tonumber(words[2]) - addItemToChatHistory("Дистанция прорисовки изменена на: " .. tonumber(words[2]), 0xFFDB40) - elseif words[1] == "setshadingcascades" and tonumber(words[2]) then - rayEngine.properties.shadingCascades = tonumber(words[2]) - addItemToChatHistory("Количество цветов для отрисовки блока изменено на: " .. tonumber(words[2]), 0xFFDB40) - elseif words[1] == "setshadingdistance" and tonumber(words[2]) then - rayEngine.properties.shadingDistance = tonumber(words[2]) - addItemToChatHistory("Дистация затенения блоков изменена на: " .. tonumber(words[2]), 0xFFDB40) - elseif words[1] == "help" then - addItemToChatHistory("Доступные команды:", 0xFFDB40) - addItemToChatHistory("/time get", 0xFFFFBF) - addItemToChatHistory("/time set ", 0xFFFFBF) - addItemToChatHistory("/time lock", 0xFFFFBF) - addItemToChatHistory(" ", 0xFFFFFF) - addItemToChatHistory("/setRenderQuality ", 0xFFFFBF) - addItemToChatHistory("/setDrawDistance ", 0xFFFFBF) - addItemToChatHistory("/setShadingCascades ", 0xFFFFBF) - addItemToChatHistory("/setShadingDistance ", 0xFFFFBF) - else - addItemToChatHistory("Неизвестная команда. Введите /help для получения списка команд", 0xFF8888) - end - else - addItemToChatHistory("> " .. text, 0xFFFFFF) - end - end - - --Активируем таймер - if rayEngine.chatTimer then event.cancel(rayEngine.chatTimer) end - rayEngine.chatEnabled = true - rayEngine.chatTimer = event.timer(rayEngine.chatShowTime, function() rayEngine.chatEnabled = false; rayEngine.chatTimer = nil; update() end) -end - -function rayEngine.toggleMinimap() - rayEngine.minimapEnabled = not rayEngine.minimapEnabled -end - -function rayEngine.toggleDebugInformation() - rayEngine.debugInformationEnabled = not rayEngine.debugInformationEnabled -end - -function rayEngine.toggleCompass() - rayEngine.compassEnabled = not rayEngine.compassEnabled - if not rayEngine.compassEnabled then rayEngine.compassImage = nil end -end - -function rayEngine.toggleWatch() - rayEngine.watchEnabled = not rayEngine.watchEnabled - if not rayEngine.watchEnabled then rayEngine.watchImage = nil end -end - -function rayEngine.drawWeapon() - if rayEngine.currentWeapon.needToFire then buffer.image(rayEngine.currentWeapon.xFire, rayEngine.currentWeapon.yFire, rayEngine.currentWeapon.fireTexture); rayEngine.currentWeapon.needToFire = false end - buffer.image(rayEngine.currentWeapon.xWeapon, rayEngine.currentWeapon.yWeapon, rayEngine.currentWeapon.weaponTexture) - buffer.image(rayEngine.currentWeapon.xCrosshair, rayEngine.currentWeapon.yCrosshair, rayEngine.currentWeapon.crosshairTexture) -end - -function rayEngine.drawStats() - local width = math.floor(buffer.screen.width * 0.3) - local height = 5 - local x, y = buffer.screen.width - width - 1, 2 - buffer.square(x, y, width, height, 0x000000, 0xFFFFFF, " ", 50) - - GUI.progressBar(x + 1, y + 4, width - 2, 1, 0x000000, 0xFF5555, rayEngine.player.health.current, rayEngine.player.health.maximum, true) -end - ----------------------------------------------------- Функции отрисовки мира ------------------------------------------------------------------ - -local function raycast(angle) - angle = math.rad(angle) - local angleSinDistance, angleCosDistance, currentDistance, xWorld, yWorld, xMap, yMap, tile = math.sin(angle) * rayEngine.properties.raycastQuality, math.cos(angle) * rayEngine.properties.raycastQuality, 0, rayEngine.player.position.x, rayEngine.player.position.y - - while true do - if currentDistance <= rayEngine.properties.drawDistance then - xMap, yMap = math.floor(xWorld / rayEngine.properties.tileWidth), math.floor(yWorld / rayEngine.properties.tileWidth) - if rayEngine.map[yMap] and rayEngine.map[yMap][xMap] then - return currentDistance, rayEngine.map[yMap][xMap] - end - - xWorld, yWorld = xWorld + angleSinDistance, yWorld + angleCosDistance - currentDistance = currentDistance + rayEngine.properties.raycastQuality - else - return nil - end - end -end - -function rayEngine.drawWorld() - --Земля - buffer.clear(rayEngine.world.colors.groundByTime) - --Небо - buffer.square(1, 1, buffer.screen.width, rayEngine.horizonPosition, rayEngine.world.colors.sky.current) - --Сцена - local startAngle, endAngle, startX, distanceToTile, tileID, height, startY, tileColor = rayEngine.player.rotation - rayEngine.player.fieldOfView / 2, rayEngine.player.rotation + rayEngine.player.fieldOfView / 2, 1 - for angle = startAngle, endAngle, rayEngine.raycastStep do - distanceToTile, tileID = raycast(angle) - if distanceToTile then - -- Получаем цвет стенки - tileColor = getTileColor(rayEngine.blocks[tileID].color, distanceToTile) - - -- Поддержка "высококачественной" doubleHeight-графики - if rayEngine.properties.useSimpleRenderer then - height = rayEngine.properties.tileWidth / distanceToTile * rayEngine.distanceToProjectionPlane - startY = rayEngine.horizonPosition - height / 2 + 1 - buffer.square(math.floor(startX), math.floor(startY), 1, math.floor(height), tileColor, 0x000000, " ") - else - height = rayEngine.properties.tileWidth / distanceToTile * rayEngine.distanceToProjectionPlane * 2 - startY = rayEngine.horizonPosition * 2 - height / 2 + 1 - buffer.semiPixelSquare(math.floor(startX), math.floor(startY), 1, height, tileColor) - end - - --ТИКСТУРКА)))00 - -- local xTexture = startX % rayEngine.properties.tileWidth + 1 - -- if xTexture >= 1 and xTexture <= buffer.screen.width then - -- local column = image.getColumn(rayEngine.wallsTexture, xTexture) - -- column = image.transform(column, 1, height) - -- buffer.image(math.floor(startX), math.floor(startY), column) - -- end - end - startX = startX + 1 - end -end - -function rayEngine.update() - local frameRenderClock = os.clock() - - rayEngine.drawWorld() - if rayEngine.currentWeapon then rayEngine.drawWeapon() end - if rayEngine.minimapEnabled then rayEngine.drawMap(3, 2, 24, 24, 50) end - -- rayEngine.drawStats() - local xTools, yTools = 3, buffer.screen.height - 25 - if rayEngine.compassEnabled then rayEngine.compass(xTools, yTools); xTools = xTools + 30 end - if rayEngine.watchEnabled then rayEngine.watch(xTools, yTools) end - if rayEngine.chatEnabled then rayEngine.chat() end - doDayNightCycle() - - if rayEngine.debugInformationEnabled then - rayEngine.drawDebugInformation(3, 2 + (rayEngine.minimapEnabled and 12 or 0), 24, 60, - "renderTime: " .. math.doubleToString((os.clock() - frameRenderClock) * 1000, 2) .. " ms", - "freeRAM: " .. math.doubleToString(computer.freeMemory() / 1024, 2) .. " KB", - "pos: " .. math.doubleToString(rayEngine.player.position.x) .. " x " .. math.doubleToString(rayEngine.player.position.y) - ) - end - - buffer.draw() -end - ----------------------------------------------------------------------------------------------------------------------------------- - -function rayEngine.changeResolution(width, height) - component.gpu.setResolution(width, height) - buffer.start() - rayEngine.calculateAllParameters() -end - -function rayEngine.fire() - rayEngine.currentWeapon.needToFire = true - rayEngine.update() - os.sleep(0.1) - rayEngine.update() -end - ----------------------------------------------------------------------------------------------------------------------------------- - -return rayEngine -F -lib/rc.lua-- Keeps track of loaded scripts to retain local values between invocation --- of their command callbacks. -local rc = {} -rc.loaded = {} - -return rc - -Flib/serialization.luap -require("advancedLua") -local serialization = {} - -------------------------------------------------- Public methods ----------------------------------------------------------------- - -function serialization.serialize(...) - return table.serialize(...) -end - -function serialization.unserialize(...) - return table.unserialize(...) -end - -function serialization.serializeToFile(...) - table.toFile(...) -end - -function serialization.unserializeFromFile(...) - return table.fromFIle(...) -end - ----------------------------------------------------------------------------------------------------------------------- - -return serialization - - - - -F -lib/sh.luaklocal event = require("event") -local fs = require("filesystem") -local process = require("process") -local shell = require("shell") -local term = require("term") -local text = require("text") -local tx = require("transforms") -local unicode = require("unicode") - -local sh = {} -sh.internal = {} - --- --[[@@]] are not just comments, but custom annotations for delayload methods. --- See package.lua and the api wiki for more information -function isWordOf(w, vs) return w and #w == 1 and not w[1].qr and tx.first(vs,{{w[1].txt}}) ~= nil end -function isWord(w,v) return isWordOf(w,{v}) end -local local_env = {event=event,fs=fs,process=process,shell=shell,term=term,text=text,tx=tx,unicode=unicode,isWordOf=isWordOf,isWord=isWord} - -------------------------------------------------------------------------------- - ---SH API - -sh.internal.globbers = {{"*",".*"},{"?","."}} -sh.internal.ec = {} -sh.internal.ec.parseCommand = 127 -sh.internal.ec.sysError = 128 -sh.internal.ec.last = 0 - -function sh.getLastExitCode() - return sh.internal.ec.last -end - -function sh.internal.command_passed(ec) - local code = sh.internal.command_result_as_code(ec) - return code == 0 -end - -function sh.internal.command_result_as_code(ec) - -- convert lua result to bash ec - if ec == false then - return 1 - elseif ec == nil or ec == true or type(ec) ~= "number" then - return 0 - else - return ec - end -end - -function sh.internal.resolveActions(input, resolver, resolved) - checkArg(1, input, "string") - checkArg(2, resolver, "function", "nil") - checkArg(3, resolved, "table", "nil") - resolver = resolver or shell.getAlias - resolved = resolved or {} - - local processed = {} - - local prev_was_delim, simple = true, true - local words, reason = text.internal.tokenize(input) - - if not words then - return nil, reason - end - - while #words > 0 do - local next = table.remove(words,1) - if isWordOf(next, {";","&&","||","|"}) then - prev_was_delim,simple = true,false - resolved = {} - elseif prev_was_delim then - prev_was_delim = false - -- if current is actionable, resolve, else pop until delim - if next and #next == 1 and not next[1].qr then - local key = next[1].txt - if key == "!" then - prev_was_delim,simple = true,false -- special redo - elseif not resolved[key] then - resolved[key] = resolver(key) - local value = resolved[key] - if value and key ~= value then - local replacement_tokens, reason = sh.internal.resolveActions(value, resolver, resolved) - if not replacement_tokens then - return replacement_tokens, reason - end - simple = simple and reason - words = tx.concat(replacement_tokens, words) - next = table.remove(words,1) - end - end - end - end - - table.insert(processed, next) - end - - return processed, simple -end - -function sh.internal.statements(input) - checkArg(1, input, "string") - - local words, reason = sh.internal.resolveActions(input) - if type(words) ~= "table" then - return words, reason - elseif #words == 0 then - return true - elseif reason and not input:find("[<>]") then - return {words}, reason - end - - -- we shall validate pipes before any statement execution - local statements = sh.internal.splitStatements(words) - for i=1,#statements do - local ok, why = sh.internal.hasValidPiping(statements[i]) - if not ok then return nil,why end - end - return statements -end - --- returns true if key is a string that represents a valid command line identifier -function sh.internal.isIdentifier(key) - if type(key) ~= "string" then - return false - end - - return key:match("^[%a_][%w_]*$") == key -end - -function sh.expand(value) - local expanded = value - :gsub("%$([_%w%?]+)", function(key) - if key == "?" then - return tostring(sh.getLastExitCode()) - end - return os.getenv(key) or '' - end) - :gsub("%${(.*)}", function(key) - if sh.internal.isIdentifier(key) then - return sh.internal.expandKey(key) - end - error("${" .. key .. "}: bad substitution") - end) - if expanded:find('`') then - expanded = sh.internal.parse_sub(expanded) - end - return expanded -end - -function sh.internal.expand(word) - if #word == 0 then return {} end - local result = '' - for i=1,#word do - local part = word[i] - -- sh.expand runs command substitution on backticks - -- if the entire quoted area is backtick quoted, then - -- we can save some checks by adding them back in - local q = part.qr and part.qr[1] == '`' and '`' or '' - result = result .. (not (part.qr and part.qr[3]) and sh.expand(q..part.txt..q) or part.txt) - end - return {result} -end - --- expand to files in path, or try key substitution --- word is a list of metadata-filled word parts --- note: text.internal.words(string) returns an array of these words -function sh.internal.evaluate(word) - checkArg(1, word, "table") - if #word == 0 then - return {} - elseif #word == 1 and word[1].qr then - return sh.internal.expand(word) - end - local function make_pattern(seg) - local result = seg - for _,glob_rule in ipairs(sh.internal.globbers) do - result = result:gsub("%%%"..glob_rule[1], glob_rule[2]) - local reduced = result - repeat - result = reduced - reduced = result:gsub(text.escapeMagic(glob_rule[2]):rep(2), glob_rule[2]) - until reduced == result - end - return result - end - local glob_pattern = '' - local has_globits = false - for i=1,#word do local part = word[i] - local next = part.txt - if not part.qr then - local escaped = text.escapeMagic(next) - next = make_pattern(escaped) - if next ~= escaped then - has_globits = true - end - end - glob_pattern = glob_pattern .. next - end - if not has_globits then - return sh.internal.expand(word) - end - local globs = sh.internal.glob(glob_pattern) - return #globs == 0 and sh.internal.expand(word) or globs -end - -function sh.hintHandler(full_line, cursor) - return sh.internal.hintHandlerImpl(full_line, cursor) -end - -function sh.internal.parseCommand(words) - checkArg(1, words, "table") - if #words == 0 then - return nil - end - local evaluated_words = {} - for i=1,#words do - for _, arg in ipairs(sh.internal.evaluate(words[i])) do - table.insert(evaluated_words, arg) - end - end - local program, reason = shell.resolve(evaluated_words[1], "lua") - if not program then - return nil, evaluated_words[1] .. ": " .. reason - end - evaluated_words = tx.sub(evaluated_words, 2) - return program, evaluated_words -end - -function sh.internal.createThreads(commands, eargs, env) - -- Piping data between programs works like so: - -- program1 gets its output replaced with our custom stream. - -- program2 gets its input replaced with our custom stream. - -- repeat for all programs - -- custom stream triggers execution of "next" program after write. - -- custom stream triggers yield before read if buffer is empty. - -- custom stream may have "redirect" entries for fallback/duplication. - local threads = {} - for i = 1, #commands do - local program, args = table.unpack(commands[i]) - local name, thread = tostring(program) - local thread_env = type(program) == "string" and env or nil - local thread, reason = process.load(program, thread_env, function() - os.setenv("_", name) - -- popen expects each process to first write an empty string - -- this is required for proper thread order - io.write('') - end, name) - - threads[i] = thread - - if thread then - -- smart check if ios should be loaded - if tx.first(args, function(token) return token == "<" or token:find(">") end) then - args, reason = sh.internal.buildCommandRedirects(thread, args) - end - end - - if not args or not thread then - for i,t in ipairs(threads) do - process.internal.close(t) - end - return nil, reason - end - - process.info(thread).data.args = tx.concat(args, eargs or {}) - end - - if #threads > 1 then - sh.internal.buildPipeChain(threads) - end - - return threads -end - -function sh.internal.runThreads(threads) - local result = {} - for i = 1, #threads do - -- Emulate CC behavior by making yields a filtered event.pull() - local thread, args = threads[i] - while coroutine.status(thread) ~= "dead" do - args = args or process.info(thread).data.args - result = table.pack(coroutine.resume(thread, table.unpack(args))) - if coroutine.status(thread) ~= "dead" then - args = sh.internal.handleThreadYield(result) - -- in case this was the end of the line, args is returned - result = args - if table.remove(args, 1) then - break - end - end - end - if not result[1] then - sh.internal.handleThreadCrash(thread, result) - break - end - end - return table.unpack(result) -end - -function sh.internal.executePipes(pipe_parts, eargs, env) - local commands = {} - for i=1,#pipe_parts do - commands[i] = table.pack(sh.internal.parseCommand(pipe_parts[i])) - if commands[i][1] == nil then - local err = commands[i][2] - if type(err) == "string" then - io.stderr:write(err,"\n") - end - return sh.internal.ec.parseCommand - end - end - local threads, reason = sh.internal.createThreads(commands, eargs, env) - if not threads then - io.stderr:write(reason,"\n") - return false - end - local result, cmd_result = sh.internal.runThreads(threads) - - if not result then - if cmd_result then - if type(cmd_result) == "string" then - cmd_result = cmd_result:gsub("^/lib/process%.lua:%d+: /", '/') - end - io.stderr:write(tostring(cmd_result),"\n") - end - return sh.internal.ec.sysError - end - return cmd_result -end - -function sh.execute(env, command, ...) - checkArg(2, command, "string") - if command:find("^%s*#") then return true, 0 end - local statements, reason = sh.internal.statements(command) - if not statements or statements == true then - return statements, reason - elseif #statements == 0 then - return true, 0 - end - - local eargs = {...} - - -- simple - if reason then - sh.internal.ec.last = sh.internal.command_result_as_code(sh.internal.executePipes(statements, eargs, env)) - return true - end - - return sh.internal.execute_complex(statements, eargs, env) -end - -function --[[@delayloaded-start@]] sh.internal.handleThreadYield(result) - local action = result[2] - if action == nil or type(action) == "number" then - return table.pack(pcall(event.pull, table.unpack(result, 2, result.n))) - else - return table.pack(coroutine.yield(table.unpack(result, 2, result.n))) - end -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.handleThreadCrash(thread, result) - if type(result[2]) == "table" and result[2].reason == "terminated" then - if result[2].code then - result[1] = true - result.n = 1 - else - result[2] = "terminated" - end - elseif type(result[2]) == "string" then - result[2] = debug.traceback(thread, result[2]) - end -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(thread, args) - local data = process.info(thread).data - local tokens, ios, handles = args, data.io, data.handles - args = {} - local from_io, to_io, mode - for i = 1, #tokens do - local token = tokens[i] - if token == "<" then - from_io = 0 - mode = "r" - else - local first_index, last_index, from_io_txt, mode_txt, to_io_txt = token:find("(%d*)(>>?)(.*)") - if mode_txt then - mode = mode_txt == ">>" and "a" or "w" - from_io = from_io_txt and tonumber(from_io_txt) or 1 - if to_io_txt ~= "" then - to_io = tonumber(to_io_txt:sub(2)) - ios[from_io] = ios[to_io] - mode = nil - end - else -- just an arg - if not mode then - table.insert(args, token) - else - local file, reason = io.open(shell.resolve(token), mode) - if not file then - return nil, "could not open '" .. token .. "': " .. reason - end - table.insert(handles, file) - ios[from_io] = file - end - mode = nil - end - end - end - - return args -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.buildPipeChain(threads) - local prev_pipe - for i=1,#threads do - local thread = threads[i] - local data = process.info(thread).data - local pio = data.io - - local pipe - if i < #threads then - pipe = require("buffer").new("rw", sh.internal.newMemoryStream()) - pipe:setvbuf("no") - pipe.stream.redirect[1] = rawget(pio, 1) - pio[1] = pipe - table.insert(data.handles, pipe) - end - - if prev_pipe then - prev_pipe.stream.redirect[0] = rawget(pio, 0) - prev_pipe.stream.next = thread - pio[0] = prev_pipe - end - - prev_pipe = pipe - end - -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.glob(glob_pattern) - local segments = text.split(glob_pattern, {"/"}, true) - local hiddens = tx.select(segments,function(e)return e:match("^%%%.")==nil end) - local function is_visible(s,i) - return not hiddens[i] or s:match("^%.") == nil - end - - local function magical(s) - for _,glob_rule in ipairs(sh.internal.globbers) do - if s:match("[^%%]-"..text.escapeMagic(glob_rule[2])) then - return true - end - end - end - - local is_abs = glob_pattern:sub(1, 1) == "/" - local root = is_abs and '' or shell.getWorkingDirectory() - local paths = {is_abs and "/" or ''} - local relative_separator = '' - - for i,segment in ipairs(segments) do - local enclosed_pattern = string.format("^(%s)/?$", segment) - local next_paths = {} - for _,path in ipairs(paths) do - if fs.isDirectory(root..path) then - if magical(segment) then - for file in fs.list(root..path) do - if file:match(enclosed_pattern) and is_visible(file, i) then - table.insert(next_paths, path..relative_separator..file:gsub("/+$",'')) - end - end - else -- not a globbing segment, just use it raw - local plain = text.removeEscapes(segment) - local fpath = root..path..relative_separator..plain - local hit = path..relative_separator..plain:gsub("/+$",'') - if fs.exists(fpath) then - table.insert(next_paths, hit) - end - end - end - end - paths = next_paths - if not next(paths) then break end - relative_separator = "/" - end - -- if no next_paths were hit here, the ENTIRE glob value is not a path - return paths -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.getMatchingPrograms(baseName) - local result = {} - local result_keys = {} -- cache for fast value lookup - -- TODO only matching files with .lua extension for now, might want to - -- extend this to other extensions at some point? env var? file attrs? - if not baseName or #baseName == 0 then - baseName = "^(.*)%.lua$" - else - baseName = "^(" .. text.escapeMagic(baseName) .. ".*)%.lua$" - end - for basePath in string.gmatch(os.getenv("PATH"), "[^:]+") do - for file in fs.list(shell.resolve(basePath)) do - local match = file:match(baseName) - if match and not result_keys[match] then - table.insert(result, match) - result_keys[match] = true - end - end - end - return result -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.getMatchingFiles(basePath, name) - local resolvedPath = shell.resolve(basePath) - local result, baseName = {} - - -- note: we strip the trailing / to make it easier to navigate through - -- directories using tab completion (since entering the / will then serve - -- as the intention to go into the currently hinted one). - -- if we have a directory but no trailing slash there may be alternatives - -- on the same level, so don't look inside that directory... (cont.) - if fs.isDirectory(resolvedPath) and name == "" then - baseName = "^(.-)/?$" - else - baseName = "^(" .. text.escapeMagic(name) .. ".-)/?$" - end - - for file in fs.list(resolvedPath) do - local match = file:match(baseName) - if match then - table.insert(result, basePath .. match) - end - end - -- (cont.) but if there's only one match and it's a directory, *then* we - -- do want to add the trailing slash here. - if #result == 1 and fs.isDirectory(shell.resolve(result[1])) then - result[1] = result[1] .. "/" - end - return result -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.hintHandlerSplit(line) - if line:sub(-1):find("%s") then - return '', line - end - local splits = text.internal.tokenize(line) - if not splits then -- parse error, e.g. unclosed quotes - return nil -- no split, no hints - end - local num_splits = #splits - if num_splits == 1 or not isWordOf(splits[num_splits-1],{";","&&","||","|"}) then - return '', line - end - local l = text.internal.normalize({splits[num_splits]})[1] - return line:sub(1,-unicode.len(l)-1), l -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.hintHandlerImpl(full_line, cursor) - -- line: text preceding the cursor: we want to hint this part (expand it) - local line = unicode.sub(full_line, 1, cursor - 1) - -- suffix: text following the cursor (if any, else empty string) to append to the hints - local suffix = unicode.sub(full_line, cursor) - -- if there is no text to hint, there are no hints - if not line or #line < 1 then - return {} - end - -- hintHandlerSplit helps make the hints work even after delimiters such as ; - -- it also catches parse errors such as unclosed quotes - local prev,line = sh.internal.hintHandlerSplit(line) - if not prev then -- failed to parse, e.g. unclosed quote, no hints - return {} - end - local result - -- prefix: text (if any) that will not be expanded (such as a command word preceding a file name that we are expanding) - -- partial: text that we want to expand - -- this first match determines if partial comes after redirect symbols such as > - local prefix, partial = line:match("^(.*[=><]%s*)(.*)$") - -- if redirection was not found, partial could just be arguments following a command - if not prefix then prefix, partial = line:match("^(.+%s+)(.*)$") end - -- partialPrefix: text of the partial that will not be expanded (i.e. a diretory path ending with /) - -- first, partialPrefix holds the whole text being expanded (we truncate later) - local partialPrefix = (partial or line) - -- name: text of the partial file name being expanded - local name = partialPrefix:gsub("^.*/", "") - -- here we remove the name text from the partialPrefix - partialPrefix = partialPrefix:sub(1, -unicode.len(name) - 1) - -- if no prefix was found and partialPrefix did not specify a closed directory path then we are expanding the first argument - -- i.e. the command word (a program name) - local searchInPath = not prefix and not partialPrefix:find("/") - if searchInPath then - result = sh.getMatchingPrograms(line) - else - result = sh.getMatchingFiles(partialPrefix, name) - end - -- in very special cases, the suffix should include a blank space to indicate to the user that the hint is discrete - local resultSuffix = suffix - if #result > 0 and unicode.sub(result[1], -1) ~= "/" and - not suffix:sub(1,1):find('%s') and - (#result == 1 or searchInPath or not prefix) then - resultSuffix = " " .. resultSuffix - end - -- prefix no longer needs to refer to just the expanding section of the text - -- here we reintroduce the previous section of the text that hintHandlerSplit cut for us - prefix = prev .. (prefix or "") - table.sort(result) - for i = 1, #result do - -- the hints define the whole line of text - result[i] = prefix .. result[i] .. resultSuffix - end - return result -end --[[@delayloaded-end@]] - --- verifies that no pipes are doubled up nor at the start nor end of words -function --[[@delayloaded-start@]] sh.internal.hasValidPiping(words, pipes) - checkArg(1, words, "table") - checkArg(2, pipes, "table", "nil") - - if #words == 0 then - return true - end - - local semi_split = tx.find(text.syntax, {";"}) -- all symbols before ; in syntax CAN be repeated - pipes = pipes or tx.sub(text.syntax, semi_split + 1) - - local pies = tx.select(words, function(parts, i) - return #parts == 1 and #text.split(parts[1].txt, pipes, true) == 0 and true or false - end) - - local bad_pipe - local last = 0 - for k,v in ipairs(pies) do - if v then - if k-last == 1 then - bad_pipe = words[k][1].txt - break - end - last=k - end - end - - if not bad_pipe and last == #pies then - bad_pipe = words[last][1].txt - end - - if bad_pipe then - return false, "parse error near " .. bad_pipe - else - return true - end -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.boolean_executor(chains, predicator) - local function not_gate(result) - return sh.internal.command_passed(result) and 1 or 0 - end - - local last = true - local boolean_stage = 1 - local negation_stage = 2 - local command_stage = 0 - local stage = negation_stage - local skip = false - - for ci=1,#chains do - local next = chains[ci] - local single = #next == 1 and #next[1] == 1 and not next[1][1].qr and next[1][1].txt - - if single == "||" then - if stage ~= command_stage or #chains == 0 then - return nil, "syntax error near unexpected token '"..single.."'" - end - if sh.internal.command_passed(last) then - skip = true - end - stage = boolean_stage - elseif single == "&&" then - if stage ~= command_stage or #chains == 0 then - return nil, "syntax error near unexpected token '"..single.."'" - end - if not sh.internal.command_passed(last) then - skip = true - end - stage = boolean_stage - elseif not skip then - local chomped = #next - local negate = sh.internal.remove_negation(next) - chomped = chomped ~= #next - if negate then - local prev = predicator - predicator = function(n,i) - local result = not_gate(prev(n,i)) - predicator = prev - return result - end - end - if chomped then - stage = negation_stage - end - if #next > 0 then - last = predicator(next,ci) - stage = command_stage - end - else - skip = false - stage = command_stage - end - end - - if stage == negation_stage then - last = not_gate(last) - end - - return last -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.splitStatements(words, semicolon) - checkArg(1, words, "table") - checkArg(2, semicolon, "string", "nil") - semicolon = semicolon or ";" - - return tx.partition(words, function(g, i, t) - if isWord(g,semicolon) then - return i, i - end - end, true) -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.splitChains(s,pc) - checkArg(1, s, "table") - checkArg(2, pc, "string", "nil") - pc = pc or "|" - return tx.partition(s, function(w) - -- each word has multiple parts due to quotes - if isWord(w,pc) then - return true - end - end, true) -- drop |s -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.groupChains(s) - checkArg(1,s,"table") - return tx.partition(s,function(w)return isWordOf(w,{"&&","||"})end) -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.remove_negation(chain) - if isWord(chain[1],"!") then - table.remove(chain, 1) - return true and not sh.internal.remove_negation(chain) - end - return false -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.newMemoryStream() - local memoryStream = {} - - function memoryStream:close() - self.closed = true - self.redirect = {} - end - - function memoryStream:seek() - return nil, "bad file descriptor" - end - - function memoryStream:read(n) - if self.closed then - return nil -- eof - end - if self.redirect[0] then - -- popen could be using this code path - -- if that is the case, it is important to leave stream.buffer alone - return self.redirect[0]:read(n) - elseif self.buffer == "" then - process.info(self.next).data.args = table.pack(coroutine.yield(table.unpack(self.result))) - end - local result = string.sub(self.buffer, 1, n) - self.buffer = string.sub(self.buffer, n + 1) - return result - end - - function memoryStream:write(value) - if not self.redirect[1] and self.closed then - -- if next is dead, ignore all writes - if coroutine.status(self.next) ~= "dead" then - error("attempt to use a closed stream") - end - elseif self.redirect[1] then - return self.redirect[1]:write(value) - elseif not self.closed then - self.buffer = self.buffer .. value - local args = process.info(self.next).data.args - self.result = table.pack(coroutine.resume(self.next, table.unpack(args))) - if coroutine.status(self.next) == "dead" then - self:close() - end - if not self.result[1] then - error(self.result[2], 0) - end - table.remove(self.result, 1) - return self - end - return nil, 'stream closed' - end - - local stream = {closed = false, buffer = "", - redirect = {}, result = {}} - local metatable = {__index = memoryStream, - __metatable = "memorystream"} - return setmetatable(stream, metatable) -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.execute_complex(statements, eargs, env) - for si=1,#statements do local s = statements[si] - local chains = sh.internal.groupChains(s) - local last_code = sh.internal.boolean_executor(chains, function(chain, chain_index) - local pipe_parts = sh.internal.splitChains(chain) - local next_args = chain_index == #chains and si == #statements and eargs or {} - return sh.internal.executePipes(pipe_parts, next_args, env) - end) - sh.internal.ec.last = sh.internal.command_result_as_code(last_code) - end - return true -end --[[@delayloaded-end@]] - - -function --[[@delayloaded-start@]] sh.internal.parse_sub(input) - -- cannot use gsub here becuase it is a [C] call, and io.popen needs to yield at times - local packed = {} - -- not using for i... because i can skip ahead - local i, len = 1, #input - - while i < len do - - local fi, si, capture = input:find("`([^`]*)`", i) - - if not fi then - table.insert(packed, input:sub(i)) - break - end - - local sub = io.popen(capture) - local result = sub:read("*a") - sub:close() - -- all whitespace is replaced by single spaces - -- we requote the result because tokenize will respect this as text - table.insert(packed, (text.trim(result):gsub("%s+"," "))) - - i = si+1 - end - - return table.concat(packed) -end --[[@delayloaded-end@]] - -return sh, local_env -F lib/shell.lua_local fs = require("filesystem") -local text = require("text") -local unicode = require("unicode") -local process = require("process") - -local shell = {} - --- Cache loaded shells for command execution. This puts the requirement on --- shells that they do not keep a global state, since they may be called --- multiple times, but reduces memory usage a lot. -local shells = setmetatable({}, {__mode="v"}) - -function shell.getShell() - local shellPath = os.getenv("SHELL") or "/bin/sh" - local shellName, reason = shell.resolve(shellPath, "lua") - if not shellName then - return nil, "cannot resolve shell `" .. shellPath .. "': " .. reason - end - if shells[shellName] then - return shells[shellName] - end - local sh, reason = loadfile(shellName, "t", env) - if sh then - shells[shellName] = sh - end - return sh, reason -end - -local function findFile(name, ext) - checkArg(1, name, "string") - local function findIn(dir) - if dir:sub(1, 1) ~= "/" then - dir = shell.resolve(dir) - end - dir = fs.concat(fs.concat(dir, name), "..") - local name = fs.name(name) - local list = fs.list(dir) - if list and name then - local files = {} - for file in list do - files[file] = true - end - if ext and unicode.sub(name, -(1 + unicode.len(ext))) == "." .. ext then - -- Name already contains extension, prioritize. - if files[name] then - return true, fs.concat(dir, name) - end - elseif files[name] then - -- Check exact name. - return true, fs.concat(dir, name) - elseif ext then - -- Check name with automatially added extension. - local name = name .. "." .. ext - if files[name] then - return true, fs.concat(dir, name) - end - end - end - return false - end - if unicode.sub(name, 1, 1) == "/" then - local found, where = findIn("/") - if found then return where end - elseif unicode.sub(name, 1, 2) == "./" then - local found, where = findIn(shell.getWorkingDirectory()) - if found then return where end - else - for path in string.gmatch(shell.getPath(), "[^:]+") do - local found, where = findIn(path) - if found then return where end - end - end - return false -end - -------------------------------------------------------------------------------- - -function shell.prime() - local data = process.info().data - for _,key in ipairs({'aliases','vars'}) do - -- first time get need to populate - local raw = rawget(data, key) - if not raw then - -- current process does not have the key - local current = data[key] - data[key] = {} - if current then - for k,v in pairs(current) do - data[key][k] = v - end - end - end - end -end - -function shell.getAlias(alias) - return process.info().data.aliases[alias] -end - -function shell.setAlias(alias, value) - checkArg(1, alias, "string") - checkArg(2, value, "string", "nil") - process.info().data.aliases[alias] = value -end - -function shell.aliases() - return pairs(process.info().data.aliases) -end - -function shell.resolveAlias(command, args) - checkArg(1, command, "string") - checkArg(2, args, "table", "nil") - args = args or {} - local program, lastProgram = command, nil - while true do - local tokens = text.tokenize(shell.getAlias(program) or program) - program = tokens[1] - if program == lastProgram then - break - end - lastProgram = program - for i = #tokens, 2, -1 do - table.insert(args, 1, tokens[i]) - end - end - return program, args -end - -function shell.getWorkingDirectory() - return os.getenv("PWD") -end - -function shell.setWorkingDirectory(dir) - checkArg(1, dir, "string") - dir = fs.canonical(dir) .. "/" - if dir == "//" then dir = "/" end - if fs.isDirectory(dir) then - os.setenv("PWD", dir) - return true - else - return nil, "not a directory" - end -end - -function shell.getPath() - return os.getenv("PATH") -end - -function shell.setPath(value) - os.setenv("PATH", value) -end - -function shell.resolve(path, ext) - if ext then - checkArg(2, ext, "string") - local where = findFile(path, ext) - if where then - return where - else - return nil, "file not found" - end - else - if unicode.sub(path, 1, 1) == "/" then - return fs.canonical(path) - else - return fs.concat(shell.getWorkingDirectory(), path) - end - end -end - -function shell.execute(command, env, ...) - local sh, reason = shell.getShell() - if not sh then - return false, reason - end - local result = table.pack(pcall(sh, env, command, ...)) - if not result[1] and type(result[2]) == "table" and result[2].reason == "terminated" then - if result[2].code then - return true - else - return false, "terminated" - end - end - return table.unpack(result, 1, result.n) -end - -function shell.parse(...) - local params = table.pack(...) - local args = {} - local options = {} - local doneWithOptions = false - for i = 1, params.n do - local param = params[i] - if not doneWithOptions and type(param) == "string" then - if param == "--" then - doneWithOptions = true -- stop processing options at `--` - elseif unicode.sub(param, 1, 2) == "--" then - if param:match("%-%-(.-)=") ~= nil then - options[param:match("%-%-(.-)=")] = param:match("=(.*)") - else - options[unicode.sub(param, 3)] = true - end - elseif unicode.sub(param, 1, 1) == "-" and param ~= "-" then - for j = 2, unicode.len(param) do - options[unicode.sub(param, j, j)] = true - end - else - table.insert(args, param) - end - else - table.insert(args, param) - end - end - return args, options -end - -------------------------------------------------------------------------------- - -return shell -F lib/sides.lualocal sides = { - [0] = "bottom", - [1] = "top", - [2] = "back", - [3] = "front", - [4] = "right", - [5] = "left", - [6] = "unknown", - - bottom = 0, - top = 1, - back = 2, - front = 3, - right = 4, - left = 5, - unknown = 6, - - down = 0, - up = 1, - north = 2, - south = 3, - west = 4, - east = 5, - - negy = 0, - posy = 1, - negz = 2, - posz = 3, - negx = 4, - posx = 5, - - forward = 3 -} - -local metatable = getmetatable(sides) or {} - --- sides[0..5] are mapped to itertable[1..6]. -local itertable = { - sides[0], - sides[1], - sides[2], - sides[3], - sides[4], - sides[5] -} - --- Future-proofing against the possible introduction of additional --- logical sides (e.g. [7] = "all", [8] = "none", etc.). -function metatable.__len(sides) - return #itertable -end - --- Allow `sides` to be iterated over like a normal (1-based) array. -function metatable.__ipairs(sides) - return ipairs(itertable) -end - -setmetatable(sides, metatable) - -------------------------------------------------------------------------------- - -return sides -Flib/syntax.lua -require("advancedLua") -local buffer = require("doubleBuffering") -local unicode = require("unicode") - -local syntax = {} - ----------------------------------------------------------------------------------------------------------------------------------------- - -syntax.indentationSeparator = "│" - -syntax.colorScheme = { - background = 0x1E1E1E, - text = 0xffffff, - strings = 0x99FF80, - loops = 0xffff98, - comments = 0x888888, - boolean = 0xFFDB40, - logic = 0xffcc66, - numbers = 0x66DBFF, - functions = 0xffcc66, - compares = 0xffff98, - lineNumbersBackground = 0x2D2D2D, - lineNumbersText = 0xCCCCCC, - scrollBarBackground = 0x444444, - scrollBarForeground = 0x33B6FF, - selection = 0x555555, - indentation = 0x3C3C3C, -} - -syntax.patterns = { - -- Комментарии - { "%-%-.+", "comments", 0, 0 }, - - -- Строки - { "\"[^\"]+\"", "strings", 0, 0 }, - - -- Циклы, условия и прочая поебень - { "while ", "loops", 0, 1 }, - { "do$", "loops", 0, 0 }, - { "do ", "loops", 0, 1 }, - { "end$", "loops", 0, 0 }, - { "end[%s%;]", "loops", 0, 1 }, - { "for ", "loops", 0, 1 }, - { " in ", "loops", 0, 1 }, - { "repeat ", "loops", 0, 1 }, - { "if ", "loops", 0, 1 }, - { "then", "loops", 0, 0 }, - { "until ", "loops", 0, 1 }, - { "return", "loops", 0, 0 }, - { "local ", "loops", 0, 1 }, - { "function ", "loops", 0, 1 }, - { "else$", "loops", 0, 0 }, - { "else[%s%;]", "loops", 0, 1 }, - { "elseif ", "loops", 0, 1 }, - { " break$", "loops", 0, 0 }, - { " break ", "loops", 0, 0 }, - - -- Истина, ложь, нулл - { "true", "boolean", 0, 0 }, - { "false", "boolean", 0, 0 }, - { "nil", "boolean", 0, 0 }, - - --Функции - { "[%s%=%{%(][^%s%(%)%{%}%[%]]+%(", "functions", 1, 1 }, - { "^[^%s%(%)%{%}%[%]]+%(", "functions", 0, 1 }, - - -- Логические выражения - { " and ", "logic", 0, 1 }, - { " or ", "logic", 0, 1 }, - { " not ", "logic", 0, 1 }, - - -- Конкатенация строк - { "[^%d]%.+[^%d]", "logic", 1, 1 }, - - -- Сравнения и мат. операции - { "[%>%<%=%~%+%-%*%/%^%#%%]", "compares", 0, 0 }, - - -- Числа - { "0x%w+", "numbers", 0, 0 }, - { "[^%a%d][%.%d]+$", "numbers", 1, 0 }, - { "[^%a%d][%.%d]+[^%a%d]", "numbers", 1, 1 }, -} - ----------------------------------------------------------------------------------------------------------------------------------------- - --- Отрисовка строки с подсвеченным синтаксисом -function syntax.highlightString(x, y, str, indentationWidth) - if y >= buffer.drawLimit.y and y <= buffer.drawLimit.y2 then - local stringLength, symbols, colors, searchFrom, starting, ending, bufferIndex = unicode.len(str), {}, {} - - for symbol = 1, stringLength do - symbols[symbol] = unicode.sub(str, symbol, symbol) - end - - for patternIndex = #syntax.patterns, 1, -1 do - searchFrom = 1 - while true do - starting, ending = string.unicodeFind(str, syntax.patterns[patternIndex][1], searchFrom) - if starting then - for symbol = starting + syntax.patterns[patternIndex][3], ending - syntax.patterns[patternIndex][4] do - colors[symbol] = syntax.colorScheme[syntax.patterns[patternIndex][2]] - end - else - break - end - searchFrom = ending + 1 - syntax.patterns[patternIndex][4] - end - end - - local notSpaceNotFound, indentationSymbolCounter = true, 1 - - for symbol = 1, stringLength do - if notSpaceNotFound then - if symbols[symbol] == " " then - colors[symbol] = syntax.colorScheme.indentation - if indentationSymbolCounter == 1 then - symbols[symbol] = syntax.indentationSeparator - indentationSymbolCounter = indentationWidth + 1 - end - else - notSpaceNotFound = false - end - indentationSymbolCounter = indentationSymbolCounter - 1 - end - - if x > buffer.drawLimit.x2 then - break - elseif x >= buffer.drawLimit.x then - bufferIndex = bufferIndex or buffer.getBufferIndexByCoordinates(x, y) - buffer.screen.new[bufferIndex + 1] = colors[symbol] or syntax.colorScheme.text - buffer.screen.new[bufferIndex + 2] = symbols[symbol] - bufferIndex = bufferIndex + 3 - end - - x = x + 1 - end - end -end - ----------------------------------------------------------------------------------------------------------------- - --- buffer.start() --- buffer.clear(0x1b1b1b) - --- buffer.square(5, 5, 30, 3, syntax.colorScheme.background, 0x0, " ") --- -- buffer.setDrawLimit(5, 5, 30, 3) --- -- syntax.highlightString(5, 6, "if not fs.exists(path) then error(\"File \\\"\"..path..\"\\\" doesnt't exsists.\\n\") end") --- syntax.highlightString(5, 6, "for i = 1, 10 do", 2) --- syntax.highlightString(5, 7, " local abc = print(123)", 2) --- syntax.highlightString(5, 8, " local abc = print(123)", 2) --- syntax.highlightString(5, 9, "end", 2) --- -- buffer.resetDrawLimit() - --- buffer.draw(true) - ----------------------------------------------------------------------------------------------------------------- - -return syntax - - - - -F lib/term.lua@local unicode = require("unicode") -local event = require("event") -local process = require("process") -local kb = require("keyboard") -local keys = kb.keys - -local term = {} -term.internal = {} - -function term.internal.window() - return process.info().data.window -end - -local W = term.internal.window - -local local_env = {unicode=unicode,event=event,process=process,W=W,kb=kb} - -function term.internal.open(...) - local dx, dy, w, h = ... - local window = {x=1,y=1,fullscreen=select("#",...)==0,dx=dx or 0,dy=dy or 0,w=w,h=h,blink=true} - return window -end - -function term.getViewport(window) - window = window or W() - return window.w, window.h, window.dx, window.dy, window.x, window.y -end - -function term.setViewport(w,h,dx,dy,x,y,window) - window = window or W() - - local gw,gh = window.gpu.getViewport() - w,h,dx,dy,x,y = w or gw,h or gh,dx or 0,dy or 0,x or 1,y or 1 - - window.w,window.h,window.dx,window.dy,window.x,window.y,window.gw,window.gh= - w,h,dx,dy,x,y, gw, gh -end - -function term.gpu(window) - window = window or W() - return window.gpu -end - -function term.clear() - local w = W() - local gpu = w.gpu - if not gpu then return end - gpu.fill(1+w.dx,1+w.dy,w.w,w.h," ") - w.x,w.y=1,1 -end - -function term.isAvailable(w) - w = w or W() - return w and not not (w.gpu and w.screen) -end - -function term.internal.pull(input, c, off, t, ...) - t=t or math.huge - if t < 0 then return end - local w,unpack=W(),table.unpack - local d,h,dx,dy,x,y=term.getViewport(w) - local out = (x<1 or x>d or y<1 or y>h) - if input and out then - input:move(0) - y=w.y - input:scroll() - end - x,y=w.x+dx,w.y+dy - local gpu - - if input or not out then - gpu=w.gpu - local sf,sb=gpu.setForeground,gpu.setBackground - c=c or {{gpu.getBackground()},{gpu.getForeground()},gpu.get(x,y)} - local c11,c12 = unpack(c[1]) - local c21,c22 = unpack(c[2]) - if not off then - sf(c11,c12) - sb(c21,c22) - end - gpu.set(x,y,c[3]) - sb(c11,c12) - sf(c21,c22) - end - - local a={pcall(event.pull,math.min(t,0.5),...)} - - if #a>1 or t<.5 then - if gpu then - gpu.set(x,y,c[3]) - end - return select(2,unpack(a)) - end - local blinking = w.blink - if input then blinking = input.blink end - return term.internal.pull(input,c,blinking and not off,t-0.5,...) -end - -function term.pull(p,...) - local a,t = {p,...} - if type(p) == "number" then t = table.remove(a,1) end - return term.internal.pull(nil,nil,nil,t,table.unpack(a)) -end - -function term.read(history,dobreak,hintHandler,pwchar,filter) - if not io.stdin.tty then return io.read() end - local ops = history or {} - ops.dobreak = ops.dobreak - if ops.dobreak==nil then ops.dobreak = dobreak end - ops.hintHandler = ops.hintHandler or hintHandler - ops.pwchar = ops.pwchar or pwchar - ops.filter = ops.filter or filter - return term.readKeyboard(ops) -end - -function term.internal.split(input) - local data,index=input.data,input.index - local dlen = unicode.len(data) - index=math.max(0,math.min(index,dlen)) - local tail=dlen-index - return unicode.sub(data,1,index),tail==0 and""or unicode.sub(data,-tail) -end - -function term.internal.build_vertical_reader(input) - input.sy = 0 - input.scroll = function(_) - _.sy = _.sy + term.internal.scroll(_.w) - _.w.y = math.min(_.w.y,_.w.h) - end - input.move = function(_,n) - local w=_.w - _.index = math.min(math.max(0,_.index+n),unicode.len(_.data)) - local s1,s2 = term.internal.split(_) - s2 = unicode.sub(s2.." ",1,1) - local data_remaining = ("_"):rep(_.promptx-1)..s1..s2 - w.y = _.prompty - _.sy - while true do - local wlen_remaining = unicode.wlen(data_remaining) - if wlen_remaining > w.w then - local line_cut = unicode.wtrunc(data_remaining, w.w+1) - data_remaining = unicode.sub(data_remaining,unicode.len(line_cut)+1) - w.y=w.y+1 - else - w.x = wlen_remaining-unicode.wlen(s2)+1 - break - end - end - end - input.clear_tail = function(_) - local win=_.w - local oi,w,h,dx,dy,ox,oy = _.index,term.getViewport(win) - _:move(math.huge) - _:move(-1) - local ex,ey=win.x,win.y - win.x,win.y,_.index=ox,oy,oi - x=oy==ey and ox or 1 - win.gpu.fill(x+dx,ey+dy,w-x+1,1," ") - end - input.update = function(_,arg) - local w,cursor,suffix=_.w - local s1,s2=term.internal.split(_) - if type(arg) == "number" then - local ndata - if arg < 0 then if _.index<=0 then return end - _:move(-1) - ndata=unicode.sub(s1,1,-2)..s2 - else if _.index>=unicode.len(_.data) then return end - s2=unicode.sub(s2,2) - ndata=s1..s2 - end - suffix=s2 - input:clear_tail() - _.data = ndata - else - _.data=s1..arg..s2 - _.index=_.index+unicode.len(arg) - cursor,suffix=arg,s2 - end - if cursor then _:draw(_.mask(cursor)) end - if suffix and suffix~="" then - local px,py,ps=w.x,w.y,_.sy - _:draw(_.mask(suffix)) - w.x,w.y=px,py-(_.sy-ps) - end - end - input.clear = function(_) - _:move(-math.huge) - _:draw((" "):rep(unicode.wlen(_.data))) - _:move(-math.huge) - _.index=0 - _.data="" - end - input.draw = function(_,text) - _.sy = _.sy + term.drawText(text,true) - end -end - -function term.internal.read_history(history,input,change) - if not change then - if unicode.wlen(input.data) > 0 then - table.insert(history.list,1,input.data) - history.list[(tonumber(os.getenv("HISTSIZE")) or 10)+1]=nil - history.list[0]=nil - end - else - local ni = history.index + change - if ni >= 0 and ni <= #history.list then - history.list[history.index]=input.data - history.index = ni - input:clear() - input:update(history.list[ni]) - end - end -end - -function term.readKeyboard(ops) - checkArg(1,ops,"table") - local filter = ops.filter and function(i) return term.internal.filter(ops.filter,i) end or term.internal.nop - local pwchar = ops.pwchar and function(i) return term.internal.mask(ops.pwchar,i) end or term.internal.nop - local history,db,hints={list=ops,index=0},ops.dobreak,{handler=ops.hintHandler} - local w=W() - local draw=io.stdin.tty and term.drawText or term.internal.nop - local input={w=w,promptx=w.x,prompty=w.y,index=0,data="",mask=pwchar} - input.blink = ops.blink - if input.blink == nil then input.blink = w.blink end - if ops.nowrap then - term.internal.build_horizontal_reader(input) - else - term.internal.build_vertical_reader(input) - end - while true do - local name, address, char, code = term.internal.pull(input) - local c = nil - local backup_cache = hints.cache - if name =="interrupted" then draw("^C\n",true) return "" - elseif name=="touch" or name=="drag" then term.internal.onTouch(input,char,code) - elseif name=="clipboard" then c=char hints.cache = nil - elseif name=="key_down" then - hints.cache = nil - local ctrl = kb.isControlDown(address) - if ctrl and code == keys.d then return - elseif char==9 then hints.cache = backup_cache term.internal.tab(input,hints) - elseif char==13 and filter(input) then - input:move(math.huge) - if db ~= false then draw("\n") end - term.internal.read_history(history,input) - return input.data.."\n" - elseif code==keys.back then - input:update(-1) - elseif code==keys.left then - input:move(ctrl and term.internal.ctrl_movement(input, -1) or -1) - elseif code==keys.right then - input:move(ctrl and term.internal.ctrl_movement(input, 1) or 1) - elseif code==keys.up then - term.internal.read_history(history,input,1) - elseif code==keys.down then - term.internal.read_history(history,input,-1) - elseif code==keys.home then - input:move(-math.huge) - elseif code==keys["end"] then - input:move(math.huge) - elseif code==keys.delete then - input:update(0) - elseif char>=32 then - c=unicode.char(char) - else - hints.cache = backup_cache - end - end - if c then input:update(c) end - end -end - --- cannot use term.write = io.write because io.write invokes metatable -function term.write(value,wrap) - local stdout = io.output() - local stream = stdout and stdout.stream - local previous_wrap = stream.wrap - stream.wrap = wrap == nil and true or wrap - stdout:write(value) - stdout:flush() - stream.wrap = previous_wrap -end - -function term.getCursor() - local w = W() - return w.x,w.y -end - -function term.setCursor(x,y) - local w = W() - w.x,w.y=x,y -end - -function term.drawText(value, wrap, window) - window = window or W() - if not window then return end - local gpu = window.gpu - if not gpu then return end - local w,h,dx,dy,x,y = term.getViewport(window) - local sy = 0 - local vlen = #value - local index = 1 - local cr_last,beeped = false,false - local function scroll(_sy,_y) - return _sy + term.internal.scroll(window,_y-h), math.min(_y,h) - end - while index <= vlen do - local si,ei = value:find("[\t\r\n\a]", index) - si = si or vlen+1 - if index==si then - local delim = value:sub(index, index) - if delim=="\t" then - x=((x-1)-((x-1)%8))+9 - elseif delim=="\r" or (delim=="\n" and not cr_last) then - x,y=1,y+1 - sy,y = scroll(sy,y) - elseif delim=="\a" and not beeped then - require("computer").beep() - beeped = true - end - cr_last = delim == "\r" - else - sy,y = scroll(sy,y) - si = si - 1 - local next = value:sub(index, si) - local wlen_needed = unicode.wlen(next) - local slen = #next - local wlen_remaining = w - x + 1 - local clean_end = "" - if wlen_remaining < wlen_needed then - next = unicode.wtrunc(next, wlen_remaining + 1) - wlen_needed = unicode.wlen(next) - clean_end = (" "):rep(wlen_remaining-wlen_needed) - if not wrap then - si = math.huge - end - end - gpu.set(x+dx,y+dy,next..clean_end) - x = x + wlen_needed - if wrap and slen ~= #next then - si = si - (slen - #next) - x = 1 - y = y + 1 - end - end - index = si + 1 - end - - window.x,window.y = x,y - return sy -end - -function term.internal.scroll(w,n) - w = w or W() - local gpu,d,h,dx,dy,x,y = w.gpu,term.getViewport(w) - n = n or (y-h) - if n <= 0 then return 0 end - gpu.copy(dx+1,dy+n+1,d,h-n,0,-n) - gpu.fill(dx+1,dy+h-n+1,d,n," ") - return n -end - -function term.internal.nop(...) - return ... -end - -function term.setCursorBlink(enabled) - W().blink=enabled -end - -function term.getCursorBlink() - return W().blink -end - -function term.bind(gpu, screen, kb, window) - window = window or W() - window.gpu = gpu or window.gpu - window.screen = screen or window.screen - window.keyboard = kb or window.keyboard - if window.fullscreen then - term.setViewport(nil,nil,nil,nil,window.x,window.y,window) - end -end - -function --[[@delayloaded-start@]] term.internal.ctrl_movement(input, dir) - local index, data = input.index, input.data - - local function isEdge(char) - return char == "" or not not char:find("%s") - end - - local last=dir<0 and 0 or unicode.len(data) - local start=index+dir+1 - for i=start,last,dir do - local a,b = unicode.sub(data, i-1, i-1), unicode.sub(data, i, i) - if isEdge(a) and not isEdge(b) then return i-(index+1) end - end - return last - index -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] term.internal.onTouch(input,gx,gy) - if input.data == "" then return end - input:move(-math.huge) - local w = W() - gx,gy=gx-w.dx,gy-w.dy - local x2,y2,d = input.w.x,input.w.y,input.w.w - local char_width_to_move = ((gy*d+gx)-(y2*d+x2)) - if char_width_to_move <= 0 then return end - local total_wlen = unicode.wlen(input.data) - if char_width_to_move >= total_wlen then - input:move(math.huge) - else - local chars_to_move = unicode.wtrunc(input.data, char_width_to_move + 1) - input:move(unicode.len(chars_to_move)) - end - -- fake white space can make the index off, redo adjustment for alignment - x2,y2,d = input.w.x,input.w.y,input.w.w - char_width_to_move = ((gy*d+gx)-(y2*d+x2)) - if (char_width_to_move < 0) then - -- using char_width_to_move as a type of index is wrong, but large enough and helps to speed this up - local up_to_cursor = unicode.sub(input.data, input.index+char_width_to_move, input.index) - local full_wlen = unicode.wlen(up_to_cursor) - local without_tail = unicode.wtrunc(up_to_cursor, full_wlen + char_width_to_move + 1) - local chars_cut = unicode.len(up_to_cursor) - unicode.len(without_tail) - input:move(-chars_cut) - end -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] term.internal.build_horizontal_reader(input) - term.internal.build_vertical_reader(input) - input.clear_tail = function(_) - local w,h,dx,dy,x,y = term.getViewport(_.w) - local s1,s2=term.internal.split(_) - local wlen = math.min(unicode.wlen(s2),w-x+1) - _.w.gpu.fill(x+dx,y+dy,wlen,1," ") - end - input.move = function(_,n) - local win = _.w - local a = _.index - local b = math.max(0,math.min(unicode.len(_.data),_.index+n)) - _.index = b - a,b = a w then - local blank - if i == unicode.len(data) then - available,blank=available-1," " - else - i,blank=i+1,"" - end - data = unicode.sub(data,1,i) - local rev = unicode.reverse(data) - local ending = unicode.wtrunc(rev, available+1) - data = unicode.reverse(ending) - gpu.set(sx,sy,data..blank) - win.x=math.min(w,_.promptx+unicode.wlen(data)) - elseif x < _.promptx then - data = unicode.sub(data,_.index+1) - if unicode.wlen(data) > available then - data = unicode.wtrunc(data,available+1) - end - gpu.set(sx,sy,data) - end - end - input.clear = function(_) - local win = _.w - local gpu,data,px=win.gpu,_.data,_.promptx - local w,h,dx,dy,x,y = term.getViewport(win) - _.index,_.data,win.x=0,"",px - gpu.fill(px+dx,y+dy,w-px+1-dx,1," ") - end -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] term.clearLine(window) - window = window or W() - local w,h,dx,dy,x,y = term.getViewport(window) - window.gpu.fill(dx+1,dy+math.max(1,math.min(y,h)),w,1," ") - window.x=1 -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] term.internal.mask(mask,input) - if not mask then return input end - if type(mask) == "function" then return mask(input) end - return mask:rep(unicode.wlen(input)) -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] term.internal.filter(filter,input) - if not filter then return true - elseif type(filter) == "string" then return input.data:match(filter) - elseif filter(input.data) then return true - else require("computer").beep(2000, 0.1) end -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] term.internal.tab(input,hints) - if not hints.handler then return end - if not hints.cache then - hints.cache = type(hints.handler)=="table" and hints.handler - or hints.handler(input.data,input.index + 1) or {} - hints.cache.i=-1 - end - local c=hints.cache - local change = kb.isShiftDown(term.keyboard().address) and -1 or 1 - c.i=(c.i+change)%math.max(#c,1) - local next=c[c.i+1] - if next then - local tail = unicode.len(input.data) - input.index - input:clear() - input:update(next) - input:move(-tail) - end -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] term.getGlobalArea(window) - local w,h,dx,dy = term.getViewport(window) - return dx+1,dy+1,w,h -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] term.screen(window) - return (window or W()).screen -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] term.keyboard(window) - window = window or W() - local kba = window.keyboard and window.keyboard.address - if kba and kb.pressedCodes[kba] then return window.keyboard end - window.keyboard=nil - local component = require("component") - if not window.screen or not component.proxy(window.screen.address) then window.screen = nil return end - local kba = window.screen.getKeyboards()[1] - if not kba then return end - window.keyboard = component.proxy(kba) - return window.keyboard -end --[[@delayloaded-end@]] - -return term, local_env -F lib/text.lua!local unicode = require("unicode") -local tx = require("transforms") - --- --[[@@]] are not just comments, but custom annotations for delayload methods. --- See package.lua and the api wiki for more information - -local text = {} -local local_env = {tx=tx,unicode=unicode} - -text.internal = {} - -text.syntax = {"^%d*>>?&%d+$",";","&&","||?","^%d*>>?",">>?","<"} - -function --[[@delayloaded-start@]] text.detab(value, tabWidth) - checkArg(1, value, "string") - checkArg(2, tabWidth, "number", "nil") - tabWidth = tabWidth or 8 - local function rep(match) - local spaces = tabWidth - match:len() % tabWidth - return match .. string.rep(" ", spaces) - end - local result = value:gsub("([^\n]-)\t", rep) -- truncate results - return result -end --[[@delayloaded-end@]] - --- used in motd -function text.padRight(value, length) - checkArg(1, value, "string", "nil") - checkArg(2, length, "number") - if not value or unicode.wlen(value) == 0 then - return string.rep(" ", length) - else - return value .. string.rep(" ", length - unicode.wlen(value)) - end -end - -function --[[@delayloaded-start@]] text.padLeft(value, length) - checkArg(1, value, "string", "nil") - checkArg(2, length, "number") - if not value or unicode.wlen(value) == 0 then - return string.rep(" ", length) - else - return string.rep(" ", length - unicode.wlen(value)) .. value - end -end --[[@delayloaded-end@]] - -function text.trim(value) -- from http://lua-users.org/wiki/StringTrim - local from = string.match(value, "^%s*()") - return from > #value and "" or string.match(value, ".*%S", from) -end - -function text.wrap(value, width, maxWidth) - checkArg(1, value, "string") - checkArg(2, width, "number") - checkArg(3, maxWidth, "number") - local line, nl = value:match("([^\r\n]*)(\r?\n?)") -- read until newline - if unicode.wlen(line) > width then -- do we even need to wrap? - local partial = unicode.wtrunc(line, width) - local wrapped = partial:match("(.*[^a-zA-Z0-9._()'`=])") - if wrapped or unicode.wlen(line) > maxWidth then - partial = wrapped or partial - return partial, unicode.sub(value, unicode.len(partial) + 1), true - else - return "", value, true -- write in new line. - end - end - local start = unicode.len(line) + unicode.len(nl) + 1 - return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0 -end - -function text.wrappedLines(value, width, maxWidth) - local line, nl - return function() - if value then - line, value, nl = text.wrap(value, width, maxWidth) - return line - end - end -end - -------------------------------------------------------------------------------- - --- tokenize allows nil for delimiters, quotes, and doNotNormalize --- always separates by whitespace --- default quote rules: '' and "" --- default delimiters: all --- default is to normalize, that is, no metadata is returned, just a list of tokens -function text.tokenize(value, doNotNormalize, quotes, delimiters) - checkArg(1, value, "string") - checkArg(2, doNotNormalize, "boolean", "nil") - checkArg(3, quotes, "table", "nil") - checkArg(4, delimiters, "table", "nil") - - local tokens, reason = text.internal.tokenize(value, quotes, delimiters) - - if type(tokens) ~= "table" then - return nil, reason - end - - if doNotNormalize then - return tokens - end - - return text.internal.normalize(tokens) -end - -function text.escapeMagic(txt) - return txt:gsub('[%(%)%.%%%+%-%*%?%[%^%$]', '%%%1') -end - -function text.removeEscapes(txt) - return txt:gsub("%%([%(%)%.%%%+%-%*%?%[%^%$])","%1") -end - -------------------------------------------------------------------------------- -function --[[@delayloaded-start@]] text.split(input, delimiters, dropDelims, di) - checkArg(1, input, "string") - checkArg(2, delimiters, "table") - checkArg(3, dropDelims, "boolean", "nil") - checkArg(4, di, "number", "nil") - - if #input == 0 then return {} end - di = di or 1 - local result = {input} - if di > #delimiters then return result end - - local function add(part, index, r, s, e) - local sub = part:sub(s,e) - if #sub == 0 then return index end - local subs = r and text.split(sub,delimiters,dropDelims,r) or {sub} - for i=1,#subs do - table.insert(result, index+i-1, subs[i]) - end - return index+#subs - end - - local i,d=1,delimiters[di] - while true do - local next = table.remove(result,i) - if not next then break end - local si,ei = next:find(d) - if si and ei and ei~=0 then -- delim found - i=add(next, i, di+1, 1, si-1) - i=dropDelims and i or add(next, i, false, si, ei) - i=add(next, i, di, ei+1) - else - i=add(next, i, di+1, 1, #next) - end - end - - return result -end --[[@delayloaded-end@]] - ------------------------------------------------------------------------------ - -function text.internal.tokenize(value, quotes, delimiters) - checkArg(1, value, "string") - checkArg(2, quotes, "table", "nil") - checkArg(3, delimiters, "table", "nil") - local custom = not not delimiters - delimiters = delimiters or text.syntax - - local words, reason = text.internal.words(value, quotes) - - local splitter = text.escapeMagic(custom and table.concat(delimiters) or "<>|;&") - if type(words) ~= "table" or - #splitter == 0 or - not value:find("["..splitter.."]") then - return words, reason - end - - return text.internal.splitWords(words, delimiters) -end - --- tokenize input by quotes and whitespace -function text.internal.words(input, quotes) - checkArg(1, input, "string") - checkArg(2, quotes, "table", "nil") - local qr = nil - quotes = quotes or {{"'","'",true},{'"','"'},{'`','`'}} - local function append(dst, txt, qr) - local size = #dst - if size == 0 or dst[size].qr ~= qr then - dst[size+1] = {txt=txt, qr=qr} - else - dst[size].txt = dst[size].txt..txt - end - end - -- token meta is {string,quote rule} - local tokens, token = {}, {} - local escaped, start = false, -1 - for i = 1, unicode.len(input) do - local char = unicode.sub(input, i, i) - if escaped then -- escaped character - escaped = false - -- include escape char if - -- 1. qr active - -- 2. the char escaped is NOT the qr closure - -- 3. qr is not literal - if qr and not qr[3] and qr[2] ~= char then - append(token, '\\', qr) - end - append(token, char, qr) - elseif char == "\\" and (not qr or not qr[3]) then - escaped = true - elseif qr and qr[2] == char then -- end of quoted string - -- if string is empty, we can still capture a quoted empty arg - if #token == 0 or #token[#token] == 0 then - append(token, '', qr) - end - qr = nil - elseif not qr and tx.first(quotes,function(Q) - qr=Q[1]==char and Q or nil return qr end) then - start = i - elseif not qr and string.find(char, "%s") then - if #token > 0 then - table.insert(tokens, token) - end - token = {} - else -- normal char - append(token, char, qr) - end - end - if qr then - return nil, "unclosed quote at index " .. start - end - - if #token > 0 then - table.insert(tokens, token) - end - - return tokens -end - --- splits each word into words at delimiters --- delimiters are kept as their own words --- quoted word parts are not split -function --[[@delayloaded-start@]] text.internal.splitWords(words, delimiters) - checkArg(1,words,"table") - checkArg(2,delimiters,"table") - - local split_words = {} - local next_word - local function add_part(part) - if next_word then - split_words[#split_words+1] = {} - end - table.insert(split_words[#split_words], part) - next_word = false - end - for wi=1,#words do local word = words[wi] - next_word = true - for pi=1,#word do local part = word[pi] - local qr = part.qr - if qr then - add_part(part) - else - local part_text_splits = text.split(part.txt, delimiters) - tx.foreach(part_text_splits, function(sub_txt, spi) - local delim = #text.split(sub_txt, delimiters, true) == 0 - next_word = next_word or delim - add_part({txt=sub_txt,qr=qr}) - next_word = delim - end) - end - end - end - - return split_words -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] text.internal.normalize(words, omitQuotes) - checkArg(1, words, "table") - checkArg(2, omitQuotes, "boolean", "nil") - local norms = {} - for _,word in ipairs(words) do - local norm = {} - for _,part in ipairs(word) do - norm = tx.concat(norm, not omitQuotes and part.qr and {part.qr[1], part.txt, part.qr[2]} or {part.txt}) - end - norms[#norms+1]=table.concat(norm) - end - return norms -end --[[@delayloaded-end@]] - -return text, local_env -D -lib/tools/Flib/tools/delayLookup.lua local data,tbl,key = ... -local z = data[tbl] - -if key then -- index - local method = z.methods[key] - local cache = z.cache[key] - if method and not cache then - local file = io.open(z.path,"r") - if file then - file:seek("set", method[1]) - local loaded = load("return function"..file:read(method[2]), "=delayed-"..key,"t",z.env) - file:close() - assert(loaded,"failed to load "..key) - cache = loaded() - --lazy_protect(key, cache) - z.cache[key] = cache - end - end - return cache -else -- pairs - local set,k,v = {} - while true do - k,v = next(tbl,k) - if not k then break end - set[k] = v - end - for k in pairs(z.methods) do - if not set[k] then - set[k] = function(...)return tbl[k](...)end - end - end - return pairs(set) -end -Flib/tools/delayParse.lualocal filepath,delay_data = ... -local file, reason = io.open(filepath, "r") -if not file then - return reason -end - -local methods = {} -local delay_start_pattern = "^%s*function%s*%-%-%[%[@delayloaded%-start@%]%]%s*(.*)$" -local delay_end_pattern = "^%s*end%s*%-%-%[%[@delayloaded%-end@%]%]%s*$" -local n,buffer,lib_name,current_method,open = 0,{} - -while true do - local line = file:read("*L") - if current_method then - local closed = not line or line:match(delay_end_pattern) - if closed then - local path,method_name,args = open:match("^(.-)([^%.]+)(%(.*)$") - current_method = current_method-#args - methods[path] = methods[path] or {} - methods[path][method_name] = {current_method,n+#line-current_method} - current_method=nil - end - elseif line then - open = line:match(delay_start_pattern) - if open then - lib_name,open = open:match("^([^%.]+)%.(.*)$") - current_method = n+#line - else - buffer[#buffer+1] = line - end - else - file:close() - break - end - n = n + #line -end - -if not next(methods) or current_method or not lib_name then - return "no methods found or unclosed marker for delayed load" -end - -buffer = table.concat(buffer) -local loader, reason = load(buffer, "="..filepath, "t", _G) -local library, local_env = loader() -if library then - local_env = local_env or {} - local_env[lib_name] = library - - local env = setmetatable(local_env, {__index=_G}) - - for path,pack in pairs(methods) do - local target = library - for name in path:gmatch("[^%.]+") do target = target[name] end - delay_data[target] = - { - methods = pack, - cache = {}, - env = env, - path = filepath - } - setmetatable(target, delay_data) - end - - return function()return library end, filepath -end - -return reason -Flib/tools/keyboard_full.luakeyboard.keys["1"] = 0x02 -keyboard.keys["2"] = 0x03 -keyboard.keys["3"] = 0x04 -keyboard.keys["4"] = 0x05 -keyboard.keys["5"] = 0x06 -keyboard.keys["6"] = 0x07 -keyboard.keys["7"] = 0x08 -keyboard.keys["8"] = 0x09 -keyboard.keys["9"] = 0x0A -keyboard.keys["0"] = 0x0B -keyboard.keys.a = 0x1E -keyboard.keys.b = 0x30 -keyboard.keys.c = 0x2E -keyboard.keys.d = 0x20 -keyboard.keys.e = 0x12 -keyboard.keys.f = 0x21 -keyboard.keys.g = 0x22 -keyboard.keys.h = 0x23 -keyboard.keys.i = 0x17 -keyboard.keys.j = 0x24 -keyboard.keys.k = 0x25 -keyboard.keys.l = 0x26 -keyboard.keys.m = 0x32 -keyboard.keys.n = 0x31 -keyboard.keys.o = 0x18 -keyboard.keys.p = 0x19 -keyboard.keys.q = 0x10 -keyboard.keys.r = 0x13 -keyboard.keys.s = 0x1F -keyboard.keys.t = 0x14 -keyboard.keys.u = 0x16 -keyboard.keys.v = 0x2F -keyboard.keys.w = 0x11 -keyboard.keys.x = 0x2D -keyboard.keys.y = 0x15 -keyboard.keys.z = 0x2C - -keyboard.keys.apostrophe = 0x28 -keyboard.keys.at = 0x91 -keyboard.keys.back = 0x0E -- backspace -keyboard.keys.backslash = 0x2B -keyboard.keys.capital = 0x3A -- capslock -keyboard.keys.colon = 0x92 -keyboard.keys.comma = 0x33 -keyboard.keys.enter = 0x1C -keyboard.keys.equals = 0x0D -keyboard.keys.grave = 0x29 -- accent grave -keyboard.keys.lbracket = 0x1A -keyboard.keys.lcontrol = 0x1D -keyboard.keys.lmenu = 0x38 -- left Alt -keyboard.keys.lshift = 0x2A -keyboard.keys.minus = 0x0C -keyboard.keys.numlock = 0x45 -keyboard.keys.pause = 0xC5 -keyboard.keys.period = 0x34 -keyboard.keys.rbracket = 0x1B -keyboard.keys.rcontrol = 0x9D -keyboard.keys.rmenu = 0xB8 -- right Alt -keyboard.keys.rshift = 0x36 -keyboard.keys.scroll = 0x46 -- Scroll Lock -keyboard.keys.semicolon = 0x27 -keyboard.keys.slash = 0x35 -- / on main keyboard -keyboard.keys.space = 0x39 -keyboard.keys.stop = 0x95 -keyboard.keys.tab = 0x0F -keyboard.keys.underline = 0x93 - --- Keypad (and numpad with numlock off) -keyboard.keys.up = 0xC8 -keyboard.keys.down = 0xD0 -keyboard.keys.left = 0xCB -keyboard.keys.right = 0xCD -keyboard.keys.home = 0xC7 -keyboard.keys["end"] = 0xCF -keyboard.keys.pageUp = 0xC9 -keyboard.keys.pageDown = 0xD1 -keyboard.keys.insert = 0xD2 -keyboard.keys.delete = 0xD3 - --- Function keys -keyboard.keys.f1 = 0x3B -keyboard.keys.f2 = 0x3C -keyboard.keys.f3 = 0x3D -keyboard.keys.f4 = 0x3E -keyboard.keys.f5 = 0x3F -keyboard.keys.f6 = 0x40 -keyboard.keys.f7 = 0x41 -keyboard.keys.f8 = 0x42 -keyboard.keys.f9 = 0x43 -keyboard.keys.f10 = 0x44 -keyboard.keys.f11 = 0x57 -keyboard.keys.f12 = 0x58 -keyboard.keys.f13 = 0x64 -keyboard.keys.f14 = 0x65 -keyboard.keys.f15 = 0x66 -keyboard.keys.f16 = 0x67 -keyboard.keys.f17 = 0x68 -keyboard.keys.f18 = 0x69 -keyboard.keys.f19 = 0x71 - --- Japanese keyboards -keyboard.keys.kana = 0x70 -keyboard.keys.kanji = 0x94 -keyboard.keys.convert = 0x79 -keyboard.keys.noconvert = 0x7B -keyboard.keys.yen = 0x7D -keyboard.keys.circumflex = 0x90 -keyboard.keys.ax = 0x96 - --- Numpad -keyboard.keys.numpad0 = 0x52 -keyboard.keys.numpad1 = 0x4F -keyboard.keys.numpad2 = 0x50 -keyboard.keys.numpad3 = 0x51 -keyboard.keys.numpad4 = 0x4B -keyboard.keys.numpad5 = 0x4C -keyboard.keys.numpad6 = 0x4D -keyboard.keys.numpad7 = 0x47 -keyboard.keys.numpad8 = 0x48 -keyboard.keys.numpad9 = 0x49 -keyboard.keys.numpadmul = 0x37 -keyboard.keys.numpaddiv = 0xB5 -keyboard.keys.numpadsub = 0x4A -keyboard.keys.numpadadd = 0x4E -keyboard.keys.numpaddecimal = 0x53 -keyboard.keys.numpadcomma = 0xB3 -keyboard.keys.numpadenter = 0x9C -keyboard.keys.numpadequals = 0x8D - --- Create inverse mapping for name lookup. -setmetatable(keyboard.keys, -{ - __index = function(tbl, k) - if type(k) ~= "number" then return end - for name,value in pairs(keyboard.keys) do - if value == k then - return name - end - end - end -}) -Flib/transforms.lua]local lib={} -lib.internal={} -function lib.internal.range_adjust(f,l,s) - checkArg(1,f,'number','nil') - checkArg(2,l,'number','nil') - checkArg(3,s,'number') - if f==nil then f=1 elseif f<0 then f=s+f+1 end - if l==nil then l=s elseif l<0 then l=s+l+1 end - return f,l -end -function lib.internal.table_view(tbl,f,l) - return setmetatable({}, - { - __index=function(b,k)return(type(k)~='number'or(k>=f and k<=l))and tbl[k]or nil end, - __len=function(b)return#tbl end, - }) -end -local adjust=lib.internal.range_adjust -local view=lib.internal.table_view - --- works like string.sub but on elements of an indexed table -function lib.sub(tbl,f,l) - checkArg(1,tbl,'table') - local r,s={},#tbl - f,l=adjust(f,l,s) - l=math.min(l,s) - for i=math.max(f,1),l do - r[#r+1]=tbl[i] - end - return r -end - --- first(p1,p2) searches for the first range in p1 that satisfies p2 -function lib.first(tbl,pred,f,l) - checkArg(1,tbl,'table') - checkArg(2,pred,'function','table') - if type(pred)=='table'then - local set;set,pred=pred,function(e,fi,tbl) - for vi=1,#set do - local v=set[vi] - if lib.begins(tbl,v,fi) then return true,#v end - end - end - end - local s=#tbl - f,l=adjust(f,l,s) - tbl=view(tbl,f,l) - for i=f,l do - local si,ei=pred(tbl[i],i,tbl) - if si then - return i,i+(ei or 1)-1 - end - end -end - --- if value was made by lib.sub then find can find from whence -function --[[@delayloaded-start@]] lib.find(tbl,v,f,l) - checkArg(1,tbl,'table') - checkArg(2,v,'table') - local s=#v - return lib.first(tbl,function(e,i,tbl) - for n=0,s-1 do - if tbl[n+i]~=v[n+1] then return nil end - end - return 1,s - end,f,l) -end --[[@delayloaded-end@]] - --- Returns a list of subsets of tbl where partitioner acts as a delimiter. -function --[[@delayloaded-start@]] lib.partition(tbl,partitioner,dropEnds,f,l) - checkArg(1,tbl,'table') - checkArg(2,partitioner,'function','table') - checkArg(3,dropEnds,'boolean','nil') - if type(partitioner)=='table'then - return lib.partition(tbl,function(e,i,tbl) - return lib.first(tbl,partitioner,i) - end,dropEnds,f,l) - end - local s=#tbl - f,l=adjust(f,l,s) - local cut=view(tbl,f,l) - local result={} - local need=true - local exp=function()if need then result[#result+1]={}need=false end end - local i=f - while i<=l do - local e=cut[i] - local ds,de=partitioner(e,i,cut) - -- true==partition here - if ds==true then ds,de=i,i - elseif ds==false then ds,de=nil,nil end - if ds~=nil then - ds,de=adjust(ds,de,l) - ds=ds>=i and ds--no more - end - if not ds then -- false or nil - exp() - table.insert(result[#result],e) - else - local sub=lib.sub(cut,i,not dropEnds and de or (ds-1)) - if #sub>0 then - exp() - result[#result+math.min(#result[#result],1)]=sub - end - -- ensure i moves forward - local ensured=math.max(math.max(de or ds,ds),i) - if de and ds and de(l-f+1)then return end - for i=1,vs do - if tbl[f+i-1]~=v[i] then return end - end - return true -end - --- calls callback(e,i,tbl) for each ith element e in table tbl from first -function lib.foreach(tbl,c,f,l) - checkArg(1,tbl,'table') - checkArg(2,c,'function','string') - local ck=c - c=type(c)=="string" and function(e) return e[ck] end or c - local s=#tbl - f,l=adjust(f,l,s) - tbl=view(tbl,f,l) - local r={} - for i=f,l do - local n,k=c(tbl[i],i,tbl) - if n~=nil then - if k then r[k]=n - else r[#r+1]=n end - end - end - return r -end -lib.select=lib.foreach -function --[[@delayloaded-start@]] lib.where(tbl,p,f,l) - return lib.foreach(tbl, - function(e,i,tbl) - return p(e,i,tbl)and e or nil - end,f,l) -end --[[@delayloaded-end@]] -function lib.concat(...) - local r,rn,k={},0 - for _,tbl in ipairs({...})do - if type(tbl)~='table'then - return nil,'parameter '..tostring(_)..' to concat is not a table' - end - local n=tbl.n or #tbl - k=k or tbl.n - for i=1,n do - rn=rn+1;r[rn]=tbl[i] - end - end - r.n=k and rn or nil - return r -end - -return lib,{adjust=adjust,view=view} -Flib/vector.lua -local vectorLibrary = {} - ------------------------------------------------------------------------------------------------------------------------- - -function vectorLibrary.newVector2(x, y) - -- checkArg(1, x, "number") - -- checkArg(2, y, "number") - return { x, y } -end - -function vectorLibrary.newVector3(x, y, z) - -- checkArg(1, x, "number") - -- checkArg(2, y, "number") - -- checkArg(3, z, "number") - return { x, y, z } -end - -function vectorLibrary.newVector4(x, y, z, w) - -- checkArg(1, x, "number") - -- checkArg(2, y, "number") - -- checkArg(3, z, "number") - -- checkArg(4, w, "number") - return { x, y, z, w } -end - ------------------------------------------------------------------------------------------------------------------------- - -return vectorLibrary - -Flib/windows.lua0 ------------------------------------------ Libraries ----------------------------------------- - --- _G.GUI, package.loaded.GUI = nil, nil - -local computer = require("computer") -local buffer = require("doubleBuffering") -local GUI = require("GUI") -local unicode = require("unicode") -local event = require("event") - ------------------------------------------ Main variables ----------------------------------------- - -local windows = {} - -windows.alignment = GUI.alignment - -windows.colors = { - background = 0xEEEEEE, - title = { - background = 0xDDDDDD, - text = 0x262626, - }, - tabBar = { - background = 0xDDDDDD, - text = 0x262626, - selectedTab = { - background = 0xCCCCCC, - text = 0x262626, - } - }, -} - ------------------------------------------ Universal window event handlers ----------------------------------------- - -local function executeObjectMethod(method, ...) - if method then method(...) end -end - -local function buttonHandler(window, object, objectIndex, eventData) - if object.switchMode then - object.pressed = not object.pressed - window:draw() - buffer.draw() - executeObjectMethod(object.onTouch, eventData) - else - object.pressed = true - window:draw() - buffer.draw() - os.sleep(0.2) - object.pressed = false - window:draw() - buffer.draw() - executeObjectMethod(object.onTouch, eventData) - end -end - -local function tabBarTabHandler(window, object, objectIndex, eventData) - object.parent.parent.selectedTab = objectIndex - window:draw() - buffer.draw() - executeObjectMethod(object.parent.parent.onTabSwitched, eventData) -end - -local function inputTextBoxHandler(window, object, objectIndex, eventData) - object:input() - window:draw() - buffer.draw() - executeObjectMethod(object.onInputFinished, eventData) -end - -local function textBoxScrollHandler(window, object, objectIndex, eventData) - if eventData[5] == 1 then - object:scrollUp() - window:draw() - buffer.draw() - else - object:scrollDown() - window:draw() - buffer.draw() - end -end - -local function horizontalSliderHandler(window, object, objectIndex, eventData) - local clickPosition = eventData[3] - object.x + 1 - object.value = object.minimumValue + (clickPosition * (object.maximumValue - object.minimumValue) / object.width) - window:draw() - buffer.draw() - executeObjectMethod(object.onValueChanged, eventData) -end - -local function switchHandler(window, object, objectIndex, eventData) - object.state = not object.state - window:draw() - buffer.draw() - executeObjectMethod(object.onStateChanged, eventData) -end - -local function comboBoxHandler(window, object, objectIndex, eventData) - object:selectItem() - executeObjectMethod(object.onItemSelected, eventData) -end - -local function menuItemHandler(window, object, objectIndex, eventData) - object.pressed = true - window:draw() - buffer.draw() - executeObjectMethod(object.onTouch, eventData) - object.pressed = false - window:draw() - buffer.draw() -end - -local function scrollBarHandler(window, object, objectIndex, eventData) - local newValue = object.value - - if eventData[1] == "touch" or eventData[1] == "drag" then - local delta = object.maximumValue - object.minimumValue + 1 - if object.height > object.width then - newValue = math.floor((eventData[4] - object.y + 1) / object.height * delta) - else - newValue = math.floor((eventData[3] - object.x + 1) / object.width * delta) - end - elseif eventData[1] == "scroll" then - if eventData[5] == 1 then - if object.value >= object.minimumValue + object.onScrollValueIncrement then - newValue = object.value - object.onScrollValueIncrement - else - newValue = object.minimumValue - end - else - if object.value <= object.maximumValue - object.onScrollValueIncrement then - newValue = object.value + object.onScrollValueIncrement - else - newValue = object.maximumValue - end - end - end - window:draw() - buffer.draw() - object.value = newValue - executeObjectMethod(object.onTouch, eventData) -end - -local function treeViewHandler(window, object, objectIndex, eventData) - if eventData[1] == "touch" then - local fileIndex = eventData[4] - object.y + object.fromFile - 1 - if object.fileList[fileIndex] then - if object.fileList[fileIndex].isDirectory then - if object.directoriesToShowContent[object.fileList[fileIndex].path] then - object.directoriesToShowContent[object.fileList[fileIndex].path] = nil - else - object.directoriesToShowContent[object.fileList[fileIndex].path] = true - end - object:updateFileList() - object:draw(); buffer.draw() - else - object.currentFile = object.fileList[fileIndex].path - object:draw(); buffer.draw() - executeObjectMethod(object.onFileSelected, object.currentFile) - end - end - elseif eventData[1] == "scroll" then - if eventData[5] == 1 then - if object.fromFile > 1 then - object.fromFile = object.fromFile - 1 - object:draw(); buffer.draw() - end - else - if object.fromFile < #object.fileList then - object.fromFile = object.fromFile + 1 - object:draw(); buffer.draw() - end - end - end -end - -local function colorSelectorHandler(window, object, objectIndex, eventData) - object.pressed = true; object:draw(); buffer.draw() - object.color = require("palette").show("auto", "auto", object.color) or object.color - object.pressed = false; object:draw(); buffer.draw() - executeObjectMethod(object.onTouch) -end - -function windows.handleEventData(window, eventData) - if eventData[1] == "touch" then - local object, objectIndex = window:getClickedObject(eventData[3], eventData[4]) - - if object then - if object.type == GUI.objectTypes.button then - buttonHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.tabBarTab then - tabBarTabHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.inputTextBox then - inputTextBoxHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.horizontalSlider then - horizontalSliderHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.switch then - switchHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.comboBox then - comboBoxHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.menuItem then - menuItemHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.scrollBar then - scrollBarHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.treeView then - treeViewHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.colorSelector then - colorSelectorHandler(window, object, objectIndex, eventData) - elseif object.onTouch then - executeObjectMethod(object.onTouch, eventData) - end - else - executeObjectMethod(window.onTouch, eventData) - end - elseif eventData[1] == "scroll" then - local object, objectIndex = window:getClickedObject(eventData[3], eventData[4]) - - if object then - if object.type == GUI.objectTypes.textBox then - textBoxScrollHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.scrollBar then - scrollBarHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.treeView then - treeViewHandler(window, object, objectIndex, eventData) - elseif object.onScroll then - executeObjectMethod(object.onScroll, eventData) - end - else - executeObjectMethod(window.onScroll, eventData) - end - elseif eventData[1] == "drag" then - local object, objectIndex = window:getClickedObject(eventData[3], eventData[4]) - if object then - if object.type == GUI.objectTypes.horizontalSlider then - horizontalSliderHandler(window, object, objectIndex, eventData) - elseif object.type == GUI.objectTypes.scrollBar then - scrollBarHandler(window, object, objectIndex, eventData) - elseif object.onDrag then - executeObjectMethod(object.onDrag, eventData) - end - else - executeObjectMethod(window.onDrag, eventData) - end - elseif eventData[1] == "drop" then - local object, objectIndex = window:getClickedObject(eventData[3], eventData[4]) - if object then - if object.onDrag then - executeObjectMethod(object.onDrop, eventData) - end - else - executeObjectMethod(window.onDrop, eventData) - end - elseif eventData[1] == "key_down" then - executeObjectMethod(window.onKeyDown, eventData) - elseif eventData[1] == "key_up" then - executeObjectMethod(window.onKeyUp, eventData) - end - - executeObjectMethod(window.onAnyEvent, eventData) -end - -function windows.handleEvents(window, pullTime) - while true do - window:handleEventData({event.pull(pullTime)}) - if window.dataToReturn then - local data = window.dataToReturn - window = nil - return table.unpack(data) - end - end -end - ------------------------------------------ Window actions ----------------------------------------- - -function windows.returnData(window, ...) - window.dataToReturn = {...} - computer.pushSignal("windowAction") -end - -function windows.close(window) - windows.returnData(window, nil) -end - ------------------------------------------ Window creation ----------------------------------------- - -function windows.correctWindowCoordinates(x, y, width, height, minimumWidth, minimumHeight) - width = minimumWidth and math.max(width, minimumWidth) or width - height = minimumHeight and math.max(height, minimumHeight) or height - x = (x == "auto" and math.floor(buffer.screen.width / 2 - width / 2)) or x - y = (y == "auto" and math.floor(buffer.screen.height / 2 - height / 2)) or y - - return x, y, width, height -end - -local function drawWindow(window) - if window.onDrawStarted then window.onDrawStarted() end - window:drawMethodOutOfWindowsLibrary() - if window.drawShadow then GUI.windowShadow(window.x, window.y, window.width, window.height, 50) end - if window.onDrawFinished then window.onDrawFinished() end -end - -local function newWindow(x, y, width, height, minimumWidth, minimumHeight) - x, y, width, height = windows.correctWindowCoordinates(x, y, width, height, minimumWidth, minimumHeight) - - local window = GUI.container(x, y, width, height) - window.minimumWidth = minimumWidth - window.minimumHeight = minimumHeight - window.drawShadow = false - window.drawMethodOutOfWindowsLibrary = window.draw - window.draw = drawWindow - window.handleEventData = windows.handleEventData - window.handleEvents = windows.handleEvents - window.close = windows.close - window.returnData = windows.returnData - - return window -end - ------------------------------------------ Window patterns ----------------------------------------- - -function windows.empty(x, y, width, height, minimumWidth, minimumHeight) - return newWindow(x, y, width, height, minimumWidth, minimumHeight) -end - -function windows.fullScreen() - return newWindow(1, 1, buffer.screen.width, buffer.screen.height) -end - -function windows.tabbed(x, y, width, height, minimumWidth, minimumHeight, ...) - local tabs = {...} - local window = newWindow(x, y, width, height, minimumWidth, minimumHeight) - window:addPanel(1, 1, window.width, window.height, 0xEEEEEE).disabled = true - window:addTabBar(1, 1, window.width, 3, 1, 0xDDDDDD, 0x262626, 0xCCCCCC, 0x262626, ...) - window:addWindowActionButtons(2, 1, false) - - return window -end - ------------------------------------------ Playground ----------------------------------------- - --- buffer.start() --- buffer.clear(0xFF8888) --- buffer.draw(true) - --- local myWindow = windows.empty(2, 2, 60, 30, 60, 30) --- myWindow:addColorSelector(2, 2, 30, 3, 0xFF00FF, "Text") --- myWindow:draw() --- buffer.draw() --- myWindow:handleEvents() - --- local myWindow = windows.empty(2, 2, 20, 40, 20, 40) --- myWindow:addTreeView(1, 1, myWindow.width, myWindow.height, 0xDDDDDD, 0x2D2D2D, 0x2D2D2D, 0xEEEEEE, 0x555555, 0x444444, 0x00DBFF, "/") --- myWindow:draw() --- buffer.draw() --- myWindow:handleEvents() - - --- local myWindow = windows.empty(2, 2, 60, 20, 60, 20) --- local scrollBar = myWindow:addScrollBar(1, 1, 20, 1, 0x444444, 0x00DBFF, 1, 100, 50, 20, 4, true) --- scrollBar.onTouch = function() --- buffer.text(4, 1, 0xFFFFFF, "Value: " .. scrollBar.value) --- end --- myWindow:draw() --- buffer.draw() --- myWindow:handleEvents() - ------------------------------------------ End of shit ----------------------------------------- - -return windows -Flib/xmlParser.luaH -local xml = {} - ------------------------------------------------------------------------------------------------------------- - -function xml.parseargs(s) - local arg = {} - string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a) - arg[w] = a - end) - return arg -end - -function xml.collect(s) - local stack = {} - local top = {} - table.insert(stack, top) - local ni,c,label,xarg, empty - local i, j = 1, 1 - while true do - ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i) - if not ni then break end - local text = string.sub(s, i, ni-1) - if not string.find(text, "^%s*$") then - table.insert(top, text) - end - if empty == "/" then -- empty element tag - table.insert(top, {label=label, xarg=xml.parseargs(xarg), empty=1}) - elseif c == "" then -- start tag - top = {label=label, xarg=xml.parseargs(xarg)} - table.insert(stack, top) -- new level - else -- end tag - local toclose = table.remove(stack) -- remove top - top = stack[#stack] - if #stack < 1 then - error("nothing to close with "..label) - end - if toclose.label ~= label then - error("trying to close "..toclose.label.." with "..label) - end - table.insert(top, toclose) - end - i = j+1 - end - local text = string.sub(s, i) - if not string.find(text, "^%s*$") then - table.insert(stack[#stack], text) - end - if #stack > 1 then - error("unclosed "..stack[#stack].label) - end - return stack[1] -end - ------------------------------------------------------------------------------------------------------------- - -return xml - - -Dbin/Fbin/address.luaGlocal computer = require("computer") -io.write(computer.address(),"\n") -F bin/alias.lualocal shell = require("shell") -local args, options = shell.parse(...) - -local ec, error_prefix = 0, "alias:" - -if options.help then - print(string.format("Usage: alias: [name[=value] ... ]", cmd_name)) - return -end - -local function validAliasName(k) - return k:match("[/%$`=|&;%(%)<> \t]") == nil -end - -local function setAlias(k, v) - if not validAliasName(k) then - io.stderr:write(string.format("%s `%s': invalid alias name\n", error_prefix, k)) - else - shell.setAlias(k, v) - end -end - -local function printAlias(k) - local v = shell.getAlias(k) - if not v then - io.stderr:write(string.format("%s %s: not found\n", error_prefix, k)) - ec = 1 - else - io.write(string.format("alias %s='%s'\n", k, v)) - end -end - -local function splitPair(arg) - local matchBegin, matchEnd = arg:find("=") - if matchBegin == nil or matchBegin == 1 then - return arg - else - return arg:sub(1, matchBegin - 1), arg:sub(matchEnd + 1) - end -end - -local function handlePair(k, v) - if v then - return setAlias(k, v) - else - return printAlias(k) - end -end - -if not next(args) then -- no args - -- print all aliases - for k,v in shell.aliases() do - print(string.format("alias %s='%s'", k, v)) - end -else - for k,v in pairs(args) do - checkArg(1,v,"string") - handlePair(splitPair(v)) - end -end - -return ec -F bin/cat.lua:local shell = require("shell") -local fs = require("filesystem") - -local args = shell.parse(...) -local ec = 0 -if #args == 0 then - repeat - local read = io.read("*L") - if read then - io.write(read) - end - until not read -else - for i = 1, #args do - local arg = args[i] - if fs.isDirectory(arg) then - io.stderr:write(string.format('cat %s: Is a directory\n', arg)) - ec = 1 - else - local file, reason = args[i] == "-" and io.stdin or io.open(shell.resolve(args[i])) - if not file then - io.stderr:write(string.format("cat: %s: %s\n",args[i],tostring(reason))) - ec = 1 - else - repeat - local line = file:read("*L") - if line then - io.write(line) - end - until not line - file:close() - end - end - end -end - -return ec -F -bin/cd.lualocal shell = require("shell") -local fs = require("filesystem") - -local args, ops = shell.parse(...) -local path = nil -local verbose = false - -if ops.help then - print( -[[Usage cd [dir] -For more options, run: man cd]]) - return -end - -if #args == 0 then - local home = os.getenv("HOME") - if not home then - io.stderr:write("cd: HOME not set\n") - return 1 - end - path = home -elseif args[1] == '-' then - verbose = true - local oldpwd = os.getenv("OLDPWD"); - if not oldpwd then - io.stderr:write("cd: OLDPWD not set\n") - return 1 - end - path = oldpwd -else - path = args[1] -end - -local resolved = shell.resolve(path) -if not fs.exists(resolved) then - io.stderr:write("cd: ",path,": No such file or directory\n") - return 1 -end - -path = resolved -local oldpwd = shell.getWorkingDirectory() -local result, reason = shell.setWorkingDirectory(path) -if not result then - io.stderr:write("cd: ",reason) - return 1 -else - os.setenv("OLDPWD", oldpwd) -end -if verbose then - os.execute("pwd") -end -F bin/clear.luaHlocal component = require("component") -local term = require("term") -component.gpu.setBackground(_G.OSSettings.shellBackground or 0x1B1B1B) -component.gpu.setForeground(_G.OSSettings.shellBackground or 0xEEEEEE) - -local width, height = component.gpu.getResolution() -component.gpu.fill(1, 1, width, height, " ") -term.setCursor(1, 1)Fbin/components.lualocal component = require("component") -local shell = require("shell") -local text = require("text") - -local args, options = shell.parse(...) -local count = tonumber(options.limit) or math.huge - -local components = {} -local padTo = 1 - -if #args == 0 then -- get all components if no filters given. - args[1] = "" -end -for _, filter in ipairs(args) do - for address, name in component.list(filter) do - if name:len() > padTo then - padTo = name:len() + 2 - end - components[address] = name - end -end - -padTo = padTo + 8 - padTo % 8 -for address, name in pairs(components) do - io.write(text.padRight(name, padTo) .. address .. '\n') - - if options.l then - local proxy = component.proxy(address) - local padTo = 1 - local methods = {} - for name, member in pairs(proxy) do - if type(member) == "table" or type(member) == "function" then - if name:len() > padTo then - padTo = name:len() + 2 - end - table.insert(methods, name) - end - end - table.sort(methods) - padTo = padTo + 8 - padTo % 8 - - for _, name in ipairs(methods) do - local doc = tostring(proxy[name]) - io.write(" " .. text.padRight(name, padTo) .. doc .. '\n') - end - end - - count = count - 1 - if count <= 0 then - break - end -end -F -bin/cp.lualocal fs = require("filesystem") -local shell = require("shell") - -local args, options = shell.parse(...) -if #args < 2 then - io.write("Usage: cp [-inrv] \n") - io.write(" -i: prompt before overwrite (overrides -n option).\n") - io.write(" -n: do not overwrite an existing file.\n") - io.write(" -r: copy directories recursively.\n") - io.write(" -u: copy only when the SOURCE file differs from the destination\n") - io.write(" file or when the destination file is missing.\n") - io.write(" -P: preserve attributes, e.g. symbolic links.\n") - io.write(" -v: verbose output.\n") - io.write(" -x: stay on original source file system.\n") - return 1 -end - -options.P = options.P or options.r - -local from = {} -for i = 1, #args - 1 do - table.insert(from, shell.resolve(args[i])) -end -local to = shell.resolve(args[#args]) - -local function status(from, to) - if options.v then - io.write(from .. " -> " .. to .. "\n") - end - os.sleep(0) -- allow interrupting -end - -local result, reason - -local function prompt(message) - io.write(message .. " [Y/n] ") - local result = io.read() - return result and (result == "" or result:sub(1, 1):lower() == "y") -end - -local function areEqual(path1, path2) - local f1 = io.open(path1, "rb") - if not f1 then - return nil, "could not open `" .. path1 .. "' for update test" - end - local f2 = io.open(path2, "rb") - if not f2 then - f1:close() - return nil, "could not open `" .. path2 .. "' for update test" - end - local result = true - local chunkSize = 4 * 1024 - repeat - local s1, s2 = f1:read(chunkSize), f2:read(chunkSize) - if s1 ~= s2 then - result = false - break - end - until not s1 or not s2 - f1:close() - f2:close() - return result -end - -local mounts = {} -for dev,path in fs.mounts() do - mounts[fs.canonical(path)] = dev -end - -local function recurse(fromPath, toPath, origin) - status(fromPath, toPath) - local isLink, target = fs.isLink(fromPath) - if isLink and options.P then - return fs.link(target, toPath) - end - if fs.isDirectory(fromPath) then - if not options.r then - io.write("omitting directory `" .. fromPath .. "'\n") - return true - end - if fs.exists(toPath) and not fs.isDirectory(toPath) then - -- my real cp always does this, even with -f, -n or -i. - return nil, "cannot overwrite non-directory `" .. toPath .. "' with directory `" .. fromPath .. "'" - end - if options.x and origin and mounts[fs.canonical(fromPath)] then - return true - end - if fs.get(fromPath) == fs.get(toPath) and fs.canonical(fs.path(toPath)):find(fs.canonical(fromPath),1,true) then - return nil, "cannot copy a directory, `" .. fromPath .. "', into itself, `" .. toPath .. "'" - end - fs.makeDirectory(toPath) - for file in fs.list(fromPath) do - local result, reason = recurse(fs.concat(fromPath, file), fs.concat(toPath, file), origin or fs.get(fromPath)) - if not result then - return nil, reason - end - end - return true - else - if fs.exists(toPath) then - if fs.canonical(fromPath) == fs.canonical(toPath) then - return nil, "`" .. fromPath .. "' and `" .. toPath .. "' are the same file" - end - if fs.isDirectory(toPath) then - if options.i then - if not prompt("overwrite `" .. toPath .. "'?") then - return true - end - elseif options.n then - return true - else -- yes, even for -f - return nil, "cannot overwrite directory `" .. toPath .. "' with non-directory" - end - else - if options.u then - if areEqual(fromPath, toPath) then - return true - end - end - if options.i then - if not prompt("overwrite `" .. toPath .. "'?") then - return true - end - elseif options.n then - return true - end - -- else: default to overwriting - end - fs.remove(toPath) - end - return fs.copy(fromPath, toPath) - end -end -for _, fromPath in ipairs(from) do - local toPath = to - if fs.isDirectory(toPath) then - toPath = fs.concat(toPath, fs.name(fromPath)) - end - result, reason = recurse(fromPath, toPath) - if not result then - if reason then - io.stderr:write(reason..'\n') - end - return 1 - end -end -F bin/date.lua"io.write(os.date("%F %T").."\n") -F -bin/df.lualocal fs = require("filesystem") -local shell = require("shell") -local text = require("text") - -local args, options = shell.parse(...) - -local function formatSize(size) - if not options.h then - return tostring(size) - elseif type(size) == "string" then - return size - end - local sizes = {"", "K", "M", "G"} - local unit = 1 - local power = options.si and 1000 or 1024 - while size > power and unit < #sizes do - unit = unit + 1 - size = size / power - end - return math.floor(size * 10) / 10 .. sizes[unit] -end - -local mounts = {} -if #args == 0 then - for proxy, path in fs.mounts() do - mounts[path] = proxy - end -else - for i = 1, #args do - local proxy, path = fs.get(args[i]) - if not proxy then - io.stderr:write(args[i], ": no such file or directory\n") - else - mounts[path] = proxy - end - end -end - -local result = {{"Filesystem", "Used", "Available", "Use%", "Mounted on"}} -for path, proxy in pairs(mounts) do - local label = proxy.getLabel() or proxy.address - local used, total = proxy.spaceUsed(), proxy.spaceTotal() - local available, percent - if total == math.huge then - used = used or "N/A" - available = "unlimited" - percent = "0%" - else - available = total - used - percent = used / total - if percent ~= percent then -- NaN - available = "N/A" - percent = "N/A" - else - percent = math.ceil(percent * 100) .. "%" - end - end - table.insert(result, {label, formatSize(used), formatSize(available), tostring(percent), path}) -end - -local m = {} -for _, row in ipairs(result) do - for col, value in ipairs(row) do - m[col] = math.max(m[col] or 1, value:len()) - end -end - -for _, row in ipairs(result) do - for col, value in ipairs(row) do - io.write(text.padRight(value, m[col] + 2)) - end - print() -end -F bin/dmesg.luaLlocal event = require "event" -local term = require "term" - -local args = {...} -local gpu = term.gpu() -local interactive = io.output().tty -local color, isPal, evt -if interactive then - color, isPal = gpu.getForeground() -end -io.write("Press 'Ctrl-C' to exit\n") -pcall(function() - repeat - if #args > 0 then - evt = table.pack(event.pullMultiple("interrupted", table.unpack(args))) - else - evt = table.pack(event.pull()) - end - if interactive then gpu.setForeground(0xCC2200) end - io.write("[" .. os.date("%T") .. "] ") - if interactive then gpu.setForeground(0x44CC00) end - io.write(tostring(evt[1]) .. string.rep(" ", math.max(10 - #tostring(evt[1]), 0) + 1)) - if interactive then gpu.setForeground(0xB0B00F) end - io.write(tostring(evt[2]) .. string.rep(" ", 37 - #tostring(evt[2]))) - if interactive then gpu.setForeground(0xFFFFFF) end - if evt.n > 2 then - for i = 3, evt.n do - io.write(" " .. tostring(evt[i])) - end - end - - io.write("\n") - until evt[1] == "interrupted" -end) -if interactive then - gpu.setForeground(color, isPal) -end - -F -bin/du.lua [local shell = require("shell") -local fs = require("filesystem") - -local args, options, reason = shell.parse(...) -if #args == 0 then - args[1] = '.' -end - -local TRY=[[ -Try 'du --help' for more information.]] - -local VERSION=[[ -du (OpenOS bin) 1.0 -Written by payonel, patterned after GNU coreutils du]] - -local HELP=[[ -Usage: du [OPTION]... [FILE]... -Summarize disk usage of each FILE, recursively for directories. - - -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G) - -s, --summarize display only a total for each argument - --help display this help and exit - --version output version information and exit]] - -if options.help then - print(HELP) - return true -end - -if options.version then - print(VERSION) - return true -end - -local function addTrailingSlash(path) - if path:sub(-1) ~= '/' then - return path .. '/' - else - return path - end -end - -local function opCheck(shortName, longName) - local enabled = options[shortName] or options[longName] - options[shortName] = nil - options[longName] = nil - return enabled -end - -local bHuman = opCheck('h', 'human-readable') -local bSummary = opCheck('s', 'summarize') - -if next(options) then - for op,v in pairs(options) do - io.stderr:write(string.format("du: invalid option -- '%s'\n", op)) - end - io.stderr:write(TRY..'\n') - return 1 -end - -local function formatSize(size) - if not bHuman then - return tostring(size) - end - local sizes = {"", "K", "M", "G"} - local unit = 1 - local power = options.si and 1000 or 1024 - while size > power and unit < #sizes do - unit = unit + 1 - size = size / power - end - - return math.floor(size * 10) / 10 .. sizes[unit] -end - -local function printSize(size, rpath) - local displaySize = formatSize(size) - io.write(string.format("%s%s\n", string.format("%-12s", displaySize), rpath)) -end - -local function visitor(rpath) - local subtotal = 0 - local dirs = 0 - local spath = shell.resolve(rpath) - - if fs.isDirectory(spath) then - local list_result = fs.list(spath) - for list_item in list_result do - local vtotal, vdirs = visitor(addTrailingSlash(rpath) .. list_item) - subtotal = subtotal + vtotal - dirs = dirs + vdirs - end - - if dirs == 0 then -- no child dirs - if not bSummary then - printSize(subtotal, rpath) - end - end - - elseif not fs.isLink(spath) then - subtotal = fs.size(spath) - end - - return subtotal, dirs -end - -for i,arg in ipairs(args) do - local path = shell.resolve(arg) - - if not fs.exists(path) then - io.stderr:write(string.format("du: cannot access '%s': no such file or directory\n", arg)) - return 1 - else - if fs.isDirectory(path) then - local total = visitor(arg) - - if bSummary then - printSize(total, arg) - end - elseif fs.isLink(path) then - printSize(0, arg) - else - printSize(fs.size(path), arg) - end - end -end - -return true -F bin/echo.lua5local args, options = require("shell").parse(...) -if options.help then - print([[`echo` writes the provided string(s) to the standard output. - -n do not output the trialing newline - --help display this help and exit]]) - return -end -io.write(table.concat(args," ")) -if not options.n then - print() -end -F bin/edit.luaFlocal event = require("event") -local fs = require("filesystem") -local keyboard = require("keyboard") -local shell = require("shell") -local term = require("term") -local text = require("text") -local unicode = require("unicode") - -if not term.isAvailable() then - return -end -local gpu = term.gpu() -local args, options = shell.parse(...) -if #args == 0 then - io.write("Usage: edit ") - return -end - -local filename = shell.resolve(args[1]) - -local readonly = options.r or fs.get(filename) == nil or fs.get(filename).isReadOnly() - -if not fs.exists(filename) then - if fs.isDirectory(filename) then - io.stderr:write("file is a directory\n") - return 1 - elseif readonly then - io.stderr:write("file system is read only\n") - return 1 - end -end - -local function loadConfig() - -- Try to load user settings. - local env = {} - local config = loadfile("/etc/edit.cfg", nil, env) - if config then - pcall(config) - end - -- Fill in defaults. - env.keybinds = env.keybinds or { - left = {{"left"}}, - right = {{"right"}}, - up = {{"up"}}, - down = {{"down"}}, - home = {{"home"}}, - eol = {{"end"}}, - pageUp = {{"pageUp"}}, - pageDown = {{"pageDown"}}, - - backspace = {{"back"}}, - delete = {{"delete"}}, - deleteLine = {{"control", "delete"}, {"shift", "delete"}}, - newline = {{"enter"}}, - - save = {{"control", "s"}}, - close = {{"control", "w"}}, - find = {{"control", "f"}}, - findnext = {{"control", "g"}, {"control", "n"}, {"f3"}} - } - -- Generate config file if it didn't exist. - if not config then - local root = fs.get("/") - if root and not root.isReadOnly() then - fs.makeDirectory("/etc") - local f = io.open("/etc/edit.cfg", "w") - if f then - local serialization = require("serialization") - for k, v in pairs(env) do - f:write(k.."="..tostring(serialization.serialize(v, math.huge)).."\n") - end - f:close() - end - end - end - return env -end - -term.clear() -term.setCursorBlink(true) - -local running = true -local buffer = {} -local scrollX, scrollY = 0, 0 -local config = loadConfig() - -local getKeyBindHandler -- forward declaration for refind() - -local function helpStatusText() - local function prettifyKeybind(label, command) - local keybind = type(config.keybinds) == "table" and config.keybinds[command] - if type(keybind) ~= "table" or type(keybind[1]) ~= "table" then return "" end - local alt, control, shift, key - for _, value in ipairs(keybind[1]) do - if value == "alt" then alt = true - elseif value == "control" then control = true - elseif value == "shift" then shift = true - else key = value end - end - if not key then return "" end - return label .. ": [" .. - (control and "Ctrl+" or "") .. - (alt and "Alt+" or "") .. - (shift and "Shift+" or "") .. - unicode.upper(key) .. - "] " - end - return prettifyKeybind("Save", "save") .. - prettifyKeybind("Close", "close") .. - prettifyKeybind("Find", "find") -end - -------------------------------------------------------------------------------- - -local function setStatus(value) - local x, y, w, h = term.getGlobalArea() - value = unicode.wlen(value) > w - 10 and unicode.wtrunc(value, w - 9) or value - value = text.padRight(value, w - 10) - gpu.set(x, y + h - 1, value) -end - -local function getArea() - local x, y, w, h = term.getGlobalArea() - return x, y, w, h - 1 -end - -local function removePrefix(line, length) - if length >= unicode.wlen(line) then - return "" - else - local prefix = unicode.wtrunc(line, length + 1) - local suffix = unicode.sub(line, unicode.len(prefix) + 1) - length = length - unicode.wlen(prefix) - if length > 0 then - suffix = (" "):rep(unicode.charWidth(suffix) - length) .. unicode.sub(suffix, 2) - end - return suffix - end -end - -local function lengthToChars(line, length) - if length > unicode.wlen(line) then - return unicode.len(line) + 1 - else - local prefix = unicode.wtrunc(line, length) - return unicode.len(prefix) + 1 - end -end - - -local function isWideAtPosition(line, x) - local index = lengthToChars(line, x) - if index > unicode.len(line) then - return false, false - end - local prefix = unicode.sub(line, 1, index) - local char = unicode.sub(line, index, index) - --isWide, isRight - return unicode.isWide(char), unicode.wlen(prefix) == x -end - -local function drawLine(x, y, w, h, lineNr) - local yLocal = lineNr - scrollY - if yLocal > 0 and yLocal <= h then - local str = removePrefix(buffer[lineNr] or "", scrollX) - str = unicode.wlen(str) > w and unicode.wtrunc(str, w + 1) or str - str = text.padRight(str, w) - gpu.set(x, y - 1 + lineNr - scrollY, str) - end -end - -local function getCursor() - local cx, cy = term.getCursor() - return cx + scrollX, cy + scrollY -end - -local function line() - local cbx, cby = getCursor() - return buffer[cby] -end - -local function getNormalizedCursor() - local cbx, cby = getCursor() - local wide, right = isWideAtPosition(buffer[cby], cbx) - if wide and right then - cbx = cbx - 1 - end - return cbx, cby -end - -local function setCursor(nbx, nby) - local x, y, w, h = getArea() - nby = math.max(1, math.min(#buffer, nby)) - - local ncy = nby - scrollY - if ncy > h then - term.setCursorBlink(false) - local sy = nby - h - local dy = math.abs(scrollY - sy) - scrollY = sy - if h > dy then - gpu.copy(x, y + dy, w, h - dy, 0, -dy) - end - for lineNr = nby - (math.min(dy, h) - 1), nby do - drawLine(x, y, w, h, lineNr) - end - elseif ncy < 1 then - term.setCursorBlink(false) - local sy = nby - 1 - local dy = math.abs(scrollY - sy) - scrollY = sy - if h > dy then - gpu.copy(x, y, w, h - dy, 0, dy) - end - for lineNr = nby, nby + (math.min(dy, h) - 1) do - drawLine(x, y, w, h, lineNr) - end - end - term.setCursor(term.getCursor(), nby - scrollY) - - nbx = math.max(1, math.min(unicode.wlen(line()) + 1, nbx)) - local wide, right = isWideAtPosition(line(), nbx) - local ncx = nbx - scrollX - if ncx > w or (ncx + 1 > w and wide and not right) then - term.setCursorBlink(false) - scrollX = nbx - w + ((wide and not right) and 1 or 0) - for lineNr = 1 + scrollY, math.min(h + scrollY, #buffer) do - drawLine(x, y, w, h, lineNr) - end - elseif ncx < 1 or (ncx - 1 < 1 and wide and right) then - term.setCursorBlink(false) - scrollX = nbx - 1 - ((wide and right) and 1 or 0) - for lineNr = 1 + scrollY, math.min(h + scrollY, #buffer) do - drawLine(x, y, w, h, lineNr) - end - end - term.setCursor(nbx - scrollX, nby - scrollY) - --update with term lib - nbx, nby = getCursor() - gpu.set(x + w - 10, y + h, text.padLeft(string.format("%d,%d", nby, nbx), 10)) -end - -local function highlight(bx, by, length, enabled) - local x, y, w, h = getArea() - local cx, cy = bx - scrollX, by - scrollY - cx = math.max(1, math.min(w, cx)) - cy = math.max(1, math.min(h, cy)) - length = math.max(1, math.min(w - cx, length)) - - local fg, fgp = gpu.getForeground() - local bg, bgp = gpu.getBackground() - if enabled then - gpu.setForeground(bg, bgp) - gpu.setBackground(fg, fgp) - end - local indexFrom = lengthToChars(buffer[by], bx) - local value = unicode.sub(buffer[by], indexFrom) - if unicode.wlen(value) > length then - value = unicode.wtrunc(value, length + 1) - end - gpu.set(x - 1 + cx, y - 1 + cy, value) - if enabled then - gpu.setForeground(fg, fgp) - gpu.setBackground(bg, bgp) - end -end - -local function home() - local cbx, cby = getCursor() - setCursor(1, cby) -end - -local function ende() - local cbx, cby = getCursor() - setCursor(unicode.wlen(line()) + 1, cby) -end - -local function left() - local cbx, cby = getNormalizedCursor() - if cbx > 1 then - local wideTarget, rightTarget = isWideAtPosition(line(), cbx - 1) - if wideTarget and rightTarget then - setCursor(cbx - 2, cby) - else - setCursor(cbx - 1, cby) - end - return true -- for backspace - elseif cby > 1 then - setCursor(cbx, cby - 1) - ende() - return true -- again, for backspace - end -end - -local function right(n) - n = n or 1 - local cbx, cby = getNormalizedCursor() - local be = unicode.wlen(line()) + 1 - local wide, right = isWideAtPosition(line(), cbx + n) - if wide and right then - n = n + 1 - end - if cbx + n <= be then - setCursor(cbx + n, cby) - elseif cby < #buffer then - setCursor(1, cby + 1) - end -end - -local function up(n) - n = n or 1 - local cbx, cby = getCursor() - if cby > 1 then - setCursor(cbx, cby - n) - end -end - -local function down(n) - n = n or 1 - local cbx, cby = getCursor() - if cby < #buffer then - setCursor(cbx, cby + n) - end -end - -local function delete(fullRow) - local cx, cy = term.getCursor() - local cbx, cby = getCursor() - local x, y, w, h = getArea() - local function deleteRow(row) - local content = table.remove(buffer, row) - local rcy = cy + (row - cby) - if rcy <= h then - gpu.copy(x, y + rcy, w, h - rcy, 0, -1) - drawLine(x, y, w, h, row + (h - rcy)) - end - return content - end - if fullRow then - term.setCursorBlink(false) - if #buffer > 1 then - deleteRow(cby) - else - buffer[cby] = "" - gpu.fill(x, y - 1 + cy, w, 1, " ") - end - setCursor(1, cby) - elseif cbx <= unicode.wlen(line()) then - term.setCursorBlink(false) - local index = lengthToChars(line(), cbx) - buffer[cby] = unicode.sub(line(), 1, index - 1) .. - unicode.sub(line(), index + 1) - drawLine(x, y, w, h, cby) - elseif cby < #buffer then - term.setCursorBlink(false) - local append = deleteRow(cby + 1) - buffer[cby] = buffer[cby] .. append - drawLine(x, y, w, h, cby) - else - return - end - setStatus(helpStatusText()) -end - -local function insert(value) - if not value or unicode.len(value) < 1 then - return - end - term.setCursorBlink(false) - local cx, cy = term.getCursor() - local cbx, cby = getCursor() - local x, y, w, h = getArea() - local index = lengthToChars(line(), cbx) - buffer[cby] = unicode.sub(line(), 1, index - 1) .. - value .. - unicode.sub(line(), index) - drawLine(x, y, w, h, cby) - right(unicode.wlen(value)) - setStatus(helpStatusText()) -end - -local function enter() - term.setCursorBlink(false) - local cx, cy = term.getCursor() - local cbx, cby = getCursor() - local x, y, w, h = getArea() - local index = lengthToChars(line(), cbx) - table.insert(buffer, cby + 1, unicode.sub(buffer[cby], index)) - buffer[cby] = unicode.sub(buffer[cby], 1, index - 1) - drawLine(x, y, w, h, cby) - if cy < h then - if cy < h - 1 then - gpu.copy(x, y + cy, w, h - (cy + 1), 0, 1) - end - drawLine(x, y, w, h, cby + 1) - end - setCursor(1, cby + 1) - setStatus(helpStatusText()) -end - -local findText = "" - -local function find() - local x, y, w, h = getArea() - local cx, cy = term.getCursor() - local cbx, cby = getCursor() - local ibx, iby = cbx, cby - while running do - if unicode.len(findText) > 0 then - local sx, sy - for syo = 1, #buffer do -- iterate lines with wraparound - sy = (iby + syo - 1 + #buffer - 1) % #buffer + 1 - sx = string.find(buffer[sy], findText, syo == 1 and ibx or 1, true) - if sx and (sx >= ibx or syo > 1) then - break - end - end - if not sx then -- special case for single matches - sy = iby - sx = string.find(buffer[sy], findText, nil, true) - end - if sx then - sx = unicode.wlen(string.sub(buffer[sy], 1, sx - 1)) + 1 - cbx, cby = sx, sy - setCursor(cbx, cby) - highlight(cbx, cby, unicode.wlen(findText), true) - end - end - term.setCursor(7 + unicode.wlen(findText), h + 1) - setStatus("Find: " .. findText) - - local _, address, char, code = term.pull("key_down") - if address == term.keyboard().address then - local handler, name = getKeyBindHandler(code) - highlight(cbx, cby, unicode.wlen(findText), false) - if name == "newline" then - break - elseif name == "close" then - handler() - elseif name == "backspace" then - findText = unicode.sub(findText, 1, -2) - elseif name == "find" or name == "findnext" then - ibx = cbx + 1 - iby = cby - elseif not keyboard.isControl(char) then - findText = findText .. unicode.char(char) - end - end - end - setCursor(cbx, cby) - setStatus(helpStatusText()) -end - -------------------------------------------------------------------------------- - -local keyBindHandlers = { - left = left, - right = right, - up = up, - down = down, - home = home, - eol = ende, - pageUp = function() - local x, y, w, h = getArea() - up(h - 1) - end, - pageDown = function() - local x, y, w, h = getArea() - down(h - 1) - end, - - backspace = function() - if not readonly and left() then - delete() - end - end, - delete = function() - if not readonly then - delete() - end - end, - deleteLine = function() - if not readonly then - delete(true) - end - end, - newline = function() - if not readonly then - enter() - end - end, - - save = function() - if readonly then return end - local new = not fs.exists(filename) - local backup - if not new then - backup = filename .. "~" - for i = 1, math.huge do - if not fs.exists(backup) then - break - end - backup = filename .. "~" .. i - end - fs.copy(filename, backup) - end - local f, reason = io.open(filename, "w") - if f then - local chars, firstLine = 0, true - for _, line in ipairs(buffer) do - if not firstLine then - line = "\n" .. line - end - firstLine = false - f:write(line) - chars = chars + unicode.len(line) - end - f:close() - local format - if new then - format = [["%s" [New] %dL,%dC written]] - else - format = [["%s" %dL,%dC written]] - end - setStatus(string.format(format, fs.name(filename), #buffer, chars)) - else - setStatus(reason) - end - if not new then - fs.remove(backup) - end - end, - close = function() - -- TODO ask to save if changed - running = false - end, - find = function() - findText = "" - find() - end, - findnext = find -} - -getKeyBindHandler = function(code) - if type(config.keybinds) ~= "table" then return end - -- Look for matches, prefer more 'precise' keybinds, e.g. prefer - -- ctrl+del over del. - local result, resultName, resultWeight = nil, nil, 0 - for command, keybinds in pairs(config.keybinds) do - if type(keybinds) == "table" and keyBindHandlers[command] then - for _, keybind in ipairs(keybinds) do - if type(keybind) == "table" then - local alt, control, shift, key - for _, value in ipairs(keybind) do - if value == "alt" then alt = true - elseif value == "control" then control = true - elseif value == "shift" then shift = true - else key = value end - end - local keyboardAddress = term.keyboard().address - if (not alt or keyboard.isAltDown(keyboardAddress)) and - (not control or keyboard.isControlDown(keyboardAddress)) and - (not shift or keyboard.isShiftDown(keyboardAddress)) and - code == keyboard.keys[key] and - #keybind > resultWeight - then - resultWeight = #keybind - resultName = command - result = keyBindHandlers[command] - end - end - end - end - end - return result, resultName -end - -------------------------------------------------------------------------------- - -local function onKeyDown(char, code) - local handler = getKeyBindHandler(code) - if handler then - handler() - elseif readonly and code == keyboard.keys.q then - running = false - elseif not readonly then - if not keyboard.isControl(char) then - insert(unicode.char(char)) - elseif unicode.char(char) == "\t" then - insert(" ") - end - end -end - -local function onClipboard(value) - value = value:gsub("\r\n", "\n") - local cbx, cby = getCursor() - local start = 1 - local l = value:find("\n", 1, true) - if l then - repeat - local line = string.sub(value, start, l - 1) - line = text.detab(line, 2) - insert(line) - enter() - start = l + 1 - l = value:find("\n", start, true) - until not l - end - insert(string.sub(value, start)) -end - -local function onClick(x, y) - setCursor(x + scrollX, y + scrollY) -end - -local function onScroll(direction) - local cbx, cby = getCursor() - setCursor(cbx, cby - direction * 12) -end - -------------------------------------------------------------------------------- - -do - local f = io.open(filename) - if f then - local x, y, w, h = getArea() - local chars = 0 - for line in f:lines() do - if line:sub(-1) == "\r" then - line = line:sub(1, -2) - end - table.insert(buffer, line) - chars = chars + unicode.len(line) - if #buffer <= h then - drawLine(x, y, w, h, #buffer) - end - end - f:close() - if #buffer == 0 then - table.insert(buffer, "") - end - local format - if readonly then - format = [["%s" [readonly] %dL,%dC]] - else - format = [["%s" %dL,%dC]] - end - setStatus(string.format(format, fs.name(filename), #buffer, chars)) - else - table.insert(buffer, "") - setStatus(string.format([["%s" [New File] ]], fs.name(filename))) - end - setCursor(1, 1) -end - -while running do - local event, address, arg1, arg2, arg3 = term.pull() - if address == term.keyboard().address or address == term.screen().address then - local blink = true - if event == "key_down" then - onKeyDown(arg1, arg2) - elseif event == "clipboard" and not readonly then - onClipboard(arg1) - elseif event == "touch" or event == "drag" then - local x, y, w, h = getArea() - arg1 = arg1 - x + 1 - arg2 = arg2 - y + 1 - if arg1 >= 1 and arg2 >= 1 and arg1 <= w and arg2 <= h then - onClick(arg1, arg2) - end - elseif event == "scroll" then - onScroll(arg3) - else - blink = false - end - if blink then - term.setCursorBlink(true) - end - end -end - -term.clear() -term.setCursorBlink(false) -F bin/find.lua local shell = require("shell") -local fs = require("filesystem") -local text = require("text") - -local USAGE = -[===[Usage: find [path] [--type=[dfs]] [--[i]name=EXPR] - --path if not specified, path is assumed to be current working directory - --type returns results of a given type, d:directory, f:file, and s:symlinks - --name specify the file name pattern. Use quote to include *. iname is - case insensitive - --help display this help and exit]===] - -local args, options = shell.parse(...) - -if (not args or not options) or options.help then - print(USAGE) - if not options.help then - return 1 - else - return -- nil return, meaning no error - end -end - -if #args > 1 then - io.stderr:write(USAGE..'\n') - return 1 -end - -local path = #args == 1 and args[1] or "." - -local bDirs = true -local bFiles = true -local bSyms = true - -local fileNamePattern = "" -local bCaseSensitive = true - -if options.iname and options.name then - io.stderr:write("find cannot define both iname and name\n") - return 1 -end - -if options.type then - bDirs = false - bFiles = false - bSyms = false - - if options.type == "f" then - bFiles = true - elseif options.type == "d" then - bDirs = true - elseif options.type == "s" then - bSyms = true - else - io.stderr:write(string.format("find: Unknown argument to type: %s\n", options.type)) - io.stderr:write(USAGE..'\n') - return 1 - end -end - -if options.iname or options.name then - bCaseSensitive = options.iname ~= nil - fileNamePattern = options.iname or options.name - - if type(fileNamePattern) ~= "string" then - io.stderr:write('find: missing argument to `name\'\n') - return 1 - end - - if not bCaseSensitive then - fileNamePattern = fileNamePattern:lower() - end - - -- prefix any * with . for gnu find glob matching - fileNamePattern = text.escapeMagic(fileNamePattern) - fileNamePattern = fileNamePattern:gsub("%%%*", ".*") -end - -local function isValidType(spath) - if not fs.exists(spath) then - return false - end - - if fileNamePattern:len() > 0 then - local fileName = spath:gsub('.*/','') - - if fileName:len() == 0 then - return false - end - - local caseFileName = fileName - - if not bCaseSensitive then - caseFileName = caseFileName:lower() - end - - local s, e = caseFileName:find(fileNamePattern) - if not s or not e then - return false - end - - if s ~= 1 or e ~= caseFileName:len() then - return false - end - end - - if fs.isDirectory(spath) then - return bDirs - elseif fs.isLink(spath) then - return bSyms - else - return bFiles - end -end - -local function visit(rpath) - local spath = shell.resolve(rpath) - - if isValidType(spath) then - local result = rpath:gsub('/+$','') - print(result) - end - - if fs.isDirectory(spath) then - local list_result = fs.list(spath) - for list_item in list_result do - visit(rpath:gsub('/+$', '') .. '/' .. list_item) - end - end -end - -visit(path) -F bin/flash.lua plocal component = require("component") -local shell = require("shell") -local fs = require("filesystem") - -local args, options = shell.parse(...) - -if #args < 1 and not options.l then - io.write("Usage: flash [-qlr] [] [label]\n") - io.write(" q: quiet mode, don't ask questions.\n") - io.write(" l: print current contents of installed EEPROM.\n") - io.write(" r: save the current contents of installed EEPROM to file.\n") - return -end - -local function printRom() - local eeprom = component.eeprom - io.write(eeprom.get()) -end - -local function readRom() - local eeprom = component.eeprom - fileName = shell.resolve(args[1]) - if not options.q then - if fs.exists(fileName) then - io.write("Are you sure you want to overwrite " .. fileName .. "?\n") - io.write("Type `y` to confirm.\n") - repeat - local response = io.read() - until response and response:lower():sub(1, 1) == "y" - end - io.write("Reading EEPROM " .. eeprom.address .. ".\n" ) - end - local bios = eeprom.get() - local file = assert(io.open(fileName, "wb")) - file:write(bios) - file:close() - if not options.q then - io.write("All done!\nThe label is '" .. eeprom.getLabel() .. "'.\n") - end -end - -local function writeRom() - local file = assert(io.open(args[1], "rb")) - local bios = file:read("*a") - file:close() - - if not options.q then - io.write("Insert the EEPROM you would like to flash.\n") - io.write("When ready to write, type `y` to confirm.\n") - repeat - local response = io.read() - until response and response:lower():sub(1, 1) == "y" - io.write("Beginning to flash EEPROM.\n") - end - - local eeprom = component.eeprom - - if not options.q then - io.write("Flashing EEPROM " .. eeprom.address .. ".\n") - io.write("Please do NOT power down or restart your computer during this operation!\n") - end - - eeprom.set(bios) - - local label = args[2] - if not options.q and not label then - io.write("Enter new label for this EEPROM. Leave input blank to leave the label unchanged.\n") - label = io.read() - end - if label and #label > 0 then - eeprom.setLabel(label) - if not options.q then - io.write("Set label to '" .. eeprom.getLabel() .. "'.\n") - end - end - - if not options.q then - io.write("All done! You can remove the EEPROM and re-insert the previous one now.\n") - end -end - -if options.l then - printRom() -elseif options.r then - readRom() -else - writeRom() -end -F bin/grep.lua!--[[ -An adaptation of Wobbo's grep -https://raw.githubusercontent.com/OpenPrograms/Wobbo-Programs/master/grep/grep.lua -]]-- - --- POSIX grep for OpenComputers --- one difference is that this version uses Lua regex, not POSIX regex. - -local fs = require("filesystem") -local shell = require("shell") -local term = require("term") - --- Process the command line arguments - -local args, options = shell.parse(...) - -local gpu = term.gpu() - -local function printUsage(ostream, msg) - local s = ostream or io.stdout - if msg then - s:write(msg,'\n') - end - s:write([[Usage: grep [OPTION]... PATTERN [FILE]... -Example: grep -i "hello world" menu.lua main.lua -for more information, run: man grep -]]) -end - -local PATTERNS = {args[1]} -local FILES = {select(2, table.unpack(args))} - -local LABEL_COLOR = 0xb000b0 -local LINE_NUM_COLOR = 0x00FF00 -local MATCH_COLOR = 0xFF0000 -local COLON_COLOR = 0x00FFFF - -local function pop(...) - local result - for _,key in ipairs({...}) do - result = options[key] or result - options[key] = nil - end - return result -end - --- Specify the variables for the options -local plain = pop('F','fixed-strings') - plain = not pop('e','--lua-regexp') and plain -local pattern_file = pop('file') -local match_whole_word = pop('w','word-regexp') -local match_whole_line = pop('x','line-regexp') -local ignore_case = pop('i','ignore-case') -local stdin_label = pop('label') or '(standard input)' -local stderr = pop('s','no-messages') and {write=function()end} or io.stderr -local invert_match = not not pop('v','invert-match') - --- no version output, just help -if pop('V','version','help') then - printUsage() - return 0 -end - -local max_matches = tonumber(pop('max-count')) or math.huge -local print_line_num = pop('n','line-number') -local search_recursively = pop('r','recursive') - --- Table with patterns to check for -if pattern_file then - local pattern_file_path = shell.resolve(pattern_file) - if not fs.exists(pattern_file_path) then - stderr:write('grep: ',pattern_file,': file not found') - return 2 - end - table.insert(FILES, 1, PATTERNS[1]) - PATTERNS = {} - for line in io.lines(pattern_file_path) do - PATTERNS[#PATTERNS+1] = line - end -end - -if #PATTERNS == 0 then - printUsage(stderr) - return 2 -end - -if #FILES == 0 then - FILES = search_recursively and {'.'} or {'-'} -end - -if not options.h and search_recursively then - options.H = true -end - -if #FILES < 2 then - options.h = true -end - -local f_only = pop('l','files-with-matches') -local no_only = pop('L','files-without-match') and not f_only - -local include_filename = pop('H','with-filename') - include_filename = not pop('h','no-filename') or include_filename - -local m_only = pop('o','only-matching') -local quiet = pop('q','quiet','silent') - -local print_count = pop('c','count') -local colorize = pop('color','colour') and io.output().tty and term.isAvailable() - -local noop = function(...)return ...;end -local setc = colorize and gpu.setForeground or noop -local getc = colorize and gpu.getForeground or noop - -local trim = pop('t','trim') -local trim_front = trim and function(s)return s:gsub('^%s+','')end or noop -local trim_back = trim and function(s)return s:gsub('%s+$','')end or noop - -if next(options) then - if not quiet then - printUsage(stderr, 'unexpected option: '..next(options)) - return 2 - end - return 0 -end --- Resolve the location of a file, without searching the path -local function resolve(file) - if file:sub(1,1) == '/' then - return fs.canonical(file) - else - if file:sub(1,2) == './' then - file = file:sub(3, -1) - end - return fs.canonical(fs.concat(shell.getWorkingDirectory(), file)) - end -end - ---- Builds a case insensitive patterns, code from stackoverflow ---- (questions/11401890/case-insensitive-lua-pattern-matching) -if ignore_case then - for i=1,#PATTERNS do - -- find an optional '%' (group 1) followed by any character (group 2) - PATTERNS[i] = PATTERNS[i]:gsub("(%%?)(.)", function(percent, letter) - if percent ~= "" or not letter:match("%a") then - -- if the '%' matched, or `letter` is not a letter, return "as is" - return percent .. letter - else -- case-insensitive - return string.format("[%s%s]", letter:lower(), letter:upper()) - end - end) - end -end - -local function getAllFiles(dir, file_list) - for node in fs.list(shell.resolve(dir)) do - local rel_path = dir:gsub("/+$","") .. '/' .. node - local resolved_path = shell.resolve(rel_path) - if fs.isDirectory(resolved_path) then - getAllFiles(rel_path, file_list) - else - file_list[#file_list+1] = rel_path - end - end -end - -if search_recursively then - local files = {} - for i,arg in ipairs(FILES) do - if fs.isDirectory(arg) then - getAllFiles(arg, files) - else - files[#files+1]=arg - end - end - FILES=files -end - --- Prepare an iterator for reading files -local function readLines() - local curHand = nil - local curFile = nil - local meta = nil - return function() - if not curFile then - local file = table.remove(FILES, 1) - if not file then - return - end - meta = {line_num=0,hits=0} - if file == "-" then - curFile = file - meta.label = stdin_label - curHand = io.input() - else - meta.label = file - local file, reason = resolve(file) - if fs.exists(file) then - curHand = io.open(file, 'r') - if not curHand then - local msg = string.format("failed to read from %s: %s", meta.label, reason) - stderr:write("grep: ",msg,"\n") - return false, 2 - else - curFile = meta.label - end - else - stderr:write("grep: ",file,": file not found\n") - return false, 2 - end - end - end - meta.line = nil - if not meta.close and curHand then - meta.line_num = meta.line_num + 1 - meta.line = curHand:read("*l") - end - if not meta.line then - curFile = nil - if curHand then - curHand:close() - end - return false, meta - else - return meta, curFile - end - end -end - -local function write(part, color) - local prev_color = color and getc() - if color then setc(color) end - io.write(part) - if color then setc(prev_color) end -end -local flush=(f_only or no_only or print_count) and function(m) - if no_only and m.hits == 0 or f_only and m.hits ~= 0 then - write(m.label, LABEL_COLOR) - write('\n') - elseif print_count then - if include_filename then - write(m.label, LABEL_COLOR) - write(':', COLON_COLOR) - end - write(m.hits) - write('\n') - end -end -local ec = nil -local any_hit_ec = 1 -local function test(m,p) - local empty_line = true - local last_index, slen = 1, #m.line - local needs_filename, needs_line_num = include_filename, print_line_num - local hit_value = 1 - while last_index <= slen and not m.close do - local i, j = m.line:find(p, last_index, plain) - local word_fail, line_fail = - match_whole_word and not (i and not (m.line:sub(i-1,i-1)..m.line:sub(j+1,j+1)):find("[%a_]")), - match_whole_line and not (i==1 and j==slen) - local matched = not ((m_only or last_index==1) and not i) - if (hit_value == 1 and word_fail) or line_fail then - matched,i,j = false - end - if invert_match == matched then break end - if max_matches == 0 then os.exit(1) end - any_hit_ec = 0 - m.hits, hit_value = m.hits + hit_value, 0 - if max_matches == m.hits or f_only or no_only then - m.close = true - end - if flush or quiet then return end - if needs_filename then - write(m.label, LABEL_COLOR) - write(':', COLON_COLOR) - needs_filename = nil - end - if needs_line_num then - write(m.line_num, LINE_NUM_COLOR) - write(':', COLON_COLOR) - needs_line_num = nil - end - local s=m_only and '' or m.line:sub(last_index,(i or 0)-1) - local g=i and m.line:sub(i,j) or '' - if i==1 then g=trim_front(g) elseif last_index==1 then s=trim_front(s) end - if j==slen then g=trim_back(g) elseif not i then s=trim_back(s) end - write(s) - write(g, MATCH_COLOR) - empty_line = false - last_index = (j or slen)+1 - if m_only or last_index>slen then - write("\n") - empty_line = true - needs_filename, needs_line_num = include_filename, print_line_num - elseif p:find("^^") then break end - end - if not empty_line then write("\n") end -end -for meta,status in readLines() do - if not meta then - if type(status) == 'table' then if flush then - flush(status) end -- this was the last object, closing out - elseif status then - ec = status or ec - end - else - for _,p in ipairs(PATTERNS) do - test(meta,p) - end - end -end - -return ec or any_hit_ec -F bin/head.lua local shell = require("shell") -local fs = require("filesystem") - -local args, options = shell.parse(...) - -local function pop(key, convert) - local result = options[key] - options[key] = nil - if result and convert then - local c = convert(result) - if not c then - error('invalid ' .. key .. ': could not convert ' .. result) - end - result = c - end - return result -end - -local bytes = pop('bytes', tonumber) -local lines = pop('lines', tonumber) -local quiet = {pop('q'), pop('quiet'), pop('silent')} -quiet = quiet[1] or quiet[2] or quiet[3] -local verbose = {pop('v'), pop('verbose')} -verbose = verbose[1] or verbose[2] -local help = pop('help') -local invalid_key = next(options) - -if bytes and lines then - invalid_key = 'bytes and lines both specified' -end - -if help or next(options) then - local invalid_key = next(options) - if invalid_key then - invalid_key = string.format('invalid option: %s\n', invalid_key) - else - invalid_key = '' - end - print(invalid_key .. [[Usage: head [--lines=n] file -Print the first 10 lines of each FILE to stdout. -For more info run: man head]]) - os.exit() -end - -if #args == 0 then - args = {'-'} -end - -if quiet and verbose then - quiet = false -end - -local function new_stream() - return - { - open=true, - capacity=math.abs(lines or bytes or 10), - bytes=bytes, - buffer=(lines and lines < 0 and {}) or (bytes and bytes < 0 and '') - } -end - -local function close(stream) - if stream.buffer then - if type(stream.buffer) == 'table' then - stream.buffer = table.concat(stream.buffer) - end - io.stdout:write(stream.buffer) - stream.buffer = nil - end - stream.open = false -end - -local function push(stream, line) - if not line then - return close(stream) - end - - local cost = stream.bytes and line:len() or 1 - stream.capacity = stream.capacity - cost - - if not stream.buffer then - if stream.bytes and stream.capacity < 0 then - line = line:sub(1,stream.capacity-1) - end - io.write(line) - if stream.capacity <= 0 then - return close(stream) - end - else - if type(stream.buffer) == 'table' then -- line storage - stream.buffer[#stream.buffer+1] = line - if stream.capacity < 0 then - table.remove(stream.buffer, 1) - stream.capacity = 0 -- zero out - end - else -- byte storage - stream.buffer = stream.buffer .. line - if stream.capacity < 0 then - stream.buffer = stream.buffer:sub(-stream.capacity+1) - stream.capacity = 0 -- zero out - end - end - end - -end - -for i=1,#args do - local arg = args[i] - local file - if arg == '-' then - arg = 'standard input' - file = setmetatable({close=function()end},{__index=io.stdin}) - else - file, reason = io.open(arg, 'r') - if not file then - io.stderr:write(string.format([[head: cannot open '%s' for reading: %s]], arg, reason)) - end - end - if file then - if verbose or #args > 1 then - io.write(string.format('==> %s <==', arg)) - end - - local stream = new_stream() - - while stream.open do - push(stream, file:read('*L')) - end - - file:close() - end -end -Fbin/hostname.lualocal args = {...} -if args[1] then - local file, reason = io.open("/etc/hostname", "w") - if not file then - io.stderr:write(reason .. "\n") - return 1 - else - file:write(args[1]) - file:close() - os.setenv("HOSTNAME", args[1]) - os.setenv("PS1", "$HOSTNAME:$PWD# ") - end -else - local file = io.open("/etc/hostname") - if file then - io.write(file:read("*l"), "\n") - file:close() - else - io.stderr:write("Hostname not set\n") - return 1 - end -end -Fbin/install.lua -alocal component = require("component") -local computer = require("computer") -local event = require("event") -local filesystem = require("filesystem") -local unicode = require("unicode") -local shell = require("shell") - -local args, options = shell.parse(...) - -local fromAddress = options.from and component.get(options.from) or filesystem.get(os.getenv("_")).address -local candidates = {} -for address in component.list("filesystem", true) do - local dev = component.proxy(address) - if not dev.isReadOnly() and dev.address ~= computer.tmpAddress() and dev.address ~= fromAddress then - table.insert(candidates, dev) - end -end - -if #candidates == 0 then - io.stderr:write("No writable disks found, aborting.\n") - return 1 -end - -for i = 1, #candidates do - local label = candidates[i].getLabel() - if label then - label = label .. " (" .. candidates[i].address:sub(1, 8) .. "...)" - else - label = candidates[i].address - end - io.write(i .. ") " .. label .. "\n") -end - -io.write("To select the device to install to, please enter a number between 1 and " .. #candidates .. ".\n") -io.write("Press 'q' to cancel the installation.\n") -local choice -while not choice do - result = io.read() - if result:sub(1, 1):lower() == "q" then - os.exit() - end - local number = tonumber(result) - if number and number > 0 and number <= #candidates then - choice = candidates[number] - else - io.write("Invalid input, please try again.\n") - end -end - -local function findMount(address) - for fs, path in filesystem.mounts() do - if fs.address == component.get(address) then - return path - end - end -end - -local name = options.name or "OpenOS" -io.write("Installing " .. name .." to device " .. (choice.getLabel() or choice.address) .. "\n") -os.sleep(0.25) -local cpPath = filesystem.concat(findMount(filesystem.get(os.getenv("_")).address), "bin/cp") -local cpOptions = "-vrx" .. (options.u and "ui " or "") -local cpSource = filesystem.concat(findMount(fromAddress), options.fromDir or "/") -local cpDest = findMount(choice.address) .. "/" -local result, reason = os.execute(cpPath .. " " .. cpOptions .. " " .. cpSource .. " " .. cpDest) -if not result then - error(reason, 0) -end -if not options.nolabelset then pcall(choice.setLabel, name) end - -if not options.noreboot then - io.write("All done! " .. ((not options.noboot) and "Set as boot device and r" or "R") .. "eboot now? [Y/n]\n") - local result = io.read() - if not result or result == "" or result:sub(1, 1):lower() == "y" then - if not options.noboot then computer.setBootAddress(choice.address)end - io.write("\nRebooting now!\n") - computer.shutdown(true) - end -end -io.write("Returning to shell.\n") -F bin/label.lualocal fs = require("filesystem") -local shell = require("shell") - -local args, options = shell.parse(...) -if #args < 1 then - io.write("Usage: label [-a] [