mirror of
https://github.com/Adorable-Catgirl/Zorya-NEO.git
synced 2024-12-24 09:58:06 +11:00
1173 lines
38 KiB
Lua
1173 lines
38 KiB
Lua
|
--[[
|
||
|
This will not support FAT32.
|
||
|
1. Because FAT16 won't even properly fit on a tape or max sized file.
|
||
|
2. Because FAT32's structure is incompatible with FAT12 and FAT16
|
||
|
--]]
|
||
|
--local vcomp = require("vcomponent")
|
||
|
local fs = {}
|
||
|
--local io = require("io")
|
||
|
|
||
|
function fs.canonical(path)
|
||
|
local result = table.concat(segments(path), "/")
|
||
|
if unicode.sub(path, 1, 1) == "/" then
|
||
|
return "/" .. result
|
||
|
else
|
||
|
return result
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function fs.segments(path)
|
||
|
local parts = {}
|
||
|
for part in path:gmatch("[^\\/]+") do
|
||
|
local current, up = part:find("^%.?%.$")
|
||
|
if current then
|
||
|
if up == 2 then
|
||
|
table.remove(parts)
|
||
|
end
|
||
|
else
|
||
|
table.insert(parts, part)
|
||
|
end
|
||
|
end
|
||
|
return parts
|
||
|
end
|
||
|
|
||
|
local NUL = '\0'
|
||
|
|
||
|
local msdos = {}
|
||
|
local _msdos = {}
|
||
|
|
||
|
function _msdos.readRawString(file, size)
|
||
|
local str = ""
|
||
|
while #str < size do
|
||
|
str = str .. file:read(size - #str)
|
||
|
end
|
||
|
return str
|
||
|
end
|
||
|
|
||
|
if string.pack then
|
||
|
function _msdos.string2number(data)
|
||
|
return string.unpack("<I" .. #data,data)
|
||
|
end
|
||
|
|
||
|
function _msdos.number2string(data, size)
|
||
|
return string.pack("<I" .. size,data)
|
||
|
end
|
||
|
else
|
||
|
function _msdos.string2number(data)
|
||
|
local count = 0
|
||
|
for i = 1,#data do
|
||
|
count = count + bit32.lshift(data:byte(i,i),(i - 1) * 8)
|
||
|
end
|
||
|
return count
|
||
|
end
|
||
|
|
||
|
function _msdos.number2string(data, size)
|
||
|
local str = ""
|
||
|
for i = 1, size do
|
||
|
str = str .. string.char(bit32.rshift(bit32.band(data, bit32.lshift(0xFF, (i - 1) * 8)), (i - 1) * 8))
|
||
|
end
|
||
|
return str
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function _msdos.validateName(name)
|
||
|
if name:find(".",nil,true) then
|
||
|
if #name > 12 then
|
||
|
return false
|
||
|
end
|
||
|
local filename, ext = name:match("(.*)%.(.+)")
|
||
|
if #filename > 8 or filename:find(".",nil,true) or #ext > 3 then
|
||
|
return false
|
||
|
end
|
||
|
else
|
||
|
if #name > 8 then
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
function _msdos.spacetrim(data)
|
||
|
while true do
|
||
|
if data:sub(-1,-1) ~= " " and data:sub(-1,-1) ~= NUL then
|
||
|
break
|
||
|
end
|
||
|
data = data:sub(1,-2)
|
||
|
end
|
||
|
return data
|
||
|
end
|
||
|
|
||
|
function _msdos.cleanPath(path)
|
||
|
return table.concat(fs.segments(path), "/"):lower()
|
||
|
end
|
||
|
|
||
|
function _msdos.readDirEntry(fatset,block,count)
|
||
|
local entry = {}
|
||
|
local filename = _msdos.spacetrim(block:sub(1,8))
|
||
|
local ext = _msdos.spacetrim(block:sub(9,11))
|
||
|
entry.rawfilename = filename .. (ext ~= "" and "." or "") .. ext
|
||
|
entry.filename = string.lower(entry.rawfilename)
|
||
|
entry.attrib = _msdos.string2number(block:sub(12,12))
|
||
|
entry.modifyT = _msdos.string2number(block:sub(23,24))
|
||
|
entry.modifyD = _msdos.string2number(block:sub(25,26))
|
||
|
local cluster
|
||
|
if fatset.fatsize == 12 then
|
||
|
cluster = _msdos.string2number(block:sub(27,28)) % 0x1000
|
||
|
else
|
||
|
cluster = _msdos.string2number(block:sub(27,28))
|
||
|
end
|
||
|
entry.cluster = cluster
|
||
|
entry.size = _msdos.string2number(block:sub(29,32))
|
||
|
return entry
|
||
|
end
|
||
|
|
||
|
function _msdos.readDirBlock(fatset, block)
|
||
|
local list = {}
|
||
|
for i = 0, (#block / 32) - 1 do
|
||
|
local data = _msdos.readDirEntry(fatset, block:sub(i * 32 + 1, (i + 1) * 32),i)
|
||
|
table.insert(list, data)
|
||
|
end
|
||
|
return list
|
||
|
end
|
||
|
|
||
|
function _msdos.cluster2block(fatset, cluster)
|
||
|
return fatset.rb + (fatset.fatc * fatset.fatbc) + (fatset.rdec * 32 / fatset.bps) + ((cluster - 2) * fatset.spc)
|
||
|
end
|
||
|
|
||
|
function _msdos.fatclusterlookup(fatset, cluster)
|
||
|
return (fatset.bps * fatset.rb) + (cluster * 2)
|
||
|
end
|
||
|
|
||
|
function _msdos.getFATEntry12(fatset, cluster)
|
||
|
cluster = cluster % 0x1000
|
||
|
local fatTable = fatset.fatCache.fatTable
|
||
|
if cluster % 2 == 0 then
|
||
|
return bit32.band(_msdos.string2number(fatTable:sub((cluster * 1.5) + 1, (cluster * 1.5) + 2)), 0x0FFF)
|
||
|
else
|
||
|
return bit32.rshift(_msdos.string2number(fatTable:sub(math.floor(cluster * 1.5) + 1, math.floor(cluster * 1.5) + 2)), 4)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function _msdos.getFATEntry16(fatset, cluster)
|
||
|
cluster = cluster % 0x10000
|
||
|
local fatTable = fatset.fatCache.fatTable
|
||
|
return _msdos.string2number(fatTable:sub((cluster * 2) + 1, (cluster * 2) + 2))
|
||
|
end
|
||
|
|
||
|
function _msdos.getFATEntry(fatset, cluster)
|
||
|
if fatset.fatsize == 12 then
|
||
|
return _msdos.getFATEntry12(fatset, cluster)
|
||
|
else
|
||
|
return _msdos.getFATEntry16(fatset, cluster)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function _msdos.setFATEntry12(fatset, file, cluster, data)
|
||
|
cluster = cluster % 0x1000
|
||
|
if cluster < 2 then
|
||
|
error("Attempted to modify cluster " .. cluster .. "\n" .. debug.traceback())
|
||
|
end
|
||
|
local fatTable = fatset.fatCache.fatTable
|
||
|
if cluster % 2 == 0 then
|
||
|
local fatcrap = _msdos.string2number(fatTable:sub((cluster * 1.5) + 1, (cluster * 1.5) + 2))
|
||
|
fatcrap = _msdos.number2string(bit32.band(fatcrap,0xF000) + bit32.band(data,0x0FFF), 2)
|
||
|
|
||
|
for i = 0,fatset.fatc - 1 do
|
||
|
file:seek("set", (fatset.bps * fatset.rb) + (i * fatset.bps * fatset.fatbc) + (cluster * 1.5))
|
||
|
file:write(fatcrap)
|
||
|
end
|
||
|
fatTable = fatTable:sub(1, (cluster * 1.5)) .. fatcrap .. fatTable:sub((cluster * 1.5) + 3)
|
||
|
else
|
||
|
local fatcrap = _msdos.string2number(fatTable:sub(math.floor(cluster * 1.5) + 1, math.floor(cluster * 1.5) + 2))
|
||
|
fatcrap = _msdos.number2string(bit32.lshift(data,4) + bit32.band(fatcrap,0x000F), 2)
|
||
|
|
||
|
for i = 0,fatset.fatc - 1 do
|
||
|
file:seek("set", (fatset.bps * fatset.rb) + math.floor(cluster * 1.5))
|
||
|
file:write(fatcrap)
|
||
|
end
|
||
|
fatTable = fatTable:sub(1, math.floor(cluster * 1.5)) .. fatcrap .. fatTable:sub(math.floor(cluster * 1.5) + 3)
|
||
|
end
|
||
|
fatset.fatCache.fatTable = fatTable
|
||
|
end
|
||
|
|
||
|
function _msdos.setFATEntry16(fatset, file, cluster, data)
|
||
|
cluster = cluster % 0x10000
|
||
|
if cluster < 2 then
|
||
|
error("Attempted to modify cluster " .. cluster .. "\n" .. debug.traceback())
|
||
|
end
|
||
|
local fatTable = fatset.fatCache.fatTable
|
||
|
local fatcrap = _msdos.number2string(data, 2)
|
||
|
for i = 0,fatset.fatc - 1 do
|
||
|
file:seek("set", (fatset.bps * fatset.rb) + (i * fatset.bps * fatset.fatbc) + (cluster * 2))
|
||
|
file:write(fatcrap)
|
||
|
end
|
||
|
fatTable = fatTable:sub(1, (cluster * 2)) .. fatcrap .. fatTable:sub((cluster * 2) + 3)
|
||
|
fatset.fatCache.fatTable = fatTable
|
||
|
end
|
||
|
|
||
|
function _msdos.setFATEntry(fatset, file, cluster, data)
|
||
|
if fatset.fatsize == 12 then
|
||
|
_msdos.setFATEntry12(fatset, file, cluster, data)
|
||
|
else
|
||
|
_msdos.setFATEntry16(fatset, file, cluster, data)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function _msdos.getClusterChain(fatset, startcluster)
|
||
|
local cache = {[startcluster] = true}
|
||
|
local chain = {startcluster}
|
||
|
local nextcluster = startcluster
|
||
|
local highcluster
|
||
|
if fatset.fatsize == 12 then
|
||
|
highcluster = 0x0FF7
|
||
|
else
|
||
|
highcluster = 0xFFF7
|
||
|
end
|
||
|
if nextcluster <= 0x0002 or nextcluster >= highcluster then
|
||
|
return chain
|
||
|
end
|
||
|
while true do
|
||
|
if fatset.fatsize == 12 then
|
||
|
nextcluster = _msdos.getFATEntry12(fatset, nextcluster)
|
||
|
else
|
||
|
nextcluster = _msdos.getFATEntry16(fatset, nextcluster)
|
||
|
end
|
||
|
table.insert(chain, nextcluster)
|
||
|
if nextcluster <= 0x0002 or nextcluster >= highcluster or cache[nextcluster] == true then
|
||
|
if nextcluster <= highcluster then
|
||
|
print("msdos: Bad cluster chain, " .. startcluster)
|
||
|
print(table.concat(chain, ","))
|
||
|
end
|
||
|
break
|
||
|
end
|
||
|
cache[nextcluster] = true
|
||
|
end
|
||
|
return chain
|
||
|
end
|
||
|
|
||
|
function _msdos.findFreeCluster(fatset)
|
||
|
local fatTable = fatset.fatCache.fatTable
|
||
|
for i = 0, fatset.tnoc - 1 do
|
||
|
if not fatset.reserved[i] then
|
||
|
local entry
|
||
|
if fatset.fatsize == 12 then
|
||
|
entry = _msdos.getFATEntry12(fatset, i)
|
||
|
else
|
||
|
entry = _msdos.getFATEntry16(fatset, i)
|
||
|
end
|
||
|
if entry == 0 then
|
||
|
return i
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function _msdos.readEntireEntry(fatset, file, startcluster)
|
||
|
local list = _msdos.getClusterChain(fatset, startcluster)
|
||
|
local data = ""
|
||
|
for i = 1,#list - 1 do
|
||
|
file:seek("set", _msdos.cluster2block(fatset, list[i]) * fatset.bps)
|
||
|
data = data .. _msdos.readRawString(file, fatset.bps * fatset.spc)
|
||
|
end
|
||
|
return data
|
||
|
end
|
||
|
|
||
|
function _msdos.searchDirectoryLists(fatset, file, path)
|
||
|
local pathsplit = {}
|
||
|
for dir in path:gmatch("[^/]+") do
|
||
|
if not _msdos.validateName(dir) then
|
||
|
return false
|
||
|
end
|
||
|
table.insert(pathsplit, dir)
|
||
|
end
|
||
|
if fatset.fatCache.directoryLists[path] ~= nil then
|
||
|
return true, fatset.fatCache.directoryLists[path].pos, fatset.fatCache.directoryLists[path].cluster
|
||
|
end
|
||
|
local blockpos = (fatset.rb + (fatset.fatc * fatset.fatbc))
|
||
|
local entrycluster
|
||
|
local found = true
|
||
|
for i = 1,#pathsplit do
|
||
|
local block
|
||
|
if i == 1 then
|
||
|
file:seek("set", fatset.bps * blockpos)
|
||
|
block = _msdos.readRawString(file, fatset.rdec * 32)
|
||
|
else
|
||
|
block = _msdos.readEntireEntry(fatset, file, entrycluster)
|
||
|
end
|
||
|
local dirlist = _msdos.readDirBlock(fatset, block)
|
||
|
found = false
|
||
|
for _,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag ~= NUL and fileflag ~= string.char(0xE5) and bit32.band(data.attrib,0x08) == 0 and data.filename ~= "." and data.filename ~= ".." then
|
||
|
if bit32.band(data.attrib,0x10) ~= 0 then
|
||
|
-- Cache folders and their cluster as well
|
||
|
local cacheName = ""
|
||
|
for j = 1, i - 1 do
|
||
|
cacheName = cacheName .. pathsplit[j] .. "/"
|
||
|
end
|
||
|
cacheName = cacheName .. data.filename
|
||
|
fatset.fatCache.directoryLists[cacheName] = {pos = _msdos.cluster2block(fatset, data.cluster), cluster = data.cluster}
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
for _,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag ~= NUL and fileflag ~= string.char(0xE5) and bit32.band(data.attrib,0x08) == 0 and data.filename ~= "." and data.filename ~= ".." then
|
||
|
if data.filename == pathsplit[i] then
|
||
|
blockpos = _msdos.cluster2block(fatset, data.cluster)
|
||
|
entrycluster = data.cluster
|
||
|
found = true
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if found == false then
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
if found == true then
|
||
|
fatset.fatCache.directoryLists[path] = {pos = blockpos, cluster = entrycluster}
|
||
|
end
|
||
|
return found, blockpos, entrycluster
|
||
|
end
|
||
|
|
||
|
function _msdos.doSomethingForFile(fatset, file, path, something)
|
||
|
local name = path:match(".-([^\\/]-%.?)$")
|
||
|
if not _msdos.validateName(name) then
|
||
|
return false
|
||
|
end
|
||
|
path = fs.canonical(path .. "/..")
|
||
|
found, blockpos, entrycluster = _msdos.searchDirectoryLists(fatset, file, path)
|
||
|
if found == false then
|
||
|
return false
|
||
|
end
|
||
|
file:seek("set", fatset.bps * blockpos)
|
||
|
local block
|
||
|
if entrycluster == nil then
|
||
|
block = _msdos.readRawString(file, fatset.rdec * 32)
|
||
|
else
|
||
|
block = _msdos.readEntireEntry(fatset, file, entrycluster)
|
||
|
end
|
||
|
local dirlist = _msdos.readDirBlock(fatset, block)
|
||
|
for index,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag ~= NUL and fileflag ~= string.char(0xE5) and bit32.band(data.attrib,0x08) == 0 and data.filename ~= "." and data.filename ~= ".." then
|
||
|
if name == data.filename then
|
||
|
something(data, index, blockpos, entrycluster)
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
function _msdos.recursiveKill(fatset, file, entrycluster)
|
||
|
local killed = {}
|
||
|
local block = _msdos.readEntireEntry(fatset, file, entrycluster)
|
||
|
local dirlist = _msdos.readDirBlock(fatset, block)
|
||
|
found = false
|
||
|
for index,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag ~= NUL and fileflag ~= string.char(0xE5) and bit32.band(data.attrib,0x08) == 0 then
|
||
|
if bit32.band(data.attrib,0x10) ~= 0 and data.filename ~= "." and data.filename ~= ".." then
|
||
|
local kill = _msdos.recursiveKill(fatset, file, data.cluster)
|
||
|
for i = 1,#kill do
|
||
|
table.insert(killed, kill[i])
|
||
|
end
|
||
|
table.insert(killed, data.cluster)
|
||
|
elseif bit32.band(data.attrib,0x10) == 0 and data.size > 0 then
|
||
|
local kill = _msdos.getClusterChain(fatset, data.cluster)
|
||
|
for i = 1, #kill - 1 do
|
||
|
table.insert(killed, kill[i])
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
return killed
|
||
|
end
|
||
|
|
||
|
function msdos.proxy(fatfile, fatsize)
|
||
|
checkArg(1,fatfile,"table")
|
||
|
checkArg(2,fatsize,"nil","number")
|
||
|
|
||
|
if fatsize == 28 then fatsize = 32 end -- Allow for FAT28
|
||
|
if fatsize ~= nil and fatsize ~= 12 and fatsize ~= 16 and fatsize ~= 32 then
|
||
|
error("Invalid FAT size",2)
|
||
|
end
|
||
|
if fatsize == 32 then
|
||
|
error("FAT32 is unsupported by this driver.",2)
|
||
|
end
|
||
|
|
||
|
--if not fs.exists(fatfile) then
|
||
|
-- error("No such file",2)
|
||
|
--end
|
||
|
----local file = io.open(fatfile,"rb")
|
||
|
if file == nil then
|
||
|
error("Needs a block device")
|
||
|
end
|
||
|
local pos, err = file:seek("set", 0x36)
|
||
|
if pos == nil then
|
||
|
error("Seeking failed: " .. err)
|
||
|
end
|
||
|
local bbs = _msdos.readRawString(file, 8)
|
||
|
local fatset = {}
|
||
|
file:seek("set", 0)
|
||
|
local boot_block = _msdos.readRawString(file, 62)
|
||
|
fatset.bps = _msdos.string2number(boot_block:sub(0x0C, 0x0D))
|
||
|
fatset.spc = _msdos.string2number(boot_block:sub(0x0E, 0x0E))
|
||
|
fatset.rb = _msdos.string2number(boot_block:sub(0x0F, 0x10))
|
||
|
fatset.fatc = _msdos.string2number(boot_block:sub(0x11, 0x11))
|
||
|
fatset.rdec = _msdos.string2number(boot_block:sub(0x12, 0x13))
|
||
|
fatset.fatbc = _msdos.string2number(boot_block:sub(0x17, 0x18))
|
||
|
fatset.hbc = _msdos.string2number(boot_block:sub(0x1D, 0x20))
|
||
|
fatset.vsn = _msdos.string2number(boot_block:sub(0x28, 0x2B))
|
||
|
fatset.bpblabel = boot_block:sub(0x2C, 0x36)
|
||
|
fatset.label = _msdos.spacetrim(fatset.bpblabel)
|
||
|
fatset.ident = boot_block:sub(0x37, 0x3E)
|
||
|
local tnos = _msdos.string2number(boot_block:sub(0x14, 0x15))
|
||
|
if tnos == 0 then
|
||
|
tnos = _msdos.string2number(boot_block:sub(0x21, 0x24))
|
||
|
end
|
||
|
fatset.tnos = tnos
|
||
|
fatset.tnoc = math.floor(tnos / fatset.spc)
|
||
|
if fatsize == nil then
|
||
|
print("msdos: Detecting FAT size ...")
|
||
|
print("msdos: Ident suggests: " .. fatset.ident)
|
||
|
if fatset.tnoc < 4085 then
|
||
|
print("msdos: Detected FAT size as FAT12")
|
||
|
fatsize = 12
|
||
|
elseif fatset.tnoc < 65525 then
|
||
|
print("msdos: Detected FAT size as FAT16")
|
||
|
fatsize = 16
|
||
|
else
|
||
|
print("msdos: Detected FAT size as FAT32")
|
||
|
fatsize = 32
|
||
|
end
|
||
|
end
|
||
|
if fatsize == 32 then
|
||
|
error("FAT32 is unsupported by this driver.",2)
|
||
|
end
|
||
|
fatset.fatsize = fatsize
|
||
|
fatset.fatCache = {}
|
||
|
fatset.fatCache.directoryLists = {}
|
||
|
-- fatset.fatCache. -- TODO: Where was I going with this? >_>
|
||
|
file:seek("set", fatset.bps * fatset.rb)
|
||
|
local fats = {}
|
||
|
for i = 1,fatset.fatc do
|
||
|
fats[i] = _msdos.readRawString(file, fatset.bps * fatset.fatbc)
|
||
|
end
|
||
|
--file:close()
|
||
|
if fatset.fatc > 1 then -- Validate FATs are the same
|
||
|
for i = 1,fatset.fatc - 1 do
|
||
|
if fats[i] ~= fats[i+1] then
|
||
|
print("msdos: FAT tables are inconsistent")
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
fatset.fatCache.fatTable = fats[1]
|
||
|
fats = nil
|
||
|
local filedescript = {}
|
||
|
local writeBuff = {} -- TODO: Use
|
||
|
fatset.readBuff = {} -- TODO: Use?
|
||
|
fatset.reserved = {} -- Clusters reserved but not used
|
||
|
local proxyObj = {}
|
||
|
proxyObj.type = "filesystem"
|
||
|
proxyObj.address = string.format("%04x",math.floor(fatset.vsn/65536)) .. "-" .. string.format("%04x",fatset.vsn % 0x10000) -- FAT Serial Number
|
||
|
local file = fatfile
|
||
|
proxyObj.isDirectory = function(path)
|
||
|
checkArg(1,path,"string")
|
||
|
path = _msdos.cleanPath(path)
|
||
|
if path == "" then
|
||
|
return true
|
||
|
end
|
||
|
local isDirectory
|
||
|
----local file = io.open(fatfile,"rb")
|
||
|
local found = _msdos.doSomethingForFile(fatset, file, path, function(data) isDirectory = bit32.band(data.attrib,0x10) ~= 0 end)
|
||
|
----file:close()
|
||
|
if not found then
|
||
|
return false
|
||
|
end
|
||
|
return isDirectory
|
||
|
end
|
||
|
proxyObj.lastModified = function(path)
|
||
|
checkArg(1,path,"string")
|
||
|
path = _msdos.cleanPath(path)
|
||
|
if path == "" then
|
||
|
-- No modification date for root directory
|
||
|
return 0
|
||
|
end
|
||
|
local modifyT, modifyD
|
||
|
----local file = io.open(fatfile,"rb")
|
||
|
local found = _msdos.doSomethingForFile(fatset, file, path, function(data) modifyT, modifyD = data.modifyT, data.modifyD end)
|
||
|
----file:close()
|
||
|
if not found then
|
||
|
return 0
|
||
|
end
|
||
|
local year = bit32.rshift(bit32.band(modifyD, 0xFE00), 9) + 1980
|
||
|
local month = bit32.rshift(bit32.band(modifyD, 0x01E0), 5)
|
||
|
local day = bit32.band(modifyD, 0x001F)
|
||
|
local hour = bit32.rshift(bit32.band(modifyT, 0xF800), 11)
|
||
|
local min = bit32.rshift(bit32.band(modifyT, 0x07E0), 5)
|
||
|
local sec = bit32.band(modifyT, 0x001F) * 2
|
||
|
return os.time{year=year, month=month, day=day, hour=hour, min=min, sec=sec}
|
||
|
end
|
||
|
proxyObj.list = function(path)
|
||
|
checkArg(1,path,"string")
|
||
|
path = _msdos.cleanPath(path)
|
||
|
--local file = io.open(fatfile,"rb")
|
||
|
found, blockpos, entrycluster = _msdos.searchDirectoryLists(fatset, file, path)
|
||
|
if found == false then
|
||
|
----file:close()
|
||
|
return nil, "no such file or directory"
|
||
|
end
|
||
|
local block
|
||
|
if entrycluster == nil then
|
||
|
file:seek("set", fatset.bps * blockpos)
|
||
|
block = _msdos.readRawString(file, fatset.rdec * 32)
|
||
|
else
|
||
|
block = _msdos.readEntireEntry(fatset, file, entrycluster)
|
||
|
end
|
||
|
----file:close()
|
||
|
local dirlist = _msdos.readDirBlock(fatset, block)
|
||
|
local fslist = {}
|
||
|
for _,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag ~= NUL and fileflag ~= string.char(0xE5) and bit32.band(data.attrib,0x08) == 0 and data.filename ~= "." and data.filename ~= ".." then
|
||
|
if bit32.band(data.attrib,0x10) ~= 0 then
|
||
|
table.insert(fslist, data.filename .. "/")
|
||
|
-- Cache folders and their cluster as well
|
||
|
local cacheName = path .. (path ~= "" and "/" or "") .. data.filename
|
||
|
fatset.fatCache.directoryLists[cacheName] = {pos = _msdos.cluster2block(fatset, data.cluster), cluster = data.cluster}
|
||
|
else
|
||
|
table.insert(fslist, data.filename)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
fslist.n = #fslist
|
||
|
return fslist
|
||
|
end
|
||
|
proxyObj.spaceTotal = function()
|
||
|
return fatset.tnoc * fatset.spc * fatset.bps
|
||
|
end
|
||
|
proxyObj.exists = function(path)
|
||
|
checkArg(1,path,"string")
|
||
|
path = _msdos.cleanPath(path)
|
||
|
if path == "" then
|
||
|
return true
|
||
|
end
|
||
|
----local file = io.open(fatfile,"rb")
|
||
|
local found = _msdos.doSomethingForFile(fatset, file, path, function() end)
|
||
|
----file:close()
|
||
|
return found
|
||
|
end
|
||
|
proxyObj.open = function(path, mode)
|
||
|
checkArg(1,path,"string")
|
||
|
checkArg(2,mode,"string")
|
||
|
if mode ~= "r" and mode ~= "rb" and mode ~= "w" and mode ~= "wb" and mode ~= "a" and mode ~= "ab" then
|
||
|
error("unsupported mode",2)
|
||
|
end
|
||
|
path = _msdos.cleanPath(path)
|
||
|
if path == "" then
|
||
|
return nil, "file not found"
|
||
|
end
|
||
|
local filecluster, filesize
|
||
|
----local file = io.open(fatfile,"rb")
|
||
|
local found = _msdos.doSomethingForFile(fatset, file, path, function(data) filecluster, filesize = data.cluster, data.size end)
|
||
|
if not found then
|
||
|
if mode:sub(1,1) ~= "w" and mode:sub(1,1) ~= "a" then
|
||
|
--file:close()
|
||
|
return nil, "file not found"
|
||
|
else
|
||
|
-- Allocate file.
|
||
|
local newname = path:match(".-([^\\/]-%.?)$")
|
||
|
if not _msdos.validateName(newname) then
|
||
|
return nil, "Invalid file path"
|
||
|
end
|
||
|
|
||
|
dirpath = fs.canonical(path .. "/..")
|
||
|
|
||
|
found, blockpos, entrycluster = _msdos.searchDirectoryLists(fatset, file, dirpath)
|
||
|
|
||
|
local block
|
||
|
if entrycluster == nil then
|
||
|
file:seek("set", fatset.bps * blockpos)
|
||
|
block = _msdos.readRawString(file, fatset.rdec * 32)
|
||
|
else
|
||
|
block = _msdos.readEntireEntry(fatset, file, entrycluster)
|
||
|
end
|
||
|
|
||
|
local dirlist = _msdos.readDirBlock(fatset, block)
|
||
|
local found = false
|
||
|
for index,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag == NUL or fileflag == string.char(0xE5) then
|
||
|
found = true
|
||
|
local filename, ext
|
||
|
if newname:find(".",nil,true) ~= nil then
|
||
|
filename, ext = newname:match("(.*)%.(.+)")
|
||
|
ext = ext .. string.rep(" ", 3 - #ext)
|
||
|
else
|
||
|
filename = newname
|
||
|
ext = " "
|
||
|
end
|
||
|
filename = filename .. string.rep(" ", 8 - #filename)
|
||
|
|
||
|
local curDate = os.date("*t")
|
||
|
local createT = bit32.lshift(curDate.hour, 11) + bit32.lshift(curDate.min, 5) + math.floor(curDate.sec/2)
|
||
|
local createD = bit32.lshift(math.max(curDate.year - 1980,0), 9) + bit32.lshift(curDate.month, 5) + curDate.day
|
||
|
if curDate.year - 1980 < 0 then
|
||
|
print("msdos: WARNING: Current year before 1980, year will be invalid")
|
||
|
end
|
||
|
local entry = filename .. ext .. string.char(0x20) .. string.rep(NUL, 10) .. _msdos.number2string(createT,2) .. _msdos.number2string(createD,2) .. string.rep(NUL, 6)
|
||
|
if entrycluster == nil then
|
||
|
----file:close()
|
||
|
--file = io.open(fatfile,"ab")
|
||
|
file:seek("set", fatset.bps * blockpos + ((index - 1) * 32))
|
||
|
file:write(entry)
|
||
|
else
|
||
|
local list = _msdos.getClusterChain(fatset, entrycluster)
|
||
|
----file:close()
|
||
|
local clusterList = math.floor((index - 1) / (fatset.bps * fatset.spc / 32))
|
||
|
local clusterPos = (index - 1) % (fatset.bps * fatset.spc / 32)
|
||
|
--file = io.open(fatfile,"ab")
|
||
|
file:seek("set", (fatset.bps * _msdos.cluster2block(fatset, list[clusterList + 1])) + (clusterPos * 32))
|
||
|
--file:write(entry)
|
||
|
end
|
||
|
----file:close()
|
||
|
--file = io.open(fatfile,"rb")
|
||
|
filecluster, filesize = 0, 0
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
-- TODO: Attempt to find, clean, and add a cluster
|
||
|
if not found then
|
||
|
print("msdos: No available entry")
|
||
|
return nil, "not enough space"
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
while true do
|
||
|
local rnddescrpt = math.random(1000000000,9999999999)
|
||
|
if filedescript[rnddescrpt] == nil then
|
||
|
filedescript[rnddescrpt] = {
|
||
|
seek = 0,
|
||
|
mode = mode:sub(1,1) == "r" and "r" or "w",
|
||
|
buffer = "",
|
||
|
size = filesize,
|
||
|
path = path
|
||
|
}
|
||
|
if filecluster == 0 then
|
||
|
filedescript[rnddescrpt].chain = {}
|
||
|
else
|
||
|
filedescript[rnddescrpt].chain = _msdos.getClusterChain(fatset, filecluster)
|
||
|
end
|
||
|
if mode:sub(1,1) == "a" then
|
||
|
-- TODO: Loading the entire file is bad.
|
||
|
-- Do a little check
|
||
|
if filecluster == 0 and filesize ~= 0 then
|
||
|
print("msdos: Zero cluster with non zero file size")
|
||
|
print(path)
|
||
|
elseif filecluster ~= 0 and filesize == 0 then
|
||
|
print("msdos: Non zero cluster with zero file size")
|
||
|
print(path)
|
||
|
elseif filecluster ~= 0 then -- Don't attempt to load nothing
|
||
|
filedescript[rnddescrpt].buffer = _msdos.readEntireEntry(fatset, file, filecluster)
|
||
|
filedescript[rnddescrpt].buffer = filedescript[rnddescrpt].buffer:sub(1, filesize)
|
||
|
end
|
||
|
end
|
||
|
----file:close()
|
||
|
return rnddescrpt
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
proxyObj.remove = function(path)
|
||
|
checkArg(1,path,"string")
|
||
|
path = _msdos.cleanPath(path)
|
||
|
if path == "" then
|
||
|
-- TODO: Simply clear the root directory and the fat table.
|
||
|
return false
|
||
|
end
|
||
|
local name = path:match(".-([^\\/]-%.?)$")
|
||
|
if not _msdos.validateName(name) then
|
||
|
return false
|
||
|
end
|
||
|
path = fs.canonical(path .. "/..")
|
||
|
----local file = io.open(fatfile,"rb")
|
||
|
found, blockpos, entrycluster = _msdos.searchDirectoryLists(fatset, file, path)
|
||
|
if found == false then
|
||
|
--file:close()
|
||
|
return false
|
||
|
end
|
||
|
local block
|
||
|
if entrycluster == nil then
|
||
|
file:seek("set", fatset.bps * blockpos)
|
||
|
block = _msdos.readRawString(file, fatset.rdec * 32)
|
||
|
else
|
||
|
block = _msdos.readEntireEntry(fatset, file, entrycluster)
|
||
|
end
|
||
|
local dirlist = _msdos.readDirBlock(fatset, block)
|
||
|
for index,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag ~= NUL and fileflag ~= string.char(0xE5) and bit32.band(data.attrib,0x08) == 0 and data.filename == name then
|
||
|
local chainlist = {}
|
||
|
if bit32.band(data.attrib,0x10) == 0 and data.size > 0 then
|
||
|
chainlist = _msdos.getClusterChain(fatset, data.cluster)
|
||
|
chainlist[#chainlist] = nil
|
||
|
end
|
||
|
if bit32.band(data.attrib,0x10) ~= 0 then
|
||
|
table.insert(chainlist, data.cluster)
|
||
|
local murder = _msdos.recursiveKill(fatset, file, data.cluster)
|
||
|
for i = 1, #murder do
|
||
|
table.insert(chainlist, murder[i])
|
||
|
end
|
||
|
end
|
||
|
if entrycluster == nil then
|
||
|
--file:close()
|
||
|
file = io.open(fatfile,"ab")
|
||
|
file:seek("set", fatset.bps * blockpos + ((index - 1) * 32))
|
||
|
file:write(string.char(0xE5))
|
||
|
else
|
||
|
local list = _msdos.getClusterChain(fatset, entrycluster)
|
||
|
local clusterList = math.floor((index - 1) / (fatset.bps * fatset.spc / 32))
|
||
|
local clusterPos = (index - 1) % (fatset.bps * fatset.spc / 32)
|
||
|
--file:close()
|
||
|
file = io.open(fatfile,"ab")
|
||
|
file:seek("set", (fatset.bps * _msdos.cluster2block(fatset, list[clusterList + 1])) + (clusterPos * 32))
|
||
|
file:write(string.char(0xE5))
|
||
|
end
|
||
|
local fakefile = {seek = function() end, write = function() end}
|
||
|
for i = 1, #chainlist do
|
||
|
_msdos.setFATEntry(fatset, fakefile, chainlist[i], 0x0000)
|
||
|
end
|
||
|
file:seek("set", fatset.bps * fatset.rb)
|
||
|
for i = 1, fatset.fatc do
|
||
|
file:write(fatset.fatCache.fatTable)
|
||
|
end
|
||
|
--file:close()
|
||
|
-- Invalidate list cache entries
|
||
|
local listCacheKill = {}
|
||
|
local preppath = ""
|
||
|
if path ~= "" then
|
||
|
preppath = path .. "/"
|
||
|
end
|
||
|
for k,v in pairs(fatset.fatCache.directoryLists) do
|
||
|
if k == preppath .. name or k:sub(1, #preppath + #name + 1) == preppath .. name .. "/" then
|
||
|
table.insert(listCacheKill, k)
|
||
|
end
|
||
|
end
|
||
|
for i = 1, #listCacheKill do
|
||
|
fatset.fatCache.directoryLists[listCacheKill[i]] = nil
|
||
|
end
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
--file:close()
|
||
|
return false
|
||
|
end
|
||
|
proxyObj.rename = function(path, newpath)
|
||
|
checkArg(1,path,"string")
|
||
|
checkArg(2,newpath,"string")
|
||
|
path = _msdos.cleanPath(path)
|
||
|
newpath = _msdos.cleanPath(newpath)
|
||
|
if path == "" or newpath == "" then
|
||
|
return false
|
||
|
elseif path == newpath then
|
||
|
return true
|
||
|
end
|
||
|
local name = path:match(".-([^\\/]-%.?)$")
|
||
|
if not _msdos.validateName(name) then
|
||
|
return false
|
||
|
end
|
||
|
local newname = newpath:match(".-([^\\/]-%.?)$")
|
||
|
if not _msdos.validateName(newname) then
|
||
|
return false
|
||
|
end
|
||
|
path = fs.canonical(path .. "/..")
|
||
|
newpath = fs.canonical(newpath .. "/..")
|
||
|
--local file = io.open(fatfile,"rb")
|
||
|
found, blockpos, entrycluster = _msdos.searchDirectoryLists(fatset, file, path)
|
||
|
if found == false then
|
||
|
--file:close()
|
||
|
return false
|
||
|
end
|
||
|
found, blockpos2, entrycluster2 = _msdos.searchDirectoryLists(fatset, file, newpath)
|
||
|
if found == false then
|
||
|
--file:close()
|
||
|
return false
|
||
|
end
|
||
|
local block
|
||
|
if entrycluster == nil then
|
||
|
file:seek("set", fatset.bps * blockpos)
|
||
|
block = _msdos.readRawString(file, fatset.rdec * 32)
|
||
|
else
|
||
|
block = _msdos.readEntireEntry(fatset, file, entrycluster)
|
||
|
end
|
||
|
local dirlist = _msdos.readDirBlock(fatset, block)
|
||
|
local entry, firstindex
|
||
|
for index,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag ~= NUL and fileflag ~= string.char(0xE5) and bit32.band(data.attrib,0x08) == 0 and data.filename == name then
|
||
|
firstindex = index
|
||
|
local filename, ext
|
||
|
if newname:find(".",nil,true) ~= nil then
|
||
|
filename = newname:match("(.*)%..*")
|
||
|
ext = newname:match(".*%.(.*)")
|
||
|
else
|
||
|
filename = newname
|
||
|
ext = ""
|
||
|
end
|
||
|
filename = filename .. string.rep(" ", 8 - #filename)
|
||
|
ext = ext .. string.rep(" ", 3 - #ext)
|
||
|
entry = filename .. ext .. _msdos.number2string(data.attrib, 1) .. string.rep(NUL, 10) .. _msdos.number2string(data.modifyT, 2) .. _msdos.number2string(data.modifyD, 2) .. _msdos.number2string(data.cluster, 2) .. _msdos.number2string(data.size, 4)
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
if entry == nil then
|
||
|
--file:close()
|
||
|
return false
|
||
|
end
|
||
|
if entrycluster2 == nil then
|
||
|
file:seek("set", fatset.bps * blockpos2)
|
||
|
block = _msdos.readRawString(file, fatset.rdec * 32)
|
||
|
else
|
||
|
block = _msdos.readEntireEntry(fatset, file, entrycluster2)
|
||
|
end
|
||
|
local dirlist = _msdos.readDirBlock(fatset, block)
|
||
|
for index,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag == NUL or fileflag == string.char(0xE5) then
|
||
|
if entrycluster2 == nil then
|
||
|
--file:close()
|
||
|
file = io.open(fatfile,"ab")
|
||
|
file:seek("set", fatset.bps * blockpos2 + ((index - 1) * 32))
|
||
|
file:write(entry)
|
||
|
else
|
||
|
local list = _msdos.getClusterChain(fatset, entrycluster2)
|
||
|
--file:close()
|
||
|
local clusterList = math.floor((index - 1) / (fatset.bps * fatset.spc / 32))
|
||
|
local clusterPos = (index - 1) % (fatset.bps * fatset.spc / 32)
|
||
|
file = io.open(fatfile,"ab")
|
||
|
file:seek("set", (fatset.bps * _msdos.cluster2block(fatset, list[clusterList + 1])) + (clusterPos * 32))
|
||
|
file:write(entry)
|
||
|
end
|
||
|
if entrycluster == nil then
|
||
|
file:seek("set", fatset.bps * blockpos + ((firstindex - 1) * 32))
|
||
|
file:write(string.rep(NUL, 32))
|
||
|
else
|
||
|
--file:close()
|
||
|
file = io.open(fatfile,"rb")
|
||
|
local list = _msdos.getClusterChain(fatset, entrycluster)
|
||
|
local clusterList = math.floor((firstindex - 1) / (fatset.bps * fatset.spc / 32))
|
||
|
local clusterPos = (firstindex - 1) % (fatset.bps * fatset.spc / 32)
|
||
|
--file:close()
|
||
|
file = io.open(fatfile,"ab")
|
||
|
file:seek("set", (fatset.bps * _msdos.cluster2block(fatset, list[clusterList + 1])) + (clusterPos * 32))
|
||
|
file:write(string.rep(NUL, 32))
|
||
|
end
|
||
|
--file:close()
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
-- TODO: Attempt to find, clean, and add a cluster
|
||
|
return false
|
||
|
end
|
||
|
proxyObj.read = function(fd, count)
|
||
|
count = count or 1
|
||
|
checkArg(1,fd,"number")
|
||
|
checkArg(2,count,"number")
|
||
|
if filedescript[fd] == nil or filedescript[fd].mode ~= "r" then
|
||
|
return nil, "bad file descriptor"
|
||
|
end
|
||
|
if #filedescript[fd].buffer >= filedescript[fd].size and filedescript[fd].seek > filedescript[fd].size then
|
||
|
return nil
|
||
|
end
|
||
|
count = math.min(count,8192)
|
||
|
if filedescript[fd].seek + count > filedescript[fd].size then
|
||
|
count = filedescript[fd].size - filedescript[fd].seek
|
||
|
end
|
||
|
if count == 0 then
|
||
|
return nil
|
||
|
end
|
||
|
while #filedescript[fd].buffer < filedescript[fd].seek + count do
|
||
|
local nextchain = (#filedescript[fd].buffer / fatset.bps / fatset.spc) + 1
|
||
|
if filedescript[fd].chain[nextchain] == nil then
|
||
|
return nil
|
||
|
end
|
||
|
local block = _msdos.cluster2block(fatset, filedescript[fd].chain[nextchain])
|
||
|
--local file = io.open(fatfile,"rb")
|
||
|
file:seek("set", block * fatset.bps)
|
||
|
local data = _msdos.readRawString(file, fatset.bps * fatset.spc)
|
||
|
--file:close()
|
||
|
filedescript[fd].buffer = filedescript[fd].buffer .. data
|
||
|
end
|
||
|
filedescript[fd].buffer = filedescript[fd].buffer:sub(1,filedescript[fd].size)
|
||
|
local data = filedescript[fd].buffer:sub(filedescript[fd].seek + 1, filedescript[fd].seek + count)
|
||
|
filedescript[fd].seek = filedescript[fd].seek + #data
|
||
|
return data
|
||
|
end
|
||
|
proxyObj.close = function(fd)
|
||
|
checkArg(1,fd,"number")
|
||
|
if filedescript[fd] == nil then
|
||
|
return nil, "bad file descriptor"
|
||
|
end
|
||
|
if filedescript[fd].mode == "w" then
|
||
|
local clusters = math.ceil(#filedescript[fd].buffer / fatset.bps / fatset.spc)
|
||
|
local chain = filedescript[fd].chain
|
||
|
if clusters ~= #chain then
|
||
|
print("msdos: Number of allocated clusters doesn't match buffer size")
|
||
|
print("AC: " .. #chain .. " BS: " .. clusters)
|
||
|
clusters = math.min(clusters, #chain)
|
||
|
end
|
||
|
if fatset.fatsize == 12 then
|
||
|
chain[clusters + 1] = 0xFFF
|
||
|
else
|
||
|
chain[clusters + 1] = 0xFFFF
|
||
|
end
|
||
|
local bpc = fatset.bps * fatset.spc
|
||
|
--local file = io.open(fatfile,"ab")
|
||
|
for i = 1, clusters do
|
||
|
local block = _msdos.cluster2block(fatset, filedescript[fd].chain[i])
|
||
|
file:seek("set", block * fatset.bps)
|
||
|
file:write(filedescript[fd].buffer:sub(((i - 1) * bpc) + 1, i * bpc))
|
||
|
end
|
||
|
local fakefile = {seek = function() end, write = function() end}
|
||
|
|
||
|
for i = 1, clusters do
|
||
|
_msdos.setFATEntry(fatset, fakefile, filedescript[fd].chain[i], filedescript[fd].chain[i + 1])
|
||
|
end
|
||
|
|
||
|
file:seek("set", fatset.bps * fatset.rb)
|
||
|
for i = 1, fatset.fatc do
|
||
|
file:write(fatset.fatCache.fatTable)
|
||
|
end
|
||
|
|
||
|
--file:close()
|
||
|
|
||
|
local data, index, blockpos, entrycluster
|
||
|
--local file = io.open(fatfile,"rb")
|
||
|
local found = _msdos.doSomethingForFile(fatset, file, filedescript[fd].path, function(data1, index1, blockpos1, entrycluster1) data, index, blockpos, entrycluster = data1, index1, blockpos1, entrycluster1 end)
|
||
|
local filename, ext
|
||
|
if data.filename:find(".",nil,true) ~= nil then
|
||
|
filename = data.filename:match("(.*)%..*")
|
||
|
ext = data.filename:match(".*%.(.*)")
|
||
|
else
|
||
|
filename = data.filename
|
||
|
ext = ""
|
||
|
end
|
||
|
filename = filename .. string.rep(" ", 8 - #filename)
|
||
|
ext = ext .. string.rep(" ", 3 - #ext)
|
||
|
local curDate = os.date("*t")
|
||
|
local createT = bit32.lshift(curDate.hour, 11) + bit32.lshift(curDate.min, 5) + math.floor(curDate.sec/2)
|
||
|
local createD = bit32.lshift(math.max(curDate.year - 1980,0), 9) + bit32.lshift(curDate.month, 5) + curDate.day
|
||
|
if curDate.year - 1980 < 0 then
|
||
|
print("msdos: WARNING: Current year before 1980, year will be invalid")
|
||
|
end
|
||
|
local entry = filename .. ext .. _msdos.number2string(data.attrib, 1) .. string.rep(NUL, 10) .. _msdos.number2string(createT, 2) .. _msdos.number2string(createD, 2) .. _msdos.number2string(clusters > 0 and filedescript[fd].chain[1] or 0, 2) .. _msdos.number2string(#filedescript[fd].buffer, 4)
|
||
|
if entrycluster == nil then
|
||
|
--file:close()
|
||
|
file = io.open(fatfile,"ab")
|
||
|
file:seek("set", fatset.bps * blockpos + ((index - 1) * 32))
|
||
|
file:write(entry)
|
||
|
else
|
||
|
local list = _msdos.getClusterChain(fatset, entrycluster)
|
||
|
--file:close()
|
||
|
local clusterList = math.floor((index - 1) / (fatset.bps * fatset.spc / 32))
|
||
|
local clusterPos = (index - 1) % (fatset.bps * fatset.spc / 32)
|
||
|
file = io.open(fatfile,"ab")
|
||
|
file:seek("set", (fatset.bps * _msdos.cluster2block(fatset, list[clusterList + 1])) + (clusterPos * 32))
|
||
|
file:write(entry)
|
||
|
end
|
||
|
--file:close()
|
||
|
end
|
||
|
filedescript[fd] = nil
|
||
|
end
|
||
|
proxyObj.getLabel = function()
|
||
|
return fatset.label
|
||
|
end
|
||
|
proxyObj.seek = function(fd,kind,offset)
|
||
|
checkArg(1,fd,"number")
|
||
|
checkArg(2,kind,"string")
|
||
|
checkArg(3,offset,"number")
|
||
|
if filedescript[fd] == nil then
|
||
|
return nil, "bad file descriptor"
|
||
|
end
|
||
|
if kind ~= "set" and kind ~= "cur" and kind ~= "end" then
|
||
|
error("invalid mode",2)
|
||
|
end
|
||
|
if offset < 0 then
|
||
|
return nil, "Negative seek offset"
|
||
|
end
|
||
|
local newpos
|
||
|
if kind == "set" then
|
||
|
newpos = offset
|
||
|
elseif kind == "cur" then
|
||
|
newpos = filedescript[fd].seek + offset
|
||
|
elseif kind == "end" then
|
||
|
if filedescript[fd].mode == "r" then
|
||
|
newpos = filedescript[fd].size + offset
|
||
|
else
|
||
|
newpos = #filedescript[fd].buffer + offset
|
||
|
end
|
||
|
end
|
||
|
return filedescript[fd].seek
|
||
|
end
|
||
|
proxyObj.size = function(path)
|
||
|
checkArg(1,path,"string")
|
||
|
path = _msdos.cleanPath(path)
|
||
|
if path == "" then
|
||
|
return 0
|
||
|
end
|
||
|
local filesize
|
||
|
--local file = io.open(fatfile,"rb")
|
||
|
local found = _msdos.doSomethingForFile(fatset, file, path, function(data) filesize = data.size end)
|
||
|
--file:close()
|
||
|
if not found then
|
||
|
return 0
|
||
|
end
|
||
|
return filesize
|
||
|
end
|
||
|
proxyObj.isReadOnly = function()
|
||
|
return false -- TODO: Check if file is readonly
|
||
|
end
|
||
|
proxyObj.setLabel = function(newlabel)
|
||
|
checkArg(1,newlabel,"string")
|
||
|
-- TODO: Check readonly status
|
||
|
newlabel = newlabel:sub(1,11)
|
||
|
origlabel = newlabel
|
||
|
fatset.label = newlabel
|
||
|
if #newlabel < 11 then
|
||
|
newlabel = newlabel .. string.rep(" ", 11 - #newlabel)
|
||
|
end
|
||
|
fatset.bpblabel = newlabel
|
||
|
--local file = io.open(fatfile,"ab")
|
||
|
file:seek("set", 0x2B)
|
||
|
file:write(newlabel)
|
||
|
--file:close()
|
||
|
return origlabel
|
||
|
end
|
||
|
proxyObj.makeDirectory = function(path)
|
||
|
-- TODO: Recursively make folders
|
||
|
checkArg(1,path,"string")
|
||
|
path = _msdos.cleanPath(path)
|
||
|
if path == "" then
|
||
|
return false
|
||
|
end
|
||
|
local name = path:match(".-([^\\/]-%.?)$")
|
||
|
if not _msdos.validateName(name) then
|
||
|
return false
|
||
|
end
|
||
|
local filename, ext
|
||
|
if name:find(".",nil,true) then
|
||
|
filename, ext = name:match("(.*)%.(.+)")
|
||
|
else
|
||
|
filename = name
|
||
|
ext = ""
|
||
|
end
|
||
|
filename = filename .. string.rep(" ", 8 - #filename)
|
||
|
ext = ext .. string.rep(" ", 3 - #ext)
|
||
|
local freeCluster = _msdos.findFreeCluster(fatset)
|
||
|
if freeCluster == nil then
|
||
|
return false
|
||
|
end
|
||
|
path = fs.canonical(path .. "/..")
|
||
|
--local file = io.open(fatfile,"rb")
|
||
|
found, blockpos, entrycluster = _msdos.searchDirectoryLists(fatset, file, path)
|
||
|
if found == false then
|
||
|
--file:close()
|
||
|
return false
|
||
|
end
|
||
|
local block
|
||
|
if entrycluster == nil then
|
||
|
file:seek("set", fatset.bps * blockpos)
|
||
|
block = _msdos.readRawString(file, fatset.rdec * 32)
|
||
|
else
|
||
|
block = _msdos.readEntireEntry(fatset, file, entrycluster)
|
||
|
end
|
||
|
local dirlist = _msdos.readDirBlock(fatset, block)
|
||
|
for index,data in ipairs(dirlist) do
|
||
|
local fileflag = data.filename:sub(1,1)
|
||
|
if fileflag == "" then fileflag = NUL end
|
||
|
if fileflag == NUL or fileflag == string.char(0xE5) then
|
||
|
local curDate = os.date("*t")
|
||
|
local createT = bit32.lshift(curDate.hour, 11) + bit32.lshift(curDate.min, 5) + math.floor(curDate.sec/2)
|
||
|
local createD = bit32.lshift(math.max(curDate.year - 1980,0), 9) + bit32.lshift(curDate.month, 5) + curDate.day
|
||
|
if curDate.year - 1980 < 0 then
|
||
|
print("msdos: WARNING: Current year before 1980, year will be invalid")
|
||
|
end
|
||
|
local entry = filename .. ext .. string.char(0x30) .. string.rep(NUL, 10) .. _msdos.number2string(createT,2) .. _msdos.number2string(createD,2) .. _msdos.number2string(freeCluster, 2) .. string.rep(NUL, 4)
|
||
|
if entrycluster == nil then
|
||
|
--file:close()
|
||
|
file = io.open(fatfile,"ab")
|
||
|
file:seek("set", fatset.bps * blockpos + ((index - 1) * 32))
|
||
|
file:write(entry)
|
||
|
else
|
||
|
local list = _msdos.getClusterChain(fatset, entrycluster)
|
||
|
--file:close()
|
||
|
local clusterList = math.floor((index - 1) / (fatset.bps * fatset.spc / 32))
|
||
|
local clusterPos = (index - 1) % (fatset.bps * fatset.spc / 32)
|
||
|
file = io.open(fatfile,"ab")
|
||
|
file:seek("set", (fatset.bps * _msdos.cluster2block(fatset, list[clusterList + 1])) + (clusterPos * 32))
|
||
|
file:write(entry)
|
||
|
end
|
||
|
file:seek("set", _msdos.cluster2block(fatset, freeCluster) * fatset.bps)
|
||
|
-- TODO: Write dot entries
|
||
|
file:write(string.rep(NUL, fatset.bps * fatset.spc))
|
||
|
_msdos.setFATEntry(fatset, file, freeCluster, 0xFFFF)
|
||
|
--file:close()
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
-- TODO: Attempt to find, clean, and add a cluster
|
||
|
--file:close()
|
||
|
return false
|
||
|
end
|
||
|
proxyObj.spaceUsed = function()
|
||
|
local count = 0
|
||
|
for i = 0, fatset.tnoc - 1 do
|
||
|
local entry = _msdos.getFATEntry(fatset, i)
|
||
|
if entry ~= 0 then
|
||
|
count = count + 1
|
||
|
end
|
||
|
end
|
||
|
return count * (fatset.spc * fatset.bps)
|
||
|
end
|
||
|
proxyObj.write = function(fd,data)
|
||
|
checkArg(1,fd,"number")
|
||
|
checkArg(2,data,"string")
|
||
|
if filedescript[fd] == nil or filedescript[fd].mode ~= "w" then
|
||
|
return nil, "bad file descriptor"
|
||
|
end
|
||
|
-- Check if we need more clusters
|
||
|
if math.ceil(#filedescript[fd].buffer / (fatset.spc * fatset.bps)) < math.ceil((#filedescript[fd].buffer + #data) / (fatset.spc * fatset.bps)) then
|
||
|
local freeCluster = _msdos.findFreeCluster(fatset)
|
||
|
if freeCluster == nil then
|
||
|
print("msdos: No more clusters")
|
||
|
return nil, "not enough space"
|
||
|
end
|
||
|
local chain = filedescript[fd].chain
|
||
|
chain[#chain + 1] = freeCluster
|
||
|
fatset.reserved[freeCluster] = true
|
||
|
end
|
||
|
if #filedescript[fd].buffer < filedescript[fd].seek then
|
||
|
filedescript[fd].buffer = filedescript[fd].buffer .. string.rep(NUL, filedescript[fd].seek - #filedescript[fd].buffer)
|
||
|
end
|
||
|
filedescript[fd].buffer = filedescript[fd].buffer:sub(1,filedescript[fd].seek) .. data .. filedescript[fd].buffer:sub(filedescript[fd].seek + #data + 1)
|
||
|
filedescript[fd].seek = filedescript[fd].seek + #data
|
||
|
return true
|
||
|
end
|
||
|
proxyObj.fat = fatset
|
||
|
--vcomp.register(proxyObj.address, proxyObj.type, proxyObj)
|
||
|
return proxyObj
|
||
|
end
|
||
|
return msdos
|