Skip to content

Commit 0d090dc

Browse files
committed
Squashed code in FATSTAT.LUA so that it fits in 6 clusters.
1 parent 50070d9 commit 0d090dc

File tree

1 file changed

+46
-135
lines changed

1 file changed

+46
-135
lines changed

demo/xtra/FATSTAT.LUA

Lines changed: 46 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,138 +1,49 @@
11
#!/usr/bin/env lua
22

33
H=[[This script shows what blocks are allocated to what files on a FAT12/16 formatted floppy disk image (.IMA).
4-
When the -z option is present any junk data from unallocated clusters will be zeroed out.
5-
This is sometimes referred to as "shredding" , doing so allows repeatable builds to be verified by checksum tools.
6-
Always backup the IMA before using this tool with the -z option on it.]]
7-
8-
if #arg < 1 then print(arg[-1] .. " " .. arg[0] .. " [-z] [IMA FILES...]" .. '\n\n' .. H) os.exit(1) end
9-
10-
local function args()
11-
F={} for _,v in ipairs(arg) do if v=="-z" then Z=true else table.insert(F, v) end end
12-
end
13-
14-
local function read_le16(str, offset)
15-
local a, b = str:byte(offset, offset + 1) return a + b * 256
16-
end
17-
18-
local function read_le32(str, offset)
19-
local a, b, c, d = str:byte(offset, offset + 3) return a + b * 256 + c * 65536 + d * 16777216
20-
end
21-
22-
local function read_fat12_table(f, fat_offset, fat_size)
23-
f:seek("set", fat_offset)
24-
local fat_data,fat,max_clusters=f:read(fat_size),{},math.floor(fat_size * 8 / 12)
25-
local function get_entry(i)
26-
local byte_index = math.floor(i * 1.5)
27-
local a,b=fat_data:byte(byte_index + 1)or 0,fat_data:byte(byte_index + 2)or 0
28-
if i % 2 == 0 then return a + (b & 0x0F) * 256 else return (a >> 4) + b * 16 end
29-
end
30-
for i = 2, max_clusters - 1 do fat[i] = get_entry(i) end return fat
31-
end
32-
33-
local function read_fat16_table(f, fat_offset, fat_size)
34-
f:seek("set", fat_offset) local fat_data,fat=f:read(fat_size),{}
35-
for i = 2, #fat_data - 1, 2 do
36-
local lo,hi=fat_data:byte(i),fat_data:byte(i + 1) fat[((i-2)//2)+2] = lo + hi * 256
37-
end return fat
38-
end
39-
40-
local function detect_fat_type(total_sectors, reserved_sectors, num_fats, sectors_per_fat, root_dir_sectors, sectors_per_cluster)
41-
local cluster_count = math.floor((total_sectors - reserved_sectors - (num_fats * sectors_per_fat) - root_dir_sectors) / sectors_per_cluster)
42-
if cluster_count < 4085 then return 12
43-
elseif cluster_count < 65525 then return 16
44-
else return 32 end
45-
end
46-
47-
local function cluster_chain(fat, start_cluster)
48-
local chain, current = {}, start_cluster
49-
while current >= 2 and current < 0xFF8 do
50-
table.insert(chain, current) current = fat[current]
51-
end return chain
52-
end
53-
54-
local function format_cluster_ranges(clusters)
55-
table.sort(clusters) local ranges, i = {}, 1
56-
while i <= #clusters do
57-
local start = clusters[i]
58-
local j = i
59-
while j + 1 <= #clusters and clusters[j + 1] == clusters[j] + 1 do
60-
j = j + 1
61-
end
62-
local finish = clusters[j]
63-
if start == finish then
64-
table.insert(ranges, string.format("<%d>", start))
65-
else
66-
table.insert(ranges, string.format("<%d-%d>", start, finish))
67-
end
68-
i = j + 1
69-
end
70-
return table.concat(ranges, " ")
71-
end
72-
73-
local function print_directory_entries(f, offset, entries, fat)
74-
f:seek("set", offset)
75-
for _ = 1, entries do
76-
local entry = f:read(32)
77-
if not entry then break end
78-
local first_byte = entry:byte(1)
79-
if first_byte == 0x00 then break end
80-
local attr = entry:byte(12)
81-
if first_byte ~= 0xE5 and (attr & 0x08) == 0 then
82-
local name,ext = entry:sub(1, 8):gsub(" +$", ""),entry:sub(9, 11):gsub(" +$", "")
83-
local fullname = name .. (ext ~= "" and "." .. ext or "")
84-
local start_cluster = read_le16(entry, 27)
85-
local clusters = cluster_chain(fat, start_cluster)
86-
io.write(string.format("\t%12s: %s\n", fullname, format_cluster_ranges(clusters)))
87-
end
88-
end
89-
end
90-
91-
local function print_free_clusters(fat, f, data_start, cluster_size)
92-
io.write("\n\t Free Space: ")
93-
local free, zero = {}, string.rep("\0",cluster_size)
94-
for i = 2, #fat do
95-
if fat[i] == 0 then
96-
table.insert(free, i)
97-
if Z then
98-
local offset = data_start + (i - 2) * cluster_size
99-
f:seek("set", offset)
100-
f:write(zero)
101-
end
102-
end
103-
end
104-
print(format_cluster_ranges(free))
105-
end
106-
107-
args() for _,file in ipairs(F) do
108-
local f = io.open(file, (Z and "r+b" or "rb"))
109-
if f then
110-
print(file .. ":")
111-
local boot = f:read(512)
112-
assert(boot:sub(1, 1):byte() == 0xEB or boot:sub(1, 1):byte() == 0xE9, "Invalid boot sector")
113-
114-
local bytes_per_sector = read_le16(boot, 12)
115-
local sectors_per_cluster = boot:byte(14)
116-
local reserved_sectors = read_le16(boot, 15)
117-
local num_fats = boot:byte(17)
118-
local root_entries = read_le16(boot, 18)
119-
local total_sectors = read_le16(boot, 19) or read_le32(boot, 32)
120-
local sectors_per_fat = read_le16(boot, 23)
121-
local root_dir_sectors = math.ceil(root_entries * 32 / bytes_per_sector)
122-
local fat_offset = reserved_sectors * bytes_per_sector
123-
local fat_size = sectors_per_fat * bytes_per_sector
124-
local root_dir_offset = (reserved_sectors + num_fats * sectors_per_fat) * bytes_per_sector
125-
126-
local fat_type = detect_fat_type(total_sectors, reserved_sectors,num_fats, sectors_per_fat, root_dir_sectors, bytes_per_sector)
127-
print(string.format("\t FAT type: FAT%s", fat_type))
128-
print(string.format("\t Bytes/sector: %d", bytes_per_sector))
129-
print(string.format("\tSectors/cluster: %d", sectors_per_cluster))print()
130-
131-
local fat = fat_type == 12 and read_fat12_table(f, fat_offset, fat_size) or fat_type == 16 and read_fat16_table(f, fat_offset, fat_size) or error("Not FAT12/16")
132-
133-
print_directory_entries(f, root_dir_offset, root_entries, fat)
134-
print_free_clusters(fat, f, (reserved_sectors + (num_fats * sectors_per_fat) + root_dir_sectors) * bytes_per_sector, sectors_per_cluster * bytes_per_sector)
135-
136-
f:close()print()
137-
end
138-
end
4+
The -z option will zero out unallocated clusters. Always backup the IMA before using this tool with the -z option on it.]]
5+
if #arg<1 then print(arg[-1].." "..arg[0].." [-z] [FILE...]"..'\n\n'..H)os.exit(1)end
6+
function A()F={} for _,v in ipairs(arg)do if v=="-z" then Z=true else table.insert(F,v)end end end
7+
function R16(s,o)local a,b=s:byte(o,o+1)return a+b*256 end
8+
function R32(s,o)local a,b,c,d=s:byte(o,o+3)return a+b*256+c*65536+d*16777216 end
9+
function F12(f,o,s)f:seek("set",o)local fd,fat,mc=f:read(s),{},math.floor(s*8/12)
10+
function E(i)local c=math.floor(i*1.5)local a,b=fd:byte(c+1)or 0,fd:byte(c+2)or 0 if i%2==0 then return a+(b&0x0F)*256 else return (a>>4)+b*16 end end
11+
for i=2,mc-1 do fat[i]=E(i)end return fat end
12+
function F16(f,o,s)
13+
f:seek("set",o)local fd,fat=f:read(s),{}
14+
for i=2,#fd-1,2 do local lo,hi=fd:byte(i),fd:byte(i+1)fat[((i-2)//2)+2]=lo+hi*256 end return fat end
15+
function FD(ts,rs,nf,sf,rd,sc)
16+
local cc=math.floor((ts-rs-(nf*sf)-rd)/sc)
17+
if cc<4085 then return 12 elseif cc<65525 then return 16 else return 32 end end
18+
function CC(fat,sc)
19+
local chain,current={},sc
20+
while current>=2 and current<0xFF8 do table.insert(chain,current)current=fat[current] end return chain end
21+
function FR(c)
22+
table.sort(c)local ranges,i={},1
23+
while i <=#c do
24+
local s,j,f=c[i],i while j+1<=#c and c[j+1]==c[j]+1 do j=j+1 end f=c[j]
25+
if s==f then table.insert(ranges,string.format("<%d>",s))
26+
else table.insert(ranges,string.format("<%d-%d>",s,f))end
27+
i=j+1 end return table.concat(ranges," ")end
28+
function DE(f,o,e,fat)
29+
f:seek("set",o)
30+
for _=1,e do
31+
local en,fb=f:read(32)if not en then break end fb=en:byte(1)if fb==0 then break end
32+
if fb~=229 and(en:byte(12)&8)==0 then
33+
local n,x=en:sub(1,8):gsub("+$",""),en:sub(9,11):gsub("+$","")n=n..(x~=""and"."..x or"")
34+
io.write(string.format("\t%12s: %s\n",n,FR(CC(fat,R16(en,27)))))end end end
35+
function FC(fat,f,st,s)
36+
io.write("\n\t Free Space: ")
37+
local free,zero={},string.rep("\0",s)
38+
for i=2,#fat do if fat[i]==0 then table.insert(free,i)if Z then local o=st+(i-2)*s f:seek("set",o)f:write(zero)end end end print(FR(free))end
39+
A()for _,file in ipairs(F)do
40+
local f=io.open(file,(Z and"r+b"or"rb"))
41+
if f then
42+
print(file..":")
43+
local boot=f:read(512)assert(boot:sub(1,1):byte()==0xEB or boot:sub(1,1):byte()==0xE9,"Invalid boot sector")
44+
local bs,sc,rs,nf,re,ts,sf=R16(boot,12),boot:byte(14),R16(boot,15),boot:byte(17),R16(boot,18),R16(boot,19)or R32(boot,32),R16(boot,23)
45+
local rd,fo,fs,ro=math.ceil(re*32/bs),rs*bs,sf*bs,(rs+nf*sf)*bs
46+
local ft=FD(ts,rs,nf,sf,rd,bs)
47+
print(string.format("\t FAT type: FAT%s\n\t Bytes/sector: %d\n\tSectors/cluster: %d\n\n",ft,bs,sc))
48+
local fat=ft==12 and F12(f,fo,fs)or ft==16 and F16(f,fo,fs)or error("Non-FAT type")
49+
DE(f,ro,re,fat)FC(fat,f,(rs+(nf*sf)+rd)*bs,sc*bs)f:close()print()end end

0 commit comments

Comments
 (0)