Advertisement
LDDestroier

PAIN (computercraft)

Apr 5th, 2016
4,480
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 101.73 KB | None | 0 0
  1. --[[
  2.     PAIN image editor for ComputerCraft
  3.     Get it with
  4.      wget https://raw.githubusercontent.com/LDDestroier/CC/master/pain.lua pain
  5.      pastebin get wJQ7jav0 pain
  6.      std ld pain pain
  7. --]]
  8.  
  9. local plc = {} -- pain local, to avoid upvalue limit
  10. plc.askToSerialize = false
  11. plc.defaultSaveFormat = 4 -- will change if importing image, or making new file with extension in name
  12. --[[
  13.     plc.defaultSaveFormat possible parameters:
  14.     1. NFP (paint)
  15.     2. NFT (npaintpro)
  16.     3. BLT (blittle)
  17.     4. Native PAIN
  18.     5. GIF
  19.     6. UCG
  20. --]]
  21.  
  22. plc.progname = fs.getName(shell.getRunningProgram())
  23. plc.apipath = ".painapi"
  24.  
  25. local painconfig = {
  26.     undoBufferSize = 8,         -- amount of times undo will save your neck
  27.     readNonImageAsNFP = true,   -- reads non-image files as NFP images
  28.     useFlattenGIF = true,       -- will flatten compressed GIFs
  29.     gridBleedThrough = false,   -- will draw grid instead of character value of dots
  30.     doFillDiagonal = false,     -- checks for diagonal dots when using fill tool
  31.     doFillAnimation = false,    -- whether or not to animate the fill tool
  32.     useSetVisible = false,      -- whether or not to use term.current().setVisible, if possible
  33. }
  34.  
  35. local useConfig = function(mode)
  36.     if mode == "save" then
  37.         local file = fs.open(fs.combine(plc.apipath,"painconfig"), "w")
  38.         file.write(textutils.serialize(painconfig))
  39.         file.close()
  40.     elseif mode == "load" then
  41.         if fs.exists(fs.combine(plc.apipath,"painconfig")) then
  42.             local file = fs.open(fs.combine(plc.apipath,"painconfig"), "r")
  43.             painconfig = textutils.unserialize(file.readAll())
  44.             file.close()
  45.         end
  46.     end
  47. end
  48.  
  49. useConfig("load")
  50. useConfig("save")
  51.  
  52. plc.displayHelp = function()
  53.     print(plc.progname)
  54.     print(plc.progname.." <filename>")
  55.     print(plc.progname.." [-h/--help]")
  56.     print("Press F1 in program for more.")
  57. end
  58.  
  59. plc.tsv = function(visible)
  60.     if term.current().setVisible and painconfig.useSetVisible then
  61.         term.current().setVisible(visible)
  62.     end
  63. end
  64.  
  65. --local undoBuffer
  66. plc.undoPos = 1
  67. plc.pMode = 0
  68. local scr_x, scr_y = term.getSize()
  69. local screenEdges = {
  70.     scr_x,
  71.     scr_y,
  72. }
  73.  
  74. plc.tArg = {...}
  75. if (plc.tArg[1] == "--help" or plc.tArg[1] == "-h") and shell then
  76.     return plc.displayHelp()
  77. end
  78.  
  79. if plc.tArg[2] == "view" then
  80.     plc.pMode = 1
  81. elseif (plc.tArg[2] == "moo") and (not fs.exists("moo")) then
  82.     return print("This PAIN does not have Super Cow Powers.")
  83. end
  84.  
  85. -- plc.fileName
  86. if (not term.isColor()) and (plc.pMode ~= 1) then
  87.     error("PAIN only works with Advanced Computers.")
  88. end
  89.  
  90. local barmsg = "Press F1 for help."
  91. local paintEncoded
  92. local lastPaintEncoded
  93. local frame = 1
  94. local doRender = false
  95. local metaHistory = {}
  96. local firstTerm, blittleTerm = term.current()
  97. plc.bepimode = false      -- this is a family-friendly program! now stand still while I murder you
  98. plc.evenDrawGrid = true   -- will you evenDraw(the)Grid ?
  99. plc.renderBlittle = false -- whether or not to render all in blittle
  100. plc.firstBG = term.getBackgroundColor()
  101. plc.firstTX = term.getTextColor()
  102. plc.changedImage = false
  103. plc.isCurrentlyFilling = false
  104. local theClipboard = {}
  105.  
  106. local _
  107. local tableconcat = table.concat
  108.  
  109. local rendback = {
  110.     b = colors.black,
  111.     t = colors.gray,
  112. }
  113.  
  114. local grid
  115.  
  116. local yield = function()
  117.     os.queueEvent("yield")
  118.     os.pullEvent("yield")
  119. end
  120.  
  121. local paint = {
  122.     scrollX = 0,
  123.     scrollY = 0,
  124.     t = colors.gray,
  125.     b = colors.white,
  126.     m = 1,  --  in case you want to use PAIN as a level editor or something
  127.     c = " ",
  128.     doGray = false,
  129. }
  130. local boxchar = {topLeft = true, topRight = true, left = true, right = true, bottomLeft = true, bottomRight = true}
  131. local swapColors = false -- swaps background and text colors, for use with those tricky box characters
  132. local scrollX, scrollY = 0, 0
  133.  
  134. local keysDown = {}
  135. local miceDown = {}
  136.  
  137. local doRenderBar = 1 -- Not true or false
  138.  
  139. local fixstr = function(str)
  140.     return str:gsub("\\(%d%d%d)",string.char)
  141. end
  142.  
  143. local choice = function(input,breakkeys,returnNumber)
  144.     local fpos = 0
  145.     repeat
  146.         event, key = os.pullEvent("key")
  147.         if type(key) == "number" then key = keys.getName(key) end
  148.         if key == nil then key = " " end
  149.         if type(breakkeys) == "table" then
  150.             for a = 1, #breakkeys do
  151.                 if key == breakkeys[a] then
  152.                     return ""
  153.                 end
  154.             end
  155.         end
  156.         fpos = string.find(input, key)
  157.     until fpos
  158.     return returnNumber and fpos or key
  159. end
  160. local explode = function(div,str)
  161.     if (div=='') then return false end
  162.     local pos,arr = 0,{}
  163.     for st,sp in function() return string.find(str,div,pos,true) end do
  164.         arr[#arr+1] = str:sub(pos,st-1)
  165.         pos = sp + 1
  166.     end
  167.     arr[#arr+1] = str:sub(pos)
  168.     return arr
  169. end
  170.  
  171. local cutString = function(max_line_length, str) -- from stack overflow
  172.    local lines = {}
  173.    local line
  174.    str:gsub('(%s*)(%S+)',
  175.       function(spc, word)
  176.          if not line or #line + #spc + #word > max_line_length then
  177.             lines[#lines+1] = line
  178.             line = word
  179.          else
  180.             line = line..spc..word
  181.          end
  182.       end
  183.    )
  184.    lines[#lines+1] = line
  185.    return lines
  186. end
  187.  
  188. local getDrawingCharacter = function(topLeft, topRight, left, right, bottomLeft, bottomRight) -- thank you oli414
  189.   local data = 128
  190.   if not bottomRight then
  191.         data = data + (topLeft and 1 or 0)
  192.         data = data + (topRight and 2 or 0)
  193.         data = data + (left and 4 or 0)
  194.         data = data + (right and 8 or 0)
  195.         data = data + (bottomLeft and 16 or 0)
  196.   else
  197.         data = data + (topLeft and 0 or 1)
  198.         data = data + (topRight and 0 or 2)
  199.         data = data + (left and 0 or 4)
  200.         data = data + (right and 0 or 8)
  201.         data = data + (bottomLeft and 0 or 16)
  202.   end
  203.   return {char = string.char(data), inverted = bottomRight}
  204. end
  205.  
  206. local cutUp = function(len,tbl)
  207.     local output = {}
  208.     local e = 0
  209.     local s
  210.     for a = 1, #tbl do
  211.         if #(tbl[a]:gsub(" ","")) == 0 then
  212.             s = {""}
  213.         else
  214.             s = cutString(len,tbl[a])
  215.         end
  216.         for b = 1, #s do
  217.             output[#output+1] = s[b]
  218.         end
  219.     end
  220.     return output
  221. end
  222.  
  223. local getEvents = function(...)
  224.     local arg, output = table.pack(...)
  225.     while true do
  226.         output = {os.pullEvent()}
  227.         for a = 1, #arg do
  228.             if type(arg[a]) == "boolean" then
  229.                 if doRender == arg[a] then
  230.                     return {}
  231.                 end
  232.             elseif output[1] == arg[a] then
  233.                 return table.unpack(output)
  234.             end
  235.         end
  236.     end
  237. end
  238.  
  239.  
  240.  
  241. local sanitize = function(sani,tize)
  242.     local _,x = string.find(sani,tize)
  243.     if x then
  244.         return sani:sub(x+1)
  245.     else
  246.         return sani
  247.     end
  248. end
  249. local ro = function(input, max)
  250.     return math.floor(input % max)
  251. end
  252.  
  253. local guiHelp = function(inputText)
  254.     term.redirect(firstTerm)
  255.     scr_x, scr_y = term.current().getSize()
  256.     local _helpText = inputText or [[
  257.  
  258. 'PAIN' super-verbose help page
  259.   Programmed by LDDestroier
  260.  
  261. (use UP/DOWN or scrollwheel, exit with Q)
  262. If you wish to use PAIN to its fullest, read everything here.
  263. You'll be image-editing like a pro in no time flat.
  264.  
  265. Syntax:
  266. >pain <filename> [view] [x] [y]
  267. >pain [-n]
  268. >pain [-h/--help]
  269.  
  270. [view]: renders the image once (optionally scrolling with [x] and [y])
  271. "-n" or no arguments: Create new document, declare name upon saving
  272. "-h" or "--help": Display short syntax help
  273.  
  274. You can see what colors are selected based on the word "PAIN" on the hotbar.
  275.  
  276. Hotkeys:
  277. left/right ctrl: Toggle the menu
  278.  
  279. left click:
  280.  +left shift = Drag and let go to draw a line
  281.  -alone      = Place a dot
  282.  
  283. Right Click: delete pixel
  284.  
  285. Middle Click, or "T": Place text down with current colors; cancel with X
  286.  
  287. "Z":
  288.  +LeftAlt = Redo
  289.  -alone   = Undo
  290.  
  291. "P": Pick colors from position onscreen; cancel with X
  292.  
  293. "N":
  294.  +LeftShift = Change character to that of a special character
  295.  -alone     = Change box character for drawing
  296.  (cancel with CTRL, N, or by clicking outside)
  297.  
  298. "[" or mouse scroll down:
  299.  +LeftShift = Change to previous text color
  300.  -alone     = Change to previous background color
  301.  
  302. "]" or mouse scroll up:
  303.  +LeftShift = Change to next text color
  304.  -alone     = Change to next background color
  305.  
  306. "F1":
  307.  -alone = Access help screen
  308.  
  309. "F3:"
  310.  -alone = View all connected monitors
  311.  
  312. Spacebar:
  313.  +LeftShift = Toggle background grid
  314.  -alone     = Toggle bar visibility
  315.  
  316. Arrow keys:
  317.  +LeftShift = Displaces the entire frame
  318.  +Tab       = Moves canvas one pixel at a time
  319.  -alone     = Looks around the canvas smoothly
  320.  
  321. "+" (or equals):
  322.  +LeftAlt    = Swap the current frame with the next frame
  323.  +LeftShift  = Merge the current frame atop the next frame
  324.  +RightShift = If you are making a new frame, duplicates the last frame
  325.  -alone      = Change to next frame
  326.  
  327. "-":
  328.  +LeftAlt   = Swap the current frame with the previous frame
  329.  +LeftShift = Merge the current frame atop the previous frame
  330.  -alone     = Change to previous frame
  331.  
  332. (oh good, you're actually reading this stuff)
  333.  
  334.  "A": Set the coordinates to 0,0
  335.  
  336.  "B": Toggle redirect to blittle, to preview in teletext characters
  337.  
  338.  "c":
  339.   +LeftAlt = Select region to copy to specified clipboard
  340.     -alone   = Input coordinates to scroll over to
  341.  
  342.  "LeftAlt + X": Select region to cut to specified clipboard
  343.  
  344.  "LeftAlt + X": Pastes from specified clipboard
  345.  
  346.  "G": toggle grayscale mode.
  347.   Everything is in shades of gray.
  348.   If you Save, it saves in grayscale.
  349.  
  350.  "F":
  351.   +LeftShift = fill all empty pixels with background color and selected box character
  352.   -alone     = activate fill tool - click anywhere to fill with color
  353.  
  354.  "M": set metadata for pixels (for game makers, otherwise please ignore)
  355.  
  356. ==================================
  357.  Thy Menu (accessible with CTRL):
  358. ==================================
  359.  
  360.  Left click on a menu item to select it.
  361.  If you click on the menubar, release on an option to select it.
  362.  
  363.  "File > Save"
  364.  Saves all frames to a specially formatted PAIN paint file. The format PAIN uses is very inefficient despite my best efforts, so Export if you don't use text or multiple frame.
  365.  
  366. "File > Save As"
  367. Same as "File > Save", but you change the filename.
  368.  
  369. "File > Export"
  370. Exports current frame to NFP, NFT, BLT, or the horribly inefficient PAIN format.
  371.  
  372. "File > Open"
  373. Opens up a file picker for you to change the image currently being edited.
  374.  
  375. "Edit > Delete Frame"
  376. Deletes the current frame. Tells you off if you try to delete the only frame.
  377.  
  378. "Edit > Clear"
  379. Deletes all pixels on the current frame.
  380.  
  381. "Edit > Crop Frame"
  382. Deletes all pixels that are outside of the screen.
  383.  
  384. "Edit > Change Box Character"
  385. Opens the block character selection. Used for making those delicious subpixel pictures.
  386.  
  387. "Edit > Change Special Character"
  388. Opens the special character selector, which lets you change the paint character to that of byte 0 to 255.
  389.  
  390. "Edit > BLittle Shrink"
  391. Shrinks the current frame using the BLittle API. Very lossy unless you use one color, or are careful with how you use colors. You can set "Always Render Grid" to true to assist in making blocky graphics.
  392.  
  393. "Edit > BLittle Grow"
  394. Grows the image by (2x, 3y) to reverse the effects of "BLittle Shrink". This isn't lossy, since all it does is inflate an image's size and converts the corresponding block characters.
  395.  
  396. "Edit > Copy"
  397. Drag to select a region of the screen, and save it in a clipboard of a specified name.
  398.  
  399. "Edit > Cut"
  400. Same as Copy, but will delete the selected region on the screen.
  401.  
  402. "Edit > Paste"
  403. Takes the contents of the specified clipboard, and plops it on the canvas where the mouse is.
  404. (The mouse will indicate the top-left corner of the pasted selection)
  405.  
  406. "Set > ..."
  407. Each option will toggle a config option (or set it's value to something else).
  408.  Changing a value is saved automatically, and effective immediately.
  409.  
  410.  "Window > Set Screen Size"
  411.  Sets the sizes of the screen border references displayed on the canvas.
  412.  You can also input the name of a monitor object, and it will use its size instead.
  413.  
  414.  "Window > Set Grid Colors"
  415.  Sets the backdrop colors to your currently selected color configuration.
  416.  
  417.  "About > PAIN"
  418.  Tells you about PAIN and its developer.
  419.  
  420.  "About > File Formats"
  421.  Tells you the ins and outs of the file formats, and a brief description of their creators.
  422.  
  423.  "About > Help"
  424.  Opens up this help page.
  425.  
  426.  "Exit"
  427.  Closes PAIN. I know, riviting stuff. You can close out of this help page with "Q", speaking of.
  428.  
  429.  
  430. I hope my PAIN causes you joy.
  431. ]]
  432.     _helpText = explode("\n",_helpText)
  433.     helpText = cutUp(scr_x,_helpText)
  434.     local helpscroll = 0
  435.     term.setBackgroundColor(colors.gray)
  436.     term.setTextColor(colors.white)
  437.     term.clear()
  438.     local evt, key
  439.     while true do
  440.         term.clear()
  441.         for a = 1, scr_y do
  442.             term.setCursorPos(1,a)
  443.             term.clearLine()
  444.             write(helpText[a-helpscroll] or "")
  445.         end
  446.         repeat
  447.             evt,key = os.pullEvent()
  448.         until evt == "key" or evt == "mouse_scroll"
  449.         if evt == "key" then
  450.             if key == keys.up then
  451.                 helpscroll = helpscroll + 1
  452.             elseif key == keys.down then
  453.                 helpscroll = helpscroll - 1
  454.             elseif key == keys.pageUp then
  455.                 helpscroll = helpscroll + scr_y
  456.             elseif key == keys.pageDown then
  457.                 helpscroll = helpscroll - scr_y
  458.             elseif (key == keys.q) or (key == keys.space) then
  459.                 doRender = true
  460.                 if plc.renderBlittle then term.redirect(blittleTerm) end
  461.                 scr_x, scr_y = term.current().getSize()
  462.                 return
  463.             end
  464.         elseif evt == "mouse_scroll" then
  465.             helpscroll = helpscroll - key
  466.         end
  467.         if helpscroll > 0 then
  468.             helpscroll = 0
  469.         elseif helpscroll < -(#helpText-(scr_y-3)) then
  470.             helpscroll = -(#helpText-(scr_y-3))
  471.         end
  472.     end
  473. end
  474.  
  475. local tableRemfind = function(tbl, str)
  476.     local out = tbl
  477.     for a = 1, #tbl do
  478.         if tbl[a] == str then
  479.             table.remove(out,a)
  480.             return out,a
  481.         end
  482.     end
  483.     return {}
  484. end
  485.  
  486. local stringShift = function(str,amt)
  487.     return str:sub(ro(amt-1,#str)+1)..str:sub(1,ro(amt-1,#str))
  488. end
  489.  
  490. local deepCopy
  491. deepCopy = function(obj)
  492.     if type(obj) ~= 'table' then return obj end
  493.     local res = {}
  494.     for k, v in pairs(obj) do res[deepCopy(k)] = deepCopy(v) end
  495.     return res
  496. end
  497.  
  498. local clearLines = function(y1, y2)
  499.     local cx,cy = term.getCursorPos()
  500.     for y = y1, y2 do
  501.         term.setCursorPos(1,y)
  502.         term.clearLine()
  503.     end
  504.     term.setCursorPos(cx,cy)
  505. end
  506.  
  507. local renderBottomBar = function(txt,extraClearY)
  508.     term.setCursorPos(1,scr_y - math.floor(#txt/scr_x))
  509.     term.setBackgroundColor(colors.lightGray)
  510.     term.setTextColor(colors.black)
  511.     clearLines(scr_y - (math.floor(#txt/scr_x) - (extraClearY or 0)), scr_y)
  512.     return write(txt)
  513. end
  514.  
  515. local bottomPrompt = function(txt,history,cho,breakkeys,returnNumber,writeIndent)
  516.     local writeIndent = renderBottomBar(txt,writeIndent)
  517.     local out
  518.     sleep(0)
  519.     if cho then
  520.         out = choice(cho,breakkeys,returnNumber)
  521.     else
  522.         out = read(_,history)
  523.     end
  524.     return out, writeIndent
  525. end
  526.  
  527. local makeSubMenu = function(x,y,options)
  528.     local longestLen = 0
  529.     for a = 1, #options do
  530.         if #options[a] > longestLen then
  531.             longestLen = #options[a]
  532.         end
  533.     end
  534.     longestLen = longestLen + 1
  535.     term.setTextColor(colors.black)
  536.     local sel = 1
  537.     local rend = function()
  538.         for a = #options, 1, -1 do
  539.             term.setCursorPos(x or 1, ((y or (scr_y-1)) - (#options-1)) + (a - 1))
  540.             term.setBackgroundColor(a == sel and colors.white or colors.lightGray)
  541.             term.write(options[a])
  542.             term.setBackgroundColor(colors.lightGray)
  543.             term.write((" "):rep(longestLen-#options[a]))
  544.         end
  545.     end
  546.     local usingMouse = false
  547.     while true do
  548.         rend()
  549.         local evt, key, mx, my = os.pullEvent()
  550.         if evt == "key" then
  551.             if key == keys.up then
  552.                 sel = sel - 1
  553.             elseif key == keys.down then
  554.                 sel = sel + 1
  555.             elseif (key == keys.enter) or (key == keys.right) then
  556.                 return sel, longestLen
  557.             elseif (key == keys.leftCtrl) or (key == keys.rightCtrl) or (key == keys.backspace) or (key == keys.left) then
  558.                 return false, longestLen
  559.             end
  560.         elseif evt == "mouse_drag" or evt == "mouse_click" then
  561.             if (mx >= x) and (mx < x+longestLen) and (my <= y and my > y-#options) then
  562.                 sel = math.min(#options,math.max(1,(my+#options) - y))
  563.                 usingMouse = true
  564.             else
  565.                 usingMouse = false
  566.                 if evt == "mouse_click" then
  567.                     return false, longestLen
  568.                 end
  569.             end
  570.         elseif evt == "mouse_up" then
  571.             if usingMouse then
  572.                 return sel, longestLen
  573.             end
  574.         end
  575.         if sel > #options then sel = 1 elseif sel < 1 then sel = #options end
  576.     end
  577. end
  578.  
  579. local getDotsInLine = function( startX, startY, endX, endY ) -- stolen from the paintutils API...nwehehehe
  580.     local out = {}
  581.     startX = math.floor(startX)
  582.     startY = math.floor(startY)
  583.     endX = math.floor(endX)
  584.     endY = math.floor(endY)
  585.     if startX == endX and startY == endY then
  586.         out = {{x=startX,y=startY}}
  587.         return out
  588.     end
  589.     local minX = math.min( startX, endX )
  590.     if minX == startX then
  591.         minY = startY
  592.         maxX = endX
  593.         maxY = endY
  594.     else
  595.         minY = endY
  596.         maxX = startX
  597.         maxY = startY
  598.     end
  599.     local xDiff = maxX - minX
  600.     local yDiff = maxY - minY
  601.     if xDiff > math.abs(yDiff) then
  602.         local y = minY
  603.         local dy = yDiff / xDiff
  604.         for x=minX,maxX do
  605.             out[#out+1] = {x=x,y=math.floor(y+0.5)}
  606.             y = y + dy
  607.         end
  608.     else
  609.         local x = minX
  610.         local dx = xDiff / yDiff
  611.         if maxY >= minY then
  612.             for y=minY,maxY do
  613.                 out[#out+1] = {x=math.floor(x+0.5),y=y}
  614.                 x = x + dx
  615.             end
  616.         else
  617.             for y=minY,maxY,-1 do
  618.                 out[#out+1] = {x=math.floor(x+0.5),y=y}
  619.                 x = x - dx
  620.             end
  621.         end
  622.     end
  623.     return out
  624. end
  625.  
  626. local movePaintEncoded = function(pe,xdiff,ydiff)
  627.     local outpootis = deepCopy(pe)
  628.     for a = 1, #outpootis do
  629.         outpootis[a].x = outpootis[a].x+xdiff
  630.         outpootis[a].y = outpootis[a].y+ydiff
  631.     end
  632.     return outpootis
  633. end
  634.  
  635. local clearRedundant = function(dots)
  636.     local input = {}
  637.     local pheight = 0
  638.     local pwidth = 0
  639.     local minX, minY = 0, 0
  640.     for a = 1, #dots do
  641.         pheight = math.max(pheight, dots[a].y)
  642.         pwidth = math.max(pwidth, dots[a].x)
  643.         minX = math.min(minX, dots[a].x)
  644.         minY = math.min(minY, dots[a].y)
  645.     end
  646.     for a = 1, #dots do
  647.         if not input[dots[a].y] then input[dots[a].y] = {} end
  648.         input[dots[a].y][dots[a].x] = dots[a]
  649.     end
  650.     local output = {}
  651.     local frame = 0
  652.     for y = minY, pheight do
  653.         for x = minX, pwidth do
  654.             if input[y] then
  655.                 if input[y][x] then
  656.                     output[#output+1] = input[y][x]
  657.                 end
  658.             end
  659.             if frame >= 50 then
  660.                 -- yield()
  661.                 frame = 0
  662.             end
  663.         end
  664.     end
  665.     return output
  666. end
  667.  
  668. local grayOut = function(color)
  669.     local c = deepCopy(_G.colors)
  670.     local grays = {
  671.         [c.white] = c.white,
  672.         [c.orange] = c.lightGray,
  673.         [c.magenta] = c.lightGray,
  674.         [c.lightBlue] = c.lightGray,
  675.         [c.yellow] = c.white,
  676.         [c.lime] = c.lightGray,
  677.         [c.pink] = c.lightGray,
  678.         [c.gray] = c.gray,
  679.         [c.lightGray] = c.lightGray,
  680.         [c.cyan] = c.lightGray,
  681.         [c.purple] = c.gray,
  682.         [c.blue] = c.gray,
  683.         [c.brown] = c.gray,
  684.         [c.green] = c.lightGray,
  685.         [c.red] = c.gray,
  686.         [c.black] = c.black,
  687.     }
  688.     if (not color) or (color == " ") then return color end
  689.     local newColor = grays[color] or 1
  690.     return newColor
  691. end
  692.  
  693. local getOnscreenCoords = function(tbl,_x,_y)
  694.     local screenTbl = {}
  695.     for a = 1, #tbl do
  696.         if tbl[a].x+paint.scrollX > 0 and tbl[a].x+paint.scrollX <= scr_x then
  697.             if tbl[a].y+paint.scrollY > 0 and tbl[a].y+paint.scrollY <= scr_y then
  698.                 screenTbl[#screenTbl+1] = {tbl[a].x+paint.scrollX,tbl[a].y+paint.scrollY}
  699.             end
  700.         end
  701.     end
  702.     if not _x and _y then
  703.         return screenTbl
  704.     else
  705.         for a = 1, #screenTbl do
  706.             if screenTbl[a][1] == _x and screenTbl[a][2] == _y then
  707.                 return true
  708.             end
  709.         end
  710.         return false
  711.     end
  712. end
  713.  
  714. local clearAllRedundant = function(info)
  715.     local output = {}
  716.     for a = 1, #info do
  717.         output[a] = clearRedundant(info[a])
  718.         if a % 4 == 0 then yield() end
  719.     end
  720.     return output
  721. end
  722.  
  723. local saveFile = function(path,info)
  724.     local output = clearAllRedundant(info)
  725.     local fileout = textutils.serialize(output):gsub("  ",""):gsub("\n",""):gsub(" = ","="):gsub(",}","}"):gsub("}},{{","}},\n{{")
  726.     if #fileout >= fs.getFreeSpace(fs.getDir(path)) then
  727.         barmsg = "Not enough space."
  728.         return
  729.     end
  730.     local file = fs.open(path,"w")
  731.     file.write(fileout)
  732.     file.close()
  733. end
  734. local renderBar = function(msg,dontSetVisible)
  735.     if (doRenderBar == 0) or plc.renderBlittle then return end
  736.     if not dontSetVisible then plc.tsv(false) end
  737.     term.setCursorPos(1,scr_y)
  738.     term.setBackgroundColor(colors.lightGray)
  739.     term.setTextColor(colors.black)
  740.     term.clearLine()
  741.     term.setBackgroundColor(paint.b or rendback.b)
  742.     term.setTextColor(paint.t or rendback.t)
  743.     term.setCursorPos(2,scr_y)
  744.     term.write("PAIN")
  745.     term.setBackgroundColor(colors.lightGray)
  746.     term.setTextColor(colors.black)
  747.     local fmsg = tableconcat({"Fr:",frame,"/",#paintEncoded," (",paint.scrollX,",",paint.scrollY,")"})
  748.     term.setCursorPos(7,scr_y)
  749.     term.write(msg)
  750.     term.setCursorPos(scr_x-(#fmsg),scr_y)
  751.     term.write(fmsg)
  752.     if not dontSetVisible then plc.tsv(true) end
  753. end
  754.  
  755. local tableFormatPE = function(input)
  756.     local doot = {}
  757.     local pwidths = {}
  758.     local pheight = 0
  759.     for k, dot in pairs(input) do
  760.         pwidths[dot.y] = math.max((pwidths[dot.y] or 0), dot.x)
  761.         pheight = math.max(pheight, dot.y)
  762.         doot[dot.y] = doot[dot.y] or {}
  763.         doot[dot.y][dot.x] = {
  764.             char = dot.c,
  765.             text = CTB(dot.t),
  766.             back = CTB(dot.b)
  767.         }
  768.     end
  769.     for y = 1, pheight do
  770.         pwidths[y] = pwidths[y] or 0
  771.         if doot[y] then
  772.             for x = 1, pwidths[y] do
  773.                 doot[y][x] = doot[y][x] or {
  774.                     text = " ",
  775.                     back = " ",
  776.                     char = " ",
  777.                 }
  778.             end
  779.         else
  780.             doot[y] = false
  781.         end
  782.     end
  783.     return doot, pheight, pwidths
  784. end
  785.  
  786. CTB = function(_color) --Color To Blit
  787.     local blitcolors = {
  788.         [0] = " ",
  789.         [colors.white] = "0",
  790.         [colors.orange] = "1",
  791.         [colors.magenta] = "2",
  792.         [colors.lightBlue] = "3",
  793.         [colors.yellow] = "4",
  794.         [colors.lime] = "5",
  795.         [colors.pink] = "6",
  796.         [colors.gray] = "7",
  797.         [colors.lightGray] = "8",
  798.         [colors.cyan] = "9",
  799.         [colors.purple] = "a",
  800.         [colors.blue] = "b",
  801.         [colors.brown] = "c",
  802.         [colors.green] = "d",
  803.         [colors.red] = "e",
  804.         [colors.black] = "f",
  805.     }
  806.     if _color == nil then return nil end
  807.     return blitcolors[_color] or "f"
  808. end
  809.  
  810. BTC = function(_color,allowZero) --Blit To Color
  811.     local blitcolors = {
  812.         [" "] = allowZero and 0 or nil,
  813.         ["0"] = colors.white,
  814.         ["1"] = colors.orange,
  815.         ["2"] = colors.magenta,
  816.         ["3"] = colors.lightBlue,
  817.         ["4"] = colors.yellow,
  818.         ["5"] = colors.lime,
  819.         ["6"] = colors.pink,
  820.         ["7"] = colors.gray,
  821.         ["8"] = colors.lightGray,
  822.         ["9"] = colors.cyan,
  823.         ["a"] = colors.purple,
  824.         ["b"] = colors.blue,
  825.         ["c"] = colors.brown,
  826.         ["d"] = colors.green,
  827.         ["e"] = colors.red,
  828.         ["f"] = colors.black,
  829.     }
  830.     if _color == nil then return nil end
  831.     return blitcolors[_color]
  832. end
  833.  
  834. local renderPainyThings = function(xscroll,yscroll,doGrid)
  835.     local yadjust = (plc.renderBlittle and 0 or doRenderBar)
  836.     if plc.bepimode then
  837.         grid = {
  838.             "Bepis",
  839.             "episB",
  840.             "pisBe",
  841.             "isBep",
  842.             "sBepi",
  843.         }
  844.     else
  845.         grid = {
  846.             "%%..",
  847.             "%%..",
  848.             "%%..",
  849.             "..%%",
  850.             "..%%",
  851.             "..%%",
  852.         }
  853.     end
  854.     term.setBackgroundColor(rendback.b)
  855.     term.setTextColor(rendback.t)
  856.     local badchar = "/"
  857.     local blittlelabel = "blittle max"
  858.     local screenlabel = "screen max"
  859.  
  860.     local dotBuffChar, dotBuffBack = "", "" --only used if gridBleedThrough is true
  861.     local doot
  862.     if doGrid then
  863.         for y = 1, scr_y - yadjust do
  864.             term.setCursorPos(1,y)
  865.             -- the single most convoluted line I've ever written that works, and I love it
  866.             term.write(stringShift(grid[ro(y+(yscroll+2),#grid)+1],xscroll+1):rep(math.ceil(scr_x/#grid[ro(y+(yscroll+2),#grid)+1])):sub(1,scr_x))
  867.             term.setCursorPos((xscroll <= 0) and (1-xscroll) or 0,y)
  868.             if ((screenEdges[2]+1)-yscroll) == y then --regular limit
  869.                 term.write( (string.rep("@", math.max(0,( (screenEdges[1])     ) - (#screenlabel+1)  )) ..screenlabel:gsub(" ","@"):upper().."@@"):sub(xscroll>0 and xscroll or 0):sub(1,1+screenEdges[1]) )
  870.             elseif (((screenEdges[2]*3)+1)-yscroll) == y then --blittle limit
  871.                 term.write( (string.rep("@", math.max(0,( ((screenEdges[1]*2))   ) - (#blittlelabel+1) ))..blittlelabel:gsub(" ","@"):upper().."@@"):sub(xscroll>0 and xscroll or 0):sub(1,1+screenEdges[1]*2) )
  872.             end
  873.             -- Stupid easter eggs, ho! --
  874.             if 1000-yscroll == y then
  875.                 term.setCursorPos(1000-xscroll,y)
  876.                 term.write(" What ARE you doing? Stop messing around! ")
  877.             end
  878.             if 2016-yscroll == y then
  879.                 term.setCursorPos(200-xscroll,y)
  880.                 term.write(" Lines don't like to be intersected, you know. ")
  881.             end
  882.             if 2017-yscroll == y then
  883.                 term.setCursorPos(200-xscroll,y)
  884.                 term.write(" It makes them very crossed. ")
  885.             end
  886.             if 800-yscroll == y then
  887.                 term.setCursorPos(1700-xscroll,y)
  888.                 term.write(" You stare deeply into the void. ")
  889.             end
  890.             if 801-yscroll == y then
  891.                 term.setCursorPos(1704-xscroll,y)
  892.                 term.write(" And the void ")
  893.             end
  894.             if 802-yscroll == y then
  895.                 term.setCursorPos(1704-xscroll,y)
  896.                 term.write(" stares back. ")
  897.             end
  898.             --Is this the end?--
  899.             if (xscroll > ((screenEdges[1]*2)-scr_x)) then
  900.                 for y = 1, scr_y do
  901.                     if y+yscroll <= (screenEdges[2]*3) then
  902.                         if not (y == scr_y and doRenderBar == 1) then
  903.                             term.setCursorPos((screenEdges[1]+1)-(xscroll-screenEdges[1]),y)
  904.                             term.write("@")
  905.                         end
  906.                     end
  907.                 end
  908.             end
  909.             if (xscroll > (screenEdges[1]-scr_x)) then --regular limit
  910.                 for y = 1, scr_y do
  911.                     if y+yscroll <= screenEdges[2] then
  912.                         if not (y == scr_y and doRenderBar == 1) then
  913.                             term.setCursorPos((screenEdges[1]+1)-xscroll,y)
  914.                             term.write("@")
  915.                         end
  916.                     end
  917.                 end
  918.             end
  919.         end
  920.         --render areas that won't save
  921.         if xscroll < 0 then
  922.             for y = 1, scr_y do
  923.                 if not (y == scr_y and doRenderBar == 1) then
  924.                     term.setCursorPos(1,y)
  925.                     term.write(badchar:rep(-xscroll))
  926.                 end
  927.             end
  928.         end
  929.         if yscroll < 0 then
  930.             for y = 1, -yscroll do
  931.                 if not (y == scr_y and doRenderBar == 1) then
  932.                     term.setCursorPos(1,y)
  933.                     term.write(badchar:rep(scr_x))
  934.                 end
  935.             end
  936.         end
  937.     else
  938.         for y = 1, scr_y - yadjust do
  939.             term.setCursorPos(1,y)
  940.             term.clearLine()
  941.         end
  942.     end
  943. end
  944.  
  945. importFromPaint = function(theInput)
  946.     local output = {}
  947.     local input
  948.     if type(theInput) == "string" then
  949.         input = explode("\n",theInput)
  950.     else
  951.         input = {}
  952.         for y = 1, #theInput do
  953.             input[y] = ""
  954.             for x = 1, #theInput[y] do
  955.                 input[y] = input[y]..(CTB(theInput[y][x]) or " ")
  956.             end
  957.         end
  958.     end
  959.     for a = 1, #input do
  960.         line = input[a]
  961.         for b = 1, #line do
  962.             if (line:sub(b,b) ~= " ") and BTC(line:sub(b,b)) then
  963.                 output[#output+1] = {
  964.                     x = b,
  965.                     y = a,
  966.                     t = colors.white,
  967.                     b = BTC(line:sub(b,b)) or colors.black,
  968.                     c = " ",
  969.                 }
  970.             end
  971.         end
  972.     end
  973.     return output
  974. end
  975.  
  976. local lddfm = {
  977.     scroll = 0,
  978.     ypaths = {}
  979. }
  980.  
  981. lddfm.scr_x, lddfm.scr_y = term.getSize()
  982.  
  983. lddfm.setPalate = function(_p)
  984.     if type(_p) ~= "table" then
  985.         _p = {}
  986.     end
  987.     lddfm.p = { --the DEFAULT color palate
  988.         bg =        _p.bg or colors.gray,           -- whole background color
  989.         d_txt =     _p.d_txt or colors.yellow,      -- directory text color
  990.         d_bg =      _p.d_bg or colors.gray,         -- directory bg color
  991.         f_txt =     _p.f_txt or colors.white,       -- file text color
  992.         f_bg =      _p.f_bg or colors.gray,         -- file bg color
  993.         p_txt =     _p.p_txt or colors.black,       -- path text color
  994.         p_bg =      _p.p_bg or colors.lightGray,    -- path bg color
  995.         close_txt = _p.close_txt or colors.gray,    -- close button text color
  996.         close_bg =  _p.close_bg or colors.lightGray,-- close button bg color
  997.         scr =       _p.scr or colors.lightGray,     -- scrollbar color
  998.         scrbar =    _p.scrbar or colors.gray,       -- scroll tab color
  999.     }
  1000. end
  1001.  
  1002. lddfm.setPalate()
  1003.  
  1004. lddfm.foldersOnTop = function(floop,path)
  1005.     local output = {}
  1006.     for a = 1, #floop do
  1007.         if fs.isDir(fs.combine(path,floop[a])) then
  1008.             table.insert(output,1,floop[a])
  1009.         else
  1010.             table.insert(output,floop[a])
  1011.         end
  1012.     end
  1013.     return output
  1014. end
  1015.  
  1016. lddfm.filterFileFolders = function(list,path,_noFiles,_noFolders,_noCD,_doHidden)
  1017.     local output = {}
  1018.     for a = 1, #list do
  1019.         local entry = fs.combine(path,list[a])
  1020.         if fs.isDir(entry) then
  1021.             if entry == ".." then
  1022.                 if not (_noCD or _noFolders) then table.insert(output,list[a]) end
  1023.             else
  1024.                 if not ((not _doHidden) and list[a]:sub(1,1) == ".") then
  1025.                     if not _noFolders then table.insert(output,list[a]) end
  1026.                 end
  1027.             end
  1028.         else
  1029.             if not ((not _doHidden) and list[a]:sub(1,1) == ".") then
  1030.                 if not _noFiles then table.insert(output,list[a]) end
  1031.             end
  1032.         end
  1033.     end
  1034.     return output
  1035. end
  1036.  
  1037. lddfm.isColor = function(col)
  1038.     for k,v in pairs(colors) do
  1039.         if v == col then
  1040.             return true, k
  1041.         end
  1042.     end
  1043.     return false
  1044. end
  1045.  
  1046. lddfm.clearLine = function(x1,x2,_y,_bg,_char)
  1047.     local cbg, bg = term.getBackgroundColor()
  1048.     local x,y = term.getCursorPos()
  1049.     local sx,sy = term.getSize()
  1050.     if type(_char) == "string" then char = _char else char = " " end
  1051.     if type(_bg) == "number" then
  1052.         if lddfm.isColor(_bg) then bg = _bg
  1053.         else bg = cbg end
  1054.     else bg = cbg end
  1055.     term.setCursorPos(x1 or 1, _y or y)
  1056.     term.setBackgroundColor(bg)
  1057.     if x2 then --it pains me to add an if statement to something as simple as this
  1058.         term.write((char or " "):rep(x2-x1))
  1059.     else
  1060.         term.write((char or " "):rep(sx-(x1 or 0)))
  1061.     end
  1062.     term.setBackgroundColor(cbg)
  1063.     term.setCursorPos(x,y)
  1064. end
  1065.  
  1066. lddfm.render = function(_x1,_y1,_x2,_y2,_rlist,_path,_rscroll,_canClose,_scrbarY)
  1067.     local px,py = term.getCursorPos()
  1068.     plc.tsv(false)
  1069.     local x1, x2, y1, y2 = _x1 or 1, _x2 or lddfm.scr_x, _y1 or 1, _y2 or lddfm.scr_y
  1070.     local rlist = _rlist or {"Invalid directory."}
  1071.     local path = _path or "And that's terrible."
  1072.     ypaths = {}
  1073.     local rscroll = _rscroll or 0
  1074.     for a = y1, y2 do
  1075.         lddfm.clearLine(x1,x2,a,lddfm.p.bg)
  1076.     end
  1077.     term.setCursorPos(x1,y1)
  1078.     term.setTextColor(lddfm.p.p_txt)
  1079.     lddfm.clearLine(x1,x2+1,y1,lddfm.p.p_bg)
  1080.     term.setBackgroundColor(lddfm.p.p_bg)
  1081.     term.write(("/"..path):sub(1,x2-x1))
  1082.     for a = 1,(y2-y1) do
  1083.         if rlist[a+rscroll] then
  1084.             term.setCursorPos(x1,a+(y1))
  1085.             if fs.isDir(fs.combine(path,rlist[a+rscroll])) then
  1086.                 lddfm.clearLine(x1,x2,a+(y1),lddfm.p.d_bg)
  1087.                 term.setTextColor(lddfm.p.d_txt)
  1088.                 term.setBackgroundColor(lddfm.p.d_bg)
  1089.             else
  1090.                 lddfm.clearLine(x1,x2,a+(y1),lddfm.p.f_bg)
  1091.                 term.setTextColor(lddfm.p.f_txt)
  1092.                 term.setBackgroundColor(lddfm.p.f_bg)
  1093.             end
  1094.             term.write(rlist[a+rscroll]:sub(1,x2-x1))
  1095.             ypaths[a+(y1)] = rlist[a+rscroll]
  1096.         else
  1097.             lddfm.clearLine(x1,x2,a+(y1),lddfm.p.bg)
  1098.         end
  1099.     end
  1100.     local scrbarY = _scrbarY or math.ceil( (y1+1)+( (_rscroll/(#_rlist-(y2-(y1+1))))*(y2-(y1+1)) ) )
  1101.     for a = y1+1, y2 do
  1102.         term.setCursorPos(x2,a)
  1103.         if a == scrbarY then
  1104.             term.setBackgroundColor(lddfm.p.scrbar)
  1105.         else
  1106.             term.setBackgroundColor(lddfm.p.scr)
  1107.         end
  1108.         term.write(" ")
  1109.     end
  1110.     if _canClose then
  1111.         term.setCursorPos(x2-4,y1)
  1112.         term.setTextColor(lddfm.p.close_txt)
  1113.         term.setBackgroundColor(lddfm.p.close_bg)
  1114.         term.write("close")
  1115.     end
  1116.     term.setCursorPos(px,py)
  1117.     plc.tsv(true)
  1118.     return scrbarY
  1119. end
  1120.  
  1121. lddfm.coolOutro = function(x1,y1,x2,y2,_bg,_txt,char)
  1122.     local cx, cy = term.getCursorPos()
  1123.     local bg, txt = term.getBackgroundColor(), term.getTextColor()
  1124.     term.setTextColor(_txt or colors.white)
  1125.     term.setBackgroundColor(_bg or colors.black)
  1126.     local _uwah = 0
  1127.     for y = y1, y2 do
  1128.         for x = x1, x2 do
  1129.             _uwah = _uwah + 1
  1130.             term.setCursorPos(x,y)
  1131.             term.write(char or " ")
  1132.             if _uwah >= math.ceil((x2-x1)*1.63) then sleep(0) _uwah = 0 end
  1133.         end
  1134.     end
  1135.     term.setTextColor(txt)
  1136.     term.setBackgroundColor(bg)
  1137.     term.setCursorPos(cx,cy)
  1138. end
  1139.  
  1140. lddfm.scrollMenu = function(amount,list,y1,y2)
  1141.     if #list >= y2-y1 then
  1142.         lddfm.scroll = lddfm.scroll + amount
  1143.         if lddfm.scroll < 0 then
  1144.             lddfm.scroll = 0
  1145.         end
  1146.         if lddfm.scroll > #list-(y2-y1) then
  1147.             lddfm.scroll = #list-(y2-y1)
  1148.         end
  1149.     end
  1150. end
  1151.  
  1152. lddfm.makeMenu = function(_x1,_y1,_x2,_y2,_path,_noFiles,_noFolders,_noCD,_noSelectFolders,_doHidden,_p,_canClose)
  1153.     if _noFiles and _noFolders then
  1154.         return false, "C'mon, man..."
  1155.     end
  1156.     if _x1 == true then
  1157.         return false, "arguments: x1, y1, x2, y2, path, noFiles, noFolders, noCD, noSelectFolders, doHidden, palate, canClose" -- a little help
  1158.     end
  1159.     lddfm.setPalate(_p)
  1160.     local path, list = _path or ""
  1161.     lddfm.scroll = 0
  1162.     local _pbg, _ptxt = term.getBackgroundColor(), term.getTextColor()
  1163.     local x1, x2, y1, y2 = _x1 or 1, _x2 or lddfm.scr_x, _y1 or 1, _y2 or lddfm.scr_y
  1164.     local keysDown = {}
  1165.     local _barrY
  1166.     while true do
  1167.         list = lddfm.foldersOnTop(lddfm.filterFileFolders(fs.list(path),path,_noFiles,_noFolders,_noCD,_doHidden),path)
  1168.         if (fs.getDir(path) ~= "..") and not (_noCD or _noFolders) then
  1169.             table.insert(list,1,"..")
  1170.         end
  1171.         _res, _barrY = pcall( function() return lddfm.render(x1,y1,x2,y2,list,path,lddfm.scroll,_canClose) end)
  1172.         if not _res then
  1173.             plc.tsv(true)
  1174.             error(_barrY)
  1175.         end
  1176.         local evt = {os.pullEvent()}
  1177.         if evt[1] == "mouse_scroll" then
  1178.             lddfm.scrollMenu(evt[2],list,y1,y2)
  1179.         elseif evt[1] == "mouse_click" then
  1180.             local butt,mx,my = evt[2],evt[3],evt[4]
  1181.             if (butt == 1 and my == y1 and mx <= x2 and mx >= x2-4) and _canClose then
  1182.                 --lddfm.coolOutro(x1,y1,x2,y2)
  1183.                 term.setTextColor(_ptxt) term.setBackgroundColor(_pbg)
  1184.                 return false
  1185.             elseif ypaths[my] and (mx >= x1 and mx < x2) then --x2 is reserved for the scrollbar, breh
  1186.                 if fs.isDir(fs.combine(path,ypaths[my])) then
  1187.                     if _noCD or butt == 3 then
  1188.                         if not _noSelectFolders or _noFolders then
  1189.                             --lddfm.coolOutro(x1,y1,x2,y2)
  1190.                             term.setTextColor(_ptxt) term.setBackgroundColor(_pbg)
  1191.                             return fs.combine(path,ypaths[my])
  1192.                         end
  1193.                     else
  1194.                         path = fs.combine(path,ypaths[my])
  1195.                         lddfm.scroll = 0
  1196.                     end
  1197.                 else
  1198.                     term.setTextColor(_ptxt) term.setBackgroundColor(_pbg)
  1199.                     return fs.combine(path,ypaths[my])
  1200.                 end
  1201.             end
  1202.         elseif evt[1] == "key" then
  1203.             keysDown[evt[2]] = true
  1204.             if evt[2] == keys.enter and not (_noFolders or _noCD or _noSelectFolders) then --the logic for _noCD being you'd normally need to go back a directory to select the current directory.
  1205.                 --lddfm.coolOutro(x1,y1,x2,y2)
  1206.                 term.setTextColor(_ptxt) term.setBackgroundColor(_pbg)
  1207.                 return path
  1208.             end
  1209.             if evt[2] == keys.up then
  1210.                 lddfm.scrollMenu(-1,list,y1,y2)
  1211.             elseif evt[2] == keys.down then
  1212.                 lddfm.scrollMenu(1,list,y1,y2)
  1213.             end
  1214.             if evt[2] == keys.pageUp then
  1215.                 lddfm.scrollMenu(y1-y2,list,y1,y2)
  1216.             elseif evt[2] == keys.pageDown then
  1217.                 lddfm.scrollMenu(y2-y1,list,y1,y2)
  1218.             end
  1219.             if evt[2] == keys.home then
  1220.                 lddfm.scroll = 0
  1221.             elseif evt[2] == keys["end"] then
  1222.                 if #list > (y2-y1) then
  1223.                     lddfm.scroll = #list-(y2-y1)
  1224.                 end
  1225.             end
  1226.             if evt[2] == keys.h then
  1227.                 if keysDown[keys.leftCtrl] or keysDown[keys.rightCtrl] then
  1228.                     _doHidden = not _doHidden
  1229.                 end
  1230.             elseif _canClose and (evt[2] == keys.x or evt[2] == keys.q or evt[2] == keys.leftCtrl) then
  1231.                 --lddfm.coolOutro(x1,y1,x2,y2)
  1232.                 term.setTextColor(_ptxt) term.setBackgroundColor(_pbg)
  1233.                 return false
  1234.             end
  1235.         elseif evt[1] == "key_up" then
  1236.             keysDown[evt[2]] = false
  1237.         end
  1238.     end
  1239. end
  1240.  
  1241. local getBlittle = function()
  1242.     if not blittle then
  1243.         if fs.exists(fs.combine(plc.apipath,"blittle")) then
  1244.             os.loadAPI(fs.combine(plc.apipath,"blittle"))
  1245.             if not blittleTerm then
  1246.                 blittleTerm = blittle.createWindow()
  1247.             end
  1248.             return blittleTerm, firstTerm
  1249.         else
  1250.             local geet = http.get("http://pastebin.com/raw/ujchRSnU")
  1251.             if not geet then
  1252.                 return false
  1253.             else
  1254.                 geet = geet.readAll()
  1255.                 local file = fs.open(fs.combine(plc.apipath,"blittle"),"w")
  1256.                 file.write(geet)
  1257.                 file.close()
  1258.                 os.loadAPI(fs.combine(plc.apipath,"blittle"))
  1259.                 --fs.delete(plc.apipath)
  1260.                 if not blittleTerm then
  1261.                     blittleTerm = blittle.createWindow()
  1262.                 end
  1263.                 return blittleTerm, firstTerm
  1264.             end
  1265.         end
  1266.     else
  1267.         if not blittleTerm then
  1268.             blittleTerm = blittle.createWindow()
  1269.         end
  1270.         return blittleTerm, firstTerm
  1271.     end
  1272. end
  1273.  
  1274. local getUCG = function()
  1275.     if not ucg then
  1276.         if fs.exists(fs.combine(plc.apipath,"ucg")) then
  1277.             os.loadAPI(fs.combine(plc.apipath,"ucg"))
  1278.             return true
  1279.         else
  1280.             local geet = http.get("https://raw.githubusercontent.com/ardera/libucg/master/src/libucg")
  1281.             if not geet then
  1282.                 return false
  1283.             else
  1284.                 geet = geet.readAll()
  1285.                 local file = fs.open(fs.combine(plc.apipath,"ucg"),"w")
  1286.                 file.write(geet)
  1287.                 file.close()
  1288.                 os.loadAPI(fs.combine(plc.apipath,"ucg"))
  1289.             end
  1290.         end
  1291.     end
  1292. end
  1293.  
  1294. local getBBPack = function()
  1295.     if not bbpack then
  1296.         if fs.exists(fs.combine(plc.apipath,"bbpack")) then
  1297.             os.loadAPI(fs.combine(plc.apipath,"bbpack"))
  1298.             return true
  1299.         else
  1300.             local geet = http.get("https://pastebin.com/raw/cUYTGbpb")
  1301.             if not geet then
  1302.                 return false
  1303.             else
  1304.                 geet = geet.readAll()
  1305.                 local file = fs.open(fs.combine(plc.apipath,"bbpack"),"w")
  1306.                 file.write(geet)
  1307.                 file.close()
  1308.                 os.loadAPI(fs.combine(plc.apipath,"bbpack"))
  1309.             end
  1310.         end
  1311.     end
  1312. end
  1313.  
  1314. local getGIF = function()
  1315.     getBBPack()
  1316.     if not GIF then
  1317.         if fs.exists(fs.combine(plc.apipath,"GIF")) then
  1318.             os.loadAPI(fs.combine(plc.apipath,"GIF"))
  1319.             return true
  1320.         else
  1321.             local geet = http.get("https://pastebin.com/raw/5uk9uRjC")
  1322.             if not geet then
  1323.                 return false
  1324.             else
  1325.                 geet = geet.readAll()
  1326.                 local file = fs.open(fs.combine(plc.apipath,"GIF"),"w")
  1327.                 file.write(geet)
  1328.                 file.close()
  1329.                 os.loadAPI(fs.combine(plc.apipath,"GIF"))
  1330.             end
  1331.         end
  1332.     end
  1333. end
  1334.  
  1335. local NFPserializeImage = function(str)
  1336.     local bepis = explode("\n",str)
  1337.     local output = {}
  1338.     for y = 1, #bepis do
  1339.         output[y] = {}
  1340.         for x = 1, #bepis[y] do
  1341.             output[y][x] = BTC(bepis[y]:sub(x,x),true)
  1342.         end
  1343.     end
  1344.     return textutils.unserialize(textutils.serialize(output):gsub("\n",""):gsub(" ",""):gsub(",}","}"))
  1345. end
  1346.  
  1347. local importFromGIF = function(filename,verbose)
  1348.     getGIF()
  1349.     local output = {}
  1350.     local image
  1351.     local rawGif = GIF.loadGIF(filename)
  1352.     if painconfig.useFlattenGIF then
  1353.         if verbose then
  1354.             print("Flattening...")
  1355.         end
  1356.         rawGif = GIF.flattenGIF(rawGif)
  1357.         sleep(0)
  1358.     end
  1359.     local cx, cy = term.getCursorPos()
  1360.     for a = 1, #rawGif do
  1361.         output[a] = importFromPaint(GIF.toPaintutils(rawGif[a]))
  1362.         if verbose then
  1363.             term.setCursorPos(cx,cy)
  1364.             write("Did "..a.."/"..#rawGif.." ")
  1365.         end
  1366.         if a % 1 then sleep(0) end --used to be a % 2, might change later
  1367.     end
  1368.     return output
  1369. end
  1370.  
  1371. local exportToPaint
  1372.  
  1373. local exportToGIF = function(input)
  1374.     getGIF()
  1375.     local outGIF = {}
  1376.     for a = 1, #paintEncoded do
  1377.         outGIF[a] = NFPserializeImage(exportToPaint(paintEncoded[a]))
  1378.         sleep(0)
  1379.     end
  1380.     if painconfig.useFlattenGIF then
  1381.         return GIF.flattenGIF(GIF.buildGIF(table.unpack(outGIF)),true)
  1382.     else
  1383.         return GIF.buildGIF(table.unpack(outGIF))
  1384.     end
  1385. end
  1386.  
  1387. local importFromUCG = function(filename)
  1388.     getUCG()
  1389.     return importFromPaint(ucg.readFile(filename))
  1390. end
  1391.  
  1392. local exportToUCG = function(filename, input)
  1393.     getUCG()
  1394.     ucg.writeFile(filename, NFPserializeImage(exportToPaint(input)))
  1395. end
  1396.  
  1397. renderPAIN = function(dots,xscroll,yscroll,doPain,dontRenderBar)
  1398.     plc.tsv(false)
  1399.     local beforeTX,beforeBG = term.getTextColor(), term.getBackgroundColor()
  1400.     local cx,cy = term.getCursorPos()
  1401.     local FUCK, SHIT = pcall(function()
  1402.             if doPain then
  1403.                 if (not plc.renderBlittle) then
  1404.                     if not dontRenderBar then
  1405.                         renderBar(barmsg,true)
  1406.                     end
  1407.                     renderPainyThings(xscroll,yscroll,plc.evenDrawGrid)
  1408.                 else
  1409.                     term.clear()
  1410.                 end
  1411.             end
  1412.             for a = 1, #dots do
  1413.                 local d = dots[a]
  1414.                 if doPain then
  1415.                     if not ((d.y-yscroll >= 1 and d.y-yscroll <= scr_y-(plc.renderBlittle and 0 or doRenderBar)) and (d.x-xscroll >= 1 and d.x-xscroll <= scr_x)) then
  1416.                         d = nil
  1417.                     end
  1418.                 end
  1419.                 if d then
  1420.                     term.setCursorPos(d.x-(xscroll or 0),d.y-(yscroll or 0))
  1421.                     term.setBackgroundColor((paint.doGray and grayOut(d.b) or d.b) or rendback.b)
  1422.                     if painconfig.gridBleedThrough then
  1423.                         term.setTextColor(rendback.t)
  1424.                         term.write((d.x >= 1 and d.y >= 1) and grid[ ro( d.y+2, #grid)+1]:sub(1+ro(d.x+-1,#grid[1]), 1+ro(d.x+-1,#grid[1])) or "/")
  1425.                     else
  1426.                         term.setTextColor(      (paint.doGray and grayOut(d.t) or d.t) or rendback.t)
  1427.                         term.write(d.c or " ")
  1428.                     end
  1429.                 end
  1430.             end
  1431.     end)
  1432.     term.setBackgroundColor(beforeBG or rendback.b)
  1433.     term.setTextColor(beforeTX or rendback.t)
  1434.     term.setCursorPos(cx,cy)
  1435.     plc.tsv(true)
  1436.     if not FUCK then error(SHIT) end --GOD DAMN IT
  1437. end
  1438.  
  1439. renderPAINFS = function(filename,xscroll,yscroll,frameNo,doPain)
  1440.     local file = fs.open(filename,"r")
  1441.     local contents = file.readAll()
  1442.     local amntFrames
  1443.     file.close()
  1444.     local tcontents = textutils.unserialize(contents)
  1445.     if type(tcontents) ~= "table" then
  1446.         tcontents = importFromPaint(contents)
  1447.     else
  1448.         amntFrames = #tcontents
  1449.         tcontents = tcontents[frameNo or 1]
  1450.     end
  1451.     renderPAIN(tcontents,xscroll,yscroll,doPain)
  1452.     return amntFrames
  1453. end
  1454.  
  1455. local putDotDown = function(dot) -- only 'x' and 'y' are required arguments
  1456.     paintEncoded[frame][#paintEncoded[frame]+1] = {
  1457.         x = dot.x + paint.scrollX,
  1458.         y = dot.y + paint.scrollY,
  1459.         c = dot.c or paint.c,
  1460.         b = dot.b or (swapColors and paint.t or paint.b),
  1461.         t = dot.t or (swapColors and paint.b or paint.t),
  1462.         m = dot.m or paint.m,
  1463.     }
  1464. end
  1465.  
  1466. local saveToUndoBuffer = function()
  1467.     if plc.undoPos < #plc.undoBuffer then
  1468.         for a = #plc.undoBuffer, plc.undoPos + 1, -1 do
  1469.             table.remove(plc.undoBuffer, a)
  1470.         end
  1471.     end
  1472.     if plc.undoPos >= painconfig.undoBufferSize then
  1473.         for a = 2, #plc.undoBuffer do
  1474.             plc.undoBuffer[a - 1] = plc.undoBuffer[a]
  1475.         end
  1476.         plc.undoBuffer[#plc.undoBuffer] = deepCopy(paintEncoded)
  1477.     else
  1478.         plc.undoPos = plc.undoPos + 1
  1479.         plc.undoBuffer[plc.undoPos] = deepCopy(paintEncoded)
  1480.     end
  1481. end
  1482.  
  1483. local doUndo = function()
  1484.     plc.undoPos = math.max(1, plc.undoPos - 1)
  1485.     paintEncoded = deepCopy(plc.undoBuffer[plc.undoPos])
  1486.     if not paintEncoded[frame] then
  1487.         frame = #paintEncoded
  1488.     end
  1489. end
  1490.  
  1491. local doRedo = function()
  1492.     plc.undoPos = math.min(#plc.undoBuffer, plc.undoPos + 1)
  1493.     paintEncoded = deepCopy(plc.undoBuffer[plc.undoPos])
  1494.     if not paintEncoded[frame] then
  1495.         frame = #paintEncoded
  1496.     end
  1497. end
  1498.  
  1499. local putDownText = function(x,y)
  1500.     term.setCursorPos(x,y)
  1501.     term.setTextColor((paint.doGray and grayOut(paint.t or rendback.t)) or (paint.t or rendback.t))
  1502.     term.setBackgroundColor((paint.doGray and grayOut(paint.b or rendback.b)) or (paint.b or rendback.b))
  1503.     local msg = read()
  1504.     if #msg > 0 then
  1505.         for a = 1, #msg do
  1506.             putDotDown({x=(x+a)-1, y=y, c=msg:sub(a,a)})
  1507.         end
  1508.     end
  1509.     saveToUndoBuffer()
  1510. end
  1511.  
  1512. local deleteDot = function(x,y) --deletes all dots at point x,y
  1513.     local good = false
  1514.     for a = #paintEncoded[frame],1,-1 do
  1515.         local b = paintEncoded[frame][a]
  1516.         if (x == b.x) and (y == b.y) then
  1517.             table.remove(paintEncoded[frame],a)
  1518.             good = true
  1519.         end
  1520.     end
  1521.     return good
  1522. end
  1523.  
  1524. exportToPaint = function(input,noTransparent) --exports paintEncoded frame to regular paint format. input is expected to be paintEncoded[frame]
  1525.     local doopTXT, doopTXCOL, doopBGCOL = {}, {}, {}
  1526.     local p = input
  1527.     local pheight = 0
  1528.     local pwidth = 0
  1529.     for a = 1, #p do
  1530.         if p[a].y > pheight then
  1531.             pheight = p[a].y
  1532.         end
  1533.         if p[a].x > pwidth then
  1534.             pwidth = p[a].x
  1535.         end
  1536.     end
  1537.     for k,v in pairs(p) do
  1538.         if not doopBGCOL[v.y] then
  1539.             doopBGCOL[v.y] = {}
  1540.             doopTXCOL[v.y] = {}
  1541.             doopTXT[v.y] = {}
  1542.         end
  1543.         doopBGCOL[v.y][v.x] = CTB(v.b)
  1544.         doopTXCOL[v.y][v.x] = CTB(v.t)
  1545.         doopTXT[v.y][v.x] = v.c
  1546.     end
  1547.     local nfpoutputTXT, nfpoutputTXCOL, nfpoutputBGCOL = "", "", ""
  1548.     for y = 1, pheight do
  1549.         if doopBGCOL[y] then
  1550.             for x = 1, pwidth do
  1551.                 if doopBGCOL[y][x] then
  1552.                     nfpoutputBGCOL = nfpoutputBGCOL..doopBGCOL[y][x]
  1553.                     nfpoutputTXCOL = nfpoutputTXCOL..doopTXCOL[y][x]
  1554.                     nfpoutputTXT = nfpoutputTXT..(((doopTXT[y][x] == " " and noTransparent) and "\128" or doopTXT[y][x]) or " ")
  1555.                 else
  1556.                     nfpoutputBGCOL = nfpoutputBGCOL..(noTransparent and "0" or " ")
  1557.                     nfpoutputTXCOL = nfpoutputTXCOL..(noTransparent and "0" or " ")
  1558.                     nfpoutputTXT = nfpoutputTXT.." "
  1559.                 end
  1560.             end
  1561.         end
  1562.         if y ~= pheight then
  1563.             nfpoutputBGCOL = nfpoutputBGCOL.."\n"
  1564.             nfpoutputTXCOL = nfpoutputTXCOL.."\n"
  1565.             nfpoutputTXT = nfpoutputTXT.."\n"
  1566.         end
  1567.     end
  1568.     return nfpoutputBGCOL, pheight, pwidth
  1569. end
  1570.  
  1571. local exportToNFT = function(input)
  1572.  
  1573.     local bgcode, txcode = "\30", "\31"
  1574.     local output = ""
  1575.     local text, back
  1576.  
  1577.     local doot, pheight, pwidths = tableFormatPE(input)
  1578.  
  1579.     for y = 1, pheight do
  1580.  
  1581.         text, back = "0", "f"
  1582.         if doot[y] then
  1583.             for x = 1, pwidths[y] do
  1584.  
  1585.                 if doot[y][x] then
  1586.                     if doot[y][x].back ~= back then
  1587.                         back = doot[y][x].back
  1588.                         output = output .. bgcode .. back
  1589.                     end
  1590.                     if doot[y][x].text ~= text then
  1591.                         text = doot[y][x].text
  1592.                         output = output .. txcode .. text
  1593.                     end
  1594.                     output = output .. doot[y][x].char
  1595.                 else
  1596.                     output = output .. " "
  1597.                 end
  1598.  
  1599.             end
  1600.         end
  1601.  
  1602.         if y < pheight then
  1603.             output = output .. "\n"
  1604.         end
  1605.     end
  1606.     return output
  1607. end
  1608.  
  1609. local importFromNFT = function(input) --imports NFT formatted string image to paintEncoded[frame] formatted table image. please return a paintEncoded[frame] formatted table.
  1610.     local tinput = explode("\n",input)
  1611.     local tcol,bcol
  1612.     local cx --represents the x position in the picture
  1613.     local sx --represents the x position in the file
  1614.     local output = {}
  1615.     for y = 1, #tinput do
  1616.         tcol,bcol = colors.white,colors.black
  1617.         cx, sx = 1, 0
  1618.         while sx < #tinput[y] do
  1619.             sx = sx + 1
  1620.             if tinput[y]:sub(sx,sx) == "\30" then
  1621.                 bcol = BTC(tinput[y]:sub(sx+1,sx+1))
  1622.                 sx = sx + 1
  1623.             elseif tinput[y]:sub(sx,sx) == "\31" then
  1624.                 tcol = BTC(tinput[y]:sub(sx+1,sx+1))
  1625.                 sx = sx + 1
  1626.             else
  1627.                 if tcol and bcol then
  1628.                     output[#output+1] = {
  1629.                         ["x"] = cx,
  1630.                         ["y"] = y,
  1631.                         ["b"] = bcol,
  1632.                         ["t"] = tcol,
  1633.                         ["c"] = tinput[y]:sub(sx,sx),
  1634.                         ["m"] = 0,
  1635.                     }
  1636.                 end
  1637.                 cx = cx + 1
  1638.             end
  1639.         end
  1640.     end
  1641.     return output
  1642. end
  1643.  
  1644. exportToBLT = function(input,filename,doAllFrames,noSave)
  1645.     local output = {}
  1646.     local thisImage,pheight,pwidth,nfpinput
  1647.     getBlittle()
  1648.     for a = doAllFrames and 1 or frame, doAllFrames and #input or frame do
  1649.         output[#output+1] = blittle.shrink(NFPserializeImage(exportToPaint(input[a]),true),colors.black)
  1650.     end
  1651.     if #output == 1 then output = output[1] end
  1652.     if not noSave then
  1653.         blittle.save(output,filename)
  1654.     end
  1655.     return output
  1656. end
  1657.  
  1658. importFromBLT = function(input) --takes in filename, not contents
  1659.     local output = {}
  1660.     getBlittle()
  1661.     local wholePic = blittle.load(input)
  1662.     if wholePic.height then wholePic = {wholePic} end
  1663.     local image
  1664.     for a = 1, #wholePic do
  1665.         image = wholePic[a]
  1666.         output[#output+1] = {}
  1667.         for y = 1, image.height*3 do
  1668.             for x = 1, math.max(#image[1][math.ceil(y/3)],#image[2][math.ceil(y/3)],#image[3][math.ceil(y/3)])*2 do
  1669.                 output[#output][#output[#output]+1] = {
  1670.                     m = 0,
  1671.                     x = x,
  1672.                     y = y,
  1673.                     t = BTC((image[2][math.ceil(y/3)]:sub(math.ceil(x/2),math.ceil(x/2)).."0"):sub(1,1)),
  1674.                     b = BTC((image[3][math.ceil(y/3)]:sub(math.ceil(x/2),math.ceil(x/2)).."0"):sub(1,1)),
  1675.                     c = BTC((image[1][math.ceil(y/3)]:sub(math.ceil(x/2),math.ceil(x/2)).." "):sub(1,1)),
  1676.                 }
  1677.             end
  1678.         end
  1679.     end
  1680.     return output
  1681. end
  1682.  
  1683. local getTheDoots = function(pe)
  1684.     local hasBadDots = false
  1685.     local baddestX,baddestY = 1,1
  1686.     for b = 1, #pe do
  1687.         local doot = pe[b]
  1688.         if doot.x <= 0 or doot.y <= 0 then
  1689.             hasBadDots = true
  1690.             if doot.x < baddestX then
  1691.                 baddestX = doot.x
  1692.             end
  1693.             if doot.y < baddestY then
  1694.                 baddestY = doot.y
  1695.             end
  1696.         end
  1697.         if b % 64 == 0 then yield() end
  1698.     end
  1699.     return baddestX, baddestY
  1700. end
  1701.  
  1702. local checkBadDots = function()
  1703.     local hasBadDots = false
  1704.     for a = 1, #paintEncoded do
  1705.         local radx,rady = getTheDoots(paintEncoded[a])
  1706.         if radx ~= 1 or rady ~= 1 then
  1707.             hasBadDots = true
  1708.         end
  1709.     end
  1710.     if hasBadDots then
  1711.         local ting = bottomPrompt("Dot(s) are OoB! Save or fix? (Y/N/F)",_,"ynf",{keys.leftCtrl,keys.rightCtrl})
  1712.         if ting == "f" then
  1713.             for a = 1, #paintEncoded do
  1714.                 local baddestX, baddestY = getTheDoots(paintEncoded[a])
  1715.                 paintEncoded[a] = movePaintEncoded(paintEncoded[a],-(baddestX-1),-(baddestY-1))
  1716.             end
  1717.         elseif ting ~= "y" then
  1718.             barmsg = ""
  1719.             return false
  1720.         end
  1721.     end
  1722. end
  1723.  
  1724. local convertToGrayscale = function(pe)
  1725.     local output = pe
  1726.     for a = 1, #pe do
  1727.         for b = 1, #pe[a] do
  1728.             output[a][b].b = grayOut(pe[a][b].b)
  1729.             output[a][b].t = grayOut(pe[a][b].t)
  1730.             if not output[a][b].m then output[a][b].m = 1 end
  1731.         end
  1732.         if a % 2 == 0 then yield() end
  1733.     end
  1734.     return output
  1735. end
  1736.  
  1737. local reRenderPAIN = function(overrideRenderBar)
  1738.     local _reallyDoRenderBar = doRenderBar
  1739. --  doRenderBar = 1
  1740.     renderPAIN(paintEncoded[frame],paint.scrollX,paint.scrollY,true,overrideRenderBar)
  1741.     doRenderBar = _reallyDoRenderBar
  1742. end
  1743.  
  1744. local fillTool = function(_frame,cx,cy,dot,isDeleting) -- "_frame" is the frame NUMBER
  1745.     local maxX, maxY = 1, 1
  1746.     local minX, minY = 1, 1
  1747.     paintEncoded = clearAllRedundant(paintEncoded)
  1748.     local frame = paintEncoded[_frame]
  1749.     local scx, scy = cx+paint.scrollX, cy+paint.scrollY
  1750.     local output = {}
  1751.     for a = 1, #frame do
  1752.         maxX = math.max(maxX, frame[a].x)
  1753.         maxY = math.max(maxY, frame[a].y)
  1754.         minX = math.min(minX, frame[a].x)
  1755.         minY = math.min(minY, frame[a].y)
  1756.     end
  1757.  
  1758.     maxX = math.max(maxX, scx)
  1759.     maxY = math.max(maxY, scy)
  1760.     minX = math.min(minX, scx)
  1761.     minY = math.min(minY, scy)
  1762.  
  1763.     maxX = math.max(maxX, screenEdges[1])
  1764.     maxY = math.max(maxY, screenEdges[2])
  1765.  
  1766.     local doop = {}
  1767.     local touched = {}
  1768.     local check = {[scy] = {[scx] = true}}
  1769.     for y = minY, maxY do
  1770.         doop[y] = {}
  1771.         touched[y] = {}
  1772.         for x = minX, maxX do
  1773.             doop[y][x] = {
  1774.                 c = " ",
  1775.                 b = 0,
  1776.                 t = 0
  1777.             }
  1778.             touched[y][x] = false
  1779.         end
  1780.     end
  1781.     for a = 1, #frame do
  1782.         doop[frame[a].y][frame[a].x] = {
  1783.             c = frame[a].c,
  1784.             t = frame[a].t,
  1785.             b = frame[a].b
  1786.         }
  1787.     end
  1788.     local initDot = {
  1789.         c = doop[scy][scx].c,
  1790.         t = doop[scy][scx].t,
  1791.         b = doop[scy][scx].b
  1792.     }
  1793.     local chkpos = function(x, y, checkList)
  1794.         if (x < minX or x > maxX) or (y < minY or y > maxY) then
  1795.             return false
  1796.         else
  1797.             if (doop[y][x].b ~= initDot.b) or (doop[y][x].t ~= initDot.t and doop[y][x].c ~= " ") or (doop[y][x].c ~= initDot.c) then
  1798.                 return false
  1799.             end
  1800.             if check[y] then
  1801.                 if check[y][x] then
  1802.                     return false
  1803.                 end
  1804.             end
  1805.             if touched[y][x] then
  1806.                 return false
  1807.             end
  1808.             return true
  1809.         end
  1810.     end
  1811.     local doBreak
  1812.     local step = 0
  1813.     local currentlyOnScreen
  1814.     while true do
  1815.         doBreak = true
  1816.         for chY, v in pairs(check) do
  1817.             for chX, isTrue in pairs(v) do
  1818.                 currentlyOnScreen = (chX-paint.scrollX >= 1 and chX-paint.scrollX <= scr_x and chY-paint.scrollY >= 1 and chY-paint.scrollY <= scr_y)
  1819.                 if isTrue and (not touched[chY][chX]) then
  1820.                     step = step + 1
  1821.                     if painconfig.doFillAnimation then
  1822.                         if currentlyOnScreen then
  1823.                             reRenderPAIN(true)
  1824.                         end
  1825.                     end
  1826.                     if isDeleting then
  1827.                         deleteDot(chX, chY)
  1828.                     else
  1829.                         frame[#frame+1] = {
  1830.                             x = chX,
  1831.                             y = chY,
  1832.                             c = dot.c,
  1833.                             t = dot.t,
  1834.                             b = dot.b
  1835.                         }
  1836.                     end
  1837.                     touched[chY][chX] = true
  1838.                     -- check adjacent
  1839.                     if chkpos(chX+1, chY) then
  1840.                         check[chY][chX+1] = true
  1841.                         doBreak = false
  1842.                     end
  1843.                     if chkpos(chX-1, chY) then
  1844.                         check[chY][chX-1] = true
  1845.                         doBreak = false
  1846.                     end
  1847.                     if chkpos(chX, chY+1) then
  1848.                         check[chY+1] = check[chY+1] or {}
  1849.                         check[chY+1][chX] = true
  1850.                         doBreak = false
  1851.                     end
  1852.                     if chkpos(chX, chY-1) then
  1853.                         check[chY-1] = check[chY-1] or {}
  1854.                         check[chY-1][chX] = true
  1855.                         doBreak = false
  1856.                     end
  1857.                     -- check diagonal
  1858.                     if painconfig.doFillDiagonal then
  1859.                         if chkpos(chX-1, chY-1) then
  1860.                             check[chY-1] = check[chY-1] or {}
  1861.                             check[chY-1][chX-1] = true
  1862.                             doBreak = false
  1863.                         end
  1864.                         if chkpos(chX+1, chY-1) then
  1865.                             check[chY-1] = check[chY-1] or {}
  1866.                             check[chY-1][chX+1] = true
  1867.                             doBreak = false
  1868.                         end
  1869.                         if chkpos(chX-1, chY+1) then
  1870.                             check[chY+1] = check[chY+1] or {}
  1871.                             check[chY+1][chX-1] = true
  1872.                             doBreak = false
  1873.                         end
  1874.                         if chkpos(chX+1, chY+1) then
  1875.                             check[chY+1] = check[chY+1] or {}
  1876.                             check[chY+1][chX+1] = true
  1877.                             doBreak = false
  1878.                         end
  1879.                     end
  1880.                     if step % ((painconfig.doFillAnimation and currentlyOnScreen) and 4 or 1024) == 0 then -- tries to prevent crash
  1881.                         sleep(0)
  1882.                     end
  1883.                 end
  1884.             end
  1885.         end
  1886.         if doBreak then
  1887.             break
  1888.         end
  1889.     end
  1890.     paintEncoded = clearAllRedundant(paintEncoded)
  1891. end
  1892.  
  1893. local boxCharSelector = function()
  1894.     local co = function(pos)
  1895.         if pos then
  1896.             term.setTextColor(colors.lime)
  1897.             term.setBackgroundColor(colors.green)
  1898.         else
  1899.             term.setTextColor(colors.lightGray)
  1900.             term.setBackgroundColor(colors.gray)
  1901.         end
  1902.     end
  1903.     local rend = function()
  1904.         term.setCursorPos(1,scr_y)
  1905.         term.setBackgroundColor(colors.lightGray)
  1906.         term.setTextColor(colors.black)
  1907.         term.clearLine()
  1908.         term.write("Press CTRL or 'N' when ready.")
  1909.         term.setCursorPos(1,scr_y-3) co(boxchar.topLeft) write("Q") co(boxchar.topRight) write("W")
  1910.         term.setCursorPos(1,scr_y-2) co(boxchar.left) write("A") co(boxchar.right) write("S")
  1911.         term.setCursorPos(1,scr_y-1) co(boxchar.bottomLeft) write("Z") co(boxchar.bottomRight) write("X")
  1912.     end
  1913.     while true do
  1914.         rend()
  1915.         local evt = {os.pullEvent()}
  1916.         if evt[1] == "key" then
  1917.             local key = evt[2]
  1918.             if key == keys.leftCtrl or key == keys.rightCtrl or key == keys.n then
  1919.                 break
  1920.             else
  1921.                 if key == keys.q then boxchar.topLeft = not boxchar.topLeft end
  1922.                 if key == keys.w then boxchar.topRight = not boxchar.topRight end
  1923.                 if key == keys.a then boxchar.left = not boxchar.left end
  1924.                 if key == keys.s then boxchar.right = not boxchar.right end
  1925.                 if key == keys.z then boxchar.bottomLeft = not boxchar.bottomLeft end
  1926.                 if key == keys.x then boxchar.bottomRight = not boxchar.bottomRight end
  1927.             end
  1928.         elseif evt[1] == "mouse_click" or evt[1] == "mouse_drag" then
  1929.             local button, mx, my = evt[2], evt[3], evt[4]
  1930.             if my >= scr_y-2 then
  1931.                 if mx == 1 then
  1932.                     if my == scr_y - 3 then boxchar.topLeft = not boxchar.topLeft end
  1933.                     if my == scr_y - 2 then boxchar.left = not boxchar.left end
  1934.                     if my == scr_y - 1 then boxchar.bottomLeft = not boxchar.bottomLeft end
  1935.                 elseif mx == 2 then
  1936.                     if my == scr_y - 3 then boxchar.topRight = not boxchar.topRight end
  1937.                     if my == scr_y - 2 then boxchar.right = not boxchar.right end
  1938.                     if my == scr_y - 1 then boxchar.bottomRight = not boxchar.bottomRight end
  1939.                 elseif evt[1] == "mouse_click" then
  1940.                     break
  1941.                 end
  1942.             elseif evt[1] == "mouse_click" then
  1943.                 break
  1944.             end
  1945.         end
  1946.     end
  1947.     if boxchar.topLeft and boxchar.topRight and boxchar.left and boxchar.right and boxchar.bottomLeft and boxchar.bottomRight then
  1948.         swapColors = false
  1949.         return " "
  1950.     else
  1951.         local output = getDrawingCharacter(boxchar.topLeft, boxchar.topRight, boxchar.left, boxchar.right, boxchar.bottomLeft, boxchar.bottomRight)
  1952.         swapColors = not output.inverted
  1953.         return output.char
  1954.     end
  1955. end
  1956.  
  1957. local specialCharSelector = function()
  1958.     local chars = {}
  1959.     local buff = 0
  1960.     for y = 1, 16 do
  1961.         for x = 1, 16 do
  1962.             chars[y] = chars[y] or {}
  1963.             chars[y][x] = string.char(buff)
  1964.                         buff = buff + 1
  1965.         end
  1966.     end
  1967.     local sy = scr_y - (#chars + 1)
  1968.     local char = paint.c
  1969.     local render = function()
  1970.         for y = 1, #chars do
  1971.             for x = 1, #chars do
  1972.                 term.setCursorPos(x,y+sy)
  1973.                 if chars[y][x] == char then
  1974.                     term.blit(chars[y][x], "5", "d")
  1975.                 else
  1976.                     term.blit(chars[y][x], "8", "7")
  1977.                 end
  1978.             end
  1979.         end
  1980.     end
  1981.     local evt, butt, x, y
  1982.     render()
  1983.  
  1984.     term.setCursorPos(1,scr_y)
  1985.     term.setBackgroundColor(colors.lightGray)
  1986.     term.setTextColor(colors.black)
  1987.     term.clearLine()
  1988.     term.write("Press CTRL or 'N' when ready.")
  1989.  
  1990.     while true do
  1991.         evt, butt, x, y = os.pullEvent()
  1992.         if (evt == "mouse_click" or evt == "mouse_drag") then
  1993.             if chars[y-sy] then
  1994.                 if chars[y-sy][x] then
  1995.                     if (chars[y-sy][x] ~= char) then
  1996.                         char = chars[y-sy][x]
  1997.                         render()
  1998.                     end
  1999.                 else
  2000.                     return char
  2001.                 end
  2002.             else
  2003.                 return char
  2004.             end
  2005.         elseif evt == "key" then
  2006.             if (butt == keys.n) or (butt == keys.leftCtrl) then
  2007.                 return char
  2008.             end
  2009.         end
  2010.     end
  2011. end
  2012.  
  2013. local dontDragThisTime = false
  2014. local resetInputState = function()
  2015.     miceDown = {}
  2016.     keysDown = {}
  2017.     isDragging = false
  2018.     dontDragThisTime = true
  2019. end
  2020.  
  2021. local gotoCoords = function()
  2022.     local newX = bottomPrompt("Goto X:")
  2023.     newX = tonumber(newX)
  2024.     local newY
  2025.         if newX then
  2026.         newY = bottomPrompt("Goto Y:")
  2027.         newY = tonumber(newY)
  2028.         paint.scrollX = newX or paint.scrollX
  2029.         paint.scrollY = newY or paint.scrollY
  2030.     end
  2031. end
  2032.  
  2033. local renderAllPAIN = function()
  2034.     renderPAIN(paintEncoded[frame],paint.scrollX,paint.scrollY,true)
  2035. end
  2036.  
  2037. local checkIfNFP = function(str) --does not check table format, only string format
  2038.     local good = {
  2039.         ['0'] = true,
  2040.         ['1'] = true,
  2041.         ['2'] = true,
  2042.         ['3'] = true,
  2043.         ['4'] = true,
  2044.         ['5'] = true,
  2045.         ['6'] = true,
  2046.         ['7'] = true,
  2047.         ['8'] = true,
  2048.         ['9'] = true,
  2049.         a = true,
  2050.         b = true,
  2051.         c = true,
  2052.         d = true,
  2053.         e = true,
  2054.         f = true,
  2055.         [" "] = true,
  2056.         ["\n"] = true
  2057.     }
  2058.     for a = 1, #str do
  2059.         if not good[str:sub(a,a):lower()] then
  2060.             return false
  2061.         end
  2062.     end
  2063.     return true
  2064. end
  2065.  
  2066. local selectRegion = function()
  2067.     local mevt, id, x1, y1 = os.pullEvent("mouse_click")
  2068.     local x2, y2, pos, redrawID
  2069.     local renderRectangle = true
  2070.     redrawID = os.startTimer(0.5)
  2071.     while true do
  2072.         mevt, id, x2, y2 = os.pullEvent()
  2073.         if mevt == "mouse_up" or mevt == "mouse_drag" or mevt == "mouse_click" then
  2074.             pos = {{
  2075.                     x1 < x2 and x1 or x2,
  2076.                     y1 < y2 and y1 or y2
  2077.                 },{
  2078.                     x1 < x2 and x2 or x1,
  2079.                     y1 < y2 and y2 or y1
  2080.             }}
  2081.         end
  2082.         if mevt == "mouse_up" then
  2083.             break
  2084.         end
  2085.         if (mevt == "mouse_drag") or (mevt == "timer" and id == redrawID) then
  2086.             renderAllPAIN()
  2087.             if renderRectangle then
  2088.                 term.setTextColor(rendback.t)
  2089.                 term.setBackgroundColor(rendback.b)
  2090.                 for y = pos[1][2], pos[2][2] do
  2091.                     if y ~= scr_y then
  2092.                         term.setCursorPos(pos[1][1], y)
  2093.                         if (y == pos[1][2] or y == pos[2][2]) then
  2094.                             term.write(("#"):rep(1 + pos[2][1] - pos[1][1]))
  2095.                         else
  2096.                             term.write("#")
  2097.                             term.setCursorPos(pos[2][1], y)
  2098.                             term.write("#")
  2099.                         end
  2100.                     end
  2101.                 end
  2102.             end
  2103.         end
  2104.         if (mevt == "timer" and id == redrawID) then
  2105.             renderRectangle = not renderRectangle
  2106.             redrawID = os.startTimer(0.25)
  2107.         end
  2108.     end
  2109.     local output = {}
  2110.     pos[1][1] = pos[1][1] + paint.scrollX
  2111.     pos[2][1] = pos[2][1] + paint.scrollX
  2112.     pos[1][2] = pos[1][2] + paint.scrollY
  2113.     pos[2][2] = pos[2][2] + paint.scrollY
  2114.     for k,v in pairs(paintEncoded[frame]) do
  2115.         if v.x >= pos[1][1] and v.x <= pos[2][1] then
  2116.             if v.y >= pos[1][2] and v.y <= pos[2][2] then
  2117.                 output[#output+1] = {
  2118.                     x = v.x - pos[1][1],
  2119.                     y = v.y - pos[1][2],
  2120.                     t = v.t,
  2121.                     c = v.c,
  2122.                     b = v.b,
  2123.                     m = v.m
  2124.                 }
  2125.             end
  2126.         end
  2127.     end
  2128.     return output, pos[1][1], pos[1][2], pos[2][1], pos[2][2]
  2129. end
  2130.  
  2131. local openNewFile = function(fname, allowNonImageNFP)
  2132.     local file = fs.open(fname,"r")
  2133.     local contents = file.readAll()
  2134.     file.close()
  2135.     if type(textutils.unserialize(contents)) ~= "table" then
  2136.         term.setTextColor(colors.white)
  2137.         if contents:sub(1,3) == "BLT" then --thank you bomb bloke for this easy identifier
  2138.             if plc.pMode ~= 1 then print("Importing from BLT...") end
  2139.             return importFromBLT(fname), 3
  2140.         elseif contents:sub(1,3) == "GIF" then
  2141.             if plc.pMode ~= 1 then print("Importing from GIF, this'll take a while...") end
  2142.             return importFromGIF(fname,true), 5
  2143.         elseif contents:sub(1,4) == "?!7\2" then
  2144.             if plc.pMode ~= 1 then print("Importing from UCG...") end
  2145.             return {importFromUCG(fname)}, 6
  2146.         elseif contents:find(string.char(30)) and contents:find(string.char(31)) then
  2147.             if plc.pMode ~= 1 then print("Importing from NFT...") end
  2148.             return {importFromNFT(contents)}, 2
  2149.         elseif (checkIfNFP(contents) or allowNonImageNFP) then
  2150.             print("Importing from NFP...")
  2151.             return {importFromPaint(contents)}, 1
  2152.         else
  2153.             return false, "That is not a valid image file."
  2154.         end
  2155.     else
  2156.         return textutils.unserialize(contents), 4
  2157.     end
  2158. end
  2159.  
  2160. local editFuncs = {
  2161.     copy = function()
  2162.         local board = bottomPrompt("Copy to board: ")
  2163.         renderAllPAIN()
  2164.         renderBottomBar("Select region to copy.")
  2165.         local selectedDots = selectRegion()
  2166.         theClipboard[board] = selectedDots
  2167.         barmsg = "Copied to '"..board.."'"
  2168.         doRender = true
  2169.         keysDown = {}
  2170.         miceDown = {}
  2171.     end,
  2172.     cut = function()
  2173.         local board = bottomPrompt("Cut to board: ")
  2174.         renderAllPAIN()
  2175.         renderBottomBar("Select region to cut.")
  2176.         local selectedDots, x1, y1, x2, y2 = selectRegion()
  2177.         theClipboard[board] = selectedDots
  2178.         local dot
  2179.         for i = #paintEncoded[frame], 1, -1 do
  2180.             dot = paintEncoded[frame][i]
  2181.             if dot.x >= x1 and dot.x <= x2 then
  2182.                 if dot.y >= y1 and dot.y <= y2 then
  2183.                     table.remove(paintEncoded[frame], i)
  2184.                 end
  2185.             end
  2186.         end
  2187.         barmsg = "Cut to '"..board.."'"
  2188.         doRender = true
  2189.         saveToUndoBuffer()
  2190.         keysDown = {}
  2191.         miceDown = {}
  2192.     end,
  2193.     paste = function()
  2194.         local board = bottomPrompt("Paste from board: ")
  2195.         renderAllPAIN()
  2196.         renderBottomBar("Click to paste. (top left corner)")
  2197.         if theClipboard[board] then
  2198.             local mevt
  2199.             repeat
  2200.                 mevt = {os.pullEvent()}
  2201.             until (mevt[1] == "key" and mevt[2] == keys.x) or (mevt[1] == "mouse_click" and mevt[2] == 1 and (mevt[4] or scr_y) <= scr_y-1)
  2202.             for k,v in pairs(theClipboard[board]) do
  2203.                 paintEncoded[frame][#paintEncoded[frame]+1] = {
  2204.                     x = v.x + paint.scrollX + (mevt[3]),
  2205.                     y = v.y + paint.scrollY + (mevt[4]),
  2206.                     c = v.c,
  2207.                     t = v.t,
  2208.                     b = v.b,
  2209.                     m = v.m
  2210.                 }
  2211.             end
  2212.             paintEncoded[frame] = clearRedundant(paintEncoded[frame])
  2213.             barmsg = "Pasted from '"..board.."'"
  2214.             doRender = true
  2215.             saveToUndoBuffer()
  2216.             keysDown = {}
  2217.             miceDown = {}
  2218.         else
  2219.             barmsg = "No such clipboard."
  2220.             doRender = true
  2221.         end
  2222.     end
  2223. }
  2224.  
  2225. local blockEnlargeFrame = function(frameNo)
  2226.    
  2227.     local frame = deepCopy(paintEncoded[frameNo])
  2228.     local charConvert = {
  2229.         ["\129"] = {{true , false},{false, false},{false, false}},
  2230.         ["\130"] = {{false, true },{false, false},{false, false}},
  2231.         ["\131"] = {{true , true },{false, false},{false, false}},
  2232.         ["\132"] = {{false, false},{true , false},{false, false}},
  2233.         ["\133"] = {{true , false},{true , false},{false, false}},
  2234.         ["\134"] = {{false, true },{true , false},{false, false}},
  2235.         ["\135"] = {{true , true },{true , false},{false, false}},
  2236.         ["\136"] = {{false, false},{false, true },{false, false}},
  2237.         ["\137"] = {{true , false},{false, true },{false, false}},
  2238.         ["\138"] = {{false, true },{false, true },{false, false}},
  2239.         ["\139"] = {{true , true },{false, true },{false, false}},
  2240.         ["\140"] = {{false, false},{true , true },{false, false}},
  2241.         ["\141"] = {{true , false},{true , true },{false, false}},
  2242.         ["\142"] = {{false, true },{true , true },{false, false}},
  2243.         ["\143"] = {{true , true },{true , true },{false, false}},
  2244.         ["\144"] = {{false, false},{false, false},{true , false}},
  2245.         ["\145"] = {{true , false},{false, false},{true , false}},
  2246.         ["\146"] = {{false, true },{false, false},{true , false}},
  2247.         ["\147"] = {{true , true },{false, false},{true , false}},
  2248.         ["\148"] = {{false, false},{true , false},{true , false}},
  2249.         ["\149"] = {{true , false},{true , false},{true , false}},
  2250.         ["\150"] = {{false, true },{true , false},{true , false}},
  2251.         ["\151"] = {{true , true },{true , false},{true , false}},
  2252.         ["\152"] = {{false, false},{false, true },{true , false}},
  2253.         ["\153"] = {{true , false},{false, true },{true , false}},
  2254.         ["\154"] = {{false, true },{false, true },{true , false}},
  2255.         ["\155"] = {{true , true },{false, true },{true , false}},
  2256.         ["\156"] = {{false, false},{true , true },{true , false}},
  2257.         ["\157"] = {{true , false},{true , true },{true , false}},
  2258.         ["\158"] = {{false, true },{true , true },{true , false}},
  2259.         ["\159"] = {{true , true },{true , true },{true , false}},
  2260.     }
  2261.     local output, b = {}
  2262.     for k, dot in pairs(frame) do
  2263.         b = charConvert[dot.c] or {{false, false},{false, false},{false, false}}
  2264.         for y = 1, #b do
  2265.             for x = 1, #b[y] do
  2266.                 output[#output+1] = {
  2267.                     x = (dot.x - 1) * 2 + (x - 0),
  2268.                     y = (dot.y - 1) * 3 + (y - 0),
  2269.                     c = " ",
  2270. --                  t = b[y][x] and dot.t or dot.b,
  2271. --                  b = b[y][x] and dot.b or dot.t
  2272.                     t = b[y][x] and dot.b or dot.t,
  2273.                     b = b[y][x] and dot.t or dot.b
  2274.                 }
  2275.             end
  2276.         end
  2277.     end
  2278.     barmsg = "Grew image."
  2279.  
  2280.     return output
  2281. end
  2282.  
  2283. local displayMenu = function()
  2284.     doRender = false
  2285.     menuOptions = {"File","Edit","Window","Set","About","Exit"}
  2286.     local diss = " "..tableconcat(menuOptions," ")
  2287.     local cleary = scr_y-math.floor(#diss/scr_x)
  2288.  
  2289.     local fileSave = function()
  2290.         checkBadDots()
  2291.         local output = deepCopy(paintEncoded)
  2292.         if paint.doGray then
  2293.             output = convertToGrayscale(output)
  2294.         end
  2295.         if not plc.fileName then
  2296.             renderBottomBar("Save as: ")
  2297.             local fnguess = read()
  2298.             if fs.isReadOnly(fnguess) then
  2299.                 barmsg = "'"..fnguess.."' is read only."
  2300.                 return false
  2301.             elseif fnguess:gsub(" ","") == "" then
  2302.                 return false
  2303.             elseif fs.isDir(fnguess) then
  2304.                 barmsg = "'"..fnguess.."' is already a directory."
  2305.                 return false
  2306.             elseif #fnguess > 255 then
  2307.                 barmsg = "Filename is too long."
  2308.                 return false
  2309.             else
  2310.                 plc.fileName = fnguess
  2311.             end
  2312.         end
  2313.         saveFile(plc.fileName,output)
  2314.         term.setCursorPos(9,scr_y)
  2315.         return plc.fileName
  2316.     end
  2317.     local filePrint = function()
  2318.         local usedDots, dot = {}, {}
  2319.         for a = 1, #paintEncoded[frame] do
  2320.             dot = paintEncoded[frame][a]
  2321.             if dot.x > paint.scrollX and dot.x < (paint.scrollX + 25) and dot.y > paint.scrollX and dot.y < (paint.scrollY + 21) then
  2322.                 if dot.c ~= " " then
  2323.                     usedDots[dot.t] = usedDots[dot.t] or {}
  2324.                     usedDots[dot.t][#usedDots[dot.t]+1] = {
  2325.                         x = dot.x - paint.scrollX,
  2326.                         y = dot.y - paint.scrollY,
  2327.                         char = dot.c
  2328.                     }
  2329.                 end
  2330.             end
  2331.         end
  2332.         local dyes = {
  2333.             [1] = "bonemeal",
  2334.             [2] = "orange dye",
  2335.             [4] = "magenta dye",
  2336.             [8] = "light blue dye",
  2337.             [16] = "dandelion yellow",
  2338.             [32] = "lime dye",
  2339.             [64] = "pink dye",
  2340.             [128] = "gray dye",
  2341.             [256] = "light gray dye",
  2342.             [512] = "cyan dye",
  2343.             [1024] = "purple dye",
  2344.             [2048] = "lapis lazuli",
  2345.             [4096] = "cocoa beans",
  2346.             [8192] = "cactus green",
  2347.             [16384] = "rose red",
  2348.             [32768] = "ink sac",
  2349.         }
  2350.         local printer = peripheral.find("printer")
  2351.         if not printer then
  2352.             barmsg = "No printer found."
  2353.             return false
  2354.         end
  2355.         local page
  2356.         for color, dotList in pairs(usedDots) do
  2357.             term.setBackgroundColor(colors.black)
  2358.             term.setTextColor((color == colors.black) and colors.gray or color)
  2359.             term.clear()
  2360.             cwrite("Please insert "..dyes[color].." into the printer.", nil, math.floor(scr_y/2))
  2361.             term.setTextColor(colors.lightGray)
  2362.             cwrite("Then, press spacebar.", nil, math.floor(scr_y/2) + 1)
  2363.             local evt
  2364.             sleep(0)
  2365.             repeat
  2366.                 evt = {os.pullEvent("key")}
  2367.             until evt[2] == keys.space
  2368.             page = page or printer.newPage()
  2369.             if not page then
  2370.                 barmsg = "Check ink/paper."
  2371.                 return
  2372.             end
  2373.             for k,v in pairs(usedDots[color]) do
  2374.                 printer.setCursorPos(v.x, v.y)
  2375.                 printer.write(v.char)
  2376.             end
  2377.         end
  2378.         printer.endPage()
  2379.         barmsg = "Printed."
  2380.     end
  2381.     local fileExport = function(menuX,getRightToIt,_fileName)
  2382.         local exportMode
  2383.         if not tonumber(getRightToIt) then
  2384.             exportMode = makeSubMenu(menuX or 8,scr_y-2,{"Paint","NFT","BLT","PAIN Native","GIF","UCG"})
  2385.         else
  2386.             exportMode = getRightToIt
  2387.         end
  2388.         if exportMode == false then return false end
  2389.         local pe, exportName, writeIndent, result
  2390.         if exportMode == 4 then
  2391.             local exNm = fileSave()
  2392.             if exNm then
  2393.                 plc.changedImage = false
  2394.                 return exNm
  2395.             else
  2396.                 return nil
  2397.             end
  2398.         else
  2399.             checkBadDots()
  2400.             if _fileName then
  2401.                 exportName, writeIndent = _fileName, #_fileName
  2402.             else
  2403.                 exportName, writeIndent = bottomPrompt("Export to: /")
  2404.             end
  2405.             nfpoutput = ""
  2406.             if fs.combine("",exportName) == "" then
  2407.                 barmsg = "Export cancelled."
  2408.                 return
  2409.             end
  2410.             if fs.isReadOnly(exportName) then
  2411.                 barmsg = "That's read-only."
  2412.                 return
  2413.             end
  2414.             if fs.exists(exportName) and (not _fileName) then
  2415.                 local plea = (plc.progname == fs.combine("",exportName)) and "Overwrite ORIGINAL file!?" or "Overwrite?"
  2416.                 result, _wIn = bottomPrompt(plea.." (Y/N)",_,"yn",{keys.leftCtrl,keys.rightCtrl})
  2417.                 writeIndent = writeIndent + _wIn
  2418.                 if result ~= "y" then return end
  2419.             end
  2420.             local output
  2421.             pe = deepCopy(paintEncoded)
  2422.             if paint.doGray then
  2423.                 pe = convertToGrayscale(pe)
  2424.             end
  2425.             local doSerializeBLT = false
  2426.         end
  2427.         if exportMode == 1 then
  2428.             output = exportToPaint(pe[frame])
  2429.             if plc.askToSerialize then
  2430.                 result, _wIn = bottomPrompt("Save as serialized? (Y/N)",_,"yn",{})
  2431.                 writeIndent = writeIndent + _wIn
  2432.             else result, _wIn = "n", 0 end
  2433.             if result == "y" then
  2434.                 output = textutils.serialize(NFPserializeImage(output)):gsub(" ",""):gsub("\n",""):gsub(",}","}")
  2435.             end
  2436.         elseif exportMode == 2 then
  2437.             output = exportToNFT(pe[frame])
  2438.         elseif exportMode == 3 then
  2439.             local doAllFrames, _wIn = bottomPrompt("Save all frames, or current? (Y/N)",_,"yn",{keys.leftCtrl,keys.rightCtrl},writeIndent)
  2440.             writeIndent = writeIndent + _wIn
  2441.             if plc.askToSerialize then
  2442.                 doSerializeBLT = bottomPrompt("Save as serialized? (Y/N)",_,"yn",{},writeIndent) == "y"
  2443.             end
  2444.             output = textutils.serialise(exportToBLT(pe,exportName,doAllFrames == "y",doSerializeBLT))
  2445.         elseif exportMode == 5 then
  2446.             getGIF()
  2447.             GIF.saveGIF(exportToGIF(pe),exportName)
  2448.         elseif exportMode == 6 then
  2449.             exportToUCG(exportName,pe[frame])
  2450.         end
  2451.         if ((exportMode ~= 3) and (exportMode ~= 4) and (exportMode ~= 5) and (exportMode ~= 6)) or doSerializeBLT then
  2452.             local file = fs.open(exportName,"w")
  2453.             file.write(output)
  2454.             file.close()
  2455.         end
  2456.         return exportName
  2457.     end
  2458.  
  2459.     local editClear = function(ignorePrompt)
  2460.         local outcum = ignorePrompt and "y" or bottomPrompt("Clear the frame? (Y/N)",_,"yn",{keys.leftCtrl,keys.rightCtrl})
  2461.         if outcum == "y" then
  2462.             paintEncoded[frame] = {}
  2463.             saveToUndoBuffer()
  2464.             barmsg = "Cleared frame "..frame.."."
  2465.         end
  2466.     end
  2467.  
  2468.     local editDelFrame = function()
  2469.         local outcum = bottomPrompt("Thou art sure? (Y/N)",_,"yn",{keys.leftCtrl,keys.rightCtrl})
  2470.         if outcum == "y" then
  2471.             if #paintEncoded == 1 then
  2472.                 return editClear(true)
  2473.             end
  2474.             table.remove(paintEncoded,frame)
  2475.             barmsg = "Deleted frame "..frame.."."
  2476.             if paintEncoded[frame-1] then
  2477.                 frame = frame - 1
  2478.             else
  2479.                 frame = frame + 1
  2480.             end
  2481.             if #paintEncoded < frame then
  2482.                 repeat
  2483.                     frame = frame - 1
  2484.                 until #paintEncoded >= frame
  2485.             end
  2486.             saveToUndoBuffer()
  2487.         end
  2488.     end
  2489.     local editCrop = function()
  2490.         local outcum = bottomPrompt("Crop all but visible? (Y/N)",_,"yn",{keys.leftCtrl,keys.rightCtrl})
  2491.         if outcum == "y" then
  2492.             local ppos = 1
  2493.             local deletedAmnt = 0
  2494.             for a = #paintEncoded[frame], 1, -1 do
  2495.                 local x, y = paintEncoded[frame][a].x, paintEncoded[frame][a].y
  2496.                 if (x <= paint.scrollX) or (x > paint.scrollX + scr_x) or (y <= paint.scrollY) or (y > paint.scrollY + scr_y) then
  2497.                     table.remove(paintEncoded[frame],a)
  2498.                     deletedAmnt = deletedAmnt + 1
  2499.                 else
  2500.                     ppos = ppos + 1
  2501.                 end
  2502.                 if ppos > #paintEncoded[frame] then break end
  2503.             end
  2504.             saveToUndoBuffer()
  2505.             barmsg = "Cropped frame."
  2506.         end
  2507.     end
  2508.     local editBoxCharSelector = function()
  2509.         paint.c = boxCharSelector()
  2510.     end
  2511.     local editSpecialCharSelector = function()
  2512.         paint.c = specialCharSelector()
  2513.     end
  2514.  
  2515.     local windowSetScrSize = function()
  2516.         local x,y
  2517.         x = bottomPrompt("Scr.X OR monitor name:",{},nil,{keys.leftCtrl,keys.rightCtrl})
  2518.         if x == "" then
  2519.             return
  2520.         elseif x == "pocket" then
  2521.             screenEdges = {26,20}
  2522.         elseif x == "turtle" then
  2523.             screenEdges = {39,13}
  2524.         elseif x == "computer" then
  2525.             screenEdges = {51,19}
  2526.         elseif tonumber(x) then
  2527.             if tonumber(x) <= 0 then
  2528.                 barmsg = "Screen X must be greater than 0."
  2529.                 return
  2530.             end
  2531.             screenEdges[1] = math.abs(tonumber(x))
  2532.             y = bottomPrompt("Scr.Y:",{},nil,{keys.leftCtrl,keys.rightCtrl})
  2533.             if tonumber(y) then
  2534.                 if tonumber(y) <= 0 then
  2535.                     barmsg = "Screen Y must be greater than 0."
  2536.                     return
  2537.                 end
  2538.                 screenEdges[2] = math.abs(tonumber(y))
  2539.             end
  2540.             barmsg = "Screen size changed."
  2541.         else
  2542.             local mon = peripheral.wrap(x)
  2543.             if not mon then
  2544.                 barmsg = "No such monitor."
  2545.                 return
  2546.             else
  2547.                 if peripheral.getType(x) ~= "monitor" then
  2548.                     barmsg = "That's not a monitor."
  2549.                     return
  2550.                 else
  2551.                     screenEdges[1], screenEdges[2] = mon.getSize()
  2552.                     barmsg = "Screen size changed."
  2553.                     return
  2554.                 end
  2555.             end
  2556.         end
  2557.     end
  2558.     local aboutPAIN = function()
  2559.         local helpText = [[
  2560.  
  2561.       
  2562.          
  2563.        
  2564.            
  2565.          
  2566.  
  2567. Advanced Paint Program
  2568.  by LDDestroier
  2569.  or EldidiStroyrr
  2570.   if you please!
  2571.  
  2572. PAIN is a multi-frame paint program with the intention of becoming a stable, well-used, and mondo-useful CC drawing utility.
  2573.  
  2574. The main focus during development is to add many functions that you might see in real programs like MSPAINT, such as lines or a proper fill tool, as well as to export/import to and from as many image formats as possible.
  2575.  
  2576. My ultimate goal is to have PAIN be the default paint program for most every operating system on the forums (for what it's worth). In order to do this, I'll need to make sure that PAIN is stable, easy to use, and can be easily limited by an OS to work with more menial tasks like making a single icon or what have you.
  2577.        
  2578. I hope my PAIN brings you joy.
  2579. ]]
  2580.         guiHelp(helpText)
  2581.     end
  2582.     local aboutFileFormats = function()
  2583.         local helpText = [[
  2584. Here's info on the file formats.
  2585.  
  2586. "NFP":
  2587. Used in rom/programs/paint, and the format for paintutils. It's a handy format, but the default rendering function is inefficient as hell, and it does not store text data, only background.
  2588. Cannot save multiple frames.
  2589.  
  2590.  "NFT":
  2591. Used in npaintpro and most everything else, it's my favorite of the file formats because it does what NFP does, but allows for text in the pictures. Useful for storing screenshots or small icons where an added level of detail is handy. Created by nitrogenfingers, thank him.
  2592. Cannot save multiple frames.
  2593.  
  2594. "BLT":
  2595. Used exclusively with Bomb Bloke's BLittle API, and as such is handy with making pictures with block characters. Just keep in mind that those 2*3 grid squares in PAIN represent individual characters in BLT.
  2596. BLT can save multiple frames!
  2597.  
  2598.  "PAIN Native":
  2599. The basic, tabular, and wholly inefficient format that PAIN uses. Useful for doing math within the program, not so much for long term file storage. It stores text, but just use NFT if you don't need multiple frames.
  2600. Obviously, this can save multiple frames.
  2601.  
  2602. "GIF":
  2603. The API was made by Bomb Bloke, huge thanks for that, but GIF is a universal file format used in real paint programs. Very useful for converting files on your computer to something like NFP, but doesn't store text. Be careful when opening up big GIF files, they can take a long time to load.
  2604. Being GIF, this saves multiple frames!
  2605.  
  2606.  "UCG":
  2607. Stands for Universal Compressed Graphics. This format was made by ardera, and uses Huffman Code and run-length encoding in order to reduce file sizes tremendously. However, it only saves backgrounds and not text data.
  2608. Cannot save multiple frames.
  2609.  
  2610.  
  2611. I recommend using NFT if you don't need multiple frames, NFP if you don't need text, UCG if the picture is really big, Native PAIN if you need both text and multiframe support, and GIF if you want to use something like MS Paint or Pinta or GIMP or whatever.
  2612. ]]
  2613.         guiHelp(helpText)
  2614.     end
  2615.     local menuPoses = {}
  2616.     local menuFunctions = {
  2617.         [1] = function() --File
  2618.             while true do
  2619.                 local output, longestLen = makeSubMenu(1,cleary-1,{
  2620.                     "Save",
  2621.                     "Save As",
  2622.                     "Export",
  2623.                     "Open",
  2624.                     ((peripheral.find("printer")) and "Print" or nil)
  2625.                 })
  2626.                 if output == 1 then -- Save
  2627.                     local _fname = fileExport(_,plc.defaultSaveFormat,plc.fileName)
  2628.                     if _fname then
  2629.                         barmsg = "Saved as '".._fname.."'"
  2630.                         lastPaintEncoded = deepCopy(paintEncoded)
  2631.                         plc.changedImage = false
  2632.                     end
  2633.                     break
  2634.                 elseif output == 2 then -- Save As
  2635.                     local oldfilename = plc.fileName
  2636.                     plc.fileName = nil
  2637.                     local res = fileExport(_,plc.defaultSaveFormat)
  2638.                     if not res then
  2639.                         plc.fileName = oldfilename
  2640.                     else
  2641.                         barmsg = "Saved as '"..plc.fileName.."'"
  2642.                     end
  2643.                     break
  2644.                 elseif output == 3 then --Export
  2645.                     local res = fileExport(longestLen+1)
  2646.                     if res then
  2647.                         barmsg = "Exported as '"..res.."'"
  2648.                     end
  2649.                     break
  2650.                 elseif output == 4 then -- Open
  2651.                     renderBottomBar("Pick an image file.")
  2652.                     local newPath = lddfm.makeMenu(2, 2, scr_x-1, scr_y-2, fs.getDir(plc.fileName or plc.progname), false, false, false, true, false, nil, true)
  2653.                     if newPath then
  2654.                         local pen, form = openNewFile(newPath, painconfig.readNonImageAsNFP)
  2655.                         if not pen then
  2656.                             barmsg = form
  2657.                         else
  2658.                             plc.fileName = newPath
  2659.                             paintEncoded, lastPaintEncoded = pen, deepCopy(pen)
  2660.                             plc.defaultSaveFormat = form
  2661.                             plc.undoPos = 1
  2662.                             plc.undoBuffer = {deepCopy(paintEncoded)}
  2663.                             barmsg = "Opened '" .. fs.getName(newPath) .. "'"
  2664.                             paint.scrollX, paint.scrollY, paint.doGray = 1, 1, false
  2665.                         end
  2666.                     end
  2667.                     break
  2668.                 elseif output == 5 then -- Print
  2669.                     filePrint()
  2670.                     break
  2671.                 elseif output == false then
  2672.                     return "nobreak"
  2673.                 end
  2674.                 reRenderPAIN(true)
  2675.             end
  2676.         end,
  2677.         [2] = function() --Edit
  2678.             local output = makeSubMenu(6,cleary-1,{
  2679.                 "Delete Frame",
  2680.                 "Clear Frame",
  2681.                 "Crop Frame",
  2682.                 "Choose Box Character",
  2683.                 "Choose Special Character",
  2684.                 "BLittle Shrink",
  2685.                 "BLittle Grow",
  2686.                 "Copy Region",
  2687.                 "Cut Region",
  2688.                 "Paste Region"
  2689.             })
  2690.             if output == 1 then
  2691.                 editDelFrame()
  2692.             elseif output == 2 then
  2693.                 editClear()
  2694.             elseif output == 3 then
  2695.                 editCrop()
  2696.             elseif output == 4 then
  2697.                 editBoxCharSelector()
  2698.             elseif output == 5 then
  2699.                 editSpecialCharSelector()
  2700.             elseif output == 6 then
  2701.                 local res = bottomPrompt("You sure? It's lossy! (Y/N)",_,"yn",{keys.leftCtrl,keys.rightCtrl})
  2702.                 if res == "y" then
  2703.                     getBlittle()
  2704.                     local bltPE = blittle.shrink(NFPserializeImage(exportToPaint(paintEncoded[frame])))
  2705.                     _G.SHRINKOUT = bltPE
  2706.                     paintEncoded[frame] = {}
  2707.                     for y = 1, bltPE.height do
  2708.                         for x = 1, bltPE.width do
  2709.                             paintEncoded[frame][#paintEncoded[frame]+1] = {
  2710.                                 c = bltPE[1][y]:sub(x,x),
  2711.                                 t = BTC(bltPE[2][y]:sub(x,x),true),
  2712.                                 b = BTC(bltPE[3][y]:sub(x,x),true),
  2713.                                 x = x,
  2714.                                 y = y,
  2715.                             }
  2716.                         end
  2717.                     end
  2718.                     saveToUndoBuffer()
  2719.                     barmsg = "Shrunk image."
  2720.                 end
  2721.             elseif output == 7 then
  2722.                 paintEncoded[frame] = blockEnlargeFrame(frame)
  2723.             elseif output == 8 then
  2724.                 editFuncs.copy()
  2725.             elseif output == 9 then
  2726.                 editFuncs.cut()
  2727.             elseif output == 10 then
  2728.                 editFuncs.paste()
  2729.             elseif output == false then
  2730.                 return "nobreak"
  2731.             end
  2732.         end,
  2733.         [3] = function() --Window
  2734.             local output = makeSubMenu(11,cleary-1,{
  2735.                 "Set Screen Size",
  2736.                 "Set Scroll XY",
  2737.                 "Set Grid Colors"
  2738.             })
  2739.             if output == 1 then
  2740.                 windowSetScrSize()
  2741.             elseif output == 2 then
  2742.                 gotoCoords()
  2743.             elseif output == 3 then
  2744.                 rendback.b = paint.b
  2745.                 rendback.t = paint.t
  2746.             elseif output == false then
  2747.                 return "nobreak"
  2748.             end
  2749.         end,
  2750.         [4] = function() --Set
  2751.             local output = makeSubMenu(17,cleary-1,{
  2752.                 (painconfig.readNonImageAsNFP   and "(T)" or "(F)") .. " Load Non-images",
  2753.                 (painconfig.useFlattenGIF       and "(T)" or "(F)") .. " Flatten GIFs",
  2754.                 (painconfig.gridBleedThrough    and "(T)" or "(F)") .. " Always Render Grid",
  2755.                 (painconfig.doFillDiagonal      and "(T)" or "(F)") .. " Fill Diagonally",
  2756.                 (painconfig.doFillAnimation     and "(T)" or "(F)") .. " Do Fill Animation",
  2757.                 (painconfig.useSetVisible       and "(T)" or "(F)") .. " Use setVisible()",
  2758.                 "(" .. painconfig.undoBufferSize .. ") Set Undo Buffer Size",
  2759.             })
  2760.             if output == 1 then
  2761.                 painconfig.readNonImageAsNFP = not painconfig.readNonImageAsNFP
  2762.             elseif output == 2 then
  2763.                 painconfig.useFlattenGIF = not painconfig.useFlattenGIF
  2764.             elseif output == 3 then
  2765.                 painconfig.gridBleedThrough = not painconfig.gridBleedThrough
  2766.             elseif output == 4 then
  2767.                 painconfig.doFillDiagonal = not painconfig.doFillDiagonal
  2768.             elseif output == 5 then
  2769.                 painconfig.doFillAnimation = not painconfig.doFillAnimation
  2770.             elseif output == 6 then
  2771.                 painconfig.useSetVisible = not painconfig.useSetVisible
  2772.             elseif output == 7 then
  2773.                 local newUndoBufferSize = bottomPrompt("New undo buffer size: ")
  2774.                 if tonumber(newUndoBufferSize) then
  2775.                     painconfig.undoBufferSize = math.abs(tonumber(newUndoBufferSize))
  2776.                     plc.undoBuffer = {deepCopy(paintEncoded)}
  2777.                     plc.undoPos = 1
  2778.                 else
  2779.                     return
  2780.                 end
  2781.             end
  2782.             useConfig("save")
  2783.         end,
  2784.         [5] = function() --About
  2785.             local output = makeSubMenu(17,cleary-1,{
  2786.                 "PAIN",
  2787.                 "File Formats",
  2788.                 "Help!"
  2789.             })
  2790.             if output == 1 then
  2791.                 aboutPAIN()
  2792.             elseif output == 2 then
  2793.                 aboutFileFormats()
  2794.             elseif output == 3 then
  2795.                 guiHelp()
  2796.             end
  2797.         end,
  2798.         [6] = function() --Exit
  2799.             if plc.changedImage then
  2800.                 local outcum = bottomPrompt("Abandon unsaved work? (Y/N)",_,"yn",{keys.leftCtrl,keys.rightCtrl})
  2801.                 sleep(0)
  2802.                 if outcum == "y" then
  2803.                     return "exit"
  2804.                 else
  2805.                     return nil
  2806.                 end
  2807.             else
  2808.                 return "exit"
  2809.             end
  2810.         end,
  2811.     }
  2812.     local cursor = 1
  2813.     local redrawmenu = true
  2814.     local initial = os.time()
  2815.     local clickdelay = 0.003
  2816.  
  2817.     local redrawTheMenu = function()
  2818.         for a = cleary,scr_y do
  2819.             term.setCursorPos(1,a)
  2820.             term.setBackgroundColor(colors.lightGray)
  2821.             term.clearLine()
  2822.         end
  2823.         term.setCursorPos(2,cleary)
  2824.         for a = 1, #menuOptions do
  2825.             if a == cursor then
  2826.                 term.setTextColor(colors.black)
  2827.                 term.setBackgroundColor(colors.white)
  2828.             else
  2829.                 term.setTextColor(colors.black)
  2830.                 term.setBackgroundColor(colors.lightGray)
  2831.             end
  2832.             menuPoses[a] = {term.getCursorPos()}
  2833.             write(menuOptions[a])
  2834.             term.setBackgroundColor(colors.lightGray)
  2835.             if a ~= #menuOptions then
  2836.                 write(" ")
  2837.             end
  2838.         end
  2839.         redrawmenu = false
  2840.     end
  2841.  
  2842.     while true do
  2843.         if redrawmenu then
  2844.             redrawTheMenu()
  2845.             redrawmenu = false
  2846.         end
  2847.         local event,key,x,y = getEvents("key","char","mouse_click","mouse_up","mouse_drag")
  2848.         if event == "key" then
  2849.             if key == keys.left then
  2850.                 redrawmenu = true
  2851.                 cursor = cursor - 1
  2852.             elseif key == keys.right then
  2853.                 redrawmenu = true
  2854.                 cursor = cursor + 1
  2855.             elseif key == keys.enter then
  2856.                 redrawmenu = true
  2857.                 local res = menuFunctions[cursor]()
  2858.                 resetInputState()
  2859.                 if res == "exit" then
  2860.                     doRender = true
  2861.                     return "exit"
  2862.                 elseif res == "nobreak" then
  2863.                     reRenderPAIN(true)
  2864.                 else
  2865.                     doRender = true
  2866.                     return
  2867.                 end
  2868.             elseif key == keys.leftCtrl or key == keys.rightCtrl then
  2869.                 doRender = true
  2870.                 return
  2871.             end
  2872.         elseif event == "char" then
  2873.             for a = 1, #menuOptions do
  2874.                 if key:lower() == menuOptions[a]:sub(1,1):lower() and a ~= cursor then
  2875.                     cursor = a
  2876.                     redrawmenu = true
  2877.                     break
  2878.                 end
  2879.             end
  2880.         elseif event == "mouse_click" or event == "mouse_up" then
  2881.             if y < cleary then
  2882.                 resetInputState()
  2883.                 doRender = true
  2884.                 return
  2885.             elseif key == 1 and initial+clickdelay < os.time() then --key? more like button
  2886.                 for a = 1, #menuPoses do
  2887.                     if y == menuPoses[a][2] then
  2888.                         if x >= menuPoses[a][1] and x <= menuPoses[a][1]+#menuOptions[a] then
  2889.                             cursor = a
  2890.                             redrawTheMenu()
  2891.                             local res = menuFunctions[a]()
  2892.                             os.queueEvent("queue")
  2893.                             os.pullEvent("queue")
  2894.                             resetInputState()
  2895.                             doRender = true
  2896.                             if res == "exit" then
  2897.                                 return "exit"
  2898.                             else
  2899.                                 return
  2900.                             end
  2901.                         end
  2902.                     end
  2903.                 end
  2904.             end
  2905.         end
  2906.         if (initial+clickdelay < os.time()) and string.find(event,"mouse") then
  2907.             if key == 1 then --key? key? what key? all I see is button!
  2908.                 for a = 1, #menuPoses do
  2909.                     if y == menuPoses[a][2] then
  2910.                         if x >= menuPoses[a][1] and x <= menuPoses[a][1]+#menuOptions[a] then
  2911.                             cursor = a
  2912.                             redrawmenu = true
  2913.                             break
  2914.                         end
  2915.                     end
  2916.                 end
  2917.             end
  2918.         end
  2919.         if cursor < 1 then
  2920.             cursor = #menuOptions
  2921.         elseif cursor > #menuOptions then
  2922.             cursor = 1
  2923.         end
  2924.     end
  2925. end
  2926.  
  2927. local lastMX,lastMY,isDragging
  2928.  
  2929. local doNonEventDrivenMovement = function() --what a STUPID function name, dude
  2930.     local didMove
  2931.     while true do
  2932.         didMove = false
  2933.         if (not keysDown[keys.leftShift]) and (not isDragging) and (not keysDown[keys.tab]) then
  2934.             if keysDown[keys.right] then
  2935.                 paint.scrollX = paint.scrollX + 1
  2936.                 didMove = true
  2937.             elseif keysDown[keys.left] then
  2938.                 paint.scrollX = paint.scrollX - 1
  2939.                 didMove = true
  2940.             end
  2941.             if keysDown[keys.down] then
  2942.                 paint.scrollY = paint.scrollY + 1
  2943.                 didMove = true
  2944.             elseif keysDown[keys.up] then
  2945.                 paint.scrollY = paint.scrollY - 1
  2946.                 didMove = true
  2947.             end
  2948.             if didMove then
  2949.                 if lastMX and lastMY then
  2950.                     if miceDown[1] then
  2951.                         os.queueEvent("mouse_click",1,lastMX,lastMY)
  2952.                     end
  2953.                     if miceDown[2] then
  2954.                         os.queueEvent("mouse_click",2,lastMX,lastMY)
  2955.                     end
  2956.                 end
  2957.                 doRender = true
  2958.             end
  2959.         end
  2960.         sleep(0)
  2961.     end
  2962. end
  2963.  
  2964. local linePoses = {}
  2965. local dragPoses = {}
  2966.  
  2967. local listAllMonitors = function()
  2968.     term.setBackgroundColor(colors.gray)
  2969.     term.setTextColor(colors.white)
  2970.     local periphs = peripheral.getNames()
  2971.     local mons = {}
  2972.     for a = 1, #periphs do
  2973.         if peripheral.getType(periphs[a]) == "monitor" then
  2974.             mons[#mons+1] = periphs[a]
  2975.         end
  2976.     end
  2977.     if #mons == 0 then
  2978.         mons[1] = "No monitors found."
  2979.     end
  2980.     term.setCursorPos(3,1)
  2981.     term.clearLine()
  2982.     term.setTextColor(colors.yellow)
  2983.     term.write("All monitors:")
  2984.     term.setTextColor(colors.white)
  2985.     for y = 1, #mons do
  2986.         term.setCursorPos(2,y+1)
  2987.         term.clearLine()
  2988.         term.write(mons[y])
  2989.     end
  2990.     sleep(0)
  2991.     getEvents("char","mouse_click")
  2992.     doRender = true
  2993. end
  2994.  
  2995. local getInput = function() --gotta catch them all
  2996.     local drawEveryEvent = false
  2997.     local doot = function()
  2998.         local button, x, y, oldmx, oldmy, origx, origy
  2999.         local isDragging = false
  3000.         local proceed = false
  3001.         local evt, oldx, oldy = {}
  3002.         local button, points, key, dir
  3003.         renderBar(barmsg)
  3004.         while true do
  3005.             evt = {getEvents("mouse_scroll","mouse_click", "mouse_drag","mouse_up","key","key_up",true)}
  3006.            
  3007.             --doRender = false
  3008.             oldx, oldy = paint.scrollX,paint.scrollY
  3009.            
  3010.             if (evt[1] == "mouse_scroll") and (not viewing) then
  3011.                 dir = evt[2]
  3012.                 if dir == 1 then
  3013.                     if keysDown[keys.leftShift] or keysDown[keys.rightShift] then
  3014.                         paint.t = paint.t * 2
  3015.                         if paint.t > 32768 then
  3016.                             paint.t = 32768
  3017.                         end
  3018.                     else
  3019.                         paint.b = paint.b * 2
  3020.                         if paint.b > 32768 then
  3021.                             paint.b = 32768
  3022.                         end
  3023.                     end
  3024.                 else
  3025.                     if keysDown[keys.leftShift] or keysDown[keys.rightShift] then
  3026.                         paint.t = math.ceil(paint.t / 2)
  3027.                         if paint.t < 1 then
  3028.                             paint.t = 1
  3029.                         end
  3030.                     else
  3031.                         paint.b = math.ceil(paint.b / 2)
  3032.                         if paint.b < 1 then
  3033.                             paint.b = 1
  3034.                         end
  3035.                     end
  3036.                 end
  3037.                 renderBar(barmsg)
  3038.             elseif ((evt[1] == "mouse_click") or (evt[1] == "mouse_drag")) and (not viewing) then
  3039.                 if evt[1] == "mouse_click" then
  3040.                     origx, origy = evt[3], evt[4]
  3041.                 end
  3042.                 oldmx,oldmy = x or evt[3], y or evt[4]
  3043.                 lastMX,lastMY = evt[3],evt[4]
  3044.                 button,x,y = evt[2],evt[3],evt[4]
  3045.                 if plc.renderBlittle then
  3046.                     x = 2*x
  3047.                     y = 3*y
  3048.                     lastMX = 2*lastMX
  3049.                     lastMY = 3*lastMY
  3050.                 end
  3051.                 linePoses = {{x=oldmx,y=oldmy},{x=x,y=y}}
  3052.                 miceDown[button] = true
  3053.                 if y <= scr_y-(plc.renderBlittle and 0 or doRenderBar) then
  3054.                     if (button == 3) then
  3055.                         putDownText(x,y)
  3056.                         miceDown = {}
  3057.                         keysDown = {}
  3058.                         doRender = true
  3059.                     elseif button == 1 then
  3060.                         if keysDown[keys.leftShift] and evt[1] == "mouse_click" then
  3061.                             isDragging = true
  3062.                         end
  3063.                         if isDragging then
  3064.                             if evt[1] == "mouse_click" or dontDragThisTime then
  3065.                                 dragPoses[1] = {x=x,y=y}
  3066.                             end
  3067.                             dragPoses[2] = {x=x,y=y}
  3068.                             points = getDotsInLine(dragPoses[1].x,dragPoses[1].y,dragPoses[2].x,dragPoses[2].y)
  3069.                             renderAllPAIN()
  3070.                             for a = 1, #points do
  3071.                                 term.setCursorPos(points[a].x, points[a].y)
  3072.                                 term.blit(paint.c, CTB(paint.t), CTB(paint.b))
  3073.                             end
  3074.                         elseif (not dontDragThisTime) then
  3075.                             if evt[1] == "mouse_drag" then
  3076.                                 points = getDotsInLine(linePoses[1].x,linePoses[1].y,linePoses[2].x,linePoses[2].y)
  3077.                                 for a = 1, #points do
  3078.                                     putDotDown({x=points[a].x, y=points[a].y})
  3079.                                 end
  3080.                             else
  3081.                                 putDotDown({x=x, y=y})
  3082.                             end
  3083.                             plc.changedImage = true
  3084.                             doRender = true
  3085.                         end
  3086.                         dontDragThisTime = false
  3087.                     elseif button == 2 and y <= scr_y-(plc.renderBlittle and 0 or doRenderBar) then
  3088.                         deleteDot(x+paint.scrollX,y+paint.scrollY)
  3089.                         plc.changedImage = true
  3090.                         doRender = true
  3091.                     end
  3092.                 elseif origy >= scr_y-(plc.renderBlittle and 0 or doRenderBar) then
  3093.                     miceDown = {}
  3094.                     keysDown = {}
  3095.                     isDragging = false
  3096.                     local res = displayMenu()
  3097.                     if res == "exit" then break end
  3098.                     doRender = true
  3099.                 end
  3100.             elseif (evt[1] == "mouse_up") and (not viewing) and (not plc.isCurrentlyFilling) then
  3101.                 origx,origy = 0,0
  3102.                 button = evt[2]
  3103.                 miceDown[button] = false
  3104.                 oldmx,oldmy = nil,nil
  3105.                 lastMX, lastMY = nil,nil
  3106.                 if isDragging then
  3107.                     points = getDotsInLine(dragPoses[1].x,dragPoses[1].y,dragPoses[2].x,dragPoses[2].y)
  3108.                     for a = 1, #points do
  3109.                         putDotDown({x=points[a].x, y=points[a].y})
  3110.                     end
  3111.                     plc.changedImage = true
  3112.                     doRender = true
  3113.                 end
  3114.                 saveToUndoBuffer()
  3115.                 isDragging = false
  3116.             elseif evt[1] == "key" then
  3117.                 key = evt[2]
  3118.                 if (isDragging or not keysDown[keys.leftShift]) and (keysDown[keys.tab]) then
  3119.                     if key == keys.right and (not keysDown[keys.right]) then
  3120.                         paint.scrollX = paint.scrollX + 1
  3121.                         doRender = true
  3122.                     elseif key == keys.left and (not keysDown[keys.left]) then
  3123.                         paint.scrollX = paint.scrollX - 1
  3124.                         doRender = true
  3125.                     end
  3126.                     if key == keys.down and (not keysDown[keys.down]) then
  3127.                         paint.scrollY = paint.scrollY + 1
  3128.                         doRender = true
  3129.                     elseif key == keys.up and (not keysDown[keys.up]) then
  3130.                         paint.scrollY = paint.scrollY - 1
  3131.                         doRender = true
  3132.                     end
  3133.                 end
  3134.                 keysDown[key] = true
  3135.                 if key == keys.space then
  3136.                     if keysDown[keys.leftShift] then
  3137.                         plc.evenDrawGrid = not plc.evenDrawGrid
  3138.                     else
  3139.                         doRenderBar = math.abs(doRenderBar-1)
  3140.                     end
  3141.                     doRender = true
  3142.                 end
  3143.                 if key == keys.b then
  3144.                     local blTerm, oldTerm = getBlittle()
  3145.                     plc.renderBlittle = not plc.renderBlittle
  3146.                     isDragging = false
  3147.                     term.setBackgroundColor(rendback.b)
  3148.                     term.clear()
  3149.                     if plc.renderBlittle then
  3150.                         term.redirect(blTerm)
  3151.                         blTerm.setVisible(true)
  3152.                     else
  3153.                         term.redirect(oldTerm)
  3154.                         blTerm.setVisible(false)
  3155.                     end
  3156.                     doRender = true
  3157.                     scr_x, scr_y = term.current().getSize()
  3158.                 end
  3159.                 if keysDown[keys.leftAlt] then
  3160.                     if (not plc.renderBlittle) then
  3161.                         if (key == keys.c) then
  3162.                             editFuncs.copy()
  3163.                         elseif (key == keys.x) then
  3164.                             editFuncs.cut()
  3165.                         elseif (key == keys.v) then
  3166.                             editFuncs.paste()
  3167.                         end
  3168.                     end
  3169.                 else
  3170.                     if (key == keys.c) and (not plc.renderBlittle) then
  3171.                         gotoCoords()
  3172.                         resetInputState()
  3173.                         doRender = true
  3174.                     end
  3175.                 end
  3176.                 if (keysDown[keys.leftShift]) and (not isDragging) then
  3177.                     if key == keys.left then
  3178.                         paintEncoded[frame] = movePaintEncoded(paintEncoded[frame],-1,0)
  3179.                         saveToUndoBuffer()
  3180.                         doRender = true
  3181.                         plc.changedImage = true
  3182.                     elseif key == keys.right then
  3183.                         paintEncoded[frame] = movePaintEncoded(paintEncoded[frame],1,0)
  3184.                         saveToUndoBuffer()
  3185.                         doRender = true
  3186.                         plc.changedImage = true
  3187.                     elseif key == keys.up then
  3188.                         paintEncoded[frame] = movePaintEncoded(paintEncoded[frame],0,-1)
  3189.                         saveToUndoBuffer()
  3190.                         doRender = true
  3191.                         plc.changedImage = true
  3192.                     elseif key == keys.down then
  3193.                         paintEncoded[frame] = movePaintEncoded(paintEncoded[frame],0,1)
  3194.                         saveToUndoBuffer()
  3195.                         doRender = true
  3196.                         plc.changedImage = true
  3197.                     end
  3198.                 end
  3199.                 if keysDown[keys.leftAlt] then
  3200.                     if #paintEncoded > 1 then
  3201.                         if key == keys.equals and paintEncoded[frame+1] then --basically plus
  3202.                             local first = deepCopy(paintEncoded[frame])
  3203.                             local next = deepCopy(paintEncoded[frame+1])
  3204.                             paintEncoded[frame] = next
  3205.                             paintEncoded[frame+1] = first
  3206.                             frame = frame + 1
  3207.                             barmsg = "Swapped prev frame."
  3208.                             doRender = true
  3209.                             plc.changedImage = true
  3210.                             saveToUndoBuffer()
  3211.                         end
  3212.                         if key == keys.minus and paintEncoded[frame-1] then
  3213.                             local first = deepCopy(paintEncoded[frame])
  3214.                             local next = deepCopy(paintEncoded[frame-1])
  3215.                             paintEncoded[frame] = next
  3216.                             paintEncoded[frame-1] = first
  3217.                             frame = frame - 1
  3218.                             barmsg = "Swapped next frame."
  3219.                             doRender = true
  3220.                             plc.changedImage = true
  3221.                             saveToUndoBuffer()
  3222.                         end
  3223.                     end
  3224.                 elseif keysDown[keys.leftShift] then
  3225.                     if #paintEncoded > 1 then
  3226.                         if key == keys.equals and paintEncoded[frame+1] then --basically plus
  3227.                             for a = 1, #paintEncoded[frame] do
  3228.                                 paintEncoded[frame+1][#paintEncoded[frame+1] + 1] = paintEncoded[frame][a]
  3229.                             end
  3230.                             table.remove(paintEncoded, frame)
  3231.                             paintEncoded = clearAllRedundant(paintEncoded)
  3232.                             barmsg = "Merged next frame."
  3233.                             doRender = true
  3234.                             plc.changedImage = true
  3235.                             saveToUndoBuffer()
  3236.                         end
  3237.                         if key == keys.minus and paintEncoded[frame-1] then
  3238.                             for a = 1, #paintEncoded[frame] do
  3239.                                 paintEncoded[frame-1][#paintEncoded[frame-1] + 1] = paintEncoded[frame][a]
  3240.                             end
  3241.                             table.remove(paintEncoded, frame)
  3242.                             frame = frame - 1
  3243.                             paintEncoded = clearAllRedundant(paintEncoded)
  3244.                             barmsg = "Merged previous frame."
  3245.                             doRender = true
  3246.                             plc.changedImage = true
  3247.                             saveToUndoBuffer()
  3248.                         end
  3249.                     end
  3250.                 else
  3251.                     if key == keys.equals then --basically 'plus'
  3252.                         if plc.renderBlittle then
  3253.                             frame = frame + 1
  3254.                             if frame > #paintEncoded then frame = 1 end
  3255.                         else
  3256.                             if not paintEncoded[frame+1] then
  3257.                                 paintEncoded[frame+1] = {}
  3258.                                 local sheet = paintEncoded[frame]
  3259.                                 if keysDown[keys.rightShift] then
  3260.                                     paintEncoded[frame+1] = deepCopy(sheet)
  3261.                                 end
  3262.                             end
  3263.                             frame = frame + 1
  3264.                         end
  3265.                         saveToUndoBuffer()
  3266.                         doRender = true
  3267.                         plc.changedImage = true
  3268.                     elseif key == keys.minus then
  3269.                         if plc.renderBlittle then
  3270.                             frame = frame - 1
  3271.                             if frame < 1 then frame = #paintEncoded end
  3272.                         else
  3273.                             if frame > 1 then
  3274.                                 frame = frame - 1
  3275.                             end
  3276.                         end
  3277.                         saveToUndoBuffer()
  3278.                         doRender = true
  3279.                         plc.changedImage = true
  3280.                     end
  3281.                 end
  3282.                 if not plc.renderBlittle then
  3283.                     if key == keys.m then
  3284.                         local incum = bottomPrompt("Set meta: ",metaHistory)
  3285.                         paint.m = incum:gsub(" ","") ~= "" and incum or paint.m
  3286.                         if paint.m ~= metaHistory[#metaHistory] then
  3287.                             metaHistory[#metaHistory+1] = paint.m
  3288.                         end
  3289.                         doRender = true
  3290.                         isDragging = false
  3291.                     end
  3292.                     if key == keys.f7 then
  3293.                         plc.bepimode = not plc.bepimode
  3294.                         doRender = true
  3295.                     end
  3296.                     if key == keys.t then
  3297.                         renderBottomBar("Click to place text.")
  3298.                         local mevt
  3299.                         repeat
  3300.                             mevt = {os.pullEvent()}
  3301.                         until (mevt[1] == "key" and mevt[2] == keys.x) or (mevt[1] == "mouse_click" and mevt[2] == 1 and (mevt[4] or scr_y) <= scr_y-(plc.renderBlittle and 0 or doRenderBar))
  3302.                         if not (mevt[1] == "key" and mevt[2] == keys.x) then
  3303.                             local x,y = mevt[3],mevt[4]
  3304.                             if plc.renderBlittle then
  3305.                                 x = 2*x
  3306.                                 y = 3*y
  3307.                             end
  3308.                             putDownText(x,y)
  3309.                             miceDown = {}
  3310.                             keysDown = {}
  3311.                         end
  3312.                         doRender = true
  3313.                         plc.changedImage = true
  3314.                         isDragging = false
  3315.                     end
  3316.                     if key == keys.f and not (keysDown[keys.leftShift] or keysDown[keys.rightShift]) and (not plc.isCurrentlyFilling) then
  3317.                         renderBottomBar("Click to fill area.")
  3318.                         local mevt
  3319.                         repeat
  3320.                             mevt = {os.pullEvent()}
  3321.                         until (mevt[1] == "key" and mevt[2] == keys.x) or (mevt[1] == "mouse_click" and mevt[2] <= 2 and (mevt[4] or scr_y) <= scr_y-(renderBlittle and 0 or doRenderBar))
  3322.                         if not (mevt[1] == "key" and mevt[2] == keys.x) then
  3323.                             local x,y = mevt[3],mevt[4]
  3324.                             if plc.renderBlittle then
  3325.                                 x = 2*x
  3326.                                 y = 3*y
  3327.                             end
  3328.                             miceDown = {}
  3329.                             keysDown = {}
  3330.                             os.queueEvent("filltool_async", frame, x, y, paint, mevt[2] == 2)
  3331.                         end
  3332.                         doRender = true
  3333.                         plc.changedImage = true
  3334.                         isDragging = false
  3335.                     end
  3336.                     if key == keys.p then
  3337.                         renderBottomBar("Pick color with cursor:")
  3338.                         paintEncoded = clearAllRedundant(paintEncoded)
  3339.                         local mevt
  3340.                         repeat
  3341.                             mevt = {os.pullEvent()}
  3342.                         until (mevt[1] == "key" and mevt[2] == keys.x) or (mevt[2] == 1 and mevt[4] <= scr_y)
  3343.                         if not (mevt[1] == "key" and mevt[2] == keys.x) then
  3344.                             local x, y = mevt[3]+paint.scrollX, mevt[4]+paint.scrollY
  3345.                             if plc.renderBlittle then
  3346.                                 x = 2*x
  3347.                                 y = 3*y
  3348.                             end
  3349.                             local p
  3350.                             for a = 1, #paintEncoded[frame] do
  3351.                                 p = paintEncoded[frame][a]
  3352.                                 if (p.x == x) and (p.y == y) then
  3353.                                     paint.t = p.t or paint.t
  3354.                                     paint.b = p.b or paint.b
  3355.                                     paint.c = p.c or paint.c
  3356.                                     paint.m = p.m or paint.m
  3357.                                     miceDown = {}
  3358.                                     keysDown = {}
  3359.                                     doRender = true
  3360.                                     isDragging = false
  3361.                                     break
  3362.                                 end
  3363.                             end
  3364.                             resetInputState()
  3365.                         end
  3366.                         doRender = true
  3367.                         isDragging = false
  3368.                     end
  3369.                     if (key == keys.leftCtrl or key == keys.rightCtrl) then
  3370.                         keysDown = {[207] = keysDown[207]}
  3371.                         isDragging = false
  3372.                         local res = displayMenu()
  3373.                         paintEncoded = clearAllRedundant(paintEncoded)
  3374.                         if res == "exit" then break end
  3375.                         doRender = true
  3376.                     end
  3377.                 end
  3378.                 if (key == keys.f and keysDown[keys.leftShift]) then
  3379.                     local deredots = {}
  3380.                     plc.changedImage = true
  3381.                     for a = 1, #paintEncoded[frame] do
  3382.                         local dot = paintEncoded[frame][a]
  3383.                         if dot.x-paint.scrollX > 0 and dot.x-paint.scrollX <= scr_x then
  3384.                             if dot.y-paint.scrollY > 0 and dot.y-paint.scrollY <= scr_y then
  3385.                                 deredots[#deredots+1] = {dot.x-paint.scrollX, dot.y-paint.scrollY}
  3386.                             end
  3387.                         end
  3388.                     end
  3389.                     for y = 1, scr_y do
  3390.                         for x = 1, scr_x do
  3391.                             local good = true
  3392.                             for a = 1, #deredots do
  3393.                                 if (deredots[a][1] == x) and (deredots[a][2] == y) then
  3394.                                     good = bad
  3395.                                     break
  3396.                                 end
  3397.                             end
  3398.                             if good then
  3399.                                 putDotDown({x=x, y=y})
  3400.                             end
  3401.                         end
  3402.                     end
  3403.                     saveToUndoBuffer()
  3404.                     doRender = true
  3405.                 end
  3406.                 if key == keys.g then
  3407.                     paint.doGray = not paint.doGray
  3408.                     plc.changedImage = true
  3409.                     saveToUndoBuffer()
  3410.                     doRender = true
  3411.                 end
  3412.                 if key == keys.a then
  3413.                     paint.scrollX = 0
  3414.                     paint.scrollY = 0
  3415.                     doRender = true
  3416.                 end
  3417.                 if key == keys.n then
  3418.                     if keysDown[keys.leftShift] then
  3419.                         paint.c = specialCharSelector()
  3420.                     else
  3421.                         paint.c = boxCharSelector()
  3422.                     end
  3423.                     resetInputState()
  3424.                     doRender = true
  3425.                 end
  3426.                 if key == keys.f1 then
  3427.                     guiHelp()
  3428.                     resetInputState()
  3429.                     isDragging = false
  3430.                 end
  3431.                 if key == keys.f3 then
  3432.                     listAllMonitors()
  3433.                     resetInputState()
  3434.                     isDragging = false
  3435.                 end
  3436.                 if key == keys.leftBracket then
  3437.                     os.queueEvent("mouse_scroll",2,1,1)
  3438.                 elseif key == keys.rightBracket then
  3439.                     os.queueEvent("mouse_scroll",1,1,1)
  3440.                 end
  3441.                 if key == keys.z then
  3442.                     if keysDown[keys.leftAlt] and plc.undoPos < #plc.undoBuffer then
  3443.                         doRedo()
  3444.                         barmsg = "Redood."
  3445.                         doRender = true
  3446.                     elseif plc.undoPos > 1 then
  3447.                         doUndo()
  3448.                         barmsg = "Undood."
  3449.                         doRender = true
  3450.                     end
  3451.                 end
  3452.             elseif evt[1] == "key_up" then
  3453.                 local key = evt[2]
  3454.                 keysDown[key] = false
  3455.             end
  3456.             if (oldx~=paint.scrollX) or (oldy~=paint.scrollY) then
  3457.                 doRender = true
  3458.             end
  3459.             if drawEveryEvent and doRender then
  3460.                 renderAllPAIN()
  3461.                 doRender = false
  3462.             end
  3463.         end
  3464.     end
  3465.     if drawEveryEvent then
  3466.         doot()
  3467.     else
  3468.         parallel.waitForAny(doot, function()
  3469.             while true do
  3470.                 sleep(0.05)
  3471.                 if doRender then
  3472.                     renderAllPAIN()
  3473.                     doRender = false
  3474.                 end
  3475.             end
  3476.         end)
  3477.     end
  3478. end
  3479.  
  3480. runPainEditor = function(...) --needs to be cleaned up
  3481.     local tArg = table.pack(...)
  3482.     if not (tArg[1] == "-n" or (not tArg[1])) then
  3483.         plc.fileName = shell.resolve(tostring(tArg[1]))
  3484.     end
  3485.  
  3486.     if not plc.fileName then
  3487.         paintEncoded = {{}}
  3488.     elseif not fs.exists(plc.fileName) then
  3489.         local ex = plc.fileName:sub(-4):lower()
  3490.         if ex == ".nfp" then
  3491.             plc.defaultSaveFormat = 1
  3492.         elseif ex == ".nft" then
  3493.             plc.defaultSaveFormat = 2
  3494.         elseif ex == ".blt" then
  3495.             plc.defaultSaveFormat = 3
  3496.         elseif ex == ".gif" then
  3497.             plc.defaultSaveFormat = 5
  3498.         elseif ex == ".ucg" then
  3499.             plc.defaultSaveFormat = 6
  3500.         else
  3501.             plc.defaultSaveFormat = 4
  3502.         end
  3503.         paintEncoded = {{}}
  3504.     elseif fs.isDir(plc.fileName) then
  3505.         if math.random(1,32) == 1 then
  3506.             write("Oh") sleep(0.2)
  3507.             write(" My") sleep(0.2)
  3508.             print(" God") sleep(0.3)
  3509.             write("That is a") sleep(0.1) term.setTextColor(colors.red)
  3510.             write(" FLIPPING") sleep(0.4)
  3511.             print(" FOLDER.") sleep(0.2) term.setTextColor(colors.white)
  3512.             print("You crazy person.") sleep(0.2)
  3513.         else
  3514.             print("That's a folder.")
  3515.         end
  3516.         return
  3517.     else
  3518.         paintEncoded, plc.defaultSaveFormat = openNewFile(plc.fileName, readNonImageAsNFP)
  3519.         if not paintEncoded then
  3520.             return print(plc.defaultSaveFormat)
  3521.         end
  3522.     end
  3523.  
  3524.     local asyncFillTool = function()
  3525.         local event, frameNo, x, y, dot
  3526.         plc.isCurrentlyFilling = false
  3527.         while true do
  3528.             event, frameNo, x, y, dot, isDeleting = os.pullEvent("filltool_async")
  3529.             plc.isCurrentlyFilling = true
  3530.             renderBottomBar("Filling area...")
  3531.             fillTool(frameNo, x, y, dot, isDeleting)
  3532.             saveToUndoBuffer()
  3533.             plc.isCurrentlyFilling = false
  3534.             reRenderPAIN(doRenderBar == 0)
  3535.         end
  3536.     end
  3537.  
  3538.     if not paintEncoded[frame] then paintEncoded = {paintEncoded} end
  3539.     if plc.pMode == 1 then
  3540.         doRenderBar = 0
  3541.         renderPAIN(paintEncoded[tonumber(tArg[5]) or 1],-(tonumber(tArg[3]) or 0),-(tonumber(tArg[4]) or 0)) -- 'pain filename view X Y frame'
  3542.         sleep(0)
  3543.         return
  3544.     else
  3545.         renderPAIN(paintEncoded[frame],paint.scrollX,paint.scrollY,true)
  3546.     end
  3547.     lastPaintEncoded = deepCopy(paintEncoded)
  3548.     plc.undoBuffer = {deepCopy(paintEncoded)}
  3549.     parallel.waitForAny(getInput, doNonEventDrivenMovement, asyncFillTool)
  3550.  
  3551.     term.setCursorPos(1,scr_y)
  3552.     term.setBackgroundColor(colors.black)
  3553.     term.clearLine()
  3554. end
  3555.  
  3556. if not shell then error("shell API is required, sorry") end
  3557.  
  3558. runPainEditor(table.unpack(plc.tArg))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement