Tridora-CPU/utils/tdrimg.py

640 lines
21 KiB
Python

#!/usr/bin/python3
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
# Copyright 2021-2025 Sebastian Lederer. See the file LICENSE.md for details
import struct
import sys
from collections import namedtuple
import os
MaxPartitions = 8
SlotFree = 1
SlotReserved = 2
SlotDeleted = 4
SlotEndScan = 8
SlotFirst = 16
SlotExtent = 32
SlotReadonly = 64
PartEnabled = 1
PartBoot = 2
PartLast = 4
PartPhysical = 8
PartDefault = 16
part_fmt = ">ii32siiiiii"
dirslot_fmt = ">ii32siiiiii"
PartSlot = namedtuple("PartSlot", "namelength maxlength name flags startBlock blocks extentSize dirSize bootBlocks")
DirSlot = namedtuple("DirSlot", "namelength maxlength name flags sizeBytes createTime modTime generation owner")
def createpart(name, flags, start_block, blocks, extent_size, dir_size, boot_blocks=0):
b = struct.pack(part_fmt, len(name), 32, bytes(name, 'utf8'), flags, start_block, blocks,
extent_size, dir_size, boot_blocks)
return b
def decodepart(data):
return struct.unpack(part_fmt, data)
def getpartslot(img, partno):
img.seek(partno * 64)
fields = decodepart(img.read(64))
name = getname(fields)
fields = list(fields)
fields[2] = name
return PartSlot._make(fields)
def createdirslot(name, flags, size, create_time, mod_time, generation, owner):
return struct.pack(dirslot_fmt, len(name), 32, bytes(name, 'utf8'), flags, size, create_time,
mod_time, generation, owner)
def decodedirslot(data):
return struct.unpack(dirslot_fmt, data)
def putdirslot(img, partstart, slotno, slotdata):
img.seek(partstart * 512 + slotno * 64)
img.write(slotdata)
def getdirslot(img, part, slotno):
partstart = part.startBlock
img.seek(partstart * 512 + slotno * 64)
fields = decodedirslot(img.read(64))
name = getname(fields)
fields = list(fields)
fields[2] = name
return DirSlot._make(fields)
def writefile(img, part, slotno, databytes):
partstart = part.startBlock
extent_size = part.extentSize
pos = partstart * 512 + slotno * extent_size
print("writefile: slot", slotno, len(databytes), "bytes at block", partstart + slotno * extent_size // 512, " pos",
pos)
img.seek(pos)
img.write(databytes)
extendfile(img, part, slotno, len(databytes))
def write_bootimage(img, startblock, data):
pos = startblock * 512
img.seek(pos)
img.write(data)
def extendfile(img, part, slotno, newSize):
dirslot = getdirslot(img, part, slotno)
extent_size = part.extentSize
old_extents = dirslot.sizeBytes // extent_size + 1
new_extents = newSize // extent_size + 1
old_last_slot = slotno + 1
new_last_slot = slotno + new_extents - 1
print("extendfile old_last_slot {} new_last_slot {} old_ext {} new_ext {} ".format(
old_last_slot, new_last_slot, old_extents, new_extents), end="")
for i in range(old_last_slot, new_last_slot + 1):
d = getdirslot(img, part, i)
if d.flags & SlotFree:
print(i," ", sep="", end="")
d = createdirslot("", SlotExtent, 0, 0, 0, 0, 0)
putdirslot(img, part.startBlock, i, d)
else:
print("Cannot extend file at dirslot",i, "- wanted size:", newSize)
listdir(img, part)
sys.exit(3)
print()
if old_extents != new_extents:
print("file at dirslot",slotno, "extended to", new_extents, "extents")
firstslot = createdirslot(dirslot.name, dirslot.flags, newSize, dirslot.createTime, dirslot.modTime,
dirslot.generation, dirslot.owner)
putdirslot(img, part.startBlock, slotno, firstslot)
def findslots(img, part, size):
size_in_extents = (size + part.extentSize - 1) // part.extentSize
last_slot = part.dirSize - 1
found = False
firstslot = 0
slotno = 0
while slotno <= last_slot and not found:
dirslot = getdirslot(img, part, slotno)
if dirslot.flags & SlotFree:
found = True
firstslot = slotno
print("found free slot at",slotno, ":", dirslot)
if size_in_extents > 1:
print("checking slot ", end='')
for s in range(1, size_in_extents):
slotno += 1
print(slotno, " ", end='')
dirslot = getdirslot(img, part, slotno)
if not (dirslot.flags & SlotFree):
print("wanted slot",slotno,"not free")
found = False
break
print()
else:
slotno += 1
if found:
return firstslot
else:
return 0
def putfile(infilename, filename, img, part, partstart, slotnr):
if filename is None:
filename = os.path.basename(infilename)
try:
extent_size = part.extentSize
with open(infilename,"rb") as infile:
print("creating file", filename, "at slot", slotnr)
content = infile.read()
d = createdirslot(filename, SlotFirst, len(content), 0, 0, 0, 0)
putdirslot(img, partstart, slotnr, d)
writefile(img, part, slotnr, content)
slotnr += len(content) // extent_size + 1
except Exception as e:
print("error reading file", infilename, "skipping", e)
return slotnr
def getname(data):
length = data[0]
name = data[2][:length].decode('utf8')
return name
def flags2str(flags):
result = ""
if flags & SlotFree:
result += "F"
if flags & SlotReserved:
result += "R"
if flags & SlotDeleted:
result += "D"
if flags & SlotEndScan:
result += "E"
if flags & SlotFirst:
result += "1"
if flags & SlotExtent:
result += "+"
if flags & SlotReadonly:
result += "o"
return result
def findvolume(img, volname):
part = None
partno = 0
while True:
part = getpartslot(img, partno)
if part.flags & PartEnabled:
if part.name == volname:
break
partno += 1
if (part.flags & PartLast) or partno >= MaxPartitions:
part = None
break
return part
def listvolumes(img):
firstvolume = None
partno = 0
done = False
while not done:
part = getpartslot(img, partno)
print(part)
if part.flags & PartEnabled:
print("part", partno, " enabled")
print("\tvolume name\t", part.name)
print("\tstart block\t", part.startBlock)
print("\tblocks\t\t", part.blocks)
print("\textentSize\t", part.extentSize)
print("\tdirSize\t\t", part.dirSize)
if firstvolume is None :
firstvolume = part
partno += 1
if (part.flags & PartLast) or partno >= MaxPartitions:
done = True
return firstvolume
def listdir(img, part, verbose=False, deleted=False):
print("Directory of {}:".format(part.name))
slotno = 0
done = False
while not done:
slot = getdirslot(img, part, slotno)
if (slot.flags & SlotFirst):
print(slot.name, slot.sizeBytes, slotno)
elif deleted and (slot.flags & SlotDeleted):
print(slot.name, slot.sizeBytes, slotno, slot.generation)
else:
if verbose:
print(flags2str(slot.flags))
slotno += 1
#if (slot.flags & SlotEndScan) or (slotno >= part.dirSize):
# done = True
if (slotno >= part.dirSize):
done = True
def findfile(img, part, name):
slotno = 0
done = False
while not done:
slot = getdirslot(img, part, slotno)
if (slot.flags & SlotFirst) and not (slot.flags & SlotDeleted):
if slot.name == name:
return slotno
slotno += 1
if (slot.flags & SlotEndScan) or (slotno >= part.dirSize):
done = True
return None
def finddeleted(img, part, name, gen):
slotno = 0
done = False
while not done:
slot = getdirslot(img, part, slotno)
if slot.flags & SlotDeleted:
if slot.name == name and slot.generation == gen:
return slotno
slotno += 1
if (slot.flags & SlotEndScan) or (slotno >= part.dirSize):
done = True
return None
def readfile(img, part, slotno):
pos = part.startBlock * 512 + slotno * part.extentSize
dirslot = getdirslot(img, part, slotno)
size = dirslot.sizeBytes
print("readfile", dirslot.name, size,"bytes from",pos)
img.seek(pos)
return img.read(size)
def parsepath(img, pathname):
volname = "SYSTEM"
if pathname.startswith("#"):
volname, filename = pathname.split(':')
volname = volname[1:]
vol = findvolume(img, volname)
if vol is None:
print("Volume {} not found".format(volname))
return (None, None)
else:
filename = pathname
vol = listvolumes(img)
return (vol, filename)
def readfromimg(img, pathname,outfilepath):
vol, filename = parsepath(img, pathname)
if vol is None:
return
listdir(img, vol)
slotno = findfile(img, vol, filename)
if slotno is None:
print("File", filename,"not found")
return
data = readfile(img, vol, slotno)
with open(outfilepath, "wb") as f:
f.write(data)
def recoverfromimg(img, pathname, gen, outfilepath):
vol, filename = parsepath(img, pathname)
if vol is None:
return
listdir(img, vol, deleted=True)
slotno = finddeleted(img, vol, filename, gen)
if slotno is None:
print("File", filename,"not found with generation no", gen)
return
data = readfile(img, vol, slotno)
with open(outfilepath, "wb") as f:
f.write(data)
def writetoimg(img, pathname, infilepath):
vol, filename = parsepath(img, pathname)
if vol is None:
return
existing_slot = findfile(img, vol, filename)
if existing_slot is not None:
print("Filename", filename, "already exists on", vol.name)
return
filesize = os.path.getsize(infilepath)
slotno = findslots(img, vol, filesize)
if slotno < 1:
print("No space on volume", vol.name)
return
putfile(infilepath, filename, img, vol, vol.startBlock, slotno)
def initfs(f, partno):
part = getpartslot(f, partno)
partstart = part.startBlock
dir_slots = part.dirSize
extent_size = part.extentSize
slots_per_extent = extent_size // 64
reserved_slots = dir_slots // slots_per_extent
print()
print("Partition {} at {}".format(part.name, part.startBlock))
print("creating",reserved_slots, "reserved directory slots")
for a in range(0,reserved_slots):
d = createdirslot("DIR", SlotReserved, 0, 0, 0, 0, 0)
putdirslot(f, partstart, a, d)
print("creating", dir_slots - reserved_slots, "free slots")
for a in range(reserved_slots, dir_slots):
d = createdirslot("", SlotFree, 0, 0, 0, 0, 0)
putdirslot(f, partstart, a, d)
return (part, partstart, reserved_slots)
def create_image_with_stuff(imgfile):
bootimage = "../lib/coreloader.prog"
dir_slots = 256
extent_size = 8192
slots_per_extent = extent_size // 64
reserved_slots = dir_slots // slots_per_extent
f = open(imgfile,"w+b")
b = createpart("PHYS", PartPhysical, 0, 16384, 4096, 0, 0)
#print(b)
f.write(b)
with open(bootimage, "rb") as bf:
bootdata = bf.read()
bootBlocks = len(bootdata) // 512 + 1
b = createpart("BOOT", PartBoot, 16, 112, 0, 0, bootBlocks)
f.write(b)
b = createpart("Testvolume 1", PartEnabled, 128, 3968, 8192, 248)
f.write(b)
b = createpart("SYSTEM", PartEnabled, 4096, 4096, 8192, 256)
f.write(b)
b = createpart("Examples", PartEnabled, 8192, 4096, 8192, 256)
f.write(b)
b = createpart("Rogue", PartEnabled + PartLast, 12288, 4096, 8192, 256)
f.write(b)
part = getpartslot(f, 2)
partstart = part.startBlock
dir_slots = part.dirSize
print("creating",reserved_slots, "reserved directory slots")
for a in range(0,reserved_slots):
d = createdirslot("DIR", SlotReserved, 0, 0, 0, 0, 0)
putdirslot(f, partstart, a, d)
print("creating", dir_slots - reserved_slots, "free slots")
for a in range(reserved_slots, dir_slots):
d = createdirslot("", SlotFree, 0, 0, 0, 0, 0)
putdirslot(f, partstart, a, d)
#d = createdirslot("obstacle", SlotFirst , 0, 0, 0, 0, 0)
#putdirslot(f, partstart, reserved_slots + 2, d)
slotnr = reserved_slots
if True:
data = bytes("ABCDEFGHIJKLMNOPQRST", "ASCII") * 410 + bytes('1234','ASCII')
d = createdirslot("A Testfile.text", SlotFirst, 0, 0, 0, 0, 0)
putdirslot(f, partstart, slotnr, d)
writefile(f, part, slotnr, data)
slotnr += len(data) // extent_size + 1
d = createdirslot("", SlotFree, 0, 0, 0, 0, 0)
putdirslot(f, partstart, slotnr, d)
slotnr += 1
d = createdirslot("Another_file.text", SlotFirst, 20, 0, 0, 0, 0)
putdirslot(f, partstart, slotnr, d)
slotnr += 1
for a in range(0,20):
d = createdirslot("", SlotFree, 0, 0, 0, 0, 0)
putdirslot(f, partstart, slotnr, d)
slotnr += 1
if True:
d = createdirslot("test3.text", SlotFirst, 20, 0, 0, 0, 0)
putdirslot(f, partstart, slotnr, d)
slotnr += 1
d = createdirslot("test1.text", SlotFirst, 20, 0, 0, 0, 0)
putdirslot(f, partstart, slotnr, d)
slotnr += 1
d = createdirslot("test2.text", SlotFirst, 20, 0, 0, 0, 0)
putdirslot(f, partstart, slotnr, d)
slotnr += 1
slotnr = putfile("../examples/test.txt", "sometext.text" , f, part, partstart, slotnr)
slotnr = putfile("../examples/chase.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/chase.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/sine.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/graph2.pas", None , f, part, partstart, slotnr)
while slotnr < dir_slots:
d = createdirslot("", SlotFree + SlotEndScan , 0, 0, 0, 0, 0)
putdirslot(f, partstart, slotnr, d)
slotnr += 1
# second partition (SYSTEM)
part = getpartslot(f, 3)
partstart = part.startBlock
dir_slots = part.dirSize
print()
print("Partition {} at {}".format(part.name, part.startBlock))
print("creating",reserved_slots, "reserved directory slots")
for a in range(0,reserved_slots):
d = createdirslot("DIR", SlotReserved, 0, 0, 0, 0, 0)
putdirslot(f, partstart, a, d)
print("creating", dir_slots - reserved_slots, "free slots")
for a in range(reserved_slots, dir_slots):
d = createdirslot("", SlotFree + SlotEndScan, 0, 0, 0, 0, 0)
putdirslot(f, partstart, a, d)
slotnr = reserved_slots
slotnr = putfile("../progs/shell.prog", "shell.prog", f, part, partstart, slotnr)
slotnr = putfile("../lib/coreloader.lsym", "coreloader.lsym", f, part, partstart, slotnr)
slotnr = putfile("../lib/coreloader.prog", "coreloader.prog", f, part, partstart, slotnr)
slotnr = putfile("../lib/stdlib.lib", None, f, part, partstart, slotnr)
slotnr = putfile("../lib/stdlib.inc", None, f, part, partstart, slotnr)
slotnr = putfile("../lib/stdlib.lsym", None, f, part, partstart, slotnr)
slotnr = putfile("../pcomp/sasm.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../pcomp/pcomp.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../pcomp/lsymgen.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../progs/reclaim.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../progs/dumpdir.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../progs/partmgr.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../progs/editor.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../progs/editor.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../progs/xfer.prog", None , f, part, partstart, slotnr)
listdir(f, part)
# third partition
part = getpartslot(f, 4)
partstart = part.startBlock
dir_slots = part.dirSize
print()
print("Partition {} at {}".format(part.name, part.startBlock))
print("creating",reserved_slots, "reserved directory slots")
for a in range(0,reserved_slots):
d = createdirslot("DIR", SlotReserved, 0, 0, 0, 0, 0)
putdirslot(f, partstart, a, d)
slotnr = reserved_slots
print("creating", dir_slots - reserved_slots, "free slots")
for a in range(reserved_slots, dir_slots):
d = createdirslot("", SlotFree + SlotEndScan, 0, 0, 0, 0, 0)
putdirslot(f, partstart, a, d)
slotnr = putfile("../examples/helloasm.s", None, f, part, partstart, slotnr)
# slotnr = putfile("helloasm.prog", "helloasm.prog", f, part, partstart, slotnr)
# slotnr = putfile("hello.prog", None , f, part, partstart, slotnr)
# slotnr = putfile("hellop.s", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/hellop.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../tests/timetest.pas", None , f, part, partstart, slotnr)
# slotnr = putfile("../tests/timetest.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../tests/readtest.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../tests/readtest.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../tests/readchartest.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../tests/readchartest.prog", None , f, part, partstart, slotnr)
# slotnr = putfile("cchangetest.pas", None , f, part, partstart, slotnr)
# slotnr = putfile("cchangetest.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../tests/test109.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../tests/test133.pas", None , f, part, partstart, slotnr)
# slotnr = putfile("../tests/test133.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../tests/test159.pas", None , f, part, partstart, slotnr)
# slotnr = putfile("../tests/test159.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../tests/umlaut.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/rtpair.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/5cubes.pas", None , f, part, partstart, slotnr)
# slotnr = putfile("../examples/5cubes.prog", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/3dcube.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/conway.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/mandelbrot.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/lines.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/pictviewer.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/ara.pict", "ara.pict" , f, part, partstart, slotnr)
slotnr = putfile("../examples/shinkansen.pict", "shinkansen.pict" , f, part, partstart, slotnr)
slotnr = putfile("../examples/snow_leopard.pict", "snow_leopard.pict" , f, part, partstart, slotnr)
slotnr = putfile("../examples/benchmarks.pas", None , f, part, partstart, slotnr)
listdir(f, part)
part, partstart, slotnr = initfs(f, 5)
slotnr = putfile("../rogue/rogue.init", None, f, part, partstart, slotnr)
slotnr = putfile("../rogue/rogue.message", None, f, part, partstart, slotnr)
slotnr = putfile("../rogue/rogue.prog", None, f, part, partstart, slotnr)
listdir(f, part)
write_bootimage(f, 16, bootdata)
f.close()
if __name__ == "__main__":
if len(sys.argv) > 1:
if sys.argv[1] == "get":
f = open(sys.argv[2], "rb")
readfromimg(f, sys.argv[3], sys.argv[4])
elif sys.argv[1] == "recover":
f = open(sys.argv[2], "rb")
recoverfromimg(f, sys.argv[3], int(sys.argv[4]), sys.argv[5])
elif sys.argv[1] == "put":
imgfile = open(sys.argv[2], "r+b")
infilepath = sys.argv[3]
destfilename = sys.argv[4]
writetoimg(imgfile, destfilename, infilepath)
elif sys.argv[1] == "createimg":
create_image_with_stuff(sys.argv[2])
sys.exit(0)