DorianH935

MDOS OS.SYS

Dec 23rd, 2025 (edited)
47
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 29.62 KB | None | 0 0
  1. -- OS.SYS - Core system library for Monolith Disk Operating System (M-DOS)
  2. -- All system functions are stored in the global 'os' table
  3.  
  4. os = {}
  5. local component = component
  6. local computer = computer
  7.  
  8. -- ==================== COMPONENT HELPERS ====================
  9. os.component = {}
  10.  
  11. function os.component.get(address, ctype)
  12. if not address then return nil end
  13. if #address < 36 then
  14. for addr in component.list(ctype, true) do
  15. if addr:sub(1, #address) == address then
  16. return addr
  17. end
  18. end
  19. return nil
  20. end
  21. return address
  22. end
  23.  
  24. function os.component.getPrimary(ctype)
  25. local addr = component.list(ctype, true)()
  26. if addr then
  27. return component.proxy(addr)
  28. end
  29. return nil
  30. end
  31.  
  32. -- ==================== DISPLAY/GPU MODULE ====================
  33. os.display = {}
  34.  
  35. local gpu = os.component.getPrimary("gpu")
  36. local screen = os.component.getPrimary("screen")
  37.  
  38. os.display.gpu = gpu
  39. os.display.screen = screen
  40. os.display.cursorX = 1
  41. os.display.cursorY = 1
  42.  
  43. function os.display.clear()
  44. if gpu then
  45. local w, h = gpu.getResolution()
  46. gpu.fill(1, 1, w, h, " ")
  47. os.display.cursorX = 1
  48. os.display.cursorY = 1
  49. end
  50. end
  51.  
  52. function os.display.write(text, x, y)
  53. if not gpu then return end
  54.  
  55. text = tostring(text)
  56.  
  57. if x and y then
  58. os.display.cursorX = x
  59. os.display.cursorY = y
  60. end
  61.  
  62. -- Make sure cursor position is valid
  63. if not os.display.cursorX then os.display.cursorX = 1 end
  64. if not os.display.cursorY then os.display.cursorY = 1 end
  65.  
  66. local w, h = gpu.getResolution()
  67.  
  68. for i = 1, #text do
  69. local char = text:sub(i, i)
  70.  
  71. if char == "\n" then
  72. os.display.cursorY = os.display.cursorY + 1
  73. os.display.cursorX = 1
  74. if os.display.cursorY > h then
  75. gpu.copy(1, 2, w, h - 1, 0, -1)
  76. gpu.fill(1, h, w, 1, " ")
  77. os.display.cursorY = h
  78. end
  79. else
  80. if os.display.cursorX > w then
  81. os.display.cursorX = 1
  82. os.display.cursorY = os.display.cursorY + 1
  83. if os.display.cursorY > h then
  84. gpu.copy(1, 2, w, h - 1, 0, -1)
  85. gpu.fill(1, h, w, 1, " ")
  86. os.display.cursorY = h
  87. end
  88. end
  89. gpu.set(os.display.cursorX, os.display.cursorY, char)
  90. os.display.cursorX = os.display.cursorX + 1
  91. end
  92. end
  93. end
  94.  
  95. function os.display.getResolution()
  96. if gpu then
  97. return gpu.getResolution()
  98. end
  99. return 50, 16
  100. end
  101.  
  102. function os.display.setResolution(w, h)
  103. if gpu then
  104. return gpu.setResolution(w, h)
  105. end
  106. return false
  107. end
  108.  
  109. function os.display.setForeground(color)
  110. if gpu then
  111. return gpu.setForeground(color)
  112. end
  113. end
  114.  
  115. function os.display.setBackground(color)
  116. if gpu then
  117. return gpu.setBackground(color)
  118. end
  119. end
  120.  
  121. -- ==================== KEYBOARD/INPUT MODULE ====================
  122. os.keyboard = {}
  123.  
  124. local keyboard = os.component.getPrimary("keyboard")
  125. os.keyboard.keyboard = keyboard
  126.  
  127. -- Track modifier key states internally
  128. local modifierStates = {
  129. shift = false,
  130. control = false,
  131. alt = false
  132. }
  133.  
  134. -- Internal function to update modifier states based on events
  135. function os.keyboard._updateModifiers(eventType, code)
  136. -- Make sure code is valid before processing
  137. if not code or type(code) ~= "number" then
  138. return
  139. end
  140.  
  141. if eventType == "key_down" then
  142. if code == 0x2A or code == 0x36 then -- lshift or rshift
  143. modifierStates.shift = true
  144. elseif code == 0x1D or code == 0x9D then -- lcontrol or rcontrol
  145. modifierStates.control = true
  146. elseif code == 0x38 or code == 0xB8 then -- lalt or ralt
  147. modifierStates.alt = true
  148. end
  149. elseif eventType == "key_up" then
  150. if code == 0x2A or code == 0x36 then
  151. modifierStates.shift = false
  152. elseif code == 0x1D or code == 0x9D then
  153. modifierStates.control = false
  154. elseif code == 0x38 or code == 0xB8 then
  155. modifierStates.alt = false
  156. end
  157. end
  158. end
  159.  
  160. function os.keyboard.isControl(char)
  161. return type(char) == "number" and (char < 0x20 or (char >= 0x7F and char <= 0x9F))
  162. end
  163.  
  164. function os.keyboard.isShiftDown()
  165. return modifierStates.shift
  166. end
  167.  
  168. function os.keyboard.isControlDown()
  169. return modifierStates.control
  170. end
  171.  
  172. function os.keyboard.isAltDown()
  173. return modifierStates.alt
  174. end
  175.  
  176. -- Fallback: check if a key is currently pressed (for older OC versions)
  177. function os.keyboard.isKeyDown(code)
  178. if keyboard and keyboard.isKeyDown then
  179. return keyboard.isKeyDown(code)
  180. end
  181. return false
  182. end
  183.  
  184. -- Key codes (from OpenOS)
  185. os.keyboard.keys = {
  186. ["1"] = 0x02, ["2"] = 0x03, ["3"] = 0x04, ["4"] = 0x05, ["5"] = 0x06,
  187. ["6"] = 0x07, ["7"] = 0x08, ["8"] = 0x09, ["9"] = 0x0A, ["0"] = 0x0B,
  188. a = 0x1E, b = 0x30, c = 0x2E, d = 0x20, e = 0x12, f = 0x21, g = 0x22,
  189. h = 0x23, i = 0x17, j = 0x24, k = 0x25, l = 0x26, m = 0x32, n = 0x31,
  190. o = 0x18, p = 0x19, q = 0x10, r = 0x13, s = 0x1F, t = 0x14, u = 0x16,
  191. v = 0x2F, w = 0x11, x = 0x2D, y = 0x15, z = 0x2C,
  192. enter = 0x1C, back = 0x0E, tab = 0x0F, space = 0x39,
  193. lshift = 0x2A, rshift = 0x36, lcontrol = 0x1D, rcontrol = 0x9D,
  194. lalt = 0x38, ralt = 0xB8,
  195. up = 0xC8, down = 0xD0, left = 0xCB, right = 0xCD,
  196. home = 0xC7, ["end"] = 0xCF, pageUp = 0xC9, pageDown = 0xD1,
  197. }
  198.  
  199. -- ==================== FILESYSTEM MODULE ====================
  200. os.fs = {}
  201.  
  202. local bootfs = component.proxy(computer.getBootAddress())
  203. os.fs.bootfs = bootfs
  204. os.fs.drives = {} -- Drive letter assignments
  205. os.fs.drives["A"] = bootfs -- Boot drive is always A:
  206. os.fs.currentDrive = "A"
  207.  
  208. function os.fs.setDrive(letter, address)
  209. letter = string.upper(letter)
  210. if letter == "A" then
  211. return false, "Drive A: is reserved for boot disk"
  212. end
  213. if address then
  214. local proxy = component.proxy(address)
  215. if proxy then
  216. os.fs.drives[letter] = proxy
  217. os.fs.saveDriveConfig()
  218. return true
  219. end
  220. return false, "Invalid component address"
  221. else
  222. os.fs.drives[letter] = nil
  223. os.fs.saveDriveConfig()
  224. return true
  225. end
  226. end
  227.  
  228. function os.fs.saveDriveConfig()
  229. local config = {}
  230. for letter, proxy in pairs(os.fs.drives) do
  231. if letter ~= "A" then -- Don't save boot drive
  232. config[letter] = proxy.address
  233. end
  234. end
  235. local data = "return " .. os.serialize(config)
  236. local handle = bootfs.open("DRIVES.CFG", "w")
  237. if handle then
  238. bootfs.write(handle, data)
  239. bootfs.close(handle)
  240. end
  241. end
  242.  
  243. function os.fs.loadDriveConfig()
  244. if bootfs.exists("DRIVES.CFG") then
  245. local handle = bootfs.open("DRIVES.CFG", "r")
  246. if handle then
  247. local data = ""
  248. repeat
  249. local chunk = bootfs.read(handle, math.huge)
  250. data = data .. (chunk or "")
  251. until not chunk
  252. bootfs.close(handle)
  253.  
  254. local func, err = load(data, "=DRIVES.CFG", "t", {})
  255. if func then
  256. local ok, config = pcall(func)
  257. if ok and type(config) == "table" then
  258. for letter, address in pairs(config) do
  259. os.fs.setDrive(letter, address)
  260. end
  261. end
  262. end
  263. end
  264. end
  265. end
  266.  
  267. function os.fs.getDrive(letter)
  268. letter = string.upper(letter)
  269. return os.fs.drives[letter]
  270. end
  271.  
  272. function os.fs.listDrives()
  273. local list = {}
  274. for letter, proxy in pairs(os.fs.drives) do
  275. table.insert(list, {letter = letter, address = proxy.address})
  276. end
  277. return list
  278. end
  279.  
  280. function os.fs.getCurrentDrive()
  281. return os.fs.currentDrive
  282. end
  283.  
  284. function os.fs.setCurrentDrive(letter)
  285. letter = string.upper(letter)
  286. if os.fs.drives[letter] then
  287. os.fs.currentDrive = letter
  288. return true
  289. end
  290. return false, "Drive not found"
  291. end
  292.  
  293. function os.fs.getCurrentProxy()
  294. return os.fs.drives[os.fs.currentDrive] or bootfs
  295. end
  296.  
  297. function os.fs.list(path)
  298. path = path or "/"
  299. local proxy = os.fs.getCurrentProxy()
  300. if not proxy then
  301. return nil, "No filesystem available"
  302. end
  303. -- Check if the component is still valid
  304. local ok, result = pcall(function()
  305. local handle = proxy.list(path)
  306. if not handle then
  307. return nil, "Failed to list directory"
  308. end
  309. local entries = {}
  310. -- proxy.list returns an iterator function, not a table
  311. if type(handle) == "function" then
  312. for entry in handle do
  313. table.insert(entries, entry)
  314. end
  315. else
  316. -- In case it returns a table directly
  317. return handle
  318. end
  319. return entries
  320. end)
  321.  
  322. if not ok then
  323. return nil, "Drive not available"
  324. end
  325. return result
  326. end
  327.  
  328. function os.fs.exists(path)
  329. local proxy = os.fs.getCurrentProxy()
  330. if not proxy then return false end
  331. local ok, result = pcall(function() return proxy.exists(path) end)
  332. return ok and result
  333. end
  334.  
  335. function os.fs.isDirectory(path)
  336. local proxy = os.fs.getCurrentProxy()
  337. if not proxy then return false end
  338. local ok, result = pcall(function() return proxy.isDirectory(path) end)
  339. return ok and result
  340. end
  341.  
  342. function os.fs.size(path)
  343. local proxy = os.fs.getCurrentProxy()
  344. if not proxy then return 0 end
  345. local ok, result = pcall(function() return proxy.size(path) end)
  346. return ok and result or 0
  347. end
  348.  
  349. function os.fs.lastModified(path)
  350. local proxy = os.fs.getCurrentProxy()
  351. if not proxy then return 0 end
  352. local ok, result = pcall(function() return proxy.lastModified(path) end)
  353. return ok and result or 0
  354. end
  355.  
  356. function os.fs.remove(path)
  357. local proxy = os.fs.getCurrentProxy()
  358. if not proxy then return false, "No filesystem available" end
  359. local ok, result = pcall(function() return proxy.remove(path) end)
  360. if not ok then return false, "Drive not available" end
  361. return result
  362. end
  363.  
  364. function os.fs.makeDirectory(path)
  365. local proxy = os.fs.getCurrentProxy()
  366. if not proxy then return false, "No filesystem available" end
  367. local ok, result = pcall(function() return proxy.makeDirectory(path) end)
  368. if not ok then return false, "Drive not available" end
  369. return result
  370. end
  371.  
  372. function os.fs.rename(from, to)
  373. local proxy = os.fs.getCurrentProxy()
  374. if not proxy then return false, "No filesystem available" end
  375. local ok, result = pcall(function() return proxy.rename(from, to) end)
  376. if not ok then return false, "Drive not available" end
  377. return result
  378. end
  379.  
  380. function os.fs.open(path, mode)
  381. local proxy = os.fs.getCurrentProxy()
  382. if not proxy then return nil, "No filesystem available" end
  383. local ok, result = pcall(function() return proxy.open(path, mode or "r") end)
  384. if not ok then return nil, "Drive not available" end
  385. return result
  386. end
  387.  
  388. function os.fs.close(handle)
  389. local proxy = os.fs.getCurrentProxy()
  390. if not proxy then return false end
  391. local ok, result = pcall(function() return proxy.close(handle) end)
  392. return ok and result
  393. end
  394.  
  395. function os.fs.read(handle, count)
  396. local proxy = os.fs.getCurrentProxy()
  397. if not proxy then return nil end
  398. local ok, result = pcall(function() return proxy.read(handle, count or math.huge) end)
  399. if not ok then return nil end
  400. return result
  401. end
  402.  
  403. function os.fs.write(handle, data)
  404. local proxy = os.fs.getCurrentProxy()
  405. if not proxy then return false end
  406. local ok, result = pcall(function() return proxy.write(handle, data) end)
  407. return ok and result
  408. end
  409.  
  410. function os.fs.readAll(path)
  411. local handle, reason = os.fs.open(path, "r")
  412. if not handle then
  413. return nil, reason
  414. end
  415. local data = ""
  416. repeat
  417. local chunk = os.fs.read(handle, math.huge)
  418. data = data .. (chunk or "")
  419. until not chunk
  420. os.fs.close(handle)
  421. return data
  422. end
  423.  
  424. function os.fs.writeAll(path, data)
  425. local handle, reason = os.fs.open(path, "w")
  426. if not handle then
  427. return nil, reason
  428. end
  429. os.fs.write(handle, data)
  430. os.fs.close(handle)
  431. return true
  432. end
  433.  
  434. -- ==================== NETWORK MODULE ====================
  435. os.network = {}
  436.  
  437. local modem = os.component.getPrimary("modem")
  438. local internet = os.component.getPrimary("internet")
  439. local tunnel = os.component.getPrimary("tunnel")
  440.  
  441. os.network.modem = modem
  442. os.network.internet = internet
  443. os.network.tunnel = tunnel
  444.  
  445. function os.network.broadcast(port, ...)
  446. if modem then
  447. return modem.broadcast(port, ...)
  448. end
  449. return false
  450. end
  451.  
  452. function os.network.send(address, port, ...)
  453. if modem then
  454. return modem.send(address, port, ...)
  455. end
  456. return false
  457. end
  458.  
  459. function os.network.open(port)
  460. if modem then
  461. return modem.open(port)
  462. end
  463. return false
  464. end
  465.  
  466. function os.network.close(port)
  467. if modem then
  468. return modem.close(port)
  469. end
  470. return false
  471. end
  472.  
  473. function os.network.isOpen(port)
  474. if modem then
  475. return modem.isOpen(port)
  476. end
  477. return false
  478. end
  479.  
  480. function os.network.httpRequest(url, postData, headers)
  481. if internet then
  482. return internet.request(url, postData, headers)
  483. end
  484. return nil, "No internet card"
  485. end
  486.  
  487. function os.network.tunnelSend(...)
  488. if tunnel then
  489. return tunnel.send(...)
  490. end
  491. return false
  492. end
  493.  
  494. -- ==================== TAPE DRIVE MODULE ====================
  495. os.tape = {}
  496.  
  497. os.tape.tapes = {} -- Drive letter assignments for tapes
  498. os.tape.currentTape = nil
  499.  
  500. -- TapeFS - Filesystem layer for tapes
  501. os.tape.BLOCK_SIZE = 512
  502. os.tape.MAGIC = "TAPEFS10" -- Magic number for formatted tapes
  503.  
  504. function os.tape.formatTape(proxy)
  505. if not proxy then return false, "No tape drive" end
  506.  
  507. local size = proxy.getSize()
  508. if size == 0 then return false, "No tape inserted" end
  509.  
  510. -- Create filesystem structure
  511. local fs = {
  512. magic = os.tape.MAGIC,
  513. blockSize = os.tape.BLOCK_SIZE,
  514. totalBlocks = math.floor(size / os.tape.BLOCK_SIZE),
  515. usedBlocks = 2, -- Header + root directory
  516. files = {},
  517. nextFileId = 1
  518. }
  519.  
  520. -- Write header to tape
  521. local header = os.serialize(fs)
  522. proxy.seek(-size) -- Seek to beginning
  523. proxy.write(header)
  524.  
  525. -- Pad to block size
  526. local padding = os.tape.BLOCK_SIZE - (#header % os.tape.BLOCK_SIZE)
  527. if padding < os.tape.BLOCK_SIZE then
  528. proxy.write(string.rep("\0", padding))
  529. end
  530.  
  531. return true
  532. end
  533.  
  534. function os.tape.readTapeFS(proxy)
  535. if not proxy then return nil end
  536.  
  537. local size = proxy.getSize()
  538. if size == 0 then return nil end
  539.  
  540. proxy.seek(-size) -- Seek to beginning
  541. local headerData = proxy.read(os.tape.BLOCK_SIZE)
  542.  
  543. if not headerData or #headerData == 0 then return nil end
  544.  
  545. -- Parse header
  546. local cleanData = headerData:match("^(.-)%z*$") or headerData
  547. local func, err = load("return " .. cleanData, "=tapefs", "t", {})
  548. if not func then return nil end
  549.  
  550. local ok, fs = pcall(func)
  551. if not ok or not fs or fs.magic ~= os.tape.MAGIC then
  552. return nil
  553. end
  554.  
  555. return fs
  556. end
  557.  
  558. function os.tape.writeTapeFS(proxy, fs)
  559. if not proxy or not fs then return false end
  560.  
  561. local header = os.serialize(fs)
  562. proxy.seek(-proxy.getSize())
  563. proxy.write(header)
  564.  
  565. -- Pad to block size
  566. local padding = os.tape.BLOCK_SIZE - (#header % os.tape.BLOCK_SIZE)
  567. if padding < os.tape.BLOCK_SIZE then
  568. proxy.write(string.rep("\0", padding))
  569. end
  570.  
  571. return true
  572. end
  573.  
  574. -- Create a filesystem-like proxy for a tape
  575. function os.tape.createTapeProxy(tapeProxy)
  576. local proxy = {}
  577. proxy.address = tapeProxy.address
  578. proxy.type = "tapefs"
  579. proxy._tape = tapeProxy
  580.  
  581. function proxy.spaceTotal()
  582. return tapeProxy.getSize()
  583. end
  584.  
  585. function proxy.spaceUsed()
  586. local fs = os.tape.readTapeFS(tapeProxy)
  587. if not fs then return 0 end
  588. return fs.usedBlocks * os.tape.BLOCK_SIZE
  589. end
  590.  
  591. function proxy.exists(path)
  592. local fs = os.tape.readTapeFS(tapeProxy)
  593. if not fs then return false end
  594.  
  595. for _, file in pairs(fs.files) do
  596. if file.path == path then
  597. return true
  598. end
  599. end
  600. return false
  601. end
  602.  
  603. function proxy.isDirectory(path)
  604. if path == "/" then return true end
  605. local fs = os.tape.readTapeFS(tapeProxy)
  606. if not fs then return false end
  607.  
  608. for _, file in pairs(fs.files) do
  609. if file.path == path and file.isDir then
  610. return true
  611. end
  612. end
  613. return false
  614. end
  615.  
  616. function proxy.list(path)
  617. local fs = os.tape.readTapeFS(tapeProxy)
  618. if not fs then return function() end end
  619.  
  620. path = path or "/"
  621. local results = {}
  622. local seen = {}
  623.  
  624. for _, file in pairs(fs.files) do
  625. if file.path:sub(1, #path) == path then
  626. local rest = file.path:sub(#path + 1)
  627. if rest:sub(1, 1) == "/" then rest = rest:sub(2) end
  628.  
  629. local nextSlash = rest:find("/")
  630. local name = nextSlash and rest:sub(1, nextSlash - 1) or rest
  631.  
  632. if name ~= "" and not seen[name] then
  633. seen[name] = true
  634. table.insert(results, name)
  635. end
  636. end
  637. end
  638.  
  639. local i = 0
  640. return function()
  641. i = i + 1
  642. return results[i]
  643. end
  644. end
  645.  
  646. function proxy.size(path)
  647. local fs = os.tape.readTapeFS(tapeProxy)
  648. if not fs then return 0 end
  649.  
  650. for _, file in pairs(fs.files) do
  651. if file.path == path then
  652. return file.size or 0
  653. end
  654. end
  655. return 0
  656. end
  657.  
  658. function proxy.open(path, mode)
  659. mode = mode or "r"
  660. local fs = os.tape.readTapeFS(tapeProxy)
  661.  
  662. if mode == "r" then
  663. if not fs then return nil end
  664.  
  665. for id, file in pairs(fs.files) do
  666. if file.path == path then
  667. -- Read file data from tape
  668. tapeProxy.seek(-tapeProxy.getSize() + file.offset)
  669. local data = tapeProxy.read(file.size)
  670.  
  671. return {
  672. _data = data,
  673. _pos = 0,
  674. _closed = false,
  675. _path = path
  676. }
  677. end
  678. end
  679. return nil
  680. elseif mode == "w" then
  681. if not fs then
  682. os.tape.formatTape(tapeProxy)
  683. fs = os.tape.readTapeFS(tapeProxy)
  684. end
  685.  
  686. -- Create new file entry
  687. local handle = {
  688. _data = "",
  689. _pos = 0,
  690. _closed = false,
  691. _path = path,
  692. _fs = fs,
  693. _tape = tapeProxy,
  694. _write = true
  695. }
  696.  
  697. return handle
  698. end
  699.  
  700. return nil
  701. end
  702.  
  703. function proxy.close(handle)
  704. if handle._closed then return true end
  705. handle._closed = true
  706.  
  707. if handle._write then
  708. -- Write file to tape
  709. local fs = handle._fs
  710. local fileId = fs.nextFileId
  711. fs.nextFileId = fs.nextFileId + 1
  712.  
  713. -- Remove old version if exists
  714. for id, file in pairs(fs.files) do
  715. if file.path == handle._path then
  716. fs.files[id] = nil
  717. fs.usedBlocks = fs.usedBlocks - math.ceil(file.size / os.tape.BLOCK_SIZE)
  718. break
  719. end
  720. end
  721.  
  722. -- Calculate offset (after header and existing files)
  723. local offset = os.tape.BLOCK_SIZE
  724. for _, file in pairs(fs.files) do
  725. local fileEnd = file.offset + file.size
  726. if fileEnd > offset then
  727. offset = fileEnd
  728. end
  729. end
  730.  
  731. -- Align to block
  732. offset = math.ceil(offset / os.tape.BLOCK_SIZE) * os.tape.BLOCK_SIZE
  733.  
  734. -- Add file entry
  735. fs.files[fileId] = {
  736. path = handle._path,
  737. size = #handle._data,
  738. offset = offset,
  739. isDir = false
  740. }
  741.  
  742. fs.usedBlocks = fs.usedBlocks + math.ceil(#handle._data / os.tape.BLOCK_SIZE)
  743.  
  744. -- Write file data
  745. handle._tape.seek(-handle._tape.getSize() + offset)
  746. handle._tape.write(handle._data)
  747.  
  748. -- Update filesystem header
  749. os.tape.writeTapeFS(handle._tape, fs)
  750. end
  751.  
  752. return true
  753. end
  754.  
  755. function proxy.read(handle, count)
  756. if handle._closed then return nil end
  757. count = count or math.huge
  758.  
  759. if handle._pos >= #handle._data then
  760. return nil
  761. end
  762.  
  763. local data = handle._data:sub(handle._pos + 1, handle._pos + count)
  764. handle._pos = handle._pos + #data
  765. return data
  766. end
  767.  
  768. function proxy.write(handle, data)
  769. if handle._closed or not handle._write then return false end
  770. handle._data = handle._data .. data
  771. return true
  772. end
  773.  
  774. function proxy.remove(path)
  775. local fs = os.tape.readTapeFS(tapeProxy)
  776. if not fs then return false end
  777.  
  778. for id, file in pairs(fs.files) do
  779. if file.path == path then
  780. fs.files[id] = nil
  781. fs.usedBlocks = fs.usedBlocks - math.ceil(file.size / os.tape.BLOCK_SIZE)
  782. os.tape.writeTapeFS(tapeProxy, fs)
  783. return true
  784. end
  785. end
  786.  
  787. return false
  788. end
  789.  
  790. function proxy.makeDirectory(path)
  791. local fs = os.tape.readTapeFS(tapeProxy)
  792. if not fs then
  793. os.tape.formatTape(tapeProxy)
  794. fs = os.tape.readTapeFS(tapeProxy)
  795. end
  796.  
  797. local fileId = fs.nextFileId
  798. fs.nextFileId = fs.nextFileId + 1
  799.  
  800. fs.files[fileId] = {
  801. path = path,
  802. size = 0,
  803. offset = 0,
  804. isDir = true
  805. }
  806.  
  807. os.tape.writeTapeFS(tapeProxy, fs)
  808. return true
  809. end
  810.  
  811. function proxy.rename(from, to)
  812. local fs = os.tape.readTapeFS(tapeProxy)
  813. if not fs then return false end
  814.  
  815. for _, file in pairs(fs.files) do
  816. if file.path == from then
  817. file.path = to
  818. os.tape.writeTapeFS(tapeProxy, fs)
  819. return true
  820. end
  821. end
  822.  
  823. return false
  824. end
  825.  
  826. function proxy.lastModified(path)
  827. return 0
  828. end
  829.  
  830. function proxy.getLabel()
  831. return "TapeFS"
  832. end
  833.  
  834. function proxy.setLabel(label)
  835. return label
  836. end
  837.  
  838. return proxy
  839. end
  840.  
  841. function os.tape.setTape(letter, address)
  842. letter = string.upper(letter)
  843. if address then
  844. local tapeProxy = component.proxy(address)
  845. if tapeProxy and tapeProxy.type == "tape_drive" then
  846. -- Create filesystem proxy wrapper
  847. local fsProxy = os.tape.createTapeProxy(tapeProxy)
  848. os.tape.tapes[letter] = tapeProxy
  849.  
  850. -- Also register as a filesystem drive
  851. os.fs.drives[letter] = fsProxy
  852.  
  853. os.tape.saveTapeConfig()
  854. return true
  855. end
  856. return false, "Invalid tape drive address"
  857. else
  858. os.tape.tapes[letter] = nil
  859. os.fs.drives[letter] = nil
  860. os.tape.saveTapeConfig()
  861. return true
  862. end
  863. end
  864.  
  865. function os.tape.saveTapeConfig()
  866. local config = {}
  867. for letter, proxy in pairs(os.tape.tapes) do
  868. config[letter] = proxy.address
  869. end
  870. local data = "return " .. os.serialize(config)
  871. local handle = bootfs.open("TAPES.CFG", "w")
  872. if handle then
  873. bootfs.write(handle, data)
  874. bootfs.close(handle)
  875. end
  876. end
  877.  
  878. function os.tape.loadTapeConfig()
  879. if bootfs.exists("TAPES.CFG") then
  880. local handle = bootfs.open("TAPES.CFG", "r")
  881. if handle then
  882. local data = ""
  883. repeat
  884. local chunk = bootfs.read(handle, math.huge)
  885. data = data .. (chunk or "")
  886. until not chunk
  887. bootfs.close(handle)
  888.  
  889. local func, err = load(data, "=TAPES.CFG", "t", {})
  890. if func then
  891. local ok, config = pcall(func)
  892. if ok and type(config) == "table" then
  893. for letter, address in pairs(config) do
  894. os.tape.setTape(letter, address)
  895. end
  896. end
  897. end
  898. end
  899. end
  900. end
  901.  
  902. function os.tape.getTape(letter)
  903. letter = string.upper(letter)
  904. return os.tape.tapes[letter]
  905. end
  906.  
  907. function os.tape.listTapes()
  908. local list = {}
  909. for letter, proxy in pairs(os.tape.tapes) do
  910. table.insert(list, {letter = letter, address = proxy.address})
  911. end
  912. return list
  913. end
  914.  
  915. function os.tape.setCurrentTape(letter)
  916. letter = string.upper(letter)
  917. if os.tape.tapes[letter] then
  918. os.tape.currentTape = letter
  919. os.tape.drive = os.tape.tapes[letter]
  920. return true
  921. end
  922. return false, "Tape not found"
  923. end
  924.  
  925. function os.tape.getCurrentTape()
  926. return os.tape.currentTape
  927. end
  928.  
  929. local tape = os.component.getPrimary("tape_drive")
  930. os.tape.drive = tape
  931.  
  932. function os.tape.play()
  933. if tape then
  934. return tape.play()
  935. end
  936. end
  937.  
  938. function os.tape.stop()
  939. if tape then
  940. return tape.stop()
  941. end
  942. end
  943.  
  944. function os.tape.pause()
  945. if tape then
  946. return tape.stop()
  947. end
  948. end
  949.  
  950. function os.tape.seek(position)
  951. if tape then
  952. return tape.seek(position)
  953. end
  954. end
  955.  
  956. function os.tape.read(count)
  957. if tape then
  958. return tape.read(count or 8192)
  959. end
  960. end
  961.  
  962. function os.tape.write(data)
  963. if tape then
  964. return tape.write(data)
  965. end
  966. end
  967.  
  968. function os.tape.getSize()
  969. if tape then
  970. return tape.getSize()
  971. end
  972. return 0
  973. end
  974.  
  975. function os.tape.getState()
  976. if tape then
  977. return tape.getState()
  978. end
  979. return "STOPPED"
  980. end
  981.  
  982. function os.tape.getPosition()
  983. if tape then
  984. return tape.getPosition()
  985. end
  986. return 0
  987. end
  988.  
  989. function os.tape.setSpeed(speed)
  990. if tape then
  991. return tape.setSpeed(speed)
  992. end
  993. end
  994.  
  995. function os.tape.setVolume(volume)
  996. if tape then
  997. return tape.setVolume(volume)
  998. end
  999. end
  1000.  
  1001. -- ==================== SOUND CARD MODULE ====================
  1002. os.sound = {}
  1003.  
  1004. local sound = os.component.getPrimary("sound")
  1005. os.sound.card = sound
  1006.  
  1007. function os.sound.setWave(channel, wave)
  1008. if sound then
  1009. return sound.setWave(channel, wave)
  1010. end
  1011. end
  1012.  
  1013. function os.sound.setFrequency(channel, freq)
  1014. if sound then
  1015. return sound.setFrequency(channel, freq)
  1016. end
  1017. end
  1018.  
  1019. function os.sound.open(channel)
  1020. if sound then
  1021. return sound.open(channel)
  1022. end
  1023. end
  1024.  
  1025. function os.sound.close(channel)
  1026. if sound then
  1027. return sound.close(channel)
  1028. end
  1029. end
  1030.  
  1031. function os.sound.setVolume(channel, volume)
  1032. if sound then
  1033. return sound.setVolume(channel, volume)
  1034. end
  1035. end
  1036.  
  1037. function os.sound.setDelay(delay)
  1038. if sound then
  1039. return sound.setDelay(delay)
  1040. end
  1041. end
  1042.  
  1043. function os.sound.process()
  1044. if sound then
  1045. return sound.process()
  1046. end
  1047. end
  1048.  
  1049. function os.sound.beep(frequency, duration)
  1050. if sound then
  1051. os.sound.setWave(1, os.sound.modes.square)
  1052. os.sound.open(1)
  1053. os.sound.setFrequency(1, frequency or 1000)
  1054. os.sound.setVolume(1, 1.0)
  1055. computer.pullSignal(duration or 0.2)
  1056. os.sound.close(1)
  1057. end
  1058. end
  1059.  
  1060. os.sound.modes = {
  1061. sine = 1,
  1062. square = 2,
  1063. triangle = 3,
  1064. sawtooth = 4,
  1065. }
  1066.  
  1067. -- ==================== EVENT/SIGNAL MODULE ====================
  1068. os.event = {}
  1069.  
  1070. function os.event.pull(timeout, filter)
  1071. local deadline = computer.uptime() + (timeout or math.huge)
  1072. repeat
  1073. local signal = table.pack(computer.pullSignal(deadline - computer.uptime()))
  1074. if signal.n > 0 then
  1075. -- Update modifier key states for keyboard events
  1076. if signal[1] == "key_down" or signal[1] == "key_up" then
  1077. os.keyboard._updateModifiers(signal[1], signal[4])
  1078. end
  1079.  
  1080. if not filter or signal[1] == filter then
  1081. return table.unpack(signal, 1, signal.n)
  1082. end
  1083. end
  1084. until computer.uptime() >= deadline
  1085. return nil
  1086. end
  1087.  
  1088. function os.event.push(...)
  1089. return computer.pushSignal(...)
  1090. end
  1091.  
  1092. -- ==================== SYSTEM UTILITIES ====================
  1093. os.system = {}
  1094.  
  1095. function os.system.shutdown(reboot)
  1096. if reboot then
  1097. computer.shutdown(true)
  1098. else
  1099. computer.shutdown()
  1100. end
  1101. end
  1102.  
  1103. function os.system.uptime()
  1104. return computer.uptime()
  1105. end
  1106.  
  1107. function os.system.freeMemory()
  1108. return computer.freeMemory()
  1109. end
  1110.  
  1111. function os.system.totalMemory()
  1112. return computer.totalMemory()
  1113. end
  1114.  
  1115. function os.system.energy()
  1116. return computer.energy()
  1117. end
  1118.  
  1119. function os.system.maxEnergy()
  1120. return computer.maxEnergy()
  1121. end
  1122.  
  1123. function os.system.sleep(seconds)
  1124. local deadline = computer.uptime() + (seconds or 0)
  1125. repeat
  1126. computer.pullSignal(deadline - computer.uptime())
  1127. until computer.uptime() >= deadline
  1128. end
  1129.  
  1130. function os.system.beep(frequency, duration)
  1131. computer.beep(frequency, duration)
  1132. end
  1133.  
  1134. -- ==================== STRING UTILITIES ====================
  1135. os.string = {}
  1136.  
  1137. function os.string.split(str, sep)
  1138. local parts = {}
  1139. local pattern = string.format("([^%s]+)", sep or " ")
  1140. for part in string.gmatch(str, pattern) do
  1141. table.insert(parts, part)
  1142. end
  1143. return parts
  1144. end
  1145.  
  1146. function os.string.trim(str)
  1147. return string.match(str, "^%s*(.-)%s*$")
  1148. end
  1149.  
  1150. function os.string.startsWith(str, prefix)
  1151. return string.sub(str, 1, #prefix) == prefix
  1152. end
  1153.  
  1154. function os.string.endsWith(str, suffix)
  1155. return string.sub(str, -#suffix) == suffix
  1156. end
  1157.  
  1158. -- ==================== SERIALIZATION ====================
  1159. function os.serialize(value, indent)
  1160. indent = indent or 0
  1161. local t = type(value)
  1162.  
  1163. if t == "number" or t == "boolean" then
  1164. return tostring(value)
  1165. elseif t == "string" then
  1166. return string.format("%q", value)
  1167. elseif t == "table" then
  1168. local parts = {}
  1169. local spaces = string.rep(" ", indent)
  1170. table.insert(parts, "{")
  1171. for k, v in pairs(value) do
  1172. local key
  1173. if type(k) == "string" and k:match("^[%a_][%w_]*$") then
  1174. key = k
  1175. else
  1176. key = "[" .. os.serialize(k) .. "]"
  1177. end
  1178. table.insert(parts, spaces .. " " .. key .. " = " .. os.serialize(v, indent + 1) .. ",")
  1179. end
  1180. table.insert(parts, spaces .. "}")
  1181. return table.concat(parts, "\n")
  1182. else
  1183. return "nil"
  1184. end
  1185. end
  1186.  
  1187. -- ==================== PATH UTILITIES ====================
  1188. os.path = {}
  1189.  
  1190. function os.path.concat(...)
  1191. local parts = {...}
  1192. local result = ""
  1193. for i, part in ipairs(parts) do
  1194. if part ~= "" then
  1195. if result ~= "" and not result:match("/$") then
  1196. result = result .. "/"
  1197. end
  1198. result = result .. part
  1199. end
  1200. end
  1201. return result
  1202. end
  1203.  
  1204. function os.path.split(path)
  1205. local dir, file = path:match("^(.+)/([^/]+)$")
  1206. if not dir then
  1207. return "/", path
  1208. end
  1209. return dir, file
  1210. end
  1211.  
  1212. function os.path.name(path)
  1213. return path:match("[^/]+$") or path
  1214. end
  1215.  
  1216. function os.path.extension(path)
  1217. return path:match("%.([^./]+)$") or ""
  1218. end
  1219.  
  1220. -- Initialize system
  1221. if gpu then
  1222. gpu.set(1, 1, "OS.SYS loaded successfully")
  1223. end
  1224.  
  1225. -- Load saved drive configurations
  1226. os.fs.loadDriveConfig()
  1227. os.tape.loadTapeConfig()
  1228.  
  1229. return true
Advertisement
Add Comment
Please, Sign In to add comment